• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2018 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 // RewriteStructSamplers: Extract samplers from structs.
7 //
8 
9 #include "compiler/translator/tree_ops/RewriteStructSamplers.h"
10 
11 #include "compiler/translator/ImmutableStringBuilder.h"
12 #include "compiler/translator/SymbolTable.h"
13 #include "compiler/translator/tree_util/IntermNode_util.h"
14 #include "compiler/translator/tree_util/IntermTraverse.h"
15 
16 namespace sh
17 {
18 namespace
19 {
20 
21 // Used to map one structure type to another (one where the samplers are removed).
22 struct StructureData
23 {
24     // The structure this was replaced with.  If nullptr, it means the structure is removed (because
25     // it had all samplers).
26     const TStructure *modified;
27     // Indexed by the field index of original structure, to get the field index of the modified
28     // structure.  For example:
29     //
30     //     struct Original
31     //     {
32     //         sampler2D s1;
33     //         vec4 f1;
34     //         sampler2D s2;
35     //         sampler2D s3;
36     //         vec4 f2;
37     //     };
38     //
39     //     struct Modified
40     //     {
41     //         vec4 f1;
42     //         vec4 f2;
43     //     };
44     //
45     //     fieldMap:
46     //         0 -> Invalid
47     //         1 -> 0
48     //         2 -> Invalid
49     //         3 -> Invalid
50     //         4 -> 1
51     //
52     TVector<int> fieldMap;
53 };
54 
55 using StructureMap        = angle::HashMap<const TStructure *, StructureData>;
56 using StructureUniformMap = angle::HashMap<const TVariable *, const TVariable *>;
57 using ExtractedSamplerMap = angle::HashMap<std::string, const TVariable *>;
58 
59 TIntermTyped *RewriteModifiedStructFieldSelectionExpression(
60     TCompiler *compiler,
61     TIntermBinary *node,
62     const StructureMap &structureMap,
63     const StructureUniformMap &structureUniformMap,
64     const ExtractedSamplerMap &extractedSamplers);
65 
RewriteExpressionVisitBinaryHelper(TCompiler * compiler,TIntermBinary * node,const StructureMap & structureMap,const StructureUniformMap & structureUniformMap,const ExtractedSamplerMap & extractedSamplers)66 TIntermTyped *RewriteExpressionVisitBinaryHelper(TCompiler *compiler,
67                                                  TIntermBinary *node,
68                                                  const StructureMap &structureMap,
69                                                  const StructureUniformMap &structureUniformMap,
70                                                  const ExtractedSamplerMap &extractedSamplers)
71 {
72     // Only interested in EOpIndexDirectStruct binary nodes.
73     if (node->getOp() != EOpIndexDirectStruct)
74     {
75         return nullptr;
76     }
77 
78     const TStructure *structure = node->getLeft()->getType().getStruct();
79     ASSERT(structure);
80 
81     // If the result of the index is not a sampler and the struct is not replaced, there's nothing
82     // to do.
83     if (!node->getType().isSampler() && structureMap.find(structure) == structureMap.end())
84     {
85         return nullptr;
86     }
87 
88     // Otherwise, replace the whole expression such that:
89     //
90     // - if sampler, it's indexed with whatever indices the parent structs were indexed with,
91     // - otherwise, the chain of field selections is rewritten by modifying the base uniform so all
92     //   the intermediate nodes would have the correct type (and therefore fields).
93     ASSERT(structureMap.find(structure) != structureMap.end());
94 
95     return RewriteModifiedStructFieldSelectionExpression(compiler, node, structureMap,
96                                                          structureUniformMap, extractedSamplers);
97 }
98 
99 // Given an expression, this traverser calculates a new expression where sampler-in-structs are
100 // replaced with their extracted ones, and field indices are adjusted for the rest of the fields.
101 // In particular, this is run on the right node of EOpIndexIndirect binary nodes, so that the
102 // expression in the index gets a chance to go through this transformation.
103 class RewriteExpressionTraverser final : public TIntermTraverser
104 {
105   public:
RewriteExpressionTraverser(TCompiler * compiler,const StructureMap & structureMap,const StructureUniformMap & structureUniformMap,const ExtractedSamplerMap & extractedSamplers)106     explicit RewriteExpressionTraverser(TCompiler *compiler,
107                                         const StructureMap &structureMap,
108                                         const StructureUniformMap &structureUniformMap,
109                                         const ExtractedSamplerMap &extractedSamplers)
110         : TIntermTraverser(true, false, false),
111           mCompiler(compiler),
112           mStructureMap(structureMap),
113           mStructureUniformMap(structureUniformMap),
114           mExtractedSamplers(extractedSamplers)
115     {}
116 
visitBinary(Visit visit,TIntermBinary * node)117     bool visitBinary(Visit visit, TIntermBinary *node) override
118     {
119         TIntermTyped *rewritten = RewriteExpressionVisitBinaryHelper(
120             mCompiler, node, mStructureMap, mStructureUniformMap, mExtractedSamplers);
121 
122         if (rewritten == nullptr)
123         {
124             return true;
125         }
126 
127         queueReplacement(rewritten, OriginalNode::IS_DROPPED);
128 
129         // Don't iterate as the expression is rewritten.
130         return false;
131     }
132 
visitSymbol(TIntermSymbol * node)133     void visitSymbol(TIntermSymbol *node) override
134     {
135         // It's impossible to reach here with a symbol that needs replacement.
136         // MonomorphizeUnsupportedFunctions makes sure that whole structs containing
137         // samplers are not passed to functions, so any instance of the struct uniform is
138         // necessarily indexed right away.  visitBinary should have already taken care of it.
139         ASSERT(mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end());
140     }
141 
142   private:
143     TCompiler *mCompiler;
144 
145     // See RewriteStructSamplersTraverser.
146     const StructureMap &mStructureMap;
147     const StructureUniformMap &mStructureUniformMap;
148     const ExtractedSamplerMap &mExtractedSamplers;
149 };
150 
151 // Rewrite the index of an EOpIndexIndirect expression.  The root can never need replacing, because
152 // it cannot be a sampler itself or of a struct type.
RewriteIndexExpression(TCompiler * compiler,TIntermTyped * expression,const StructureMap & structureMap,const StructureUniformMap & structureUniformMap,const ExtractedSamplerMap & extractedSamplers)153 void RewriteIndexExpression(TCompiler *compiler,
154                             TIntermTyped *expression,
155                             const StructureMap &structureMap,
156                             const StructureUniformMap &structureUniformMap,
157                             const ExtractedSamplerMap &extractedSamplers)
158 {
159     RewriteExpressionTraverser traverser(compiler, structureMap, structureUniformMap,
160                                          extractedSamplers);
161     expression->traverse(&traverser);
162     bool valid = traverser.updateTree(compiler, expression);
163     ASSERT(valid);
164 }
165 
166 // Given an expression such as the following:
167 //
168 //                                                    EOpIndexDirectStruct (sampler)
169 //                                                    /                  \
170 //                                               EOpIndex*           field index
171 //                                              /        \
172 //                                EOpIndexDirectStruct   index 2
173 //                                /                  \
174 //                           EOpIndex*           field index
175 //                          /        \
176 //            EOpIndexDirectStruct   index 1
177 //            /                  \
178 //     Uniform Struct           field index
179 //
180 // produces:
181 //
182 //                                EOpIndex*
183 //                                /      \
184 //                           EOpIndex*  index 2
185 //                          /        \
186 //                      sampler    index 1
187 //
188 // Alternatively, if the expression is as such:
189 //
190 //                                                    EOpIndexDirectStruct
191 //                                                    /                  \
192 //                        (modified struct type) EOpIndex*           field index
193 //                                              /        \
194 //                                EOpIndexDirectStruct   index 2
195 //                                /                  \
196 //                           EOpIndex*           field index
197 //                          /        \
198 //            EOpIndexDirectStruct   index 1
199 //            /                  \
200 //     Uniform Struct           field index
201 //
202 // produces:
203 //
204 //                                                    EOpIndexDirectStruct
205 //                                                    /                  \
206 //                                               EOpIndex*     mapped field index
207 //                                              /        \
208 //                                EOpIndexDirectStruct   index 2
209 //                                /                  \
210 //                           EOpIndex*      mapped field index
211 //                          /        \
212 //            EOpIndexDirectStruct   index 1
213 //            /                  \
214 //     Uniform Struct     mapped field index
215 //
RewriteModifiedStructFieldSelectionExpression(TCompiler * compiler,TIntermBinary * node,const StructureMap & structureMap,const StructureUniformMap & structureUniformMap,const ExtractedSamplerMap & extractedSamplers)216 TIntermTyped *RewriteModifiedStructFieldSelectionExpression(
217     TCompiler *compiler,
218     TIntermBinary *node,
219     const StructureMap &structureMap,
220     const StructureUniformMap &structureUniformMap,
221     const ExtractedSamplerMap &extractedSamplers)
222 {
223     ASSERT(node->getOp() == EOpIndexDirectStruct);
224 
225     const bool isSampler = node->getType().isSampler();
226 
227     TIntermSymbol *baseUniform = nullptr;
228     std::string samplerName;
229 
230     TVector<TIntermBinary *> indexNodeStack;
231 
232     // Iterate once and build the name of the sampler.
233     TIntermBinary *iter = node;
234     while (baseUniform == nullptr)
235     {
236         indexNodeStack.push_back(iter);
237         baseUniform = iter->getLeft()->getAsSymbolNode();
238 
239         if (isSampler)
240         {
241             if (iter->getOp() == EOpIndexDirectStruct)
242             {
243                 // When indexed into a struct, get the field name instead and construct the sampler
244                 // name.
245                 samplerName.insert(0, iter->getIndexStructFieldName().data());
246                 samplerName.insert(0, "_");
247             }
248 
249             if (baseUniform)
250             {
251                 // If left is a symbol, we have reached the end of the chain.  Use the struct name
252                 // to finish building the name of the sampler.
253                 samplerName.insert(0, baseUniform->variable().name().data());
254             }
255         }
256 
257         iter = iter->getLeft()->getAsBinaryNode();
258     }
259 
260     TIntermTyped *rewritten = nullptr;
261 
262     if (isSampler)
263     {
264         ASSERT(extractedSamplers.find(samplerName) != extractedSamplers.end());
265         rewritten = new TIntermSymbol(extractedSamplers.at(samplerName));
266     }
267     else
268     {
269         const TVariable *baseUniformVar = &baseUniform->variable();
270         ASSERT(structureUniformMap.find(baseUniformVar) != structureUniformMap.end());
271         rewritten = new TIntermSymbol(structureUniformMap.at(baseUniformVar));
272     }
273 
274     // Iterate again and build the expression from bottom up.
275     for (auto it = indexNodeStack.rbegin(); it != indexNodeStack.rend(); ++it)
276     {
277         TIntermBinary *indexNode = *it;
278 
279         switch (indexNode->getOp())
280         {
281             case EOpIndexDirectStruct:
282                 if (!isSampler)
283                 {
284                     // Remap the field.
285                     const TStructure *structure = indexNode->getLeft()->getType().getStruct();
286                     ASSERT(structureMap.find(structure) != structureMap.end());
287 
288                     TIntermConstantUnion *asConstantUnion =
289                         indexNode->getRight()->getAsConstantUnion();
290                     ASSERT(asConstantUnion);
291 
292                     const int fieldIndex = asConstantUnion->getIConst(0);
293                     ASSERT(fieldIndex <
294                            static_cast<int>(structureMap.at(structure).fieldMap.size()));
295 
296                     const int mappedFieldIndex = structureMap.at(structure).fieldMap[fieldIndex];
297 
298                     rewritten = new TIntermBinary(EOpIndexDirectStruct, rewritten,
299                                                   CreateIndexNode(mappedFieldIndex));
300                 }
301                 break;
302 
303             case EOpIndexDirect:
304                 rewritten = new TIntermBinary(EOpIndexDirect, rewritten, indexNode->getRight());
305                 break;
306 
307             case EOpIndexIndirect:
308             {
309                 // Run RewriteExpressionTraverser on the right node.  It may itself be an expression
310                 // with a sampler inside that needs to be rewritten, or simply use a field of a
311                 // struct that's remapped.
312                 TIntermTyped *indexExpression = indexNode->getRight();
313                 RewriteIndexExpression(compiler, indexExpression, structureMap, structureUniformMap,
314                                        extractedSamplers);
315                 rewritten = new TIntermBinary(EOpIndexIndirect, rewritten, indexExpression);
316                 break;
317             }
318 
319             default:
320                 UNREACHABLE();
321                 break;
322         }
323     }
324 
325     return rewritten;
326 }
327 
328 class RewriteStructSamplersTraverser final : public TIntermTraverser
329 {
330   public:
RewriteStructSamplersTraverser(TCompiler * compiler,TSymbolTable * symbolTable)331     explicit RewriteStructSamplersTraverser(TCompiler *compiler, TSymbolTable *symbolTable)
332         : TIntermTraverser(true, false, false, symbolTable),
333           mCompiler(compiler),
334           mRemovedUniformsCount(0)
335     {}
336 
removedUniformsCount() const337     int removedUniformsCount() const { return mRemovedUniformsCount; }
338 
339     // Each struct sampler declaration is stripped of its samplers. New uniforms are added for each
340     // stripped struct sampler.
visitDeclaration(Visit visit,TIntermDeclaration * decl)341     bool visitDeclaration(Visit visit, TIntermDeclaration *decl) override
342     {
343         if (!mInGlobalScope)
344         {
345             return true;
346         }
347 
348         const TIntermSequence &sequence = *(decl->getSequence());
349         TIntermTyped *declarator        = sequence.front()->getAsTyped();
350         const TType &type               = declarator->getType();
351 
352         if (!type.isStructureContainingSamplers())
353         {
354             return false;
355         }
356 
357         TIntermSequence newSequence;
358 
359         if (type.isStructSpecifier())
360         {
361             // If this is just a struct definition (not a uniform variable declaration of a
362             // struct type), just remove the samplers.  They are not instantiated yet.
363             const TStructure *structure = type.getStruct();
364             ASSERT(structure && mStructureMap.find(structure) == mStructureMap.end());
365 
366             stripStructSpecifierSamplers(structure, &newSequence);
367         }
368         else
369         {
370             const TStructure *structure = type.getStruct();
371 
372             // If the structure is defined at the same time, create the mapping to the stripped
373             // version first.
374             if (mStructureMap.find(structure) == mStructureMap.end())
375             {
376                 stripStructSpecifierSamplers(structure, &newSequence);
377             }
378 
379             // Then, extract the samplers from the struct and create global-scope variables instead.
380             TIntermSymbol *asSymbol = declarator->getAsSymbolNode();
381             ASSERT(asSymbol);
382             const TVariable &variable = asSymbol->variable();
383             ASSERT(variable.symbolType() != SymbolType::Empty);
384 
385             extractStructSamplerUniforms(variable, structure, &newSequence);
386         }
387 
388         mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), decl,
389                                         std::move(newSequence));
390 
391         return false;
392     }
393 
394     // Same implementation as in RewriteExpressionTraverser.  That traverser cannot replace root.
visitBinary(Visit visit,TIntermBinary * node)395     bool visitBinary(Visit visit, TIntermBinary *node) override
396     {
397         TIntermTyped *rewritten = RewriteExpressionVisitBinaryHelper(
398             mCompiler, node, mStructureMap, mStructureUniformMap, mExtractedSamplers);
399 
400         if (rewritten == nullptr)
401         {
402             return true;
403         }
404 
405         queueReplacement(rewritten, OriginalNode::IS_DROPPED);
406 
407         // Don't iterate as the expression is rewritten.
408         return false;
409     }
410 
411     // Same implementation as in RewriteExpressionTraverser.  That traverser cannot replace root.
visitSymbol(TIntermSymbol * node)412     void visitSymbol(TIntermSymbol *node) override
413     {
414         ASSERT(mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end());
415     }
416 
417   private:
418     // Removes all samplers from a struct specifier.
stripStructSpecifierSamplers(const TStructure * structure,TIntermSequence * newSequence)419     void stripStructSpecifierSamplers(const TStructure *structure, TIntermSequence *newSequence)
420     {
421         TFieldList *newFieldList = new TFieldList;
422         ASSERT(structure->containsSamplers());
423 
424         // Add this struct to the struct map
425         ASSERT(mStructureMap.find(structure) == mStructureMap.end());
426         StructureData *modifiedData = &mStructureMap[structure];
427 
428         modifiedData->modified = nullptr;
429         modifiedData->fieldMap.resize(structure->fields().size(), std::numeric_limits<int>::max());
430 
431         for (size_t fieldIndex = 0; fieldIndex < structure->fields().size(); ++fieldIndex)
432         {
433             const TField *field    = structure->fields()[fieldIndex];
434             const TType &fieldType = *field->type();
435 
436             // If the field is a sampler, or a struct that's entirely removed, skip it.
437             if (!fieldType.isSampler() && !isRemovedStructType(fieldType))
438             {
439                 TType *newType = nullptr;
440 
441                 // Otherwise, if it's a struct that's replaced, create a new field of the replaced
442                 // type.
443                 if (fieldType.isStructureContainingSamplers())
444                 {
445                     const TStructure *fieldStruct = fieldType.getStruct();
446                     ASSERT(mStructureMap.find(fieldStruct) != mStructureMap.end());
447 
448                     const TStructure *modifiedStruct = mStructureMap[fieldStruct].modified;
449                     ASSERT(modifiedStruct);
450 
451                     newType = new TType(modifiedStruct, true);
452                     if (fieldType.isArray())
453                     {
454                         newType->makeArrays(fieldType.getArraySizes());
455                     }
456                 }
457                 else
458                 {
459                     // If not, duplicate the field as is.
460                     newType = new TType(fieldType);
461                 }
462 
463                 // Record the mapping of the field indices, so future EOpIndexDirectStruct's into
464                 // this struct can be fixed up.
465                 modifiedData->fieldMap[fieldIndex] = static_cast<int>(newFieldList->size());
466 
467                 TField *newField =
468                     new TField(newType, field->name(), field->line(), field->symbolType());
469                 newFieldList->push_back(newField);
470             }
471         }
472 
473         // Prune empty structs.
474         if (newFieldList->empty())
475         {
476             return;
477         }
478 
479         // Declare a new struct with the same name and the new fields.
480         modifiedData->modified =
481             new TStructure(mSymbolTable,
482                            structure->symbolType() == SymbolType::Empty ? kEmptyImmutableString
483                                                                         : structure->name(),
484                            newFieldList, structure->symbolType());
485         TType *newStructType = new TType(modifiedData->modified, true);
486         TVariable *newStructVar =
487             new TVariable(mSymbolTable, kEmptyImmutableString, newStructType, SymbolType::Empty);
488         TIntermSymbol *newStructRef = new TIntermSymbol(newStructVar);
489 
490         TIntermDeclaration *structDecl = new TIntermDeclaration;
491         structDecl->appendDeclarator(newStructRef);
492 
493         newSequence->push_back(structDecl);
494     }
495 
496     // Returns true if the type is a struct that was removed because we extracted all the members.
isRemovedStructType(const TType & type) const497     bool isRemovedStructType(const TType &type) const
498     {
499         const TStructure *structure = type.getStruct();
500         if (structure == nullptr)
501         {
502             // Not a struct
503             return false;
504         }
505 
506         // A struct is removed if it is in the map, but doesn't have a replacement struct.
507         auto iter = mStructureMap.find(structure);
508         return iter != mStructureMap.end() && iter->second.modified == nullptr;
509     }
510 
511     // Removes samplers from struct uniforms. For each sampler removed also adds a new globally
512     // defined sampler uniform.
extractStructSamplerUniforms(const TVariable & variable,const TStructure * structure,TIntermSequence * newSequence)513     void extractStructSamplerUniforms(const TVariable &variable,
514                                       const TStructure *structure,
515                                       TIntermSequence *newSequence)
516     {
517         ASSERT(structure->containsSamplers());
518         ASSERT(mStructureMap.find(structure) != mStructureMap.end());
519 
520         const TType &type = variable.getType();
521         enterArray(type);
522 
523         for (const TField *field : structure->fields())
524         {
525             extractFieldSamplers(variable.name().data(), field, newSequence);
526         }
527 
528         // If there's a replacement structure (because there are non-sampler fields in the struct),
529         // add a declaration with that type.
530         const TStructure *modified = mStructureMap[structure].modified;
531         if (modified != nullptr)
532         {
533             TType *newType = new TType(modified, false);
534             if (type.isArray())
535             {
536                 newType->makeArrays(type.getArraySizes());
537             }
538             newType->setQualifier(EvqUniform);
539             const TVariable *newVariable =
540                 new TVariable(mSymbolTable, variable.name(), newType, variable.symbolType());
541 
542             TIntermDeclaration *newDecl = new TIntermDeclaration();
543             newDecl->appendDeclarator(new TIntermSymbol(newVariable));
544 
545             newSequence->push_back(newDecl);
546 
547             ASSERT(mStructureUniformMap.find(&variable) == mStructureUniformMap.end());
548             mStructureUniformMap[&variable] = newVariable;
549         }
550         else
551         {
552             mRemovedUniformsCount++;
553         }
554 
555         exitArray(type);
556     }
557 
558     // Extracts samplers from a field of a struct. Works with nested structs and arrays.
extractFieldSamplers(const std::string & prefix,const TField * field,TIntermSequence * newSequence)559     void extractFieldSamplers(const std::string &prefix,
560                               const TField *field,
561                               TIntermSequence *newSequence)
562     {
563         const TType &fieldType = *field->type();
564         if (fieldType.isSampler() || fieldType.isStructureContainingSamplers())
565         {
566             std::string newPrefix = prefix + "_" + field->name().data();
567 
568             if (fieldType.isSampler())
569             {
570                 extractSampler(newPrefix, fieldType, newSequence);
571             }
572             else
573             {
574                 enterArray(fieldType);
575                 const TStructure *structure = fieldType.getStruct();
576                 for (const TField *nestedField : structure->fields())
577                 {
578                     extractFieldSamplers(newPrefix, nestedField, newSequence);
579                 }
580                 exitArray(fieldType);
581             }
582         }
583     }
584 
GenerateArraySizesFromStack(TVector<unsigned int> * sizesOut)585     void GenerateArraySizesFromStack(TVector<unsigned int> *sizesOut)
586     {
587         sizesOut->reserve(mArraySizeStack.size());
588 
589         for (auto it = mArraySizeStack.rbegin(); it != mArraySizeStack.rend(); ++it)
590         {
591             sizesOut->push_back(*it);
592         }
593     }
594 
595     // Extracts a sampler from a struct. Declares the new extracted sampler.
extractSampler(const std::string & newName,const TType & fieldType,TIntermSequence * newSequence)596     void extractSampler(const std::string &newName,
597                         const TType &fieldType,
598                         TIntermSequence *newSequence)
599     {
600         ASSERT(fieldType.isSampler());
601 
602         TType *newType = new TType(fieldType);
603 
604         // Add array dimensions accumulated so far due to struct arrays.  Note that to support
605         // nested arrays, mArraySizeStack has the outermost size in the front.  |makeArrays| thus
606         // expects this in reverse order.
607         TVector<unsigned int> parentArraySizes;
608         GenerateArraySizesFromStack(&parentArraySizes);
609         newType->makeArrays(parentArraySizes);
610 
611         ImmutableStringBuilder nameBuilder(newName.size() + 1);
612         nameBuilder << newName;
613 
614         newType->setQualifier(EvqUniform);
615         TVariable *newVariable =
616             new TVariable(mSymbolTable, nameBuilder, newType, SymbolType::AngleInternal);
617         TIntermSymbol *newSymbol = new TIntermSymbol(newVariable);
618 
619         TIntermDeclaration *samplerDecl = new TIntermDeclaration;
620         samplerDecl->appendDeclarator(newSymbol);
621 
622         newSequence->push_back(samplerDecl);
623 
624         // TODO: Use a temp name instead of generating a name as currently done.  There is no
625         // guarantee that these generated names cannot clash.  Create a mapping from the previous
626         // name to the name assigned to the temp variable so ShaderVariable::mappedName can be
627         // updated post-transformation.  http://anglebug.com/4301
628         ASSERT(mExtractedSamplers.find(newName) == mExtractedSamplers.end());
629         mExtractedSamplers[newName] = newVariable;
630     }
631 
enterArray(const TType & arrayType)632     void enterArray(const TType &arrayType)
633     {
634         const TSpan<const unsigned int> &arraySizes = arrayType.getArraySizes();
635         for (auto it = arraySizes.rbegin(); it != arraySizes.rend(); ++it)
636         {
637             unsigned int arraySize = *it;
638             mArraySizeStack.push_back(arraySize);
639         }
640     }
641 
exitArray(const TType & arrayType)642     void exitArray(const TType &arrayType)
643     {
644         mArraySizeStack.resize(mArraySizeStack.size() - arrayType.getNumArraySizes());
645     }
646 
647     TCompiler *mCompiler;
648     int mRemovedUniformsCount;
649 
650     // Map structures with samplers to ones that have their samplers removed.
651     StructureMap mStructureMap;
652 
653     // Map uniform variables of structure type that are replaced with another variable.
654     StructureUniformMap mStructureUniformMap;
655 
656     // Map a constructed sampler name to its variable.  Used to replace an expression that uses this
657     // sampler with the extracted one.
658     ExtractedSamplerMap mExtractedSamplers;
659 
660     // A stack of array sizes.  Used to figure out the array dimensions of the extracted sampler,
661     // for example when it's nested in an array of structs in an array of structs.
662     TVector<unsigned int> mArraySizeStack;
663 };
664 }  // anonymous namespace
665 
RewriteStructSamplers(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,int * removedUniformsCountOut)666 bool RewriteStructSamplers(TCompiler *compiler,
667                            TIntermBlock *root,
668                            TSymbolTable *symbolTable,
669                            int *removedUniformsCountOut)
670 {
671     RewriteStructSamplersTraverser traverser(compiler, symbolTable);
672     root->traverse(&traverser);
673     *removedUniformsCountOut = traverser.removedUniformsCount();
674     return traverser.updateTree(compiler, root);
675 }
676 }  // namespace sh
677