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
7 #include "angle_gl.h"
8 #include "compiler/translator/SymbolTable.h"
9 #include "compiler/translator/VariableInfo.h"
10 #include "compiler/translator/util.h"
11 #include "common/utilities.h"
12
13 namespace sh
14 {
15
16 namespace
17 {
18
GetBlockLayoutType(TLayoutBlockStorage blockStorage)19 BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
20 {
21 switch (blockStorage)
22 {
23 case EbsPacked: return BLOCKLAYOUT_PACKED;
24 case EbsShared: return BLOCKLAYOUT_SHARED;
25 case EbsStd140: return BLOCKLAYOUT_STANDARD;
26 default: UNREACHABLE(); return BLOCKLAYOUT_SHARED;
27 }
28 }
29
ExpandUserDefinedVariable(const ShaderVariable & variable,const std::string & name,const std::string & mappedName,bool markStaticUse,std::vector<ShaderVariable> * expanded)30 void ExpandUserDefinedVariable(const ShaderVariable &variable,
31 const std::string &name,
32 const std::string &mappedName,
33 bool markStaticUse,
34 std::vector<ShaderVariable> *expanded)
35 {
36 ASSERT(variable.isStruct());
37
38 const std::vector<ShaderVariable> &fields = variable.fields;
39
40 for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
41 {
42 const ShaderVariable &field = fields[fieldIndex];
43 ExpandVariable(field,
44 name + "." + field.name,
45 mappedName + "." + field.mappedName,
46 markStaticUse,
47 expanded);
48 }
49 }
50
51 template <class VarT>
FindVariable(const TString & name,std::vector<VarT> * infoList)52 VarT *FindVariable(const TString &name,
53 std::vector<VarT> *infoList)
54 {
55 // TODO(zmo): optimize this function.
56 for (size_t ii = 0; ii < infoList->size(); ++ii)
57 {
58 if ((*infoList)[ii].name.c_str() == name)
59 return &((*infoList)[ii]);
60 }
61
62 return NULL;
63 }
64
65 }
66
CollectVariables(std::vector<sh::Attribute> * attribs,std::vector<sh::OutputVariable> * outputVariables,std::vector<sh::Uniform> * uniforms,std::vector<sh::Varying> * varyings,std::vector<sh::InterfaceBlock> * interfaceBlocks,ShHashFunction64 hashFunction,const TSymbolTable & symbolTable,const TExtensionBehavior & extensionBehavior)67 CollectVariables::CollectVariables(std::vector<sh::Attribute> *attribs,
68 std::vector<sh::OutputVariable> *outputVariables,
69 std::vector<sh::Uniform> *uniforms,
70 std::vector<sh::Varying> *varyings,
71 std::vector<sh::InterfaceBlock> *interfaceBlocks,
72 ShHashFunction64 hashFunction,
73 const TSymbolTable &symbolTable,
74 const TExtensionBehavior &extensionBehavior)
75 : TIntermTraverser(true, false, false),
76 mAttribs(attribs),
77 mOutputVariables(outputVariables),
78 mUniforms(uniforms),
79 mVaryings(varyings),
80 mInterfaceBlocks(interfaceBlocks),
81 mDepthRangeAdded(false),
82 mPointCoordAdded(false),
83 mFrontFacingAdded(false),
84 mFragCoordAdded(false),
85 mInstanceIDAdded(false),
86 mVertexIDAdded(false),
87 mPositionAdded(false),
88 mPointSizeAdded(false),
89 mLastFragDataAdded(false),
90 mFragColorAdded(false),
91 mFragDataAdded(false),
92 mFragDepthEXTAdded(false),
93 mFragDepthAdded(false),
94 mSecondaryFragColorEXTAdded(false),
95 mSecondaryFragDataEXTAdded(false),
96 mHashFunction(hashFunction),
97 mSymbolTable(symbolTable),
98 mExtensionBehavior(extensionBehavior)
99 {
100 }
101
102 // We want to check whether a uniform/varying is statically used
103 // because we only count the used ones in packing computing.
104 // Also, gl_FragCoord, gl_PointCoord, and gl_FrontFacing count
105 // toward varying counting if they are statically used in a fragment
106 // shader.
visitSymbol(TIntermSymbol * symbol)107 void CollectVariables::visitSymbol(TIntermSymbol *symbol)
108 {
109 ASSERT(symbol != NULL);
110 ShaderVariable *var = NULL;
111 const TString &symbolName = symbol->getSymbol();
112
113 if (IsVarying(symbol->getQualifier()))
114 {
115 var = FindVariable(symbolName, mVaryings);
116 }
117 else if (symbol->getType().getBasicType() == EbtInterfaceBlock)
118 {
119 UNREACHABLE();
120 }
121 else if (symbolName == "gl_DepthRange")
122 {
123 ASSERT(symbol->getQualifier() == EvqUniform);
124
125 if (!mDepthRangeAdded)
126 {
127 Uniform info;
128 const char kName[] = "gl_DepthRange";
129 info.name = kName;
130 info.mappedName = kName;
131 info.type = GL_STRUCT_ANGLEX;
132 info.arraySize = 0;
133 info.precision = GL_NONE;
134 info.staticUse = true;
135
136 ShaderVariable nearInfo;
137 const char kNearName[] = "near";
138 nearInfo.name = kNearName;
139 nearInfo.mappedName = kNearName;
140 nearInfo.type = GL_FLOAT;
141 nearInfo.arraySize = 0;
142 nearInfo.precision = GL_HIGH_FLOAT;
143 nearInfo.staticUse = true;
144
145 ShaderVariable farInfo;
146 const char kFarName[] = "far";
147 farInfo.name = kFarName;
148 farInfo.mappedName = kFarName;
149 farInfo.type = GL_FLOAT;
150 farInfo.arraySize = 0;
151 farInfo.precision = GL_HIGH_FLOAT;
152 farInfo.staticUse = true;
153
154 ShaderVariable diffInfo;
155 const char kDiffName[] = "diff";
156 diffInfo.name = kDiffName;
157 diffInfo.mappedName = kDiffName;
158 diffInfo.type = GL_FLOAT;
159 diffInfo.arraySize = 0;
160 diffInfo.precision = GL_HIGH_FLOAT;
161 diffInfo.staticUse = true;
162
163 info.fields.push_back(nearInfo);
164 info.fields.push_back(farInfo);
165 info.fields.push_back(diffInfo);
166
167 mUniforms->push_back(info);
168 mDepthRangeAdded = true;
169 }
170 }
171 else
172 {
173 switch (symbol->getQualifier())
174 {
175 case EvqAttribute:
176 case EvqVertexIn:
177 var = FindVariable(symbolName, mAttribs);
178 break;
179 case EvqFragmentOut:
180 var = FindVariable(symbolName, mOutputVariables);
181 break;
182 case EvqUniform:
183 {
184 const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
185 if (interfaceBlock)
186 {
187 InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
188 ASSERT(namedBlock);
189 var = FindVariable(symbolName, &namedBlock->fields);
190
191 // Set static use on the parent interface block here
192 namedBlock->staticUse = true;
193 }
194 else
195 {
196 var = FindVariable(symbolName, mUniforms);
197 }
198
199 // It's an internal error to reference an undefined user uniform
200 ASSERT(symbolName.compare(0, 3, "gl_") != 0 || var);
201 }
202 break;
203 case EvqFragCoord:
204 if (!mFragCoordAdded)
205 {
206 Varying info;
207 const char kName[] = "gl_FragCoord";
208 info.name = kName;
209 info.mappedName = kName;
210 info.type = GL_FLOAT_VEC4;
211 info.arraySize = 0;
212 info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
213 info.staticUse = true;
214 info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
215 mVaryings->push_back(info);
216 mFragCoordAdded = true;
217 }
218 return;
219 case EvqFrontFacing:
220 if (!mFrontFacingAdded)
221 {
222 Varying info;
223 const char kName[] = "gl_FrontFacing";
224 info.name = kName;
225 info.mappedName = kName;
226 info.type = GL_BOOL;
227 info.arraySize = 0;
228 info.precision = GL_NONE;
229 info.staticUse = true;
230 info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
231 mVaryings->push_back(info);
232 mFrontFacingAdded = true;
233 }
234 return;
235 case EvqPointCoord:
236 if (!mPointCoordAdded)
237 {
238 Varying info;
239 const char kName[] = "gl_PointCoord";
240 info.name = kName;
241 info.mappedName = kName;
242 info.type = GL_FLOAT_VEC2;
243 info.arraySize = 0;
244 info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
245 info.staticUse = true;
246 info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
247 mVaryings->push_back(info);
248 mPointCoordAdded = true;
249 }
250 return;
251 case EvqInstanceID:
252 if (!mInstanceIDAdded)
253 {
254 Attribute info;
255 const char kName[] = "gl_InstanceID";
256 info.name = kName;
257 info.mappedName = kName;
258 info.type = GL_INT;
259 info.arraySize = 0;
260 info.precision = GL_HIGH_INT; // Defined by spec.
261 info.staticUse = true;
262 info.location = -1;
263 mAttribs->push_back(info);
264 mInstanceIDAdded = true;
265 }
266 return;
267 case EvqVertexID:
268 if (!mVertexIDAdded)
269 {
270 Attribute info;
271 const char kName[] = "gl_VertexID";
272 info.name = kName;
273 info.mappedName = kName;
274 info.type = GL_INT;
275 info.arraySize = 0;
276 info.precision = GL_HIGH_INT; // Defined by spec.
277 info.staticUse = true;
278 info.location = -1;
279 mAttribs->push_back(info);
280 mVertexIDAdded = true;
281 }
282 return;
283 case EvqPosition:
284 if (!mPositionAdded)
285 {
286 Varying info;
287 const char kName[] = "gl_Position";
288 info.name = kName;
289 info.mappedName = kName;
290 info.type = GL_FLOAT_VEC4;
291 info.arraySize = 0;
292 info.precision = GL_HIGH_FLOAT; // Defined by spec.
293 info.staticUse = true;
294 info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
295 mVaryings->push_back(info);
296 mPositionAdded = true;
297 }
298 return;
299 case EvqPointSize:
300 if (!mPointSizeAdded)
301 {
302 Varying info;
303 const char kName[] = "gl_PointSize";
304 info.name = kName;
305 info.mappedName = kName;
306 info.type = GL_FLOAT;
307 info.arraySize = 0;
308 info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
309 info.staticUse = true;
310 info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
311 mVaryings->push_back(info);
312 mPointSizeAdded = true;
313 }
314 return;
315 case EvqLastFragData:
316 if (!mLastFragDataAdded)
317 {
318 Varying info;
319 const char kName[] = "gl_LastFragData";
320 info.name = kName;
321 info.mappedName = kName;
322 info.type = GL_FLOAT_VEC4;
323 info.arraySize = static_cast<const TVariable*>(mSymbolTable.findBuiltIn("gl_MaxDrawBuffers", 100))->getConstPointer()->getIConst();
324 info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
325 info.staticUse = true;
326 info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
327 mVaryings->push_back(info);
328 mLastFragDataAdded = true;
329 }
330 return;
331 case EvqFragColor:
332 if (!mFragColorAdded)
333 {
334 OutputVariable info;
335 const char kName[] = "gl_FragColor";
336 info.name = kName;
337 info.mappedName = kName;
338 info.type = GL_FLOAT_VEC4;
339 info.arraySize = 0;
340 info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
341 info.staticUse = true;
342 mOutputVariables->push_back(info);
343 mFragColorAdded = true;
344 }
345 return;
346 case EvqFragData:
347 if (!mFragDataAdded)
348 {
349 OutputVariable info;
350 const char kName[] = "gl_FragData";
351 info.name = kName;
352 info.mappedName = kName;
353 info.type = GL_FLOAT_VEC4;
354 if (::IsExtensionEnabled(mExtensionBehavior, "GL_EXT_draw_buffers"))
355 {
356 info.arraySize = static_cast<const TVariable *>(
357 mSymbolTable.findBuiltIn("gl_MaxDrawBuffers", 100))
358 ->getConstPointer()
359 ->getIConst();
360 }
361 else
362 {
363 info.arraySize = 1;
364 }
365 info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
366 info.staticUse = true;
367 mOutputVariables->push_back(info);
368 mFragDataAdded = true;
369 }
370 return;
371 case EvqFragDepthEXT:
372 if (!mFragDepthEXTAdded)
373 {
374 OutputVariable info;
375 const char kName[] = "gl_FragDepthEXT";
376 info.name = kName;
377 info.mappedName = kName;
378 info.type = GL_FLOAT;
379 info.arraySize = 0;
380 info.precision =
381 GLVariablePrecision(static_cast<const TVariable *>(
382 mSymbolTable.findBuiltIn("gl_FragDepthEXT", 100))
383 ->getType());
384 info.staticUse = true;
385 mOutputVariables->push_back(info);
386 mFragDepthEXTAdded = true;
387 }
388 return;
389 case EvqFragDepth:
390 if (!mFragDepthAdded)
391 {
392 OutputVariable info;
393 const char kName[] = "gl_FragDepth";
394 info.name = kName;
395 info.mappedName = kName;
396 info.type = GL_FLOAT;
397 info.arraySize = 0;
398 info.precision = GL_HIGH_FLOAT;
399 info.staticUse = true;
400 mOutputVariables->push_back(info);
401 mFragDepthAdded = true;
402 }
403 return;
404 case EvqSecondaryFragColorEXT:
405 if (!mSecondaryFragColorEXTAdded)
406 {
407 OutputVariable info;
408 const char kName[] = "gl_SecondaryFragColorEXT";
409 info.name = kName;
410 info.mappedName = kName;
411 info.type = GL_FLOAT_VEC4;
412 info.arraySize = 0;
413 info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
414 info.staticUse = true;
415 mOutputVariables->push_back(info);
416 mSecondaryFragColorEXTAdded = true;
417 }
418 return;
419 case EvqSecondaryFragDataEXT:
420 if (!mSecondaryFragDataEXTAdded)
421 {
422 OutputVariable info;
423 const char kName[] = "gl_SecondaryFragDataEXT";
424 info.name = kName;
425 info.mappedName = kName;
426 info.type = GL_FLOAT_VEC4;
427
428 const TVariable *maxDualSourceDrawBuffersVar = static_cast<const TVariable *>(
429 mSymbolTable.findBuiltIn("gl_MaxDualSourceDrawBuffersEXT", 100));
430 info.arraySize = maxDualSourceDrawBuffersVar->getConstPointer()->getIConst();
431 info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
432 info.staticUse = true;
433 mOutputVariables->push_back(info);
434 mSecondaryFragDataEXTAdded = true;
435 }
436 return;
437 default:
438 break;
439 }
440 }
441 if (var)
442 {
443 var->staticUse = true;
444 }
445 }
446
447 class NameHashingTraverser : public GetVariableTraverser
448 {
449 public:
NameHashingTraverser(ShHashFunction64 hashFunction,const TSymbolTable & symbolTable)450 NameHashingTraverser(ShHashFunction64 hashFunction,
451 const TSymbolTable &symbolTable)
452 : GetVariableTraverser(symbolTable),
453 mHashFunction(hashFunction)
454 {}
455
456 private:
visitVariable(ShaderVariable * variable)457 void visitVariable(ShaderVariable *variable) override
458 {
459 TString stringName = TString(variable->name.c_str());
460 variable->mappedName = TIntermTraverser::hash(stringName, mHashFunction).c_str();
461 }
462
463 ShHashFunction64 mHashFunction;
464 };
465
466 // Attributes, which cannot have struct fields, are a special case
467 template <>
visitVariable(const TIntermSymbol * variable,std::vector<Attribute> * infoList) const468 void CollectVariables::visitVariable(const TIntermSymbol *variable,
469 std::vector<Attribute> *infoList) const
470 {
471 ASSERT(variable);
472 const TType &type = variable->getType();
473 ASSERT(!type.getStruct());
474
475 Attribute attribute;
476
477 attribute.type = GLVariableType(type);
478 attribute.precision = GLVariablePrecision(type);
479 attribute.name = variable->getSymbol().c_str();
480 attribute.arraySize = type.getArraySize();
481 attribute.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str();
482 attribute.location = variable->getType().getLayoutQualifier().location;
483
484 infoList->push_back(attribute);
485 }
486
487 template <>
visitVariable(const TIntermSymbol * variable,std::vector<OutputVariable> * infoList) const488 void CollectVariables::visitVariable(const TIntermSymbol *variable,
489 std::vector<OutputVariable> *infoList) const
490 {
491 ASSERT(variable);
492 const TType &type = variable->getType();
493 ASSERT(!type.getStruct());
494
495 OutputVariable attribute;
496
497 attribute.type = GLVariableType(type);
498 attribute.precision = GLVariablePrecision(type);
499 attribute.name = variable->getSymbol().c_str();
500 attribute.arraySize = type.getArraySize();
501 attribute.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str();
502 attribute.location = variable->getType().getLayoutQualifier().location;
503
504 infoList->push_back(attribute);
505 }
506
507 template <>
visitVariable(const TIntermSymbol * variable,std::vector<InterfaceBlock> * infoList) const508 void CollectVariables::visitVariable(const TIntermSymbol *variable,
509 std::vector<InterfaceBlock> *infoList) const
510 {
511 InterfaceBlock interfaceBlock;
512 const TInterfaceBlock *blockType = variable->getType().getInterfaceBlock();
513 ASSERT(blockType);
514
515 interfaceBlock.name = blockType->name().c_str();
516 interfaceBlock.mappedName =
517 TIntermTraverser::hash(blockType->name().c_str(), mHashFunction).c_str();
518 interfaceBlock.instanceName = (blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
519 interfaceBlock.arraySize = variable->getArraySize();
520 interfaceBlock.isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
521 interfaceBlock.layout = GetBlockLayoutType(blockType->blockStorage());
522
523 // Gather field information
524 for (const TField *field : blockType->fields())
525 {
526 const TType &fieldType = *field->type();
527
528 NameHashingTraverser traverser(mHashFunction, mSymbolTable);
529 traverser.traverse(fieldType, field->name(), &interfaceBlock.fields);
530
531 interfaceBlock.fields.back().isRowMajorLayout = (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
532 }
533
534 infoList->push_back(interfaceBlock);
535 }
536
537 template <typename VarT>
visitVariable(const TIntermSymbol * variable,std::vector<VarT> * infoList) const538 void CollectVariables::visitVariable(const TIntermSymbol *variable,
539 std::vector<VarT> *infoList) const
540 {
541 NameHashingTraverser traverser(mHashFunction, mSymbolTable);
542 traverser.traverse(variable->getType(), variable->getSymbol(), infoList);
543 }
544
545 template <typename VarT>
visitInfoList(const TIntermSequence & sequence,std::vector<VarT> * infoList) const546 void CollectVariables::visitInfoList(const TIntermSequence &sequence,
547 std::vector<VarT> *infoList) const
548 {
549 for (size_t seqIndex = 0; seqIndex < sequence.size(); seqIndex++)
550 {
551 const TIntermSymbol *variable = sequence[seqIndex]->getAsSymbolNode();
552 // The only case in which the sequence will not contain a
553 // TIntermSymbol node is initialization. It will contain a
554 // TInterBinary node in that case. Since attributes, uniforms,
555 // and varyings cannot be initialized in a shader, we must have
556 // only TIntermSymbol nodes in the sequence.
557 ASSERT(variable != NULL);
558 visitVariable(variable, infoList);
559 }
560 }
561
visitDeclaration(Visit,TIntermDeclaration * node)562 bool CollectVariables::visitDeclaration(Visit, TIntermDeclaration *node)
563 {
564 const TIntermSequence &sequence = *(node->getSequence());
565 ASSERT(!sequence.empty());
566
567 const TIntermTyped &typedNode = *(sequence.front()->getAsTyped());
568 TQualifier qualifier = typedNode.getQualifier();
569
570 if (typedNode.getBasicType() == EbtInterfaceBlock)
571 {
572 visitInfoList(sequence, mInterfaceBlocks);
573 return false;
574 }
575 else if (qualifier == EvqAttribute || qualifier == EvqVertexIn || qualifier == EvqFragmentOut ||
576 qualifier == EvqUniform || IsVarying(qualifier))
577 {
578 switch (qualifier)
579 {
580 case EvqAttribute:
581 case EvqVertexIn:
582 visitInfoList(sequence, mAttribs);
583 break;
584 case EvqFragmentOut:
585 visitInfoList(sequence, mOutputVariables);
586 break;
587 case EvqUniform:
588 visitInfoList(sequence, mUniforms);
589 break;
590 default:
591 visitInfoList(sequence, mVaryings);
592 break;
593 }
594
595 return false;
596 }
597
598 return true;
599 }
600
visitBinary(Visit,TIntermBinary * binaryNode)601 bool CollectVariables::visitBinary(Visit, TIntermBinary *binaryNode)
602 {
603 if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
604 {
605 // NOTE: we do not determine static use for individual blocks of an array
606 TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped();
607 ASSERT(blockNode);
608
609 TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion();
610 ASSERT(constantUnion);
611
612 const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
613 InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
614 ASSERT(namedBlock);
615 namedBlock->staticUse = true;
616
617 unsigned int fieldIndex = constantUnion->getUConst(0);
618 ASSERT(fieldIndex < namedBlock->fields.size());
619 namedBlock->fields[fieldIndex].staticUse = true;
620 return false;
621 }
622
623 return true;
624 }
625
ExpandVariable(const ShaderVariable & variable,const std::string & name,const std::string & mappedName,bool markStaticUse,std::vector<ShaderVariable> * expanded)626 void ExpandVariable(const ShaderVariable &variable,
627 const std::string &name,
628 const std::string &mappedName,
629 bool markStaticUse,
630 std::vector<ShaderVariable> *expanded)
631 {
632 if (variable.isStruct())
633 {
634 if (variable.isArray())
635 {
636 for (unsigned int elementIndex = 0; elementIndex < variable.elementCount();
637 elementIndex++)
638 {
639 std::string lname = name + ::ArrayString(elementIndex);
640 std::string lmappedName = mappedName + ::ArrayString(elementIndex);
641 ExpandUserDefinedVariable(variable, lname, lmappedName, markStaticUse, expanded);
642 }
643 }
644 else
645 {
646 ExpandUserDefinedVariable(variable, name, mappedName, markStaticUse, expanded);
647 }
648 }
649 else
650 {
651 ShaderVariable expandedVar = variable;
652
653 expandedVar.name = name;
654 expandedVar.mappedName = mappedName;
655
656 // Mark all expanded fields as used if the parent is used
657 if (markStaticUse)
658 {
659 expandedVar.staticUse = true;
660 }
661
662 if (expandedVar.isArray())
663 {
664 expandedVar.name += "[0]";
665 expandedVar.mappedName += "[0]";
666 }
667
668 expanded->push_back(expandedVar);
669 }
670 }
671
ExpandUniforms(const std::vector<Uniform> & compact,std::vector<ShaderVariable> * expanded)672 void ExpandUniforms(const std::vector<Uniform> &compact,
673 std::vector<ShaderVariable> *expanded)
674 {
675 for (size_t variableIndex = 0; variableIndex < compact.size(); variableIndex++)
676 {
677 const ShaderVariable &variable = compact[variableIndex];
678 ExpandVariable(variable, variable.name, variable.mappedName, variable.staticUse, expanded);
679 }
680 }
681
682 }
683