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