1//
2// Copyright (c) 2002-2013 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// CollectVariables.cpp: Collect lists of shader interface variables based on the AST.
7
8#include "compiler/translator/CollectVariables.h"
9
10#include "angle_gl.h"
11#include "common/utilities.h"
12#include "compiler/translator/HashNames.h"
13#include "compiler/translator/SymbolTable.h"
14#include "compiler/translator/tree_util/IntermTraverse.h"
15#include "compiler/translator/util.h"
16
17namespace sh
18{
19
20namespace
21{
22
23BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
24{
25 switch (blockStorage)
26 {
27 case EbsPacked:
28 return BLOCKLAYOUT_PACKED;
29 case EbsShared:
30 return BLOCKLAYOUT_SHARED;
31 case EbsStd140:
32 return BLOCKLAYOUT_STD140;
33 case EbsStd430:
34 return BLOCKLAYOUT_STD430;
35 default:
36 UNREACHABLE();
37 return BLOCKLAYOUT_SHARED;
38 }
39}
40
41// TODO([email protected]): implement GL_EXT_shader_io_blocks.
42BlockType GetBlockType(TQualifier qualifier)
43{
44 switch (qualifier)
45 {
46 case EvqUniform:
47 return BlockType::BLOCK_UNIFORM;
48 case EvqBuffer:
49 return BlockType::BLOCK_BUFFER;
50 case EvqPerVertexIn:
51 return BlockType::BLOCK_IN;
52 default:
53 UNREACHABLE();
54 return BlockType::BLOCK_UNIFORM;
55 }
56}
57
58template <class VarT>
59VarT *FindVariable(const ImmutableString &name, std::vector<VarT> *infoList)
60{
61 // TODO(zmo): optimize this function.
62 for (size_t ii = 0; ii < infoList->size(); ++ii)
63 {
64 if (name == (*infoList)[ii].name)
65 return &((*infoList)[ii]);
66 }
67
68 return nullptr;
69}
70
71// Note that this shouldn't be called for interface blocks - active information is collected for
72// individual fields in case of interface blocks.
73void MarkActive(ShaderVariable *variable)
74{
75 if (!variable->active)
76 {
77 if (variable->isStruct())
78 {
79 // Conservatively assume all fields are statically used as well.
80 for (auto &field : variable->fields)
81 {
82 MarkActive(&field);
83 }
84 }
85 ASSERT(variable->staticUse);
86 variable->active = true;
87 }
88}
89
90ShaderVariable *FindVariableInInterfaceBlock(const ImmutableString &name,
91 const TInterfaceBlock *interfaceBlock,
92 std::vector<InterfaceBlock> *infoList)
93{
94 ASSERT(interfaceBlock);
95 InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), infoList);
96 ASSERT(namedBlock);
97
98 // Set static use on the parent interface block here
99 namedBlock->staticUse = true;
100 namedBlock->active = true;
101 return FindVariable(name, &namedBlock->fields);
102}
103
104// Traverses the intermediate tree to collect all attributes, uniforms, varyings, fragment outputs,
105// and interface blocks.
106class CollectVariablesTraverser : public TIntermTraverser
107{
108 public:
109 CollectVariablesTraverser(std::vector<Attribute> *attribs,
110 std::vector<OutputVariable> *outputVariables,
111 std::vector<Uniform> *uniforms,
112 std::vector<Varying> *inputVaryings,
113 std::vector<Varying> *outputVaryings,
114 std::vector<InterfaceBlock> *uniformBlocks,
115 std::vector<InterfaceBlock> *shaderStorageBlocks,
116 std::vector<InterfaceBlock> *inBlocks,
117 ShHashFunction64 hashFunction,
118 TSymbolTable *symbolTable,
119 GLenum shaderType,
120 const TExtensionBehavior &extensionBehavior);
121
122 bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) override;
123 void visitSymbol(TIntermSymbol *symbol) override;
124 bool visitDeclaration(Visit, TIntermDeclaration *node) override;
125 bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
126
127 private:
128 std::string getMappedName(const TSymbol *symbol) const;
129
130 void setFieldOrVariableProperties(const TType &type,
131 bool staticUse,
132 ShaderVariable *variableOut) const;
133 void setFieldProperties(const TType &type,
134 const ImmutableString &name,
135 bool staticUse,
136 ShaderVariable *variableOut) const;
137 void setCommonVariableProperties(const TType &type,
138 const TVariable &variable,
139 ShaderVariable *variableOut) const;
140
141 Attribute recordAttribute(const TIntermSymbol &variable) const;
142 OutputVariable recordOutputVariable(const TIntermSymbol &variable) const;
143 Varying recordVarying(const TIntermSymbol &variable) const;
144 void recordInterfaceBlock(const char *instanceName,
145 const TType &interfaceBlockType,
146 InterfaceBlock *interfaceBlock) const;
147 Uniform recordUniform(const TIntermSymbol &variable) const;
148
149 void setBuiltInInfoFromSymbol(const TVariable &variable, ShaderVariable *info);
150
151 void recordBuiltInVaryingUsed(const TVariable &variable,
152 bool *addedFlag,
153 std::vector<Varying> *varyings);
154 void recordBuiltInFragmentOutputUsed(const TVariable &variable, bool *addedFlag);
155 void recordBuiltInAttributeUsed(const TVariable &variable, bool *addedFlag);
156 InterfaceBlock *recordGLInUsed(const TType &glInType);
157 InterfaceBlock *findNamedInterfaceBlock(const ImmutableString &name) const;
158
159 std::vector<Attribute> *mAttribs;
160 std::vector<OutputVariable> *mOutputVariables;
161 std::vector<Uniform> *mUniforms;
162 std::vector<Varying> *mInputVaryings;
163 std::vector<Varying> *mOutputVaryings;
164 std::vector<InterfaceBlock> *mUniformBlocks;
165 std::vector<InterfaceBlock> *mShaderStorageBlocks;
166 std::vector<InterfaceBlock> *mInBlocks;
167
168 std::map<std::string, InterfaceBlockField *> mInterfaceBlockFields;
169
170 // Shader uniforms
171 bool mDepthRangeAdded;
172
173 // Vertex Shader builtins
174 bool mInstanceIDAdded;
175 bool mVertexIDAdded;
176 bool mPointSizeAdded;
177 bool mDrawIDAdded;
178
179 // Vertex Shader and Geometry Shader builtins
180 bool mPositionAdded;
181
182 // Fragment Shader builtins
183 bool mPointCoordAdded;
184 bool mFrontFacingAdded;
185 bool mFragCoordAdded;
186 bool mLastFragDataAdded;
187 bool mFragColorAdded;
188 bool mFragDataAdded;
189 bool mFragDepthEXTAdded;
190 bool mFragDepthAdded;
191 bool mSecondaryFragColorEXTAdded;
192 bool mSecondaryFragDataEXTAdded;
193
194 // Geometry Shader builtins
195 bool mPerVertexInAdded;
196 bool mPrimitiveIDInAdded;
197 bool mInvocationIDAdded;
198
199 // Geometry Shader and Fragment Shader builtins
200 bool mPrimitiveIDAdded;
201 bool mLayerAdded;
202
203 ShHashFunction64 mHashFunction;
204
205 GLenum mShaderType;
206 const TExtensionBehavior &mExtensionBehavior;
207};
208
209CollectVariablesTraverser::CollectVariablesTraverser(
210 std::vector<sh::Attribute> *attribs,
211 std::vector<sh::OutputVariable> *outputVariables,
212 std::vector<sh::Uniform> *uniforms,
213 std::vector<sh::Varying> *inputVaryings,
214 std::vector<sh::Varying> *outputVaryings,
215 std::vector<sh::InterfaceBlock> *uniformBlocks,
216 std::vector<sh::InterfaceBlock> *shaderStorageBlocks,
217 std::vector<sh::InterfaceBlock> *inBlocks,
218 ShHashFunction64 hashFunction,
219 TSymbolTable *symbolTable,
220 GLenum shaderType,
221 const TExtensionBehavior &extensionBehavior)
222 : TIntermTraverser(true, false, false, symbolTable),
223 mAttribs(attribs),
224 mOutputVariables(outputVariables),
225 mUniforms(uniforms),
226 mInputVaryings(inputVaryings),
227 mOutputVaryings(outputVaryings),
228 mUniformBlocks(uniformBlocks),
229 mShaderStorageBlocks(shaderStorageBlocks),
230 mInBlocks(inBlocks),
231 mDepthRangeAdded(false),
232 mInstanceIDAdded(false),
233 mVertexIDAdded(false),
234 mPointSizeAdded(false),
235 mDrawIDAdded(false),
236 mPositionAdded(false),
237 mPointCoordAdded(false),
238 mFrontFacingAdded(false),
239 mFragCoordAdded(false),
240 mLastFragDataAdded(false),
241 mFragColorAdded(false),
242 mFragDataAdded(false),
243 mFragDepthEXTAdded(false),
244 mFragDepthAdded(false),
245 mSecondaryFragColorEXTAdded(false),
246 mSecondaryFragDataEXTAdded(false),
247 mPerVertexInAdded(false),
248 mPrimitiveIDInAdded(false),
249 mInvocationIDAdded(false),
250 mPrimitiveIDAdded(false),
251 mLayerAdded(false),
252 mHashFunction(hashFunction),
253 mShaderType(shaderType),
254 mExtensionBehavior(extensionBehavior)
255{}
256
257std::string CollectVariablesTraverser::getMappedName(const TSymbol *symbol) const
258{
259 return HashName(symbol, mHashFunction, nullptr).data();
260}
261
262void CollectVariablesTraverser::setBuiltInInfoFromSymbol(const TVariable &variable,
263 ShaderVariable *info)
264{
265 const TType &type = variable.getType();
266
267 info->name = variable.name().data();
268 info->mappedName = variable.name().data();
269 info->type = GLVariableType(type);
270 info->precision = GLVariablePrecision(type);
271 if (auto *arraySizes = type.getArraySizes())
272 {
273 info->arraySizes.assign(arraySizes->begin(), arraySizes->end());
274 }
275}
276
277void CollectVariablesTraverser::recordBuiltInVaryingUsed(const TVariable &variable,
278 bool *addedFlag,
279 std::vector<Varying> *varyings)
280{
281 ASSERT(varyings);
282 if (!(*addedFlag))
283 {
284 Varying info;
285 setBuiltInInfoFromSymbol(variable, &info);
286 info.staticUse = true;
287 info.active = true;
288 info.isInvariant = mSymbolTable->isVaryingInvariant(variable);
289 varyings->push_back(info);
290 (*addedFlag) = true;
291 }
292}
293
294void CollectVariablesTraverser::recordBuiltInFragmentOutputUsed(const TVariable &variable,
295 bool *addedFlag)
296{
297 if (!(*addedFlag))
298 {
299 OutputVariable info;
300 setBuiltInInfoFromSymbol(variable, &info);
301 info.staticUse = true;
302 info.active = true;
303 mOutputVariables->push_back(info);
304 (*addedFlag) = true;
305 }
306}
307
308void CollectVariablesTraverser::recordBuiltInAttributeUsed(const TVariable &variable,
309 bool *addedFlag)
310{
311 if (!(*addedFlag))
312 {
313 Attribute info;
314 setBuiltInInfoFromSymbol(variable, &info);
315 info.staticUse = true;
316 info.active = true;
317 info.location = -1;
318 mAttribs->push_back(info);
319 (*addedFlag) = true;
320 }
321}
322
323InterfaceBlock *CollectVariablesTraverser::recordGLInUsed(const TType &glInType)
324{
325 if (!mPerVertexInAdded)
326 {
327 ASSERT(glInType.getQualifier() == EvqPerVertexIn);
328 InterfaceBlock info;
329 recordInterfaceBlock("gl_in", glInType, &info);
330
331 mPerVertexInAdded = true;
332 mInBlocks->push_back(info);
333 return &mInBlocks->back();
334 }
335 else
336 {
337 return FindVariable(ImmutableString("gl_PerVertex"), mInBlocks);
338 }
339}
340
341bool CollectVariablesTraverser::visitInvariantDeclaration(Visit visit,
342 TIntermInvariantDeclaration *node)
343{
344 // We should not mark variables as active just based on an invariant declaration, so we don't
345 // traverse the symbols declared invariant.
346 return false;
347}
348
349// We want to check whether a uniform/varying is active because we need to skip updating inactive
350// ones. We also only count the active ones in packing computing. Also, gl_FragCoord, gl_PointCoord,
351// and gl_FrontFacing count toward varying counting if they are active in a fragment shader.
352void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
353{
354 ASSERT(symbol != nullptr);
355
356 if (symbol->variable().symbolType() == SymbolType::AngleInternal ||
357 symbol->variable().symbolType() == SymbolType::Empty)
358 {
359 // Internal variables or nameless variables are not collected.
360 return;
361 }
362
363 ShaderVariable *var = nullptr;
364
365 const ImmutableString &symbolName = symbol->getName();
366
367 // Check the qualifier from the variable, not from the symbol node. The node may have a
368 // different qualifier if it's the result of a folded ternary node.
369 TQualifier qualifier = symbol->variable().getType().getQualifier();
370
371 if (IsVaryingIn(qualifier))
372 {
373 var = FindVariable(symbolName, mInputVaryings);
374 }
375 else if (IsVaryingOut(qualifier))
376 {
377 var = FindVariable(symbolName, mOutputVaryings);
378 }
379 else if (symbol->getType().getBasicType() == EbtInterfaceBlock)
380 {
381 UNREACHABLE();
382 }
383 else if (symbolName == "gl_DepthRange")
384 {
385 ASSERT(qualifier == EvqUniform);
386
387 if (!mDepthRangeAdded)
388 {
389 Uniform info;
390 const char kName[] = "gl_DepthRange";
391 info.name = kName;
392 info.mappedName = kName;
393 info.type = GL_NONE;
394 info.precision = GL_NONE;
395 info.staticUse = true;
396 info.active = true;
397
398 ShaderVariable nearInfo(GL_FLOAT);
399 const char kNearName[] = "near";
400 nearInfo.name = kNearName;
401 nearInfo.mappedName = kNearName;
402 nearInfo.precision = GL_HIGH_FLOAT;
403 nearInfo.staticUse = true;
404 nearInfo.active = true;
405
406 ShaderVariable farInfo(GL_FLOAT);
407 const char kFarName[] = "far";
408 farInfo.name = kFarName;
409 farInfo.mappedName = kFarName;
410 farInfo.precision = GL_HIGH_FLOAT;
411 farInfo.staticUse = true;
412 farInfo.active = true;
413
414 ShaderVariable diffInfo(GL_FLOAT);
415 const char kDiffName[] = "diff";
416 diffInfo.name = kDiffName;
417 diffInfo.mappedName = kDiffName;
418 diffInfo.precision = GL_HIGH_FLOAT;
419 diffInfo.staticUse = true;
420 diffInfo.active = true;
421
422 info.fields.push_back(nearInfo);
423 info.fields.push_back(farInfo);
424 info.fields.push_back(diffInfo);
425
426 mUniforms->push_back(info);
427 mDepthRangeAdded = true;
428 }
429 }
430 else
431 {
432 switch (qualifier)
433 {
434 case EvqAttribute:
435 case EvqVertexIn:
436 var = FindVariable(symbolName, mAttribs);
437 break;
438 case EvqFragmentOut:
439 var = FindVariable(symbolName, mOutputVariables);
440 break;
441 case EvqUniform:
442 {
443 const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
444 if (interfaceBlock)
445 {
446 var = FindVariableInInterfaceBlock(symbolName, interfaceBlock, mUniformBlocks);
447 }
448 else
449 {
450 var = FindVariable(symbolName, mUniforms);
451 }
452
453 // It's an internal error to reference an undefined user uniform
454 ASSERT(!symbolName.beginsWith("gl_") || var);
455 }
456 break;
457 case EvqBuffer:
458 {
459 const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
460 var =
461 FindVariableInInterfaceBlock(symbolName, interfaceBlock, mShaderStorageBlocks);
462 }
463 break;
464 case EvqFragCoord:
465 recordBuiltInVaryingUsed(symbol->variable(), &mFragCoordAdded, mInputVaryings);
466 return;
467 case EvqFrontFacing:
468 recordBuiltInVaryingUsed(symbol->variable(), &mFrontFacingAdded, mInputVaryings);
469 return;
470 case EvqPointCoord:
471 recordBuiltInVaryingUsed(symbol->variable(), &mPointCoordAdded, mInputVaryings);
472 return;
473 case EvqInstanceID:
474 // Whenever the SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW option is set,
475 // gl_InstanceID is added inside expressions to initialize ViewID_OVR and
476 // InstanceID. Note that gl_InstanceID is not added to the symbol table for ESSL1
477 // shaders.
478 recordBuiltInAttributeUsed(symbol->variable(), &mInstanceIDAdded);
479 return;
480 case EvqVertexID:
481 recordBuiltInAttributeUsed(symbol->variable(), &mVertexIDAdded);
482 return;
483 case EvqPosition:
484 recordBuiltInVaryingUsed(symbol->variable(), &mPositionAdded, mOutputVaryings);
485 return;
486 case EvqPointSize:
487 recordBuiltInVaryingUsed(symbol->variable(), &mPointSizeAdded, mOutputVaryings);
488 return;
489 case EvqDrawID:
490 recordBuiltInAttributeUsed(symbol->variable(), &mDrawIDAdded);
491 return;
492 case EvqLastFragData:
493 recordBuiltInVaryingUsed(symbol->variable(), &mLastFragDataAdded, mInputVaryings);
494 return;
495 case EvqFragColor:
496 recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragColorAdded);
497 return;
498 case EvqFragData:
499 if (!mFragDataAdded)
500 {
501 OutputVariable info;
502 setBuiltInInfoFromSymbol(symbol->variable(), &info);
503 if (!IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers))
504 {
505 ASSERT(info.arraySizes.size() == 1u);
506 info.arraySizes.back() = 1u;
507 }
508 info.staticUse = true;
509 info.active = true;
510 mOutputVariables->push_back(info);
511 mFragDataAdded = true;
512 }
513 return;
514 case EvqFragDepthEXT:
515 recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragDepthEXTAdded);
516 return;
517 case EvqFragDepth:
518 recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragDepthAdded);
519 return;
520 case EvqSecondaryFragColorEXT:
521 recordBuiltInFragmentOutputUsed(symbol->variable(), &mSecondaryFragColorEXTAdded);
522 return;
523 case EvqSecondaryFragDataEXT:
524 recordBuiltInFragmentOutputUsed(symbol->variable(), &mSecondaryFragDataEXTAdded);
525 return;
526 case EvqInvocationID:
527 recordBuiltInVaryingUsed(symbol->variable(), &mInvocationIDAdded, mInputVaryings);
528 break;
529 case EvqPrimitiveIDIn:
530 recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDInAdded, mInputVaryings);
531 break;
532 case EvqPrimitiveID:
533 if (mShaderType == GL_GEOMETRY_SHADER_EXT)
534 {
535 recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDAdded,
536 mOutputVaryings);
537 }
538 else
539 {
540 ASSERT(mShaderType == GL_FRAGMENT_SHADER);
541 recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDAdded,
542 mInputVaryings);
543 }
544 break;
545 case EvqLayer:
546 if (mShaderType == GL_GEOMETRY_SHADER_EXT)
547 {
548 recordBuiltInVaryingUsed(symbol->variable(), &mLayerAdded, mOutputVaryings);
549 }
550 else if (mShaderType == GL_FRAGMENT_SHADER)
551 {
552 recordBuiltInVaryingUsed(symbol->variable(), &mLayerAdded, mInputVaryings);
553 }
554 else
555 {
556 ASSERT(mShaderType == GL_VERTEX_SHADER &&
557 IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview2));
558 }
559 break;
560 default:
561 break;
562 }
563 }
564 if (var)
565 {
566 MarkActive(var);
567 }
568}
569
570void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
571 bool staticUse,
572 ShaderVariable *variableOut) const
573{
574 ASSERT(variableOut);
575
576 variableOut->staticUse = staticUse;
577
578 const TStructure *structure = type.getStruct();
579 if (!structure)
580 {
581 variableOut->type = GLVariableType(type);
582 variableOut->precision = GLVariablePrecision(type);
583 }
584 else
585 {
586 // Structures use a NONE type that isn't exposed outside ANGLE.
587 variableOut->type = GL_NONE;
588 if (structure->symbolType() != SymbolType::Empty)
589 {
590 variableOut->structName = structure->name().data();
591 }
592
593 const TFieldList &fields = structure->fields();
594
595 for (const TField *field : fields)
596 {
597 // Regardless of the variable type (uniform, in/out etc.) its fields are always plain
598 // ShaderVariable objects.
599 ShaderVariable fieldVariable;
600 setFieldProperties(*field->type(), field->name(), staticUse, &fieldVariable);
601 variableOut->fields.push_back(fieldVariable);
602 }
603 }
604 if (auto *arraySizes = type.getArraySizes())
605 {
606 variableOut->arraySizes.assign(arraySizes->begin(), arraySizes->end());
607 }
608}
609
610void CollectVariablesTraverser::setFieldProperties(const TType &type,
611 const ImmutableString &name,
612 bool staticUse,
613 ShaderVariable *variableOut) const
614{
615 ASSERT(variableOut);
616 setFieldOrVariableProperties(type, staticUse, variableOut);
617 variableOut->name.assign(name.data(), name.length());
618 variableOut->mappedName = HashName(name, mHashFunction, nullptr).data();
619}
620
621void CollectVariablesTraverser::setCommonVariableProperties(const TType &type,
622 const TVariable &variable,
623 ShaderVariable *variableOut) const
624{
625 ASSERT(variableOut);
626
627 variableOut->staticUse = mSymbolTable->isStaticallyUsed(variable);
628 setFieldOrVariableProperties(type, variableOut->staticUse, variableOut);
629 ASSERT(variable.symbolType() != SymbolType::Empty);
630 variableOut->name.assign(variable.name().data(), variable.name().length());
631 variableOut->mappedName = getMappedName(&variable);
632}
633
634Attribute CollectVariablesTraverser::recordAttribute(const TIntermSymbol &variable) const
635{
636 const TType &type = variable.getType();
637 ASSERT(!type.getStruct());
638
639 Attribute attribute;
640 setCommonVariableProperties(type, variable.variable(), &attribute);
641
642 attribute.location = type.getLayoutQualifier().location;
643 return attribute;
644}
645
646OutputVariable CollectVariablesTraverser::recordOutputVariable(const TIntermSymbol &variable) const
647{
648 const TType &type = variable.getType();
649 ASSERT(!type.getStruct());
650
651 OutputVariable outputVariable;
652 setCommonVariableProperties(type, variable.variable(), &outputVariable);
653
654 outputVariable.location = type.getLayoutQualifier().location;
655 outputVariable.index = type.getLayoutQualifier().index;
656 return outputVariable;
657}
658
659Varying CollectVariablesTraverser::recordVarying(const TIntermSymbol &variable) const
660{
661 const TType &type = variable.getType();
662
663 Varying varying;
664 setCommonVariableProperties(type, variable.variable(), &varying);
665 varying.location = type.getLayoutQualifier().location;
666
667 switch (type.getQualifier())
668 {
669 case EvqVaryingIn:
670 case EvqVaryingOut:
671 case EvqVertexOut:
672 case EvqSmoothOut:
673 case EvqFlatOut:
674 case EvqCentroidOut:
675 case EvqGeometryOut:
676 if (mSymbolTable->isVaryingInvariant(variable.variable()) || type.isInvariant())
677 {
678 varying.isInvariant = true;
679 }
680 break;
681 default:
682 break;
683 }
684
685 varying.interpolation = GetInterpolationType(type.getQualifier());
686 return varying;
687}
688
689// TODO([email protected]): implement GL_EXT_shader_io_blocks.
690void CollectVariablesTraverser::recordInterfaceBlock(const char *instanceName,
691 const TType &interfaceBlockType,
692 InterfaceBlock *interfaceBlock) const
693{
694 ASSERT(interfaceBlockType.getBasicType() == EbtInterfaceBlock);
695 ASSERT(interfaceBlock);
696
697 const TInterfaceBlock *blockType = interfaceBlockType.getInterfaceBlock();
698 ASSERT(blockType);
699
700 interfaceBlock->name = blockType->name().data();
701 interfaceBlock->mappedName = getMappedName(blockType);
702 if (instanceName != nullptr)
703 {
704 interfaceBlock->instanceName = instanceName;
705 const TSymbol *blockSymbol = nullptr;
706 if (strncmp(instanceName, "gl_in", 5u) == 0)
707 {
708 blockSymbol = mSymbolTable->getGlInVariableWithArraySize();
709 }
710 else
711 {
712 blockSymbol = mSymbolTable->findGlobal(ImmutableString(instanceName));
713 }
714 ASSERT(blockSymbol && blockSymbol->isVariable());
715 interfaceBlock->staticUse =
716 mSymbolTable->isStaticallyUsed(*static_cast<const TVariable *>(blockSymbol));
717 }
718 ASSERT(!interfaceBlockType.isArrayOfArrays()); // Disallowed by GLSL ES 3.10 section 4.3.9
719 interfaceBlock->arraySize =
720 interfaceBlockType.isArray() ? interfaceBlockType.getOutermostArraySize() : 0;
721
722 interfaceBlock->blockType = GetBlockType(interfaceBlockType.getQualifier());
723 if (interfaceBlock->blockType == BlockType::BLOCK_UNIFORM ||
724 interfaceBlock->blockType == BlockType::BLOCK_BUFFER)
725 {
726 // TODO(oetuaho): Remove setting isRowMajorLayout.
727 interfaceBlock->isRowMajorLayout = false;
728 interfaceBlock->binding = blockType->blockBinding();
729 interfaceBlock->layout = GetBlockLayoutType(blockType->blockStorage());
730 }
731
732 // Gather field information
733 bool anyFieldStaticallyUsed = false;
734 for (const TField *field : blockType->fields())
735 {
736 const TType &fieldType = *field->type();
737
738 bool staticUse = false;
739 if (instanceName == nullptr)
740 {
741 // Static use of individual fields has been recorded, since they are present in the
742 // symbol table as variables.
743 const TSymbol *fieldSymbol = mSymbolTable->findGlobal(field->name());
744 ASSERT(fieldSymbol && fieldSymbol->isVariable());
745 staticUse =
746 mSymbolTable->isStaticallyUsed(*static_cast<const TVariable *>(fieldSymbol));
747 if (staticUse)
748 {
749 anyFieldStaticallyUsed = true;
750 }
751 }
752
753 InterfaceBlockField fieldVariable;
754 setFieldProperties(fieldType, field->name(), staticUse, &fieldVariable);
755 fieldVariable.isRowMajorLayout =
756 (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
757 interfaceBlock->fields.push_back(fieldVariable);
758 }
759 if (anyFieldStaticallyUsed)
760 {
761 interfaceBlock->staticUse = true;
762 }
763}
764
765Uniform CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const
766{
767 Uniform uniform;
768 setCommonVariableProperties(variable.getType(), variable.variable(), &uniform);
769 uniform.binding = variable.getType().getLayoutQualifier().binding;
770 uniform.imageUnitFormat =
771 GetImageInternalFormatType(variable.getType().getLayoutQualifier().imageInternalFormat);
772 uniform.location = variable.getType().getLayoutQualifier().location;
773 uniform.offset = variable.getType().getLayoutQualifier().offset;
774 uniform.readonly = variable.getType().getMemoryQualifier().readonly;
775 uniform.writeonly = variable.getType().getMemoryQualifier().writeonly;
776 return uniform;
777}
778
779bool CollectVariablesTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
780{
781 const TIntermSequence &sequence = *(node->getSequence());
782 ASSERT(!sequence.empty());
783
784 const TIntermTyped &typedNode = *(sequence.front()->getAsTyped());
785 TQualifier qualifier = typedNode.getQualifier();
786
787 bool isShaderVariable = qualifier == EvqAttribute || qualifier == EvqVertexIn ||
788 qualifier == EvqFragmentOut || qualifier == EvqUniform ||
789 IsVarying(qualifier);
790
791 if (typedNode.getBasicType() != EbtInterfaceBlock && !isShaderVariable)
792 {
793 return true;
794 }
795
796 for (TIntermNode *variableNode : sequence)
797 {
798 // The only case in which the sequence will not contain a TIntermSymbol node is
799 // initialization. It will contain a TInterBinary node in that case. Since attributes,
800 // uniforms, varyings, outputs and interface blocks cannot be initialized in a shader, we
801 // must have only TIntermSymbol nodes in the sequence in the cases we are interested in.
802 const TIntermSymbol &variable = *variableNode->getAsSymbolNode();
803 if (variable.variable().symbolType() == SymbolType::AngleInternal)
804 {
805 // Internal variables are not collected.
806 continue;
807 }
808
809 // TODO([email protected]): implement GL_EXT_shader_io_blocks.
810 if (typedNode.getBasicType() == EbtInterfaceBlock)
811 {
812 InterfaceBlock interfaceBlock;
813 recordInterfaceBlock(variable.variable().symbolType() != SymbolType::Empty
814 ? variable.getName().data()
815 : nullptr,
816 variable.getType(), &interfaceBlock);
817
818 switch (qualifier)
819 {
820 case EvqUniform:
821 mUniformBlocks->push_back(interfaceBlock);
822 break;
823 case EvqBuffer:
824 mShaderStorageBlocks->push_back(interfaceBlock);
825 break;
826 default:
827 UNREACHABLE();
828 }
829 }
830 else
831 {
832 ASSERT(variable.variable().symbolType() != SymbolType::Empty);
833 switch (qualifier)
834 {
835 case EvqAttribute:
836 case EvqVertexIn:
837 mAttribs->push_back(recordAttribute(variable));
838 break;
839 case EvqFragmentOut:
840 mOutputVariables->push_back(recordOutputVariable(variable));
841 break;
842 case EvqUniform:
843 mUniforms->push_back(recordUniform(variable));
844 break;
845 default:
846 if (IsVaryingIn(qualifier))
847 {
848 mInputVaryings->push_back(recordVarying(variable));
849 }
850 else
851 {
852 ASSERT(IsVaryingOut(qualifier));
853 mOutputVaryings->push_back(recordVarying(variable));
854 }
855 break;
856 }
857 }
858 }
859
860 // None of the recorded variables can have initializers, so we don't need to traverse the
861 // declarators.
862 return false;
863}
864
865// TODO([email protected]): add search on mInBlocks and mOutBlocks when implementing
866// GL_EXT_shader_io_blocks.
867InterfaceBlock *CollectVariablesTraverser::findNamedInterfaceBlock(
868 const ImmutableString &blockName) const
869{
870 InterfaceBlock *namedBlock = FindVariable(blockName, mUniformBlocks);
871 if (!namedBlock)
872 {
873 namedBlock = FindVariable(blockName, mShaderStorageBlocks);
874 }
875 return namedBlock;
876}
877
878bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
879{
880 if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
881 {
882 // NOTE: we do not determine static use / activeness for individual blocks of an array.
883 TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped();
884 ASSERT(blockNode);
885
886 TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion();
887 ASSERT(constantUnion);
888
889 InterfaceBlock *namedBlock = nullptr;
890
891 bool traverseIndexExpression = false;
892 TIntermBinary *interfaceIndexingNode = blockNode->getAsBinaryNode();
893 if (interfaceIndexingNode)
894 {
895 TIntermTyped *interfaceNode = interfaceIndexingNode->getLeft()->getAsTyped();
896 ASSERT(interfaceNode);
897
898 const TType &interfaceType = interfaceNode->getType();
899 if (interfaceType.getQualifier() == EvqPerVertexIn)
900 {
901 namedBlock = recordGLInUsed(interfaceType);
902 ASSERT(namedBlock);
903
904 // We need to continue traversing to collect useful variables in the index
905 // expression of gl_in.
906 traverseIndexExpression = true;
907 }
908 }
909
910 const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
911 if (!namedBlock)
912 {
913 namedBlock = findNamedInterfaceBlock(interfaceBlock->name());
914 }
915 ASSERT(namedBlock);
916 ASSERT(namedBlock->staticUse);
917 namedBlock->active = true;
918 unsigned int fieldIndex = static_cast<unsigned int>(constantUnion->getIConst(0));
919 ASSERT(fieldIndex < namedBlock->fields.size());
920 // TODO(oetuaho): Would be nicer to record static use of fields of named interface blocks
921 // more accurately at parse time - now we only mark the fields statically used if they are
922 // active. http://anglebug.com/2440
923 namedBlock->fields[fieldIndex].staticUse = true;
924 namedBlock->fields[fieldIndex].active = true;
925
926 if (traverseIndexExpression)
927 {
928 ASSERT(interfaceIndexingNode);
929 interfaceIndexingNode->getRight()->traverse(this);
930 }
931 return false;
932 }
933
934 return true;
935}
936
937} // anonymous namespace
938
939void CollectVariables(TIntermBlock *root,
940 std::vector<Attribute> *attributes,
941 std::vector<OutputVariable> *outputVariables,
942 std::vector<Uniform> *uniforms,
943 std::vector<Varying> *inputVaryings,
944 std::vector<Varying> *outputVaryings,
945 std::vector<InterfaceBlock> *uniformBlocks,
946 std::vector<InterfaceBlock> *shaderStorageBlocks,
947 std::vector<InterfaceBlock> *inBlocks,
948 ShHashFunction64 hashFunction,
949 TSymbolTable *symbolTable,
950 GLenum shaderType,
951 const TExtensionBehavior &extensionBehavior)
952{
953 CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings,
954 outputVaryings, uniformBlocks, shaderStorageBlocks, inBlocks,
955 hashFunction, symbolTable, shaderType, extensionBehavior);
956 root->traverse(&collect);
957}
958
959} // namespace sh
960