1 /*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/ganesh/gl/builders/GrGLProgramBuilder.h"
9
10 #include "include/gpu/GrDirectContext.h"
11 #include "src/base/SkAutoMalloc.h"
12 #include "src/core/SkReadBuffer.h"
13 #include "src/core/SkTraceEvent.h"
14 #include "src/core/SkWriteBuffer.h"
15 #include "src/gpu/PipelineUtils.h"
16 #include "src/gpu/Swizzle.h"
17 #include "src/gpu/ganesh/GrAutoLocaleSetter.h"
18 #include "src/gpu/ganesh/GrDirectContextPriv.h"
19 #include "src/gpu/ganesh/GrFragmentProcessor.h"
20 #include "src/gpu/ganesh/GrGeometryProcessor.h"
21 #include "src/gpu/ganesh/GrPersistentCacheUtils.h"
22 #include "src/gpu/ganesh/GrProgramDesc.h"
23 #include "src/gpu/ganesh/GrShaderCaps.h"
24 #include "src/gpu/ganesh/GrXferProcessor.h"
25 #include "src/gpu/ganesh/gl/GrGLGpu.h"
26 #include "src/gpu/ganesh/gl/GrGLProgram.h"
27 #include "src/gpu/ganesh/gl/builders/GrGLProgramBuilder.h"
28 #include "src/sksl/SkSLProgramKind.h"
29 #include "src/sksl/SkSLProgramSettings.h"
30 #include "src/utils/SkShaderUtils.h"
31
32 #include <memory>
33 #include "src/gpu/ganesh/gl/builders/GrGLShaderStringBuilder.h"
34 #include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
35
36 #define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X)
37 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X)
38
cleanup_shaders(GrGLGpu * gpu,const SkTDArray<GrGLuint> & shaderIDs)39 static void cleanup_shaders(GrGLGpu* gpu, const SkTDArray<GrGLuint>& shaderIDs) {
40 for (int i = 0; i < shaderIDs.size(); ++i) {
41 GR_GL_CALL(gpu->glInterface(), DeleteShader(shaderIDs[i]));
42 }
43 }
44
cleanup_program(GrGLGpu * gpu,GrGLuint programID,const SkTDArray<GrGLuint> & shaderIDs)45 static void cleanup_program(GrGLGpu* gpu, GrGLuint programID,
46 const SkTDArray<GrGLuint>& shaderIDs) {
47 GR_GL_CALL(gpu->glInterface(), DeleteProgram(programID));
48 cleanup_shaders(gpu, shaderIDs);
49 }
50
CreateProgram(GrDirectContext * dContext,const GrProgramDesc & desc,const GrProgramInfo & programInfo,const GrGLPrecompiledProgram * precompiledProgram)51 sk_sp<GrGLProgram> GrGLProgramBuilder::CreateProgram(
52 GrDirectContext* dContext,
53 const GrProgramDesc& desc,
54 const GrProgramInfo& programInfo,
55 const GrGLPrecompiledProgram* precompiledProgram) {
56 TRACE_EVENT0_ALWAYS("skia.shaders", "shader_compile");
57 GrAutoLocaleSetter als("C");
58
59 GrGLGpu* glGpu = static_cast<GrGLGpu*>(dContext->priv().getGpu());
60
61 // create a builder. This will be handed off to effects so they can use it to add
62 // uniforms, varyings, textures, etc
63 GrGLProgramBuilder builder(glGpu, desc, programInfo);
64
65 auto persistentCache = dContext->priv().getPersistentCache();
66 if (persistentCache && !precompiledProgram) {
67 sk_sp<SkData> key = SkData::MakeWithoutCopy(desc.asKey(), desc.keyLength());
68 builder.fCached = persistentCache->load(*key);
69 // the eventual end goal is to completely skip emitAndInstallProcs on a cache hit, but it's
70 // doing necessary setup in addition to generating the SkSL code. Currently we are only able
71 // to skip the SkSL->GLSL step on a cache hit.
72 }
73 if (!builder.emitAndInstallProcs()) {
74 return nullptr;
75 }
76 return builder.finalize(precompiledProgram);
77 }
78
79 /////////////////////////////////////////////////////////////////////////////
80
GrGLProgramBuilder(GrGLGpu * gpu,const GrProgramDesc & desc,const GrProgramInfo & programInfo)81 GrGLProgramBuilder::GrGLProgramBuilder(GrGLGpu* gpu,
82 const GrProgramDesc& desc,
83 const GrProgramInfo& programInfo)
84 : INHERITED(desc, programInfo)
85 , fGpu(gpu)
86 , fVaryingHandler(this)
87 , fUniformHandler(this)
88 , fVertexAttributeCnt(0)
89 , fInstanceAttributeCnt(0)
90 , fVertexStride(0)
91 , fInstanceStride(0) {}
92
caps() const93 const GrCaps* GrGLProgramBuilder::caps() const {
94 return fGpu->caps();
95 }
96
compileAndAttachShaders(const std::string & glsl,GrGLuint programId,GrGLenum type,SkTDArray<GrGLuint> * shaderIds,bool shaderWasCached,GrContextOptions::ShaderErrorHandler * errHandler)97 bool GrGLProgramBuilder::compileAndAttachShaders(const std::string& glsl,
98 GrGLuint programId,
99 GrGLenum type,
100 SkTDArray<GrGLuint>* shaderIds,
101 bool shaderWasCached,
102 GrContextOptions::ShaderErrorHandler* errHandler) {
103 GrGLGpu* gpu = this->gpu();
104 GrGLuint shaderId = GrGLCompileAndAttachShader(gpu->glContext(),
105 programId,
106 type,
107 glsl,
108 shaderWasCached,
109 gpu->pipelineBuilder()->stats(),
110 errHandler);
111 if (!shaderId) {
112 return false;
113 }
114
115 *shaderIds->append() = shaderId;
116 return true;
117 }
118
computeCountsAndStrides(GrGLuint programID,const GrGeometryProcessor & geomProc,bool bindAttribLocations)119 void GrGLProgramBuilder::computeCountsAndStrides(GrGLuint programID,
120 const GrGeometryProcessor& geomProc,
121 bool bindAttribLocations) {
122 fVertexAttributeCnt = geomProc.numVertexAttributes();
123 fInstanceAttributeCnt = geomProc.numInstanceAttributes();
124 fAttributes = std::make_unique<GrGLProgram::Attribute[]>(
125 fVertexAttributeCnt + fInstanceAttributeCnt);
126 auto addAttr = [&](int i, const auto& a) {
127 fAttributes[i].fCPUType = a.cpuType();
128 fAttributes[i].fGPUType = a.gpuType();
129 fAttributes[i].fOffset = *a.offset();
130 fAttributes[i].fLocation = i;
131 if (bindAttribLocations) {
132 GL_CALL(BindAttribLocation(programID, i, a.name()));
133 }
134 };
135 fVertexStride = geomProc.vertexStride();
136 int i = 0;
137 for (auto attr : geomProc.vertexAttributes()) {
138 addAttr(i++, attr);
139 }
140 fInstanceStride = geomProc.instanceStride();
141 for (auto attr : geomProc.instanceAttributes()) {
142 addAttr(i++, attr);
143 }
144 SkASSERT(fInstanceStride == geomProc.instanceStride());
145 }
146
addInputVars(const SkSL::Program::Interface & interface)147 void GrGLProgramBuilder::addInputVars(const SkSL::Program::Interface& interface) {
148 uint8_t useRTFlip = interface.fRTFlipUniform;
149 if (!this->gpu()->glCaps().shaderCaps()->fCanUseFragCoord) {
150 useRTFlip &= ~SkSL::Program::Interface::kRTFlip_FragCoord;
151 }
152
153 if (useRTFlip != SkSL::Program::Interface::kRTFlip_None) {
154 this->addRTFlipUniform(SKSL_RTFLIP_NAME);
155 }
156 }
157
158 static constexpr SkFourByteTag kSKSL_Tag = SkSetFourByteTag('S', 'K', 'S', 'L');
159 static constexpr SkFourByteTag kGLSL_Tag = SkSetFourByteTag('G', 'L', 'S', 'L');
160 static constexpr SkFourByteTag kGLPB_Tag = SkSetFourByteTag('G', 'L', 'P', 'B');
161
storeShaderInCache(const SkSL::Program::Interface & interface,GrGLuint programID,const std::string shaders[],bool isSkSL,SkSL::ProgramSettings * settings)162 void GrGLProgramBuilder::storeShaderInCache(const SkSL::Program::Interface& interface,
163 GrGLuint programID,
164 const std::string shaders[],
165 bool isSkSL,
166 SkSL::ProgramSettings* settings) {
167 if (!this->gpu()->getContext()->priv().getPersistentCache()) {
168 return;
169 }
170 sk_sp<SkData> key = SkData::MakeWithoutCopy(this->desc().asKey(), this->desc().keyLength());
171 SkString description = GrProgramDesc::Describe(fProgramInfo, *fGpu->caps());
172 if (fGpu->glCaps().programBinarySupport()) {
173 // binary cache
174 GrGLsizei length = 0;
175 GL_CALL(GetProgramiv(programID, GL_PROGRAM_BINARY_LENGTH, &length));
176 if (length > 0) {
177 SkBinaryWriteBuffer writer({});
178 writer.writeInt(GrPersistentCacheUtils::GetCurrentVersion());
179 writer.writeUInt(kGLPB_Tag);
180
181 writer.writePad32(&interface, sizeof(interface));
182
183 SkAutoSMalloc<2048> binary(length);
184 GrGLenum binaryFormat;
185 GL_CALL(GetProgramBinary(programID, length, &length, &binaryFormat, binary.get()));
186
187 writer.writeUInt(binaryFormat);
188 writer.writeInt(length);
189 writer.writePad32(binary.get(), length);
190
191 auto data = writer.snapshotAsData();
192 this->gpu()->getContext()->priv().getPersistentCache()->store(*key, *data, description);
193 }
194 } else {
195 // source cache, plus metadata to allow for a complete precompile
196 GrPersistentCacheUtils::ShaderMetadata meta;
197 meta.fSettings = settings;
198 meta.fHasSecondaryColorOutput = fFS.hasSecondaryOutput();
199 for (auto attr : this->geometryProcessor().vertexAttributes()) {
200 meta.fAttributeNames.emplace_back(attr.name());
201 }
202 for (auto attr : this->geometryProcessor().instanceAttributes()) {
203 meta.fAttributeNames.emplace_back(attr.name());
204 }
205
206 auto data = GrPersistentCacheUtils::PackCachedShaders(isSkSL ? kSKSL_Tag : kGLSL_Tag,
207 shaders, &interface, 1, &meta);
208 this->gpu()->getContext()->priv().getPersistentCache()->store(*key, *data, description);
209 }
210 }
211
finalize(const GrGLPrecompiledProgram * precompiledProgram)212 sk_sp<GrGLProgram> GrGLProgramBuilder::finalize(const GrGLPrecompiledProgram* precompiledProgram) {
213 TRACE_EVENT0("skia.shaders", TRACE_FUNC);
214
215 // verify we can get a program id
216 GrGLuint programID;
217 if (precompiledProgram) {
218 programID = precompiledProgram->fProgramID;
219 } else {
220 GL_CALL_RET(programID, CreateProgram());
221 }
222 if (0 == programID) {
223 return nullptr;
224 }
225
226 if (this->gpu()->glCaps().programBinarySupport() &&
227 this->gpu()->glCaps().programParameterSupport() &&
228 this->gpu()->getContext()->priv().getPersistentCache() &&
229 !precompiledProgram) {
230 GL_CALL(ProgramParameteri(programID, GR_GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GR_GL_TRUE));
231 }
232
233 this->finalizeShaders();
234
235 // compile shaders and bind attributes / uniforms
236 auto errorHandler = this->gpu()->getContext()->priv().getShaderErrorHandler();
237 const GrGeometryProcessor& geomProc = this->geometryProcessor();
238 SkSL::ProgramSettings settings;
239 settings.fSharpenTextures = true;
240 settings.fFragColorIsInOut = this->fragColorIsInOut();
241
242 SkSL::Program::Interface interface;
243 SkTDArray<GrGLuint> shadersToDelete;
244
245 bool cached = fCached.get() != nullptr;
246 bool usedProgramBinaries = false;
247 std::string glsl[kGrShaderTypeCount];
248 const std::string* sksl[kGrShaderTypeCount] = {
249 &fVS.fCompilerString,
250 &fFS.fCompilerString,
251 };
252 std::string cached_sksl[kGrShaderTypeCount];
253 if (precompiledProgram) {
254 // This is very similar to when we get program binaries. We even set that flag, as it's
255 // used to prevent other compile work later, and to force re-querying uniform locations.
256 this->addInputVars(precompiledProgram->fInterface);
257 this->computeCountsAndStrides(programID, geomProc, false);
258 usedProgramBinaries = true;
259 } else if (cached) {
260 TRACE_EVENT0_ALWAYS("skia.shaders", "cache_hit");
261 SkReadBuffer reader(fCached->data(), fCached->size());
262 SkFourByteTag shaderType = GrPersistentCacheUtils::GetType(&reader);
263
264 switch (shaderType) {
265 case kGLPB_Tag: {
266 // Program binary cache hit. We may opt not to use this if we don't trust program
267 // binaries on this driver
268 if (!fGpu->glCaps().programBinarySupport()) {
269 cached = false;
270 break;
271 }
272 reader.readPad32(&interface, sizeof(interface));
273 GrGLenum binaryFormat = reader.readUInt();
274 GrGLsizei length = reader.readInt();
275 const void* binary = reader.skip(length);
276 if (!reader.isValid()) {
277 break;
278 }
279 if (length <= 0 || !fGpu->glCaps().programBinaryFormatIsValid(binaryFormat)) {
280 cached = false;
281 break;
282 }
283 GL_CALL(ProgramBinary(programID, binaryFormat, const_cast<void*>(binary), length));
284 // Pass nullptr for the error handler. We don't want to treat this as a compile
285 // failure (we can still recover by compiling the program from source, below).
286 // Clients won't be directly notified, but they can infer this from the trace
287 // events, and from the traffic to the persistent cache.
288 cached = GrGLCheckLinkStatus(fGpu, programID, /*shaderWasCached=*/true,
289 /*errorHandler=*/nullptr, nullptr, nullptr);
290 if (cached) {
291 this->addInputVars(interface);
292 this->computeCountsAndStrides(programID, geomProc, false);
293 }
294 usedProgramBinaries = cached;
295 break;
296 }
297
298 case kGLSL_Tag:
299 // Source cache hit, we don't need to compile the SkSL->GLSL
300 GrPersistentCacheUtils::UnpackCachedShaders(&reader, glsl, &interface, 1);
301 break;
302
303 case kSKSL_Tag:
304 // SkSL cache hit, this should only happen in tools overriding the generated SkSL
305 if (GrPersistentCacheUtils::UnpackCachedShaders(
306 &reader, cached_sksl, &interface, 1)) {
307 for (int i = 0; i < kGrShaderTypeCount; ++i) {
308 sksl[i] = &cached_sksl[i];
309 }
310 }
311 break;
312
313 default:
314 // We got something invalid, so pretend it wasn't there
315 reader.validate(false);
316 break;
317 }
318 if (!reader.isValid()) {
319 cached = false;
320 }
321 }
322 if (!usedProgramBinaries) {
323 TRACE_EVENT0_ALWAYS("skia.shaders", "cache_miss");
324 // Either a cache miss, or we got something other than binaries from the cache
325
326 /*
327 Fragment Shader
328 */
329 if (glsl[kFragment_GrShaderType].empty()) {
330 // Don't have cached GLSL, need to compile SkSL->GLSL
331 if (fFS.fForceHighPrecision) {
332 settings.fForceHighPrecision = true;
333 }
334 if (!skgpu::SkSLToGLSL(this->gpu()->caps()->shaderCaps(),
335 *sksl[kFragment_GrShaderType],
336 SkSL::ProgramKind::kFragment,
337 settings,
338 &glsl[kFragment_GrShaderType],
339 &interface,
340 errorHandler)) {
341 cleanup_program(fGpu, programID, shadersToDelete);
342 return nullptr;
343 }
344 }
345
346 this->addInputVars(interface);
347 if (!this->compileAndAttachShaders(glsl[kFragment_GrShaderType],
348 programID,
349 GR_GL_FRAGMENT_SHADER,
350 &shadersToDelete,
351 cached,
352 errorHandler)) {
353 cleanup_program(fGpu, programID, shadersToDelete);
354 return nullptr;
355 }
356
357 /*
358 Vertex Shader
359 */
360 if (glsl[kVertex_GrShaderType].empty()) {
361 // Don't have cached GLSL, need to compile SkSL->GLSL
362 SkSL::Program::Interface unusedInterface;
363 if (!skgpu::SkSLToGLSL(this->gpu()->caps()->shaderCaps(),
364 *sksl[kVertex_GrShaderType],
365 SkSL::ProgramKind::kVertex,
366 settings,
367 &glsl[kVertex_GrShaderType],
368 &unusedInterface,
369 errorHandler)) {
370 cleanup_program(fGpu, programID, shadersToDelete);
371 return nullptr;
372 }
373 }
374 if (!this->compileAndAttachShaders(glsl[kVertex_GrShaderType],
375 programID,
376 GR_GL_VERTEX_SHADER,
377 &shadersToDelete,
378 cached,
379 errorHandler)) {
380 cleanup_program(fGpu, programID, shadersToDelete);
381 return nullptr;
382 }
383
384 // This also binds vertex attribute locations.
385 this->computeCountsAndStrides(programID, geomProc, true);
386
387 this->bindProgramResourceLocations(programID);
388
389 {
390 TRACE_EVENT0_ALWAYS("skia.shaders", "driver_link_program");
391 GL_CALL(LinkProgram(programID));
392 if (!GrGLCheckLinkStatus(fGpu, programID, cached, errorHandler, sksl, glsl)) {
393 cleanup_program(fGpu, programID, shadersToDelete);
394 return nullptr;
395 }
396 }
397 }
398 this->resolveProgramResourceLocations(programID, usedProgramBinaries);
399
400 cleanup_shaders(fGpu, shadersToDelete);
401
402 // We can't cache SkSL or GLSL if we were given a precompiled program, but there's not
403 // much point in doing so.
404 if (!cached && !precompiledProgram) {
405 bool isSkSL = false;
406 if (fGpu->getContext()->priv().options().fShaderCacheStrategy ==
407 GrContextOptions::ShaderCacheStrategy::kSkSL) {
408 for (int i = 0; i < kGrShaderTypeCount; ++i) {
409 glsl[i] = SkShaderUtils::PrettyPrint(*sksl[i]);
410 }
411 isSkSL = true;
412 }
413 this->storeShaderInCache(interface, programID, glsl, isSkSL, &settings);
414 }
415 return this->createProgram(programID);
416 }
417
bindProgramResourceLocations(GrGLuint programID)418 void GrGLProgramBuilder::bindProgramResourceLocations(GrGLuint programID) {
419 fUniformHandler.bindUniformLocations(programID, fGpu->glCaps());
420
421 const GrGLCaps& caps = this->gpu()->glCaps();
422 if (caps.bindFragDataLocationSupport()) {
423 SkASSERT(caps.shaderCaps()->mustDeclareFragmentShaderOutput());
424 GL_CALL(BindFragDataLocation(programID, 0,
425 GrGLSLFragmentShaderBuilder::DeclaredColorOutputName()));
426 if (fFS.hasSecondaryOutput()) {
427 GL_CALL(BindFragDataLocationIndexed(programID, 0, 1,
428 GrGLSLFragmentShaderBuilder::DeclaredSecondaryColorOutputName()));
429 }
430 }
431 }
432
resolveProgramResourceLocations(GrGLuint programID,bool force)433 void GrGLProgramBuilder::resolveProgramResourceLocations(GrGLuint programID, bool force) {
434 fUniformHandler.getUniformLocations(programID, fGpu->glCaps(), force);
435 }
436
createProgram(GrGLuint programID)437 sk_sp<GrGLProgram> GrGLProgramBuilder::createProgram(GrGLuint programID) {
438 return GrGLProgram::Make(fGpu,
439 fUniformHandles,
440 programID,
441 fUniformHandler.fUniforms,
442 fUniformHandler.fSamplers,
443 std::move(fGPImpl),
444 std::move(fXPImpl),
445 std::move(fFPImpls),
446 std::move(fAttributes),
447 fVertexAttributeCnt,
448 fInstanceAttributeCnt,
449 fVertexStride,
450 fInstanceStride);
451 }
452
PrecompileProgram(GrDirectContext * dContext,GrGLPrecompiledProgram * precompiledProgram,const SkData & cachedData)453 bool GrGLProgramBuilder::PrecompileProgram(GrDirectContext* dContext,
454 GrGLPrecompiledProgram* precompiledProgram,
455 const SkData& cachedData) {
456 SkReadBuffer reader(cachedData.data(), cachedData.size());
457 SkFourByteTag shaderType = GrPersistentCacheUtils::GetType(&reader);
458 if (shaderType != kSKSL_Tag) {
459 // TODO: Support GLSL, and maybe even program binaries, too?
460 return false;
461 }
462
463 GrGLGpu* glGpu = static_cast<GrGLGpu*>(dContext->priv().getGpu());
464
465 const GrGLInterface* gl = glGpu->glInterface();
466 auto errorHandler = dContext->priv().getShaderErrorHandler();
467
468 SkSL::ProgramSettings settings;
469 settings.fSharpenTextures = true;
470 GrPersistentCacheUtils::ShaderMetadata meta;
471 meta.fSettings = &settings;
472
473 std::string shaders[kGrShaderTypeCount];
474 SkSL::Program::Interface interface;
475 if (!GrPersistentCacheUtils::UnpackCachedShaders(&reader, shaders, &interface, 1, &meta)) {
476 return false;
477 }
478
479 GrGLuint programID;
480 GR_GL_CALL_RET(gl, programID, CreateProgram());
481 if (0 == programID) {
482 return false;
483 }
484
485 SkTDArray<GrGLuint> shadersToDelete;
486
487 auto compileShader = [&](SkSL::ProgramKind kind, const std::string& sksl, GrGLenum type) {
488 std::string glsl;
489 SkSL::Program::Interface unusedInterface;
490 if (!skgpu::SkSLToGLSL(glGpu->caps()->shaderCaps(),
491 sksl,
492 kind,
493 settings,
494 &glsl,
495 &unusedInterface,
496 errorHandler)) {
497 return false;
498 }
499
500 if (GrGLuint shaderID = GrGLCompileAndAttachShader(glGpu->glContext(),
501 programID,
502 type,
503 glsl,
504 /*shaderWasCached=*/false,
505 glGpu->pipelineBuilder()->stats(),
506 errorHandler)) {
507 shadersToDelete.push_back(shaderID);
508 return true;
509 } else {
510 return false;
511 }
512 };
513
514 if (!compileShader(SkSL::ProgramKind::kFragment,
515 shaders[kFragment_GrShaderType],
516 GR_GL_FRAGMENT_SHADER) ||
517 !compileShader(SkSL::ProgramKind::kVertex,
518 shaders[kVertex_GrShaderType],
519 GR_GL_VERTEX_SHADER)) {
520 cleanup_program(glGpu, programID, shadersToDelete);
521 return false;
522 }
523
524 for (int i = 0; i < meta.fAttributeNames.size(); ++i) {
525 GR_GL_CALL(glGpu->glInterface(), BindAttribLocation(programID, i,
526 meta.fAttributeNames[i].c_str()));
527 }
528
529 const GrGLCaps& caps = glGpu->glCaps();
530 if (caps.bindFragDataLocationSupport()) {
531 SkASSERT(caps.shaderCaps()->mustDeclareFragmentShaderOutput());
532 GR_GL_CALL(glGpu->glInterface(),
533 BindFragDataLocation(programID, 0,
534 GrGLSLFragmentShaderBuilder::DeclaredColorOutputName()));
535
536 if (meta.fHasSecondaryColorOutput) {
537 GR_GL_CALL(glGpu->glInterface(),
538 BindFragDataLocationIndexed(programID, 0, 1,
539 GrGLSLFragmentShaderBuilder::DeclaredSecondaryColorOutputName()));
540 }
541 }
542
543 GR_GL_CALL(glGpu->glInterface(), LinkProgram(programID));
544 GrGLint linked = GR_GL_INIT_ZERO;
545 GR_GL_CALL(glGpu->glInterface(), GetProgramiv(programID, GR_GL_LINK_STATUS, &linked));
546 if (!linked) {
547 cleanup_program(glGpu, programID, shadersToDelete);
548 return false;
549 }
550
551 cleanup_shaders(glGpu, shadersToDelete);
552
553 precompiledProgram->fProgramID = programID;
554 precompiledProgram->fInterface = interface;
555 return true;
556 }
557