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