1 //
2 // Copyright 2020 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // ProgramExecutable.cpp: Collects the interfaces common to both Programs and
7 // ProgramPipelines in order to execute/draw with either.
8
9 #include "libANGLE/ProgramExecutable.h"
10
11 #include "libANGLE/Context.h"
12 #include "libANGLE/Program.h"
13 #include "libANGLE/Shader.h"
14
15 namespace gl
16 {
17 namespace
18 {
IncludeSameArrayElement(const std::set<std::string> & nameSet,const std::string & name)19 bool IncludeSameArrayElement(const std::set<std::string> &nameSet, const std::string &name)
20 {
21 std::vector<unsigned int> subscripts;
22 std::string baseName = ParseResourceName(name, &subscripts);
23 for (const std::string &nameInSet : nameSet)
24 {
25 std::vector<unsigned int> arrayIndices;
26 std::string arrayName = ParseResourceName(nameInSet, &arrayIndices);
27 if (baseName == arrayName &&
28 (subscripts.empty() || arrayIndices.empty() || subscripts == arrayIndices))
29 {
30 return true;
31 }
32 }
33 return false;
34 }
35
36 // Find the matching varying or field by name.
FindOutputVaryingOrField(const ProgramMergedVaryings & varyings,ShaderType stage,const std::string & name)37 const sh::ShaderVariable *FindOutputVaryingOrField(const ProgramMergedVaryings &varyings,
38 ShaderType stage,
39 const std::string &name)
40 {
41 const sh::ShaderVariable *var = nullptr;
42 for (const ProgramVaryingRef &ref : varyings)
43 {
44 if (ref.frontShaderStage != stage)
45 {
46 continue;
47 }
48
49 const sh::ShaderVariable *varying = ref.get(stage);
50 if (varying->name == name)
51 {
52 var = varying;
53 break;
54 }
55 GLuint fieldIndex = 0;
56 var = varying->findField(name, &fieldIndex);
57 if (var != nullptr)
58 {
59 break;
60 }
61 }
62 return var;
63 }
64 } // anonymous namespace
65
ProgramExecutable()66 ProgramExecutable::ProgramExecutable()
67 : mMaxActiveAttribLocation(0),
68 mAttributesTypeMask(0),
69 mAttributesMask(0),
70 mActiveSamplerRefCounts{},
71 mCanDrawWith(false),
72 mYUVOutput(false),
73 mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS),
74 mDefaultUniformRange(0, 0),
75 mSamplerUniformRange(0, 0),
76 mImageUniformRange(0, 0),
77 mFragmentInoutRange(0, 0),
78 mPipelineHasGraphicsUniformBuffers(false),
79 mPipelineHasComputeUniformBuffers(false),
80 mPipelineHasGraphicsStorageBuffers(false),
81 mPipelineHasComputeStorageBuffers(false),
82 mPipelineHasGraphicsAtomicCounterBuffers(false),
83 mPipelineHasComputeAtomicCounterBuffers(false),
84 mPipelineHasGraphicsDefaultUniforms(false),
85 mPipelineHasComputeDefaultUniforms(false),
86 mPipelineHasGraphicsTextures(false),
87 mPipelineHasComputeTextures(false),
88 mPipelineHasGraphicsImages(false),
89 mPipelineHasComputeImages(false),
90 mIsCompute(false),
91 // [GL_EXT_geometry_shader] Table 20.22
92 mGeometryShaderInputPrimitiveType(PrimitiveMode::Triangles),
93 mGeometryShaderOutputPrimitiveType(PrimitiveMode::TriangleStrip),
94 mGeometryShaderInvocations(1),
95 mGeometryShaderMaxVertices(0),
96 mTessControlShaderVertices(0),
97 mTessGenMode(GL_NONE),
98 mTessGenSpacing(GL_NONE),
99 mTessGenVertexOrder(GL_NONE),
100 mTessGenPointMode(GL_NONE)
101 {
102 reset();
103 }
104
ProgramExecutable(const ProgramExecutable & other)105 ProgramExecutable::ProgramExecutable(const ProgramExecutable &other)
106 : mLinkedGraphicsShaderStages(other.mLinkedGraphicsShaderStages),
107 mLinkedComputeShaderStages(other.mLinkedComputeShaderStages),
108 mActiveAttribLocationsMask(other.mActiveAttribLocationsMask),
109 mMaxActiveAttribLocation(other.mMaxActiveAttribLocation),
110 mAttributesTypeMask(other.mAttributesTypeMask),
111 mAttributesMask(other.mAttributesMask),
112 mActiveSamplersMask(other.mActiveSamplersMask),
113 mActiveSamplerRefCounts(other.mActiveSamplerRefCounts),
114 mActiveSamplerTypes(other.mActiveSamplerTypes),
115 mActiveSamplerYUV(other.mActiveSamplerYUV),
116 mActiveSamplerFormats(other.mActiveSamplerFormats),
117 mActiveSamplerShaderBits(other.mActiveSamplerShaderBits),
118 mActiveImagesMask(other.mActiveImagesMask),
119 mActiveImageShaderBits(other.mActiveImageShaderBits),
120 mCanDrawWith(other.mCanDrawWith),
121 mOutputVariables(other.mOutputVariables),
122 mOutputLocations(other.mOutputLocations),
123 mSecondaryOutputLocations(other.mSecondaryOutputLocations),
124 mYUVOutput(other.mYUVOutput),
125 mProgramInputs(other.mProgramInputs),
126 mLinkedTransformFeedbackVaryings(other.mLinkedTransformFeedbackVaryings),
127 mTransformFeedbackStrides(other.mTransformFeedbackStrides),
128 mTransformFeedbackBufferMode(other.mTransformFeedbackBufferMode),
129 mUniforms(other.mUniforms),
130 mDefaultUniformRange(other.mDefaultUniformRange),
131 mSamplerUniformRange(other.mSamplerUniformRange),
132 mUniformBlocks(other.mUniformBlocks),
133 mActiveUniformBlockBindings(other.mActiveUniformBlockBindings),
134 mAtomicCounterBuffers(other.mAtomicCounterBuffers),
135 mImageUniformRange(other.mImageUniformRange),
136 mComputeShaderStorageBlocks(other.mComputeShaderStorageBlocks),
137 mGraphicsShaderStorageBlocks(other.mGraphicsShaderStorageBlocks),
138 mFragmentInoutRange(other.mFragmentInoutRange),
139 mPipelineHasGraphicsUniformBuffers(other.mPipelineHasGraphicsUniformBuffers),
140 mPipelineHasComputeUniformBuffers(other.mPipelineHasComputeUniformBuffers),
141 mPipelineHasGraphicsStorageBuffers(other.mPipelineHasGraphicsStorageBuffers),
142 mPipelineHasComputeStorageBuffers(other.mPipelineHasComputeStorageBuffers),
143 mPipelineHasGraphicsAtomicCounterBuffers(other.mPipelineHasGraphicsAtomicCounterBuffers),
144 mPipelineHasComputeAtomicCounterBuffers(other.mPipelineHasComputeAtomicCounterBuffers),
145 mPipelineHasGraphicsDefaultUniforms(other.mPipelineHasGraphicsDefaultUniforms),
146 mPipelineHasComputeDefaultUniforms(other.mPipelineHasComputeDefaultUniforms),
147 mPipelineHasGraphicsTextures(other.mPipelineHasGraphicsTextures),
148 mPipelineHasComputeTextures(other.mPipelineHasComputeTextures),
149 mPipelineHasGraphicsImages(other.mPipelineHasGraphicsImages),
150 mPipelineHasComputeImages(other.mPipelineHasComputeImages),
151 mIsCompute(other.mIsCompute)
152 {
153 reset();
154 }
155
156 ProgramExecutable::~ProgramExecutable() = default;
157
reset()158 void ProgramExecutable::reset()
159 {
160 resetInfoLog();
161 mActiveAttribLocationsMask.reset();
162 mAttributesTypeMask.reset();
163 mAttributesMask.reset();
164 mMaxActiveAttribLocation = 0;
165
166 mActiveSamplersMask.reset();
167 mActiveSamplerRefCounts = {};
168 mActiveSamplerTypes.fill(TextureType::InvalidEnum);
169 mActiveSamplerYUV.reset();
170 mActiveSamplerFormats.fill(SamplerFormat::InvalidEnum);
171
172 mActiveImagesMask.reset();
173
174 mProgramInputs.clear();
175 mLinkedTransformFeedbackVaryings.clear();
176 mTransformFeedbackStrides.clear();
177 mUniforms.clear();
178 mUniformBlocks.clear();
179 mActiveUniformBlockBindings.reset();
180 mComputeShaderStorageBlocks.clear();
181 mGraphicsShaderStorageBlocks.clear();
182 mAtomicCounterBuffers.clear();
183 mOutputVariables.clear();
184 mOutputLocations.clear();
185 mSecondaryOutputLocations.clear();
186 mYUVOutput = false;
187 mSamplerBindings.clear();
188 mComputeImageBindings.clear();
189 mGraphicsImageBindings.clear();
190
191 mPipelineHasGraphicsUniformBuffers = false;
192 mPipelineHasComputeUniformBuffers = false;
193 mPipelineHasGraphicsStorageBuffers = false;
194 mPipelineHasComputeStorageBuffers = false;
195 mPipelineHasGraphicsAtomicCounterBuffers = false;
196 mPipelineHasComputeAtomicCounterBuffers = false;
197 mPipelineHasGraphicsDefaultUniforms = false;
198 mPipelineHasComputeDefaultUniforms = false;
199 mPipelineHasGraphicsTextures = false;
200 mPipelineHasComputeTextures = false;
201
202 mGeometryShaderInputPrimitiveType = PrimitiveMode::Triangles;
203 mGeometryShaderOutputPrimitiveType = PrimitiveMode::TriangleStrip;
204 mGeometryShaderInvocations = 1;
205 mGeometryShaderMaxVertices = 0;
206
207 mTessControlShaderVertices = 0;
208 mTessGenMode = GL_NONE;
209 mTessGenSpacing = GL_NONE;
210 mTessGenVertexOrder = GL_NONE;
211 mTessGenPointMode = GL_NONE;
212 }
213
load(bool isSeparable,gl::BinaryInputStream * stream)214 void ProgramExecutable::load(bool isSeparable, gl::BinaryInputStream *stream)
215 {
216 static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
217 "Too many vertex attribs for mask: All bits of mAttributesTypeMask types and "
218 "mask fit into 32 bits each");
219 mAttributesTypeMask = gl::ComponentTypeMask(stream->readInt<uint32_t>());
220 mAttributesMask = gl::AttributesMask(stream->readInt<uint32_t>());
221 mActiveAttribLocationsMask = gl::AttributesMask(stream->readInt<uint32_t>());
222 mMaxActiveAttribLocation = stream->readInt<unsigned int>();
223
224 unsigned int fragmentInoutRangeLow = stream->readInt<uint32_t>();
225 unsigned int fragmentInoutRangeHigh = stream->readInt<uint32_t>();
226 mFragmentInoutRange = RangeUI(fragmentInoutRangeLow, fragmentInoutRangeHigh);
227
228 mLinkedGraphicsShaderStages = ShaderBitSet(stream->readInt<uint8_t>());
229 mLinkedComputeShaderStages = ShaderBitSet(stream->readInt<uint8_t>());
230 mIsCompute = stream->readBool();
231
232 mPipelineHasGraphicsUniformBuffers = stream->readBool();
233 mPipelineHasComputeUniformBuffers = stream->readBool();
234 mPipelineHasGraphicsStorageBuffers = stream->readBool();
235 mPipelineHasComputeStorageBuffers = stream->readBool();
236 mPipelineHasGraphicsAtomicCounterBuffers = stream->readBool();
237 mPipelineHasComputeAtomicCounterBuffers = stream->readBool();
238 mPipelineHasGraphicsDefaultUniforms = stream->readBool();
239 mPipelineHasComputeDefaultUniforms = stream->readBool();
240 mPipelineHasGraphicsTextures = stream->readBool();
241 mPipelineHasComputeTextures = stream->readBool();
242
243 mGeometryShaderInputPrimitiveType = stream->readEnum<PrimitiveMode>();
244 mGeometryShaderOutputPrimitiveType = stream->readEnum<PrimitiveMode>();
245 mGeometryShaderInvocations = stream->readInt<int>();
246 mGeometryShaderMaxVertices = stream->readInt<int>();
247
248 mTessControlShaderVertices = stream->readInt<int>();
249 mTessGenMode = stream->readInt<GLenum>();
250 mTessGenSpacing = stream->readInt<GLenum>();
251 mTessGenVertexOrder = stream->readInt<GLenum>();
252 mTessGenPointMode = stream->readInt<GLenum>();
253
254 size_t attribCount = stream->readInt<size_t>();
255 ASSERT(getProgramInputs().empty());
256 for (size_t attribIndex = 0; attribIndex < attribCount; ++attribIndex)
257 {
258 sh::ShaderVariable attrib;
259 LoadShaderVar(stream, &attrib);
260 attrib.location = stream->readInt<int>();
261 mProgramInputs.push_back(attrib);
262 }
263
264 size_t uniformCount = stream->readInt<size_t>();
265 ASSERT(getUniforms().empty());
266 for (size_t uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
267 {
268 LinkedUniform uniform;
269 LoadShaderVar(stream, &uniform);
270
271 uniform.bufferIndex = stream->readInt<int>();
272 LoadBlockMemberInfo(stream, &uniform.blockInfo);
273
274 stream->readIntVector<unsigned int>(&uniform.outerArraySizes);
275
276 uniform.typeInfo = &GetUniformTypeInfo(uniform.type);
277
278 // Active shader info
279 for (ShaderType shaderType : gl::AllShaderTypes())
280 {
281 uniform.setActive(shaderType, stream->readBool());
282 }
283
284 mUniforms.push_back(uniform);
285 }
286
287 size_t uniformBlockCount = stream->readInt<size_t>();
288 ASSERT(getUniformBlocks().empty());
289 for (size_t uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount; ++uniformBlockIndex)
290 {
291 InterfaceBlock uniformBlock;
292 LoadInterfaceBlock(stream, &uniformBlock);
293 mUniformBlocks.push_back(uniformBlock);
294
295 mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
296 }
297
298 size_t shaderStorageBlockCount = stream->readInt<size_t>();
299 ASSERT(getShaderStorageBlocks().empty());
300 for (size_t shaderStorageBlockIndex = 0; shaderStorageBlockIndex < shaderStorageBlockCount;
301 ++shaderStorageBlockIndex)
302 {
303 InterfaceBlock shaderStorageBlock;
304 LoadInterfaceBlock(stream, &shaderStorageBlock);
305 if (isCompute())
306 {
307 mComputeShaderStorageBlocks.push_back(shaderStorageBlock);
308 }
309 else
310 {
311 mGraphicsShaderStorageBlocks.push_back(shaderStorageBlock);
312 }
313 }
314
315 size_t atomicCounterBufferCount = stream->readInt<size_t>();
316 ASSERT(getAtomicCounterBuffers().empty());
317 for (size_t bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
318 {
319 AtomicCounterBuffer atomicCounterBuffer;
320 LoadShaderVariableBuffer(stream, &atomicCounterBuffer);
321
322 mAtomicCounterBuffers.push_back(atomicCounterBuffer);
323 }
324
325 size_t transformFeedbackVaryingCount = stream->readInt<size_t>();
326 ASSERT(mLinkedTransformFeedbackVaryings.empty());
327 for (size_t transformFeedbackVaryingIndex = 0;
328 transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
329 ++transformFeedbackVaryingIndex)
330 {
331 sh::ShaderVariable varying;
332 stream->readIntVector<unsigned int>(&varying.arraySizes);
333 stream->readInt(&varying.type);
334 stream->readString(&varying.name);
335
336 GLuint arrayIndex = stream->readInt<GLuint>();
337
338 mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
339 }
340
341 mTransformFeedbackBufferMode = stream->readInt<GLint>();
342
343 size_t outputCount = stream->readInt<size_t>();
344 ASSERT(getOutputVariables().empty());
345 for (size_t outputIndex = 0; outputIndex < outputCount; ++outputIndex)
346 {
347 sh::ShaderVariable output;
348 LoadShaderVar(stream, &output);
349 output.location = stream->readInt<int>();
350 output.index = stream->readInt<int>();
351 mOutputVariables.push_back(output);
352 }
353
354 size_t outputVarCount = stream->readInt<size_t>();
355 ASSERT(getOutputLocations().empty());
356 for (size_t outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
357 {
358 VariableLocation locationData;
359 stream->readInt(&locationData.arrayIndex);
360 stream->readInt(&locationData.index);
361 stream->readBool(&locationData.ignored);
362 mOutputLocations.push_back(locationData);
363 }
364
365 size_t secondaryOutputVarCount = stream->readInt<size_t>();
366 ASSERT(getSecondaryOutputLocations().empty());
367 for (size_t outputIndex = 0; outputIndex < secondaryOutputVarCount; ++outputIndex)
368 {
369 VariableLocation locationData;
370 stream->readInt(&locationData.arrayIndex);
371 stream->readInt(&locationData.index);
372 stream->readBool(&locationData.ignored);
373 mSecondaryOutputLocations.push_back(locationData);
374 }
375
376 unsigned int defaultUniformRangeLow = stream->readInt<unsigned int>();
377 unsigned int defaultUniformRangeHigh = stream->readInt<unsigned int>();
378 mDefaultUniformRange = RangeUI(defaultUniformRangeLow, defaultUniformRangeHigh);
379
380 unsigned int samplerRangeLow = stream->readInt<unsigned int>();
381 unsigned int samplerRangeHigh = stream->readInt<unsigned int>();
382 mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
383
384 size_t samplerCount = stream->readInt<size_t>();
385 for (size_t samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
386 {
387 TextureType textureType = stream->readEnum<TextureType>();
388 GLenum samplerType = stream->readInt<GLenum>();
389 SamplerFormat format = stream->readEnum<SamplerFormat>();
390 size_t bindingCount = stream->readInt<size_t>();
391 mSamplerBindings.emplace_back(textureType, samplerType, format, bindingCount);
392 }
393
394 unsigned int imageRangeLow = stream->readInt<unsigned int>();
395 unsigned int imageRangeHigh = stream->readInt<unsigned int>();
396 mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
397
398 size_t imageBindingCount = stream->readInt<size_t>();
399 for (size_t imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
400 {
401 size_t elementCount = stream->readInt<size_t>();
402 TextureType textureType = static_cast<TextureType>(stream->readInt<unsigned int>());
403 ImageBinding imageBinding(elementCount, textureType);
404 for (size_t elementIndex = 0; elementIndex < elementCount; ++elementIndex)
405 {
406 imageBinding.boundImageUnits[elementIndex] = stream->readInt<unsigned int>();
407 }
408 if (isCompute())
409 {
410 mComputeImageBindings.emplace_back(imageBinding);
411 }
412 else
413 {
414 mGraphicsImageBindings.emplace_back(imageBinding);
415 }
416 }
417
418 // These values are currently only used by PPOs, so only load them when the program is marked
419 // separable to save memory.
420 if (isSeparable)
421 {
422 for (ShaderType shaderType : mLinkedGraphicsShaderStages)
423 {
424 mLinkedOutputVaryings[shaderType].resize(stream->readInt<size_t>());
425 for (sh::ShaderVariable &variable : mLinkedOutputVaryings[shaderType])
426 {
427 LoadShaderVar(stream, &variable);
428 }
429 mLinkedInputVaryings[shaderType].resize(stream->readInt<size_t>());
430 for (sh::ShaderVariable &variable : mLinkedInputVaryings[shaderType])
431 {
432 LoadShaderVar(stream, &variable);
433 }
434 mLinkedShaderVersions[shaderType] = stream->readInt<int>();
435 }
436 for (ShaderType shaderType : mLinkedComputeShaderStages)
437 {
438 mLinkedOutputVaryings[shaderType].resize(stream->readInt<size_t>());
439 for (sh::ShaderVariable &variable : mLinkedOutputVaryings[shaderType])
440 {
441 LoadShaderVar(stream, &variable);
442 }
443 mLinkedInputVaryings[shaderType].resize(stream->readInt<size_t>());
444 for (sh::ShaderVariable &variable : mLinkedInputVaryings[shaderType])
445 {
446 LoadShaderVar(stream, &variable);
447 }
448 mLinkedShaderVersions[shaderType] = stream->readInt<int>();
449 }
450 }
451 }
452
save(bool isSeparable,gl::BinaryOutputStream * stream) const453 void ProgramExecutable::save(bool isSeparable, gl::BinaryOutputStream *stream) const
454 {
455 static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
456 "All bits of mAttributesTypeMask types and mask fit into 32 bits each");
457 stream->writeInt(static_cast<uint32_t>(mAttributesTypeMask.to_ulong()));
458 stream->writeInt(static_cast<uint32_t>(mAttributesMask.to_ulong()));
459 stream->writeInt(static_cast<uint32_t>(mActiveAttribLocationsMask.to_ulong()));
460 stream->writeInt(mMaxActiveAttribLocation);
461
462 stream->writeInt(mFragmentInoutRange.low());
463 stream->writeInt(mFragmentInoutRange.high());
464
465 stream->writeInt(mLinkedGraphicsShaderStages.bits());
466 stream->writeInt(mLinkedComputeShaderStages.bits());
467 stream->writeBool(mIsCompute);
468
469 stream->writeBool(mPipelineHasGraphicsUniformBuffers);
470 stream->writeBool(mPipelineHasComputeUniformBuffers);
471 stream->writeBool(mPipelineHasGraphicsStorageBuffers);
472 stream->writeBool(mPipelineHasComputeStorageBuffers);
473 stream->writeBool(mPipelineHasGraphicsAtomicCounterBuffers);
474 stream->writeBool(mPipelineHasComputeAtomicCounterBuffers);
475 stream->writeBool(mPipelineHasGraphicsDefaultUniforms);
476 stream->writeBool(mPipelineHasComputeDefaultUniforms);
477 stream->writeBool(mPipelineHasGraphicsTextures);
478 stream->writeBool(mPipelineHasComputeTextures);
479
480 ASSERT(mGeometryShaderInvocations >= 1 && mGeometryShaderMaxVertices >= 0);
481 stream->writeEnum(mGeometryShaderInputPrimitiveType);
482 stream->writeEnum(mGeometryShaderOutputPrimitiveType);
483 stream->writeInt(mGeometryShaderInvocations);
484 stream->writeInt(mGeometryShaderMaxVertices);
485
486 stream->writeInt(mTessControlShaderVertices);
487 stream->writeInt(mTessGenMode);
488 stream->writeInt(mTessGenSpacing);
489 stream->writeInt(mTessGenVertexOrder);
490 stream->writeInt(mTessGenPointMode);
491
492 stream->writeInt(getProgramInputs().size());
493 for (const sh::ShaderVariable &attrib : getProgramInputs())
494 {
495 WriteShaderVar(stream, attrib);
496 stream->writeInt(attrib.location);
497 }
498
499 stream->writeInt(getUniforms().size());
500 for (const LinkedUniform &uniform : getUniforms())
501 {
502 WriteShaderVar(stream, uniform);
503
504 stream->writeInt(uniform.bufferIndex);
505 WriteBlockMemberInfo(stream, uniform.blockInfo);
506
507 stream->writeIntVector(uniform.outerArraySizes);
508
509 // Active shader info
510 for (ShaderType shaderType : gl::AllShaderTypes())
511 {
512 stream->writeBool(uniform.isActive(shaderType));
513 }
514 }
515
516 stream->writeInt(getUniformBlocks().size());
517 for (const InterfaceBlock &uniformBlock : getUniformBlocks())
518 {
519 WriteInterfaceBlock(stream, uniformBlock);
520 }
521
522 stream->writeInt(getShaderStorageBlocks().size());
523 for (const InterfaceBlock &shaderStorageBlock : getShaderStorageBlocks())
524 {
525 WriteInterfaceBlock(stream, shaderStorageBlock);
526 }
527
528 stream->writeInt(mAtomicCounterBuffers.size());
529 for (const AtomicCounterBuffer &atomicCounterBuffer : getAtomicCounterBuffers())
530 {
531 WriteShaderVariableBuffer(stream, atomicCounterBuffer);
532 }
533
534 stream->writeInt(getLinkedTransformFeedbackVaryings().size());
535 for (const auto &var : getLinkedTransformFeedbackVaryings())
536 {
537 stream->writeIntVector(var.arraySizes);
538 stream->writeInt(var.type);
539 stream->writeString(var.name);
540
541 stream->writeIntOrNegOne(var.arrayIndex);
542 }
543
544 stream->writeInt(getTransformFeedbackBufferMode());
545
546 stream->writeInt(getOutputVariables().size());
547 for (const sh::ShaderVariable &output : getOutputVariables())
548 {
549 WriteShaderVar(stream, output);
550 stream->writeInt(output.location);
551 stream->writeInt(output.index);
552 }
553
554 stream->writeInt(getOutputLocations().size());
555 for (const auto &outputVar : getOutputLocations())
556 {
557 stream->writeInt(outputVar.arrayIndex);
558 stream->writeIntOrNegOne(outputVar.index);
559 stream->writeBool(outputVar.ignored);
560 }
561
562 stream->writeInt(getSecondaryOutputLocations().size());
563 for (const auto &outputVar : getSecondaryOutputLocations())
564 {
565 stream->writeInt(outputVar.arrayIndex);
566 stream->writeIntOrNegOne(outputVar.index);
567 stream->writeBool(outputVar.ignored);
568 }
569
570 stream->writeInt(getDefaultUniformRange().low());
571 stream->writeInt(getDefaultUniformRange().high());
572
573 stream->writeInt(getSamplerUniformRange().low());
574 stream->writeInt(getSamplerUniformRange().high());
575
576 stream->writeInt(getSamplerBindings().size());
577 for (const auto &samplerBinding : getSamplerBindings())
578 {
579 stream->writeEnum(samplerBinding.textureType);
580 stream->writeInt(samplerBinding.samplerType);
581 stream->writeEnum(samplerBinding.format);
582 stream->writeInt(samplerBinding.boundTextureUnits.size());
583 }
584
585 stream->writeInt(getImageUniformRange().low());
586 stream->writeInt(getImageUniformRange().high());
587
588 stream->writeInt(getImageBindings().size());
589 for (const auto &imageBinding : getImageBindings())
590 {
591 stream->writeInt(imageBinding.boundImageUnits.size());
592 stream->writeInt(static_cast<unsigned int>(imageBinding.textureType));
593 for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
594 {
595 stream->writeInt(imageBinding.boundImageUnits[i]);
596 }
597 }
598
599 // These values are currently only used by PPOs, so only save them when the program is marked
600 // separable to save memory.
601 if (isSeparable)
602 {
603 for (ShaderType shaderType : mLinkedGraphicsShaderStages)
604 {
605 stream->writeInt(mLinkedOutputVaryings[shaderType].size());
606 for (const sh::ShaderVariable &shaderVariable : mLinkedOutputVaryings[shaderType])
607 {
608 WriteShaderVar(stream, shaderVariable);
609 }
610 stream->writeInt(mLinkedInputVaryings[shaderType].size());
611 for (const sh::ShaderVariable &shaderVariable : mLinkedInputVaryings[shaderType])
612 {
613 WriteShaderVar(stream, shaderVariable);
614 }
615 stream->writeInt(mLinkedShaderVersions[shaderType]);
616 }
617 for (ShaderType shaderType : mLinkedComputeShaderStages)
618 {
619 stream->writeInt(mLinkedOutputVaryings[shaderType].size());
620 for (const sh::ShaderVariable &shaderVariable : mLinkedOutputVaryings[shaderType])
621 {
622 WriteShaderVar(stream, shaderVariable);
623 }
624 stream->writeInt(mLinkedInputVaryings[shaderType].size());
625 for (const sh::ShaderVariable &shaderVariable : mLinkedInputVaryings[shaderType])
626 {
627 WriteShaderVar(stream, shaderVariable);
628 }
629 stream->writeInt(mLinkedShaderVersions[shaderType]);
630 }
631 }
632 }
633
getInfoLogLength() const634 int ProgramExecutable::getInfoLogLength() const
635 {
636 return static_cast<int>(mInfoLog.getLength());
637 }
638
getInfoLog(GLsizei bufSize,GLsizei * length,char * infoLog) const639 void ProgramExecutable::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const
640 {
641 return mInfoLog.getLog(bufSize, length, infoLog);
642 }
643
getInfoLogString() const644 std::string ProgramExecutable::getInfoLogString() const
645 {
646 return mInfoLog.str();
647 }
648
isAttribLocationActive(size_t attribLocation) const649 bool ProgramExecutable::isAttribLocationActive(size_t attribLocation) const
650 {
651 // TODO(timvp): http://anglebug.com/3570: Enable this assert here somehow.
652 // ASSERT(!mLinkingState);
653 ASSERT(attribLocation < mActiveAttribLocationsMask.size());
654 return mActiveAttribLocationsMask[attribLocation];
655 }
656
getAttributesMask() const657 AttributesMask ProgramExecutable::getAttributesMask() const
658 {
659 // TODO(timvp): http://anglebug.com/3570: Enable this assert here somehow.
660 // ASSERT(!mLinkingState);
661 return mAttributesMask;
662 }
663
hasDefaultUniforms() const664 bool ProgramExecutable::hasDefaultUniforms() const
665 {
666 return !getDefaultUniformRange().empty() ||
667 (isCompute() ? mPipelineHasComputeDefaultUniforms : mPipelineHasGraphicsDefaultUniforms);
668 }
669
hasTextures() const670 bool ProgramExecutable::hasTextures() const
671 {
672 return !getSamplerBindings().empty() ||
673 (isCompute() ? mPipelineHasComputeTextures : mPipelineHasGraphicsTextures);
674 }
675
676 // TODO: http://anglebug.com/3570: Remove mHas*UniformBuffers once PPO's have valid data in
677 // mUniformBlocks
hasUniformBuffers() const678 bool ProgramExecutable::hasUniformBuffers() const
679 {
680 return !getUniformBlocks().empty() ||
681 (isCompute() ? mPipelineHasComputeUniformBuffers : mPipelineHasGraphicsUniformBuffers);
682 }
683
hasStorageBuffers() const684 bool ProgramExecutable::hasStorageBuffers() const
685 {
686 return (isCompute() ? hasComputeStorageBuffers() : hasGraphicsStorageBuffers());
687 }
688
hasGraphicsStorageBuffers() const689 bool ProgramExecutable::hasGraphicsStorageBuffers() const
690 {
691 return !mGraphicsShaderStorageBlocks.empty() || mPipelineHasGraphicsStorageBuffers;
692 }
693
hasComputeStorageBuffers() const694 bool ProgramExecutable::hasComputeStorageBuffers() const
695 {
696 return !mComputeShaderStorageBlocks.empty() || mPipelineHasComputeStorageBuffers;
697 }
698
hasAtomicCounterBuffers() const699 bool ProgramExecutable::hasAtomicCounterBuffers() const
700 {
701 return !getAtomicCounterBuffers().empty() ||
702 (isCompute() ? mPipelineHasComputeAtomicCounterBuffers
703 : mPipelineHasGraphicsAtomicCounterBuffers);
704 }
705
hasImages() const706 bool ProgramExecutable::hasImages() const
707 {
708 return (isCompute() ? hasComputeImages() : hasGraphicsImages());
709 }
710
hasGraphicsImages() const711 bool ProgramExecutable::hasGraphicsImages() const
712 {
713 return !mGraphicsImageBindings.empty() || mPipelineHasGraphicsImages;
714 }
715
hasComputeImages() const716 bool ProgramExecutable::hasComputeImages() const
717 {
718 return !mComputeImageBindings.empty() || mPipelineHasComputeImages;
719 }
720
usesFramebufferFetch() const721 bool ProgramExecutable::usesFramebufferFetch() const
722 {
723 return (mFragmentInoutRange.length() > 0);
724 }
725
getUniformIndexFromImageIndex(GLuint imageIndex) const726 GLuint ProgramExecutable::getUniformIndexFromImageIndex(GLuint imageIndex) const
727 {
728 ASSERT(imageIndex < mImageUniformRange.length());
729 return imageIndex + mImageUniformRange.low();
730 }
731
updateActiveSamplers(const ProgramState & programState)732 void ProgramExecutable::updateActiveSamplers(const ProgramState &programState)
733 {
734 const std::vector<SamplerBinding> &samplerBindings = programState.getSamplerBindings();
735
736 for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex)
737 {
738 const SamplerBinding &samplerBinding = samplerBindings[samplerIndex];
739 uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(samplerIndex);
740 const gl::LinkedUniform &samplerUniform = programState.getUniforms()[uniformIndex];
741
742 for (GLint textureUnit : samplerBinding.boundTextureUnits)
743 {
744 if (++mActiveSamplerRefCounts[textureUnit] == 1)
745 {
746 mActiveSamplerTypes[textureUnit] = samplerBinding.textureType;
747 mActiveSamplerYUV[textureUnit] = IsSamplerYUVType(samplerBinding.samplerType);
748 mActiveSamplerFormats[textureUnit] = samplerBinding.format;
749 mActiveSamplerShaderBits[textureUnit] = samplerUniform.activeShaders();
750 }
751 else
752 {
753 if (mActiveSamplerTypes[textureUnit] != samplerBinding.textureType)
754 {
755 // Conflicts are marked with InvalidEnum
756 mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum;
757 }
758 if (mActiveSamplerYUV.test(textureUnit) !=
759 IsSamplerYUVType(samplerBinding.samplerType))
760 {
761 mActiveSamplerYUV[textureUnit] = false;
762 }
763 if (mActiveSamplerFormats[textureUnit] != samplerBinding.format)
764 {
765 mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum;
766 }
767 }
768 mActiveSamplersMask.set(textureUnit);
769 }
770 }
771 }
772
updateActiveImages(const ProgramExecutable & executable)773 void ProgramExecutable::updateActiveImages(const ProgramExecutable &executable)
774 {
775 const std::vector<ImageBinding> *imageBindings = getImageBindings();
776 for (uint32_t imageIndex = 0; imageIndex < imageBindings->size(); ++imageIndex)
777 {
778 const gl::ImageBinding &imageBinding = imageBindings->at(imageIndex);
779
780 uint32_t uniformIndex = executable.getUniformIndexFromImageIndex(imageIndex);
781 const gl::LinkedUniform &imageUniform = executable.getUniforms()[uniformIndex];
782 const ShaderBitSet shaderBits = imageUniform.activeShaders();
783 for (GLint imageUnit : imageBinding.boundImageUnits)
784 {
785 mActiveImagesMask.set(imageUnit);
786 if (isCompute())
787 {
788 mActiveImageShaderBits[imageUnit].set(gl::ShaderType::Compute);
789 }
790 else
791 {
792 mActiveImageShaderBits[imageUnit] |= shaderBits;
793 }
794 }
795 }
796 }
797
setSamplerUniformTextureTypeAndFormat(size_t textureUnitIndex,std::vector<SamplerBinding> & samplerBindings)798 void ProgramExecutable::setSamplerUniformTextureTypeAndFormat(
799 size_t textureUnitIndex,
800 std::vector<SamplerBinding> &samplerBindings)
801 {
802 bool foundBinding = false;
803 TextureType foundType = TextureType::InvalidEnum;
804 bool foundYUV = false;
805 SamplerFormat foundFormat = SamplerFormat::InvalidEnum;
806
807 for (const SamplerBinding &binding : samplerBindings)
808 {
809 // A conflict exists if samplers of different types are sourced by the same texture unit.
810 // We need to check all bound textures to detect this error case.
811 for (GLuint textureUnit : binding.boundTextureUnits)
812 {
813 if (textureUnit == textureUnitIndex)
814 {
815 if (!foundBinding)
816 {
817 foundBinding = true;
818 foundType = binding.textureType;
819 foundYUV = IsSamplerYUVType(binding.samplerType);
820 foundFormat = binding.format;
821 }
822 else
823 {
824 if (foundType != binding.textureType)
825 {
826 foundType = TextureType::InvalidEnum;
827 }
828 if (foundYUV != IsSamplerYUVType(binding.samplerType))
829 {
830 foundYUV = false;
831 }
832 if (foundFormat != binding.format)
833 {
834 foundFormat = SamplerFormat::InvalidEnum;
835 }
836 }
837 }
838 }
839 }
840
841 mActiveSamplerTypes[textureUnitIndex] = foundType;
842 mActiveSamplerYUV[textureUnitIndex] = foundYUV;
843 mActiveSamplerFormats[textureUnitIndex] = foundFormat;
844 }
845
updateCanDrawWith()846 void ProgramExecutable::updateCanDrawWith()
847 {
848 mCanDrawWith =
849 (hasLinkedShaderStage(ShaderType::Vertex) && hasLinkedShaderStage(ShaderType::Fragment));
850 }
851
saveLinkedStateInfo(const ProgramState & state)852 void ProgramExecutable::saveLinkedStateInfo(const ProgramState &state)
853 {
854 for (ShaderType shaderType : getLinkedShaderStages())
855 {
856 Shader *shader = state.getAttachedShader(shaderType);
857 ASSERT(shader);
858 mLinkedOutputVaryings[shaderType] = shader->getOutputVaryings();
859 mLinkedInputVaryings[shaderType] = shader->getInputVaryings();
860 mLinkedShaderVersions[shaderType] = shader->getShaderVersion();
861 }
862 }
863
isYUVOutput() const864 bool ProgramExecutable::isYUVOutput() const
865 {
866 return !isCompute() && mYUVOutput;
867 }
868
getLinkedTransformFeedbackStage() const869 ShaderType ProgramExecutable::getLinkedTransformFeedbackStage() const
870 {
871 return GetLastPreFragmentStage(mLinkedGraphicsShaderStages);
872 }
873
linkMergedVaryings(const Context * context,const HasAttachedShaders & programOrPipeline,const ProgramMergedVaryings & mergedVaryings,const std::vector<std::string> & transformFeedbackVaryingNames,bool isSeparable,ProgramVaryingPacking * varyingPacking)874 bool ProgramExecutable::linkMergedVaryings(
875 const Context *context,
876 const HasAttachedShaders &programOrPipeline,
877 const ProgramMergedVaryings &mergedVaryings,
878 const std::vector<std::string> &transformFeedbackVaryingNames,
879 bool isSeparable,
880 ProgramVaryingPacking *varyingPacking)
881 {
882 ShaderType tfStage = programOrPipeline.getTransformFeedbackStage();
883
884 if (!linkValidateTransformFeedback(context, mergedVaryings, tfStage,
885 transformFeedbackVaryingNames))
886 {
887 return false;
888 }
889
890 // Map the varyings to the register file
891 // In WebGL, we use a slightly different handling for packing variables.
892 gl::PackMode packMode = PackMode::ANGLE_RELAXED;
893 if (context->getLimitations().noFlexibleVaryingPacking)
894 {
895 // D3D9 pack mode is strictly more strict than WebGL, so takes priority.
896 packMode = PackMode::ANGLE_NON_CONFORMANT_D3D9;
897 }
898 else if (context->getExtensions().webglCompatibility)
899 {
900 packMode = PackMode::WEBGL_STRICT;
901 }
902
903 // Build active shader stage map.
904 ShaderBitSet activeShadersMask;
905 for (ShaderType shaderType : kAllGraphicsShaderTypes)
906 {
907 // - Check for attached shaders to handle the case of a Program linking the currently
908 // attached shaders.
909 // - Check for linked shaders to handle the case of a PPO linking separable programs before
910 // drawing.
911 if (programOrPipeline.getAttachedShader(shaderType) ||
912 getLinkedShaderStages().test(shaderType))
913 {
914 activeShadersMask[shaderType] = true;
915 }
916 }
917
918 if (!varyingPacking->collectAndPackUserVaryings(mInfoLog, context->getCaps(), packMode,
919 activeShadersMask, mergedVaryings,
920 transformFeedbackVaryingNames, isSeparable))
921 {
922 return false;
923 }
924
925 gatherTransformFeedbackVaryings(mergedVaryings, tfStage, transformFeedbackVaryingNames);
926 updateTransformFeedbackStrides();
927
928 return true;
929 }
930
linkValidateTransformFeedback(const Context * context,const ProgramMergedVaryings & varyings,ShaderType stage,const std::vector<std::string> & transformFeedbackVaryingNames)931 bool ProgramExecutable::linkValidateTransformFeedback(
932 const Context *context,
933 const ProgramMergedVaryings &varyings,
934 ShaderType stage,
935 const std::vector<std::string> &transformFeedbackVaryingNames)
936 {
937 const Version &version = context->getClientVersion();
938
939 // Validate the tf names regardless of the actual program varyings.
940 std::set<std::string> uniqueNames;
941 for (const std::string &tfVaryingName : transformFeedbackVaryingNames)
942 {
943 if (version < Version(3, 1) && tfVaryingName.find('[') != std::string::npos)
944 {
945 mInfoLog << "Capture of array elements is undefined and not supported.";
946 return false;
947 }
948 if (version >= Version(3, 1))
949 {
950 if (IncludeSameArrayElement(uniqueNames, tfVaryingName))
951 {
952 mInfoLog << "Two transform feedback varyings include the same array element ("
953 << tfVaryingName << ").";
954 return false;
955 }
956 }
957 else
958 {
959 if (uniqueNames.count(tfVaryingName) > 0)
960 {
961 mInfoLog << "Two transform feedback varyings specify the same output variable ("
962 << tfVaryingName << ").";
963 return false;
964 }
965 }
966 uniqueNames.insert(tfVaryingName);
967 }
968
969 // Validate against program varyings.
970 size_t totalComponents = 0;
971 for (const std::string &tfVaryingName : transformFeedbackVaryingNames)
972 {
973 std::vector<unsigned int> subscripts;
974 std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
975
976 const sh::ShaderVariable *var = FindOutputVaryingOrField(varyings, stage, baseName);
977 if (var == nullptr)
978 {
979 mInfoLog << "Transform feedback varying " << tfVaryingName
980 << " does not exist in the vertex shader.";
981 return false;
982 }
983
984 // Validate the matching variable.
985 if (var->isStruct())
986 {
987 mInfoLog << "Struct cannot be captured directly (" << baseName << ").";
988 return false;
989 }
990
991 size_t elementCount = 0;
992 size_t componentCount = 0;
993
994 if (var->isArray())
995 {
996 if (version < Version(3, 1))
997 {
998 mInfoLog << "Capture of arrays is undefined and not supported.";
999 return false;
1000 }
1001
1002 // GLSL ES 3.10 section 4.3.6: A vertex output can't be an array of arrays.
1003 ASSERT(!var->isArrayOfArrays());
1004
1005 if (!subscripts.empty() && subscripts[0] >= var->getOutermostArraySize())
1006 {
1007 mInfoLog << "Cannot capture outbound array element '" << tfVaryingName << "'.";
1008 return false;
1009 }
1010 elementCount = (subscripts.empty() ? var->getOutermostArraySize() : 1);
1011 }
1012 else
1013 {
1014 if (!subscripts.empty())
1015 {
1016 mInfoLog << "Varying '" << baseName
1017 << "' is not an array to be captured by element.";
1018 return false;
1019 }
1020 elementCount = 1;
1021 }
1022
1023 const Caps &caps = context->getCaps();
1024
1025 // TODO(jmadill): Investigate implementation limits on D3D11
1026 componentCount = VariableComponentCount(var->type) * elementCount;
1027 if (mTransformFeedbackBufferMode == GL_SEPARATE_ATTRIBS &&
1028 componentCount > static_cast<GLuint>(caps.maxTransformFeedbackSeparateComponents))
1029 {
1030 mInfoLog << "Transform feedback varying " << tfVaryingName << " components ("
1031 << componentCount << ") exceed the maximum separate components ("
1032 << caps.maxTransformFeedbackSeparateComponents << ").";
1033 return false;
1034 }
1035
1036 totalComponents += componentCount;
1037 if (mTransformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS &&
1038 totalComponents > static_cast<GLuint>(caps.maxTransformFeedbackInterleavedComponents))
1039 {
1040 mInfoLog << "Transform feedback varying total components (" << totalComponents
1041 << ") exceed the maximum interleaved components ("
1042 << caps.maxTransformFeedbackInterleavedComponents << ").";
1043 return false;
1044 }
1045 }
1046 return true;
1047 }
1048
gatherTransformFeedbackVaryings(const ProgramMergedVaryings & varyings,ShaderType stage,const std::vector<std::string> & transformFeedbackVaryingNames)1049 void ProgramExecutable::gatherTransformFeedbackVaryings(
1050 const ProgramMergedVaryings &varyings,
1051 ShaderType stage,
1052 const std::vector<std::string> &transformFeedbackVaryingNames)
1053 {
1054 // Gather the linked varyings that are used for transform feedback, they should all exist.
1055 mLinkedTransformFeedbackVaryings.clear();
1056 for (const std::string &tfVaryingName : transformFeedbackVaryingNames)
1057 {
1058 std::vector<unsigned int> subscripts;
1059 std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
1060 size_t subscript = GL_INVALID_INDEX;
1061 if (!subscripts.empty())
1062 {
1063 subscript = subscripts.back();
1064 }
1065 for (const ProgramVaryingRef &ref : varyings)
1066 {
1067 if (ref.frontShaderStage != stage)
1068 {
1069 continue;
1070 }
1071
1072 const sh::ShaderVariable *varying = ref.get(stage);
1073 if (baseName == varying->name)
1074 {
1075 mLinkedTransformFeedbackVaryings.emplace_back(*varying,
1076 static_cast<GLuint>(subscript));
1077 break;
1078 }
1079 else if (varying->isStruct())
1080 {
1081 GLuint fieldIndex = 0;
1082 const auto *field = varying->findField(tfVaryingName, &fieldIndex);
1083 if (field != nullptr)
1084 {
1085 mLinkedTransformFeedbackVaryings.emplace_back(*field, *varying);
1086 break;
1087 }
1088 }
1089 }
1090 }
1091 }
1092
updateTransformFeedbackStrides()1093 void ProgramExecutable::updateTransformFeedbackStrides()
1094 {
1095 if (mTransformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS)
1096 {
1097 mTransformFeedbackStrides.resize(1);
1098 size_t totalSize = 0;
1099 for (const TransformFeedbackVarying &varying : mLinkedTransformFeedbackVaryings)
1100 {
1101 totalSize += varying.size() * VariableExternalSize(varying.type);
1102 }
1103 mTransformFeedbackStrides[0] = static_cast<GLsizei>(totalSize);
1104 }
1105 else
1106 {
1107 mTransformFeedbackStrides.resize(mLinkedTransformFeedbackVaryings.size());
1108 for (size_t i = 0; i < mLinkedTransformFeedbackVaryings.size(); i++)
1109 {
1110 TransformFeedbackVarying &varying = mLinkedTransformFeedbackVaryings[i];
1111 mTransformFeedbackStrides[i] =
1112 static_cast<GLsizei>(varying.size() * VariableExternalSize(varying.type));
1113 }
1114 }
1115 }
1116
validateSamplersImpl(InfoLog * infoLog,const Caps & caps) const1117 bool ProgramExecutable::validateSamplersImpl(InfoLog *infoLog, const Caps &caps) const
1118 {
1119 // if any two active samplers in a program are of different types, but refer to the same
1120 // texture image unit, and this is the current program, then ValidateProgram will fail, and
1121 // DrawArrays and DrawElements will issue the INVALID_OPERATION error.
1122 for (size_t textureUnit : mActiveSamplersMask)
1123 {
1124 if (mActiveSamplerTypes[textureUnit] == TextureType::InvalidEnum)
1125 {
1126 if (infoLog)
1127 {
1128 (*infoLog) << "Samplers of conflicting types refer to the same texture "
1129 "image unit ("
1130 << textureUnit << ").";
1131 }
1132
1133 mCachedValidateSamplersResult = false;
1134 return false;
1135 }
1136 }
1137
1138 mCachedValidateSamplersResult = true;
1139 return true;
1140 }
1141
1142 } // namespace gl
1143