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