1 //
2 // Copyright (C) 2016-2017 LunarG, Inc.
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions
8 // are met:
9 //
10 // Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //
13 // Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following
15 // disclaimer in the documentation and/or other materials provided
16 // with the distribution.
17 //
18 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its
19 // contributors may be used to endorse or promote products derived
20 // from this software without specific prior written permission.
21 //
22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 // POSSIBILITY OF SUCH DAMAGE.
34 //
35
36 #include "../Include/Common.h"
37 #include "../Include/InfoSink.h"
38 #include "../Include/Types.h"
39
40 #include "gl_types.h"
41 #include "iomapper.h"
42 #include "SymbolTable.h"
43
44 //
45 // Map IO bindings.
46 //
47 // High-level algorithm for one stage:
48 //
49 // 1. Traverse all code (live+dead) to find the explicitly provided bindings.
50 //
51 // 2. Traverse (just) the live code to determine which non-provided bindings
52 // require auto-numbering. We do not auto-number dead ones.
53 //
54 // 3. Traverse all the code to apply the bindings:
55 // a. explicitly given bindings are offset according to their type
56 // b. implicit live bindings are auto-numbered into the holes, using
57 // any open binding slot.
58 // c. implicit dead bindings are left un-bound.
59 //
60
61 namespace glslang {
62
63 class TVarGatherTraverser : public TLiveTraverser {
64 public:
TVarGatherTraverser(const TIntermediate & i,bool traverseDeadCode,TVarLiveMap & inList,TVarLiveMap & outList,TVarLiveMap & uniformList)65 TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList)
66 : TLiveTraverser(i, traverseDeadCode, true, true, false)
67 , inputList(inList)
68 , outputList(outList)
69 , uniformList(uniformList)
70 {
71 }
72
visitSymbol(TIntermSymbol * base)73 virtual void visitSymbol(TIntermSymbol* base)
74 {
75 TVarLiveMap* target = nullptr;
76 if (base->getQualifier().storage == EvqVaryingIn)
77 target = &inputList;
78 else if (base->getQualifier().storage == EvqVaryingOut)
79 target = &outputList;
80 else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant() && !base->getQualifier().isShaderRecord())
81 target = &uniformList;
82 // If a global is being visited, then we should also traverse it incase it's evaluation
83 // ends up visiting inputs we want to tag as live
84 else if (base->getQualifier().storage == EvqGlobal)
85 addGlobalReference(base->getAccessName());
86
87 if (target) {
88 TVarEntryInfo ent = {base->getId(), base, ! traverseAll};
89 ent.stage = intermediate.getStage();
90 TVarLiveMap::iterator at = target->find(
91 ent.symbol->getAccessName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
92 if (at != target->end() && at->second.id == ent.id)
93 at->second.live = at->second.live || ! traverseAll; // update live state
94 else
95 (*target)[ent.symbol->getAccessName()] = ent;
96 }
97 }
98
99 private:
100 TVarLiveMap& inputList;
101 TVarLiveMap& outputList;
102 TVarLiveMap& uniformList;
103 };
104
105 class TVarSetTraverser : public TLiveTraverser
106 {
107 public:
TVarSetTraverser(const TIntermediate & i,const TVarLiveMap & inList,const TVarLiveMap & outList,const TVarLiveMap & uniformList)108 TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList)
109 : TLiveTraverser(i, true, true, true, false)
110 , inputList(inList)
111 , outputList(outList)
112 , uniformList(uniformList)
113 {
114 }
115
visitSymbol(TIntermSymbol * base)116 virtual void visitSymbol(TIntermSymbol* base) {
117 const TVarLiveMap* source;
118 if (base->getQualifier().storage == EvqVaryingIn)
119 source = &inputList;
120 else if (base->getQualifier().storage == EvqVaryingOut)
121 source = &outputList;
122 else if (base->getQualifier().isUniformOrBuffer())
123 source = &uniformList;
124 else
125 return;
126
127 TVarEntryInfo ent = { base->getId() };
128 // Fix a defect, when block has no instance name, we need to find its block name
129 TVarLiveMap::const_iterator at = source->find(base->getAccessName());
130 if (at == source->end())
131 return;
132
133 if (at->second.id != ent.id)
134 return;
135
136 if (at->second.newBinding != -1)
137 base->getWritableType().getQualifier().layoutBinding = at->second.newBinding;
138 if (at->second.newSet != -1)
139 base->getWritableType().getQualifier().layoutSet = at->second.newSet;
140 if (at->second.newLocation != -1)
141 base->getWritableType().getQualifier().layoutLocation = at->second.newLocation;
142 if (at->second.newComponent != -1)
143 base->getWritableType().getQualifier().layoutComponent = at->second.newComponent;
144 if (at->second.newIndex != -1)
145 base->getWritableType().getQualifier().layoutIndex = at->second.newIndex;
146 if (at->second.upgradedToPushConstant)
147 base->getWritableType().getQualifier().layoutPushConstant = true;
148 }
149
150 private:
151 const TVarLiveMap& inputList;
152 const TVarLiveMap& outputList;
153 const TVarLiveMap& uniformList;
154 };
155
156 struct TNotifyUniformAdaptor
157 {
158 EShLanguage stage;
159 TIoMapResolver& resolver;
TNotifyUniformAdaptorglslang::TNotifyUniformAdaptor160 inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
161 : stage(s)
162 , resolver(r)
163 {
164 }
165
operator ()glslang::TNotifyUniformAdaptor166 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
167 {
168 resolver.notifyBinding(stage, entKey.second);
169 }
170
171 private:
172 TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&) = delete;
173 };
174
175 struct TNotifyInOutAdaptor
176 {
177 EShLanguage stage;
178 TIoMapResolver& resolver;
TNotifyInOutAdaptorglslang::TNotifyInOutAdaptor179 inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
180 : stage(s)
181 , resolver(r)
182 {
183 }
184
operator ()glslang::TNotifyInOutAdaptor185 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
186 {
187 resolver.notifyInOut(entKey.second.stage, entKey.second);
188 }
189
190 private:
191 TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete;
192 };
193
194 struct TResolverUniformAdaptor {
TResolverUniformAdaptorglslang::TResolverUniformAdaptor195 TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TVarLiveMap* uniform[EShLangCount], TInfoSink& i, bool& e)
196 : stage(s)
197 , resolver(r)
198 , infoSink(i)
199 , error(e)
200 {
201 memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
202 }
203
operator ()glslang::TResolverUniformAdaptor204 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
205 TVarEntryInfo& ent = entKey.second;
206 ent.clearNewAssignments();
207 const bool isValid = resolver.validateBinding(stage, ent);
208 if (isValid) {
209 resolver.resolveSet(ent.stage, ent);
210 resolver.resolveBinding(ent.stage, ent);
211 resolver.resolveUniformLocation(ent.stage, ent);
212
213 if (ent.newBinding != -1) {
214 if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
215 TString err = "mapped binding out of range: " + entKey.first;
216
217 infoSink.info.message(EPrefixInternalError, err.c_str());
218 error = true;
219 }
220
221 if (ent.symbol->getQualifier().hasBinding()) {
222 for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
223 if (idx == ent.stage || uniformVarMap[idx] == nullptr)
224 continue;
225 auto entKey2 = uniformVarMap[idx]->find(entKey.first);
226 if (entKey2 != uniformVarMap[idx]->end()) {
227 entKey2->second.newBinding = ent.newBinding;
228 }
229 }
230 }
231 }
232 if (ent.newSet != -1) {
233 if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
234 TString err = "mapped set out of range: " + entKey.first;
235
236 infoSink.info.message(EPrefixInternalError, err.c_str());
237 error = true;
238 }
239 if (ent.symbol->getQualifier().hasSet()) {
240 for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
241 if ((idx == stage) || (uniformVarMap[idx] == nullptr))
242 continue;
243 auto entKey2 = uniformVarMap[idx]->find(entKey.first);
244 if (entKey2 != uniformVarMap[idx]->end()) {
245 entKey2->second.newSet = ent.newSet;
246 }
247 }
248 }
249 }
250 } else {
251 TString errorMsg = "Invalid binding: " + entKey.first;
252 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
253 error = true;
254 }
255 }
256
setStageglslang::TResolverUniformAdaptor257 inline void setStage(EShLanguage s) { stage = s; }
258
259 EShLanguage stage;
260 TIoMapResolver& resolver;
261 TInfoSink& infoSink;
262 bool& error;
263 TVarLiveMap* uniformVarMap[EShLangCount];
264 private:
265 TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete;
266 };
267
268 struct TResolverInOutAdaptor {
TResolverInOutAdaptorglslang::TResolverInOutAdaptor269 TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
270 : stage(s)
271 , resolver(r)
272 , infoSink(i)
273 , error(e)
274 {
275 }
276
operator ()glslang::TResolverInOutAdaptor277 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
278 {
279 TVarEntryInfo& ent = entKey.second;
280 ent.clearNewAssignments();
281 const bool isValid = resolver.validateInOut(ent.stage, ent);
282 if (isValid) {
283 resolver.resolveInOutLocation(stage, ent);
284 resolver.resolveInOutComponent(stage, ent);
285 resolver.resolveInOutIndex(stage, ent);
286 } else {
287 TString errorMsg;
288 if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
289 errorMsg = "Invalid shader In/Out variable semantic: ";
290 errorMsg += ent.symbol->getType().getQualifier().semanticName;
291 } else {
292 errorMsg = "Invalid shader In/Out variable: ";
293 errorMsg += ent.symbol->getName();
294 }
295 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
296 error = true;
297 }
298 }
299
setStageglslang::TResolverInOutAdaptor300 inline void setStage(EShLanguage s) { stage = s; }
301
302 EShLanguage stage;
303 TIoMapResolver& resolver;
304 TInfoSink& infoSink;
305 bool& error;
306
307 private:
308 TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete;
309 };
310
311 // The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
312 // xxTODO: maybe this logic should be moved into the resolver's "validateInOut" and "validateUniform"
313
314 struct TSymbolValidater
315 {
TSymbolValidaterglslang::TSymbolValidater316 TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
317 TVarLiveMap* uniform[EShLangCount], bool& hadError, EProfile profile, int version)
318 : resolver(r)
319 , infoSink(i)
320 , hadError(hadError)
321 , profile(profile)
322 , version(version)
323 {
324 memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
325 memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
326 memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
327
328 std::map<TString, TString> anonymousMemberMap;
329 std::vector<TRange> usedUniformLocation;
330 std::vector<TString> usedUniformName;
331 usedUniformLocation.clear();
332 usedUniformName.clear();
333 for (int i = 0; i < EShLangCount; i++) {
334 if (uniformVarMap[i]) {
335 for (auto uniformVar : *uniformVarMap[i])
336 {
337 TIntermSymbol* pSymbol = uniformVar.second.symbol;
338 TQualifier qualifier = uniformVar.second.symbol->getQualifier();
339 TString symbolName = pSymbol->getAccessName();
340
341 // All the uniform needs multi-stage location check (block/default)
342 int uniformLocation = qualifier.layoutLocation;
343
344 if (uniformLocation != TQualifier::layoutLocationEnd) {
345 // Total size of current uniform, could be block, struct or other types.
346 int size = TIntermediate::computeTypeUniformLocationSize(pSymbol->getType());
347
348 TRange locationRange(uniformLocation, uniformLocation + size - 1);
349
350 // Combine location and component ranges
351 int overlapLocation = -1;
352 bool diffLocation = false;
353
354 // Check for collisions, except for vertex inputs on desktop targeting OpenGL
355 overlapLocation = checkLocationOverlap(locationRange, usedUniformLocation, symbolName, usedUniformName, diffLocation);
356
357 // Overlap locations of uniforms, regardless of components (multi stages)
358 if (overlapLocation == -1) {
359 usedUniformLocation.push_back(locationRange);
360 usedUniformName.push_back(symbolName);
361 }
362 else if (overlapLocation >= 0) {
363 if (diffLocation == true) {
364 TString err = ("Uniform location should be equal for same uniforms: " +std::to_string(overlapLocation)).c_str();
365 infoSink.info.message(EPrefixInternalError, err.c_str());
366 hadError = true;
367 break;
368 }
369 else {
370 TString err = ("Uniform location overlaps across stages: " + std::to_string(overlapLocation)).c_str();
371 infoSink.info.message(EPrefixInternalError, err.c_str());
372 hadError = true;
373 break;
374 }
375 }
376 }
377
378 if ((uniformVar.second.symbol->getBasicType() == EbtBlock) &&
379 IsAnonymous(uniformVar.second.symbol->getName()))
380 {
381 auto blockType = uniformVar.second.symbol->getType().getStruct();
382 for (size_t memberIdx = 0; memberIdx < blockType->size(); ++memberIdx) {
383 auto memberName = (*blockType)[memberIdx].type->getFieldName();
384 if (anonymousMemberMap.find(memberName) != anonymousMemberMap.end())
385 {
386 if (anonymousMemberMap[memberName] != uniformVar.second.symbol->getType().getTypeName())
387 {
388 TString err = "Invalid block member name: " + memberName;
389 infoSink.info.message(EPrefixInternalError, err.c_str());
390 hadError = true;
391 break;
392 }
393 }
394 else
395 {
396 anonymousMemberMap[memberName] = uniformVar.second.symbol->getType().getTypeName();
397 }
398 }
399 }
400 if (hadError)
401 break;
402 }
403 }
404 }
405 }
406
407 // In case we need to new an intermediate, which costs too much
checkLocationOverlapglslang::TSymbolValidater408 int checkLocationOverlap(const TRange& locationRange, std::vector<TRange>& usedUniformLocation, const TString symbolName, std::vector<TString>& usedUniformName, bool& diffLocation)
409 {
410 for (size_t r = 0; r < usedUniformLocation.size(); ++r) {
411 if (usedUniformName[r] == symbolName) {
412 diffLocation = true;
413 return (usedUniformLocation[r].start == locationRange.start &&
414 usedUniformLocation[r].last == locationRange.last)
415 ? -2 : std::max(locationRange.start, usedUniformLocation[r].start);
416 }
417 if (locationRange.overlap(usedUniformLocation[r])) {
418 // there is a collision; pick one
419 return std::max(locationRange.start, usedUniformLocation[r].start);
420 }
421 }
422
423 return -1; // no collision
424 }
425
operator ()glslang::TSymbolValidater426 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
427 TVarEntryInfo& ent1 = entKey.second;
428 TIntermSymbol* base = ent1.symbol;
429 const TType& type = ent1.symbol->getType();
430 const TString& name = entKey.first;
431 TString mangleName1, mangleName2;
432 EShLanguage stage = ent1.stage;
433 EShLanguage preStage, currentStage, nextStage;
434
435 preStage = EShLangCount;
436 for (int i = stage - 1; i >= 0; i--) {
437 if (inVarMaps[i] != nullptr) {
438 preStage = static_cast<EShLanguage>(i);
439 break;
440 }
441 }
442 currentStage = stage;
443 nextStage = EShLangCount;
444 for (int i = stage + 1; i < EShLangCount; i++) {
445 if (inVarMaps[i] != nullptr) {
446 nextStage = static_cast<EShLanguage>(i);
447 break;
448 }
449 }
450
451 if (type.getQualifier().isArrayedIo(stage)) {
452 TType subType(type, 0);
453 subType.appendMangledName(mangleName1);
454 } else {
455 type.appendMangledName(mangleName1);
456 }
457
458
459 // basic checking that symbols match
460 // more extensive checking in the link stage
461 if (base->getQualifier().storage == EvqVaryingIn) {
462 // validate stage in;
463 if (preStage == EShLangCount)
464 return;
465 if (TSymbolTable::isBuiltInSymbol(base->getId()))
466 return;
467 if (outVarMaps[preStage] != nullptr) {
468 auto ent2 = outVarMaps[preStage]->find(name);
469 uint32_t location = base->getType().getQualifier().layoutLocation;
470 if (ent2 == outVarMaps[preStage]->end() &&
471 location != glslang::TQualifier::layoutLocationEnd) {
472 for (auto var = outVarMaps[preStage]->begin(); var != ent2; var++) {
473 if (var->second.symbol->getType().getQualifier().layoutLocation == location) {
474 ent2 = var;
475 break;
476 }
477 }
478 }
479 if (ent2 != outVarMaps[preStage]->end()) {
480 auto& type1 = base->getType();
481 auto& type2 = ent2->second.symbol->getType();
482 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
483 if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) {
484 TType subType(ent2->second.symbol->getType(), 0);
485 subType.appendMangledName(mangleName2);
486 } else {
487 ent2->second.symbol->getType().appendMangledName(mangleName2);
488 }
489
490 if (mangleName1 == mangleName2) {
491 // For ES 3.0 only, other versions have no such restrictions
492 // According to ES 3.0 spec: The type and presence of the interpolation qualifiers and
493 // storage qualifiers of variables with the same name declared in all linked shaders must
494 // match, otherwise the link command will fail.
495 if (profile == EEsProfile && version == 300) {
496 // Don't need to check smooth qualifier, as it uses the default interpolation mode
497 if (ent1.stage == EShLangFragment && type1.isBuiltIn() == false) {
498 if (type1.getQualifier().flat != type2.getQualifier().flat ||
499 type1.getQualifier().nopersp != type2.getQualifier().nopersp) {
500 TString err = "Interpolation qualifier mismatch : " + entKey.first;
501 infoSink.info.message(EPrefixInternalError, err.c_str());
502 hadError = true;
503 }
504 }
505 }
506 return;
507 }
508 else {
509 // Deal with input/output pairs where one is a block member but the other is loose,
510 // e.g. with ARB_separate_shader_objects
511 if (type1.getBasicType() == EbtBlock &&
512 type1.isStruct() && !type2.isStruct()) {
513 // Iterate through block members tracking layout
514 glslang::TString name;
515 type1.getStruct()->begin()->type->appendMangledName(name);
516 if (name == mangleName2
517 && type1.getQualifier().layoutLocation == type2.getQualifier().layoutLocation) return;
518 }
519 if (type2.getBasicType() == EbtBlock &&
520 type2.isStruct() && !type1.isStruct()) {
521 // Iterate through block members tracking layout
522 glslang::TString name;
523 type2.getStruct()->begin()->type->appendMangledName(name);
524 if (name == mangleName1
525 && type1.getQualifier().layoutLocation == type2.getQualifier().layoutLocation) return;
526 }
527 TString err = "Invalid In/Out variable type : " + entKey.first;
528 infoSink.info.message(EPrefixInternalError, err.c_str());
529 hadError = true;
530 }
531 }
532 else if (!base->getType().isBuiltIn()) {
533 // According to spec: A link error is generated if any statically referenced input variable
534 // or block does not have a matching output
535 if (profile == EEsProfile && ent1.live) {
536 hadError = true;
537 TString errorStr = name + ": not been declare as a output variable in pre shader stage.";
538 infoSink.info.message(EPrefixError, errorStr.c_str());
539 }
540 }
541 return;
542 }
543 } else if (base->getQualifier().storage == EvqVaryingOut) {
544 // validate stage out;
545 if (nextStage == EShLangCount)
546 return;
547 if (TSymbolTable::isBuiltInSymbol(base->getId()))
548 return;
549 if (inVarMaps[nextStage] != nullptr) {
550 auto ent2 = inVarMaps[nextStage]->find(name);
551 if (ent2 != inVarMaps[nextStage]->end()) {
552 if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
553 TType subType(ent2->second.symbol->getType(), 0);
554 subType.appendMangledName(mangleName2);
555 } else {
556 ent2->second.symbol->getType().appendMangledName(mangleName2);
557 }
558 if (mangleName1 == mangleName2)
559 return;
560 else {
561 TString err = "Invalid In/Out variable type : " + entKey.first;
562 infoSink.info.message(EPrefixInternalError, err.c_str());
563 hadError = true;
564 }
565 }
566 return;
567 }
568 } else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) {
569 // validate uniform type;
570 for (int i = 0; i < EShLangCount; i++) {
571 if (i != currentStage && outVarMaps[i] != nullptr) {
572 auto ent2 = uniformVarMap[i]->find(name);
573 if (ent2 != uniformVarMap[i]->end()) {
574 ent2->second.symbol->getType().appendMangledName(mangleName2);
575 if (mangleName1 != mangleName2) {
576 ent2->second.symbol->getType().sameElementType(type);
577 TString err = "Invalid Uniform variable type : " + entKey.first;
578 infoSink.info.message(EPrefixInternalError, err.c_str());
579 hadError = true;
580 }
581 mangleName2.clear();
582
583 // validate instance name of blocks
584 if (hadError == false &&
585 base->getType().getBasicType() == EbtBlock &&
586 IsAnonymous(base->getName()) != IsAnonymous(ent2->second.symbol->getName())) {
587 TString err = "Matched uniform block names must also either all be lacking "
588 "an instance name or all having an instance name: " + entKey.first;
589 infoSink.info.message(EPrefixInternalError, err.c_str());
590 hadError = true;
591 }
592
593 // validate uniform block member qualifier and member names
594 auto& type1 = base->getType();
595 auto& type2 = ent2->second.symbol->getType();
596 if (hadError == false && base->getType().getBasicType() == EbtBlock) {
597 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), true);
598 }
599 else {
600 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
601 }
602 }
603 else if (base->getBasicType() == EbtBlock)
604 {
605 if (IsAnonymous(base->getName()))
606 {
607 // The name of anonymous block member can't same with default uniform variable.
608 auto blockType1 = base->getType().getStruct();
609 for (size_t memberIdx = 0; memberIdx < blockType1->size(); ++memberIdx) {
610 auto memberName = (*blockType1)[memberIdx].type->getFieldName();
611 if (uniformVarMap[i]->find(memberName) != uniformVarMap[i]->end())
612 {
613 TString err = "Invalid Uniform variable name : " + memberName;
614 infoSink.info.message(EPrefixInternalError, err.c_str());
615 hadError = true;
616 break;
617 }
618 }
619 }
620 }
621 }
622 }
623 }
624 }
625
626 TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount];
627
628 // Use for mark current shader stage for resolver
629 TIoMapResolver& resolver;
630 TInfoSink& infoSink;
631 bool& hadError;
632 EProfile profile;
633 int version;
634
635 private:
636 TSymbolValidater& operator=(TSymbolValidater&) = delete;
637
qualifierCheckglslang::TSymbolValidater638 bool qualifierCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
639 {
640 bool hasError = false;
641 const TQualifier& qualifier1 = type1->getQualifier();
642 const TQualifier& qualifier2 = type2->getQualifier();
643
644 if (((isBlock == false) &&
645 (type1->getQualifier().storage == EvqUniform && type2->getQualifier().storage == EvqUniform)) ||
646 (type1->getQualifier().storage == EvqGlobal && type2->getQualifier().storage == EvqGlobal)) {
647 if (qualifier1.precision != qualifier2.precision) {
648 hasError = true;
649 std::string errorStr = name + ": have precision conflict cross stage.";
650 infoSink.info.message(EPrefixError, errorStr.c_str());
651 }
652 if (qualifier1.hasFormat() && qualifier2.hasFormat()) {
653 if (qualifier1.layoutFormat != qualifier2.layoutFormat) {
654 hasError = true;
655 std::string errorStr = name + ": have layout format conflict cross stage.";
656 infoSink.info.message(EPrefixError, errorStr.c_str());
657 }
658
659 }
660 }
661
662 if (isBlock == true) {
663 if (qualifier1.layoutPacking != qualifier2.layoutPacking) {
664 hasError = true;
665 std::string errorStr = name + ": have layoutPacking conflict cross stage.";
666 infoSink.info.message(EPrefixError, errorStr.c_str());
667 }
668 if (qualifier1.layoutMatrix != qualifier2.layoutMatrix) {
669 hasError = true;
670 std::string errorStr = name + ": have layoutMatrix conflict cross stage.";
671 infoSink.info.message(EPrefixError, errorStr.c_str());
672 }
673 if (qualifier1.layoutOffset != qualifier2.layoutOffset) {
674 hasError = true;
675 std::string errorStr = name + ": have layoutOffset conflict cross stage.";
676 infoSink.info.message(EPrefixError, errorStr.c_str());
677 }
678 if (qualifier1.layoutAlign != qualifier2.layoutAlign) {
679 hasError = true;
680 std::string errorStr = name + ": have layoutAlign conflict cross stage.";
681 infoSink.info.message(EPrefixError, errorStr.c_str());
682 }
683 }
684
685 return hasError;
686 }
687
typeCheckglslang::TSymbolValidater688 bool typeCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
689 {
690 bool hasError = false;
691 if (!(type1->isStruct() && type2->isStruct())) {
692 hasError = hasError || qualifierCheck(type1, type2, name, isBlock);
693 }
694 else {
695 if (type1->getBasicType() == EbtBlock && type2->getBasicType() == EbtBlock)
696 isBlock = true;
697 const TTypeList* typeList1 = type1->getStruct();
698 const TTypeList* typeList2 = type2->getStruct();
699
700 std::string newName = name;
701 size_t memberCount = typeList1->size();
702 size_t index2 = 0;
703 for (size_t index = 0; index < memberCount; index++, index2++) {
704 // Skip inactive member
705 if (typeList1->at(index).type->getBasicType() == EbtVoid)
706 continue;
707 while (index2 < typeList2->size() && typeList2->at(index2).type->getBasicType() == EbtVoid) {
708 ++index2;
709 }
710
711 // TypeList1 has more members in list
712 if (index2 == typeList2->size()) {
713 std::string errorStr = name + ": struct mismatch.";
714 infoSink.info.message(EPrefixError, errorStr.c_str());
715 hasError = true;
716 break;
717 }
718
719 if (typeList1->at(index).type->getFieldName() != typeList2->at(index2).type->getFieldName()) {
720 std::string errorStr = name + ": member name mismatch.";
721 infoSink.info.message(EPrefixError, errorStr.c_str());
722 hasError = true;
723 }
724 else {
725 newName = typeList1->at(index).type->getFieldName().c_str();
726 }
727 hasError = hasError || typeCheck(typeList1->at(index).type, typeList2->at(index2).type, newName, isBlock);
728 }
729
730 while (index2 < typeList2->size())
731 {
732 // TypeList2 has more members
733 if (typeList2->at(index2).type->getBasicType() != EbtVoid) {
734 std::string errorStr = name + ": struct mismatch.";
735 infoSink.info.message(EPrefixError, errorStr.c_str());
736 hasError = true;
737 break;
738 }
739 ++index2;
740 }
741 }
742 return hasError;
743 }
744 };
745
746 struct TSlotCollector {
TSlotCollectorglslang::TSlotCollector747 TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
748
operator ()glslang::TSlotCollector749 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
750 resolver.reserverStorageSlot(entKey.second, infoSink);
751 resolver.reserverResourceSlot(entKey.second, infoSink);
752 }
753 TIoMapResolver& resolver;
754 TInfoSink& infoSink;
755
756 private:
757 TSlotCollector& operator=(TSlotCollector&) = delete;
758 };
759
TDefaultIoResolverBase(const TIntermediate & intermediate)760 TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
761 : referenceIntermediate(intermediate)
762 , nextUniformLocation(intermediate.getUniformLocationBase())
763 , nextInputLocation(0)
764 , nextOutputLocation(0)
765 {
766 memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
767 memset(stageIntermediates, 0, sizeof(TIntermediate*) * (EShLangCount));
768 stageIntermediates[intermediate.getStage()] = &intermediate;
769 }
770
getBaseBinding(EShLanguage stage,TResourceType res,unsigned int set) const771 int TDefaultIoResolverBase::getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const {
772 return stageIntermediates[stage] ? selectBaseBinding(stageIntermediates[stage]->getShiftBinding(res), stageIntermediates[stage]->getShiftBindingForSet(res, set))
773 : selectBaseBinding(referenceIntermediate.getShiftBinding(res), referenceIntermediate.getShiftBindingForSet(res, set));
774 }
775
getResourceSetBinding(EShLanguage stage) const776 const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding(EShLanguage stage) const {
777 return stageIntermediates[stage] ? stageIntermediates[stage]->getResourceSetBinding()
778 : referenceIntermediate.getResourceSetBinding();
779 }
780
doAutoBindingMapping() const781 bool TDefaultIoResolverBase::doAutoBindingMapping() const { return referenceIntermediate.getAutoMapBindings(); }
782
doAutoLocationMapping() const783 bool TDefaultIoResolverBase::doAutoLocationMapping() const { return referenceIntermediate.getAutoMapLocations(); }
784
findSlot(int set,int slot)785 TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
786 return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
787 }
788
checkEmpty(int set,int slot)789 bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
790 TSlotSet::iterator at = findSlot(set, slot);
791 return ! (at != slots[set].end() && *at == slot);
792 }
793
reserveSlot(int set,int slot,int size)794 int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
795 TSlotSet::iterator at = findSlot(set, slot);
796 // tolerate aliasing, by not double-recording aliases
797 // (policy about appropriateness of the alias is higher up)
798 for (int i = 0; i < size; i++) {
799 if (at == slots[set].end() || *at != slot + i)
800 at = slots[set].insert(at, slot + i);
801 ++at;
802 }
803 return slot;
804 }
805
getFreeSlot(int set,int base,int size)806 int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
807 TSlotSet::iterator at = findSlot(set, base);
808 if (at == slots[set].end())
809 return reserveSlot(set, base, size);
810 // look for a big enough gap
811 for (; at != slots[set].end(); ++at) {
812 if (*at - base >= size)
813 break;
814 base = *at + 1;
815 }
816 return reserveSlot(set, base, size);
817 }
818
resolveSet(EShLanguage stage,TVarEntryInfo & ent)819 int TDefaultIoResolverBase::resolveSet(EShLanguage stage, TVarEntryInfo& ent) {
820 const TType& type = ent.symbol->getType();
821 if (type.getQualifier().hasSet()) {
822 return ent.newSet = type.getQualifier().layoutSet;
823 }
824 // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
825 if (getResourceSetBinding(stage).size() == 1) {
826 return ent.newSet = atoi(getResourceSetBinding(stage)[0].c_str());
827 }
828 return ent.newSet = 0;
829 }
830
resolveUniformLocation(EShLanguage,TVarEntryInfo & ent)831 int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
832 const TType& type = ent.symbol->getType();
833 const char* name = ent.symbol->getAccessName().c_str();
834 // kick out of not doing this
835 if (! doAutoLocationMapping()) {
836 return ent.newLocation = -1;
837 }
838 // no locations added if already present, a built-in variable, a block, or an opaque
839 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
840 type.isAtomic() || type.isSpirvType() || (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) {
841 return ent.newLocation = -1;
842 }
843 // no locations on blocks of built-in variables
844 if (type.isStruct()) {
845 if (type.getStruct()->size() < 1) {
846 return ent.newLocation = -1;
847 }
848 if ((*type.getStruct())[0].type->isBuiltIn()) {
849 return ent.newLocation = -1;
850 }
851 }
852 int location = referenceIntermediate.getUniformLocationOverride(name);
853 if (location != -1) {
854 return ent.newLocation = location;
855 }
856 location = nextUniformLocation;
857 nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
858 return ent.newLocation = location;
859 }
860
resolveInOutLocation(EShLanguage stage,TVarEntryInfo & ent)861 int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
862 const TType& type = ent.symbol->getType();
863 // kick out of not doing this
864 if (! doAutoLocationMapping()) {
865 return ent.newLocation = -1;
866 }
867
868 // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
869 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getQualifier().hasSpirvDecorate()) {
870 return ent.newLocation = -1;
871 }
872
873 // no locations on blocks of built-in variables
874 if (type.isStruct()) {
875 if (type.getStruct()->size() < 1) {
876 return ent.newLocation = -1;
877 }
878 if ((*type.getStruct())[0].type->isBuiltIn()) {
879 return ent.newLocation = -1;
880 }
881 }
882 // point to the right input or output location counter
883 int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
884 // Placeholder. This does not do proper cross-stage lining up, nor
885 // work with mixed location/no-location declarations.
886 int location = nextLocation;
887 int typeLocationSize;
888 // Don’t take into account the outer-most array if the stage’s
889 // interface is automatically an array.
890 typeLocationSize = computeTypeLocationSize(type, stage);
891 nextLocation += typeLocationSize;
892 return ent.newLocation = location;
893 }
894
resolveInOutComponent(EShLanguage,TVarEntryInfo & ent)895 int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
896 return ent.newComponent = -1;
897 }
898
resolveInOutIndex(EShLanguage,TVarEntryInfo & ent)899 int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; }
900
computeTypeLocationSize(const TType & type,EShLanguage stage)901 uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
902 int typeLocationSize;
903 // Don’t take into account the outer-most array if the stage’s
904 // interface is automatically an array.
905 if (type.getQualifier().isArrayedIo(stage)) {
906 TType elementType(type, 0);
907 typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
908 } else {
909 typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
910 }
911 return typeLocationSize;
912 }
913
914 //TDefaultGlslIoResolver
getResourceType(const glslang::TType & type)915 TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
916 if (isImageType(type)) {
917 return EResImage;
918 }
919 if (isTextureType(type)) {
920 return EResTexture;
921 }
922 if (isSsboType(type)) {
923 return EResSsbo;
924 }
925 if (isSamplerType(type)) {
926 return EResSampler;
927 }
928 if (isUboType(type)) {
929 return EResUbo;
930 }
931 return EResCount;
932 }
933
TDefaultGlslIoResolver(const TIntermediate & intermediate)934 TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
935 : TDefaultIoResolverBase(intermediate)
936 , preStage(EShLangCount)
937 , currentStage(EShLangCount)
938 { }
939
resolveInOutLocation(EShLanguage stage,TVarEntryInfo & ent)940 int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
941 const TType& type = ent.symbol->getType();
942 const TString& name = ent.symbol->getAccessName();
943 if (currentStage != stage) {
944 preStage = currentStage;
945 currentStage = stage;
946 }
947 // kick out if not doing this
948 if (! doAutoLocationMapping()) {
949 return ent.newLocation = -1;
950 }
951 // expand the location to each element if the symbol is a struct or array
952 if (type.getQualifier().hasLocation()) {
953 return ent.newLocation = type.getQualifier().layoutLocation;
954 }
955 // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
956 if (type.isBuiltIn() || type.getQualifier().hasSpirvDecorate()) {
957 return ent.newLocation = -1;
958 }
959 // no locations on blocks of built-in variables
960 if (type.isStruct()) {
961 if (type.getStruct()->size() < 1) {
962 return ent.newLocation = -1;
963 }
964 if ((*type.getStruct())[0].type->isBuiltIn()) {
965 return ent.newLocation = -1;
966 }
967 }
968 int typeLocationSize = computeTypeLocationSize(type, stage);
969 int location = type.getQualifier().layoutLocation;
970 bool hasLocation = false;
971 EShLanguage keyStage(EShLangCount);
972 TStorageQualifier storage;
973 storage = EvqInOut;
974 if (type.getQualifier().isPipeInput()) {
975 // If this symbol is a input, search pre stage's out
976 keyStage = preStage;
977 }
978 if (type.getQualifier().isPipeOutput()) {
979 // If this symbol is a output, search next stage's in
980 keyStage = currentStage;
981 }
982 // The in/out in current stage is not declared with location, but it is possible declared
983 // with explicit location in other stages, find the storageSlotMap firstly to check whether
984 // the in/out has location
985 int resourceKey = buildStorageKey(keyStage, storage);
986 if (! storageSlotMap[resourceKey].empty()) {
987 TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name);
988 if (iter != storageSlotMap[resourceKey].end()) {
989 // If interface resource be found, set it has location and this symbol's new location
990 // equal the symbol's explicit location declaration in pre or next stage.
991 //
992 // vs: out vec4 a;
993 // fs: layout(..., location = 3,...) in vec4 a;
994 hasLocation = true;
995 location = iter->second;
996 // if we want deal like that:
997 // vs: layout(location=4) out vec4 a;
998 // out vec4 b;
999 //
1000 // fs: in vec4 a;
1001 // layout(location = 4) in vec4 b;
1002 // we need retraverse the map.
1003 }
1004 if (! hasLocation) {
1005 // If interface resource note found, It's mean the location in two stage are both implicit declarat.
1006 // So we should find a new slot for this interface.
1007 //
1008 // vs: out vec4 a;
1009 // fs: in vec4 a;
1010 location = getFreeSlot(resourceKey, 0, typeLocationSize);
1011 storageSlotMap[resourceKey][name] = location;
1012 }
1013 } else {
1014 // the first interface declarated in a program.
1015 TVarSlotMap varSlotMap;
1016 location = getFreeSlot(resourceKey, 0, typeLocationSize);
1017 varSlotMap[name] = location;
1018 storageSlotMap[resourceKey] = varSlotMap;
1019 }
1020 //Update location
1021 return ent.newLocation = location;
1022 }
1023
resolveUniformLocation(EShLanguage,TVarEntryInfo & ent)1024 int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
1025 const TType& type = ent.symbol->getType();
1026 const TString& name = ent.symbol->getAccessName();
1027 // kick out of not doing this
1028 if (! doAutoLocationMapping()) {
1029 return ent.newLocation = -1;
1030 }
1031 // expand the location to each element if the symbol is a struct or array
1032 if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) {
1033 return ent.newLocation = type.getQualifier().layoutLocation;
1034 } else {
1035 // no locations added if already present, a built-in variable, a block, or an opaque
1036 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
1037 type.isAtomic() || type.isSpirvType() ||
1038 (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) {
1039 return ent.newLocation = -1;
1040 }
1041 // no locations on blocks of built-in variables
1042 if (type.isStruct()) {
1043 if (type.getStruct()->size() < 1) {
1044 return ent.newLocation = -1;
1045 }
1046 if ((*type.getStruct())[0].type->isBuiltIn()) {
1047 return ent.newLocation = -1;
1048 }
1049 }
1050 }
1051 int location = referenceIntermediate.getUniformLocationOverride(name.c_str());
1052 if (location != -1) {
1053 return ent.newLocation = location;
1054 }
1055
1056 int size = TIntermediate::computeTypeUniformLocationSize(type);
1057
1058 // The uniform in current stage is not declared with location, but it is possible declared
1059 // with explicit location in other stages, find the storageSlotMap firstly to check whether
1060 // the uniform has location
1061 bool hasLocation = false;
1062 int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
1063 TVarSlotMap& slotMap = storageSlotMap[resourceKey];
1064 // Check dose shader program has uniform resource
1065 if (! slotMap.empty()) {
1066 // If uniform resource not empty, try find a same name uniform
1067 TVarSlotMap::iterator iter = slotMap.find(name);
1068 if (iter != slotMap.end()) {
1069 // If uniform resource be found, set it has location and this symbol's new location
1070 // equal the uniform's explicit location declaration in other stage.
1071 //
1072 // vs: uniform vec4 a;
1073 // fs: layout(..., location = 3,...) uniform vec4 a;
1074 hasLocation = true;
1075 location = iter->second;
1076 }
1077 if (! hasLocation) {
1078 // No explicit location declaration in other stage.
1079 // So we should find a new slot for this uniform.
1080 //
1081 // vs: uniform vec4 a;
1082 // fs: uniform vec4 a;
1083 location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
1084 storageSlotMap[resourceKey][name] = location;
1085 }
1086 } else {
1087 // the first uniform declaration in a program.
1088 TVarSlotMap varSlotMap;
1089 location = getFreeSlot(resourceKey, 0, size);
1090 varSlotMap[name] = location;
1091 storageSlotMap[resourceKey] = varSlotMap;
1092 }
1093 return ent.newLocation = location;
1094 }
1095
resolveBinding(EShLanguage stage,TVarEntryInfo & ent)1096 int TDefaultGlslIoResolver::resolveBinding(EShLanguage stage, TVarEntryInfo& ent) {
1097 const TType& type = ent.symbol->getType();
1098 const TString& name = ent.symbol->getAccessName();
1099 // On OpenGL arrays of opaque types take a separate binding for each element
1100 int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1101 TResourceType resource = getResourceType(type);
1102 // don't need to handle uniform symbol, it will be handled in resolveUniformLocation
1103 if (resource == EResUbo && type.getBasicType() != EbtBlock) {
1104 return ent.newBinding = -1;
1105 }
1106 // There is no 'set' qualifier in OpenGL shading language, each resource has its own
1107 // binding name space, so remap the 'set' to resource type which make each resource
1108 // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
1109 int set = referenceIntermediate.getSpv().openGl != 0 ? resource : ent.newSet;
1110 int resourceKey = set;
1111 if (resource < EResCount) {
1112 if (type.getQualifier().hasBinding()) {
1113 int newBinding = reserveSlot(resourceKey, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1114 return ent.newBinding = newBinding;
1115
1116 } else {
1117 // The resource in current stage is not declared with binding, but it is possible declared
1118 // with explicit binding in other stages, find the resourceSlotMap firstly to check whether
1119 // the resource has binding, don't need to allocate if it already has a binding
1120 bool hasBinding = false;
1121 ent.newBinding = -1; // leave as -1 if it isn't set below
1122
1123 if (! resourceSlotMap[resourceKey].empty()) {
1124 TVarSlotMap::iterator iter = resourceSlotMap[resourceKey].find(name);
1125 if (iter != resourceSlotMap[resourceKey].end()) {
1126 hasBinding = true;
1127 ent.newBinding = iter->second;
1128 }
1129 }
1130 if (!hasBinding && (ent.live && doAutoBindingMapping())) {
1131 // find free slot, the caller did make sure it passes all vars with binding
1132 // first and now all are passed that do not have a binding and needs one
1133 int binding = getFreeSlot(resourceKey, getBaseBinding(stage, resource, set), numBindings);
1134 resourceSlotMap[resourceKey][name] = binding;
1135 ent.newBinding = binding;
1136 }
1137 return ent.newBinding;
1138 }
1139 }
1140 return ent.newBinding = -1;
1141 }
1142
beginResolve(EShLanguage stage)1143 void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
1144 // reset stage state
1145 if (stage == EShLangCount)
1146 preStage = currentStage = stage;
1147 // update stage state
1148 else if (currentStage != stage) {
1149 preStage = currentStage;
1150 currentStage = stage;
1151 }
1152 }
1153
endResolve(EShLanguage)1154 void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
1155 // TODO nothing
1156 }
1157
beginCollect(EShLanguage stage)1158 void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
1159 // reset stage state
1160 if (stage == EShLangCount)
1161 preStage = currentStage = stage;
1162 // update stage state
1163 else if (currentStage != stage) {
1164 preStage = currentStage;
1165 currentStage = stage;
1166 }
1167 }
1168
endCollect(EShLanguage)1169 void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
1170 // TODO nothing
1171 }
1172
reserverStorageSlot(TVarEntryInfo & ent,TInfoSink & infoSink)1173 void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1174 const TType& type = ent.symbol->getType();
1175 const TString& name = ent.symbol->getAccessName();
1176 TStorageQualifier storage = type.getQualifier().storage;
1177 EShLanguage stage(EShLangCount);
1178 switch (storage) {
1179 case EvqUniform:
1180 if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) {
1181 //
1182 // Reserve the slots for the uniforms who has explicit location
1183 int storageKey = buildStorageKey(EShLangCount, EvqUniform);
1184 int location = type.getQualifier().layoutLocation;
1185 TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1186 TVarSlotMap::iterator iter = varSlotMap.find(name);
1187 if (iter == varSlotMap.end()) {
1188 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1189 reserveSlot(storageKey, location, numLocations);
1190 varSlotMap[name] = location;
1191 } else {
1192 // Allocate location by name for OpenGL driver, so the uniform in different
1193 // stages should be declared with the same location
1194 if (iter->second != location) {
1195 TString errorMsg = "Invalid location: " + name;
1196 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1197 hasError = true;
1198 }
1199 }
1200 }
1201 break;
1202 case EvqVaryingIn:
1203 case EvqVaryingOut:
1204 //
1205 // Reserve the slots for the inout who has explicit location
1206 if (type.getQualifier().hasLocation()) {
1207 stage = storage == EvqVaryingIn ? preStage : stage;
1208 stage = storage == EvqVaryingOut ? currentStage : stage;
1209 int storageKey = buildStorageKey(stage, EvqInOut);
1210 int location = type.getQualifier().layoutLocation;
1211 TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1212 TVarSlotMap::iterator iter = varSlotMap.find(name);
1213 if (iter == varSlotMap.end()) {
1214 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1215 reserveSlot(storageKey, location, numLocations);
1216 varSlotMap[name] = location;
1217 } else {
1218 // Allocate location by name for OpenGL driver, so the uniform in different
1219 // stages should be declared with the same location
1220 if (iter->second != location) {
1221 TString errorMsg = "Invalid location: " + name;
1222 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1223 hasError = true;
1224 }
1225 }
1226 }
1227 break;
1228 default:
1229 break;
1230 }
1231 }
1232
reserverResourceSlot(TVarEntryInfo & ent,TInfoSink & infoSink)1233 void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1234 const TType& type = ent.symbol->getType();
1235 const TString& name = ent.symbol->getAccessName();
1236 TResourceType resource = getResourceType(type);
1237 int set = referenceIntermediate.getSpv().openGl != 0 ? resource : resolveSet(ent.stage, ent);
1238 int resourceKey = set;
1239
1240 if (type.getQualifier().hasBinding()) {
1241 TVarSlotMap& varSlotMap = resourceSlotMap[resourceKey];
1242 TVarSlotMap::iterator iter = varSlotMap.find(name);
1243 int binding = type.getQualifier().layoutBinding + getBaseBinding(ent.stage, resource, set);
1244
1245 if (iter == varSlotMap.end()) {
1246 // Reserve the slots for the ubo, ssbo and opaques who has explicit binding
1247 int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1248 varSlotMap[name] = binding;
1249 reserveSlot(resourceKey, binding, numBindings);
1250 } else {
1251 // Allocate binding by name for OpenGL driver, so the resource in different
1252 // stages should be declared with the same binding
1253 if (iter->second != binding) {
1254 TString errorMsg = "Invalid binding: " + name;
1255 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1256 hasError = true;
1257 }
1258 }
1259 }
1260 }
1261
1262 //TDefaultGlslIoResolver end
1263
1264 /*
1265 * Basic implementation of glslang::TIoMapResolver that replaces the
1266 * previous offset behavior.
1267 * It does the same, uses the offsets for the corresponding uniform
1268 * types. Also respects the EOptionAutoMapBindings flag and binds
1269 * them if needed.
1270 */
1271 /*
1272 * Default resolver
1273 */
1274 struct TDefaultIoResolver : public TDefaultIoResolverBase {
TDefaultIoResolverglslang::TDefaultIoResolver1275 TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1276
validateBindingglslang::TDefaultIoResolver1277 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1278
getResourceTypeglslang::TDefaultIoResolver1279 TResourceType getResourceType(const glslang::TType& type) override {
1280 if (isImageType(type)) {
1281 return EResImage;
1282 }
1283 if (isTextureType(type)) {
1284 return EResTexture;
1285 }
1286 if (isSsboType(type)) {
1287 return EResSsbo;
1288 }
1289 if (isSamplerType(type)) {
1290 return EResSampler;
1291 }
1292 if (isUboType(type)) {
1293 return EResUbo;
1294 }
1295 return EResCount;
1296 }
1297
resolveBindingglslang::TDefaultIoResolver1298 int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1299 const TType& type = ent.symbol->getType();
1300 const int set = getLayoutSet(type);
1301 // On OpenGL arrays of opaque types take a seperate binding for each element
1302 int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1303 TResourceType resource = getResourceType(type);
1304 if (resource < EResCount) {
1305 if (type.getQualifier().hasBinding()) {
1306 return ent.newBinding = reserveSlot(
1307 set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1308 } else if (ent.live && doAutoBindingMapping()) {
1309 // find free slot, the caller did make sure it passes all vars with binding
1310 // first and now all are passed that do not have a binding and needs one
1311 return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set), numBindings);
1312 }
1313 }
1314 return ent.newBinding = -1;
1315 }
1316 };
1317
1318 #ifdef ENABLE_HLSL
1319 /********************************************************************************
1320 The following IO resolver maps types in HLSL register space, as follows:
1321
1322 t - for shader resource views (SRV)
1323 TEXTURE1D
1324 TEXTURE1DARRAY
1325 TEXTURE2D
1326 TEXTURE2DARRAY
1327 TEXTURE3D
1328 TEXTURECUBE
1329 TEXTURECUBEARRAY
1330 TEXTURE2DMS
1331 TEXTURE2DMSARRAY
1332 STRUCTUREDBUFFER
1333 BYTEADDRESSBUFFER
1334 BUFFER
1335 TBUFFER
1336
1337 s - for samplers
1338 SAMPLER
1339 SAMPLER1D
1340 SAMPLER2D
1341 SAMPLER3D
1342 SAMPLERCUBE
1343 SAMPLERSTATE
1344 SAMPLERCOMPARISONSTATE
1345
1346 u - for unordered access views (UAV)
1347 RWBYTEADDRESSBUFFER
1348 RWSTRUCTUREDBUFFER
1349 APPENDSTRUCTUREDBUFFER
1350 CONSUMESTRUCTUREDBUFFER
1351 RWBUFFER
1352 RWTEXTURE1D
1353 RWTEXTURE1DARRAY
1354 RWTEXTURE2D
1355 RWTEXTURE2DARRAY
1356 RWTEXTURE3D
1357
1358 b - for constant buffer views (CBV)
1359 CBUFFER
1360 CONSTANTBUFFER
1361 ********************************************************************************/
1362 struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
TDefaultHlslIoResolverglslang::TDefaultHlslIoResolver1363 TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1364
validateBindingglslang::TDefaultHlslIoResolver1365 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1366
getResourceTypeglslang::TDefaultHlslIoResolver1367 TResourceType getResourceType(const glslang::TType& type) override {
1368 if (isUavType(type)) {
1369 return EResUav;
1370 }
1371 if (isSrvType(type)) {
1372 return EResTexture;
1373 }
1374 if (isSamplerType(type)) {
1375 return EResSampler;
1376 }
1377 if (isUboType(type)) {
1378 return EResUbo;
1379 }
1380 return EResCount;
1381 }
1382
resolveBindingglslang::TDefaultHlslIoResolver1383 int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1384 const TType& type = ent.symbol->getType();
1385 const int set = getLayoutSet(type);
1386 TResourceType resource = getResourceType(type);
1387 if (resource < EResCount) {
1388 if (type.getQualifier().hasBinding()) {
1389 return ent.newBinding = reserveSlot(set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding);
1390 } else if (ent.live && doAutoBindingMapping()) {
1391 // find free slot, the caller did make sure it passes all vars with binding
1392 // first and now all are passed that do not have a binding and needs one
1393 return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set));
1394 }
1395 }
1396 return ent.newBinding = -1;
1397 }
1398 };
1399 #endif
1400
1401 // Map I/O variables to provided offsets, and make bindings for
1402 // unbound but live variables.
1403 //
1404 // Returns false if the input is too malformed to do this.
addStage(EShLanguage stage,TIntermediate & intermediate,TInfoSink & infoSink,TIoMapResolver * resolver)1405 bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1406 bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
1407 intermediate.getAutoMapLocations();
1408 // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1409 // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1410 for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1411 somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1412 intermediate.hasShiftBindingForSet(TResourceType(res));
1413 }
1414 if (! somethingToDo && resolver == nullptr)
1415 return true;
1416 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
1417 return false;
1418 TIntermNode* root = intermediate.getTreeRoot();
1419 if (root == nullptr)
1420 return false;
1421 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1422 TDefaultIoResolver defaultResolver(intermediate);
1423 #ifdef ENABLE_HLSL
1424 TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1425 if (resolver == nullptr) {
1426 // TODO: use a passed in IO mapper for this
1427 if (intermediate.usingHlslIoMapping())
1428 resolver = &defaultHlslResolver;
1429 else
1430 resolver = &defaultResolver;
1431 }
1432 #else
1433 resolver = &defaultResolver;
1434 #endif
1435 resolver->addStage(stage, intermediate);
1436
1437 TVarLiveMap inVarMap, outVarMap, uniformVarMap;
1438 TVarLiveVector inVector, outVector, uniformVector;
1439 TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
1440 TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
1441 root->traverse(&iter_binding_all);
1442 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1443 while (! iter_binding_live.destinations.empty()) {
1444 TIntermNode* destination = iter_binding_live.destinations.back();
1445 iter_binding_live.destinations.pop_back();
1446 destination->traverse(&iter_binding_live);
1447 }
1448
1449 // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
1450 for (auto& var : inVarMap) { inVector.push_back(var); }
1451 std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1452 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1453 });
1454 for (auto& var : outVarMap) { outVector.push_back(var); }
1455 std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1456 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1457 });
1458 for (auto& var : uniformVarMap) { uniformVector.push_back(var); }
1459 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1460 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1461 });
1462 bool hadError = false;
1463 TVarLiveMap* dummyUniformVarMap[EShLangCount] = {};
1464 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1465 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1466 TResolverUniformAdaptor uniformResolve(stage, *resolver, dummyUniformVarMap, infoSink, hadError);
1467 TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
1468 resolver->beginNotifications(stage);
1469 std::for_each(inVector.begin(), inVector.end(), inOutNotify);
1470 std::for_each(outVector.begin(), outVector.end(), inOutNotify);
1471 std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
1472 resolver->endNotifications(stage);
1473 resolver->beginResolve(stage);
1474 for (auto& var : inVector) { inOutResolve(var); }
1475 std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
1476 auto at = inVarMap.find(p.second.symbol->getAccessName());
1477 if (at != inVarMap.end() && p.second.id == at->second.id)
1478 at->second = p.second;
1479 });
1480 for (auto& var : outVector) { inOutResolve(var); }
1481 std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
1482 auto at = outVarMap.find(p.second.symbol->getAccessName());
1483 if (at != outVarMap.end() && p.second.id == at->second.id)
1484 at->second = p.second;
1485 });
1486 std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
1487 std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
1488 auto at = uniformVarMap.find(p.second.symbol->getAccessName());
1489 if (at != uniformVarMap.end() && p.second.id == at->second.id)
1490 at->second = p.second;
1491 });
1492 resolver->endResolve(stage);
1493 if (!hadError) {
1494 TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
1495 root->traverse(&iter_iomap);
1496 }
1497 return !hadError;
1498 }
1499
1500 // Map I/O variables to provided offsets, and make bindings for
1501 // unbound but live variables.
1502 //
1503 // Returns false if the input is too malformed to do this.
addStage(EShLanguage stage,TIntermediate & intermediate,TInfoSink & infoSink,TIoMapResolver * resolver)1504 bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1505 bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
1506 intermediate.getAutoMapBindings() ||
1507 intermediate.getAutoMapLocations();
1508
1509 // Profile and version are use for symbol validate.
1510 profile = intermediate.getProfile();
1511 version = intermediate.getVersion();
1512
1513 // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1514 // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1515 for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1516 somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1517 intermediate.hasShiftBindingForSet(TResourceType(res));
1518 }
1519 if (! somethingToDo && resolver == nullptr) {
1520 return true;
1521 }
1522 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) {
1523 return false;
1524 }
1525 TIntermNode* root = intermediate.getTreeRoot();
1526 if (root == nullptr) {
1527 return false;
1528 }
1529 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1530 TDefaultGlslIoResolver defaultResolver(intermediate);
1531 #ifdef ENABLE_HLSL
1532 TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1533 if (resolver == nullptr) {
1534 // TODO: use a passed in IO mapper for this
1535 if (intermediate.usingHlslIoMapping())
1536 resolver = &defaultHlslResolver;
1537 else
1538 resolver = &defaultResolver;
1539 }
1540 #else
1541 if (resolver == nullptr) {
1542 resolver = &defaultResolver;
1543 }
1544 #endif
1545 resolver->addStage(stage, intermediate);
1546 inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap();
1547 TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
1548 *uniformVarMap[stage]);
1549 TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage],
1550 *uniformVarMap[stage]);
1551 root->traverse(&iter_binding_all);
1552 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1553 while (! iter_binding_live.destinations.empty()) {
1554 TIntermNode* destination = iter_binding_live.destinations.back();
1555 iter_binding_live.destinations.pop_back();
1556 destination->traverse(&iter_binding_live);
1557 }
1558
1559 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1560 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1561 // Resolve current stage input symbol location with previous stage output here,
1562 // uniform symbol, ubo, ssbo and opaque symbols are per-program resource,
1563 // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap()
1564 resolver->beginNotifications(stage);
1565 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify);
1566 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify);
1567 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify);
1568 resolver->endNotifications(stage);
1569 TSlotCollector slotCollector(*resolver, infoSink);
1570 resolver->beginCollect(stage);
1571 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector);
1572 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector);
1573 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector);
1574 resolver->endCollect(stage);
1575 intermediates[stage] = &intermediate;
1576 return !hadError;
1577 }
1578
doMap(TIoMapResolver * resolver,TInfoSink & infoSink)1579 bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
1580 resolver->endResolve(EShLangCount);
1581 if (!hadError) {
1582 //Resolve uniform location, ubo/ssbo/opaque bindings across stages
1583 TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, uniformVarMap, infoSink, hadError);
1584 TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
1585 TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps,
1586 outVarMaps, uniformVarMap, hadError, profile, version);
1587
1588 TVarLiveVector inVectors[EShLangCount];
1589 TVarLiveVector outVectors[EShLangCount];
1590 TVarLiveVector uniformVector;
1591
1592 resolver->beginResolve(EShLangCount);
1593 for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
1594 if (inVarMaps[stage] != nullptr) {
1595 inOutResolve.setStage(EShLanguage(stage));
1596
1597 // copy vars into a sorted list
1598 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(),
1599 [&inVectors, stage](TVarLivePair p) { inVectors[stage].push_back(p); });
1600 std::sort(inVectors[stage].begin(), inVectors[stage].end(),
1601 [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1602 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1603 });
1604
1605 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(),
1606 [&outVectors, stage](TVarLivePair p) { outVectors[stage].push_back(p); });
1607 std::sort(outVectors[stage].begin(), outVectors[stage].end(),
1608 [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1609 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1610 });
1611
1612 for (auto& var : inVectors[stage]) { symbolValidater(var); }
1613 for (auto& var : inVectors[stage]) { inOutResolve(var); }
1614 for (auto& var : outVectors[stage]) { symbolValidater(var); }
1615 for (auto& var : outVectors[stage]) { inOutResolve(var); }
1616
1617 // copy results back into maps
1618 std::for_each(inVectors[stage].begin(), inVectors[stage].end(),
1619 [this, stage](TVarLivePair p) {
1620 auto at = inVarMaps[stage]->find(p.first);
1621 if (at != inVarMaps[stage]->end())
1622 at->second = p.second;
1623 });
1624
1625 std::for_each(outVectors[stage].begin(), outVectors[stage].end(),
1626 [this, stage](TVarLivePair p) {
1627 auto at = outVarMaps[stage]->find(p.first);
1628 if (at != outVarMaps[stage]->end())
1629 at->second = p.second;
1630 });
1631
1632 }
1633 if (uniformVarMap[stage] != nullptr) {
1634 uniformResolve.setStage(EShLanguage(stage));
1635 for (auto& var : *(uniformVarMap[stage])) { uniformVector.push_back(var); }
1636 }
1637 }
1638 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1639 return TVarEntryInfo::TOrderByPriorityAndLive()(p1.second, p2.second);
1640 });
1641 for (auto& var : uniformVector) { symbolValidater(var); }
1642 for (auto& var : uniformVector) { uniformResolve(var); }
1643 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1644 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1645 });
1646 resolver->endResolve(EShLangCount);
1647 if (autoPushConstantBlockName.length()) {
1648 bool upgraded = false;
1649 for (size_t stage = 0; stage < EShLangCount; stage++) {
1650 if (intermediates[stage] != nullptr) {
1651 TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1652 auto at = pUniformVarMap[stage]->find(autoPushConstantBlockName);
1653 if (at == pUniformVarMap[stage]->end())
1654 continue;
1655 TQualifier& qualifier = at->second.symbol->getQualifier();
1656 if (!qualifier.isUniform())
1657 continue;
1658 TType& t = at->second.symbol->getWritableType();
1659 int size, stride;
1660 TIntermediate::getBaseAlignment(t, size, stride, autoPushConstantBlockPacking,
1661 qualifier.layoutMatrix == ElmRowMajor);
1662 if (size <= int(autoPushConstantMaxSize)) {
1663 qualifier.setBlockStorage(EbsPushConstant);
1664 qualifier.layoutPacking = autoPushConstantBlockPacking;
1665 // Push constants don't have set/binding etc. decorations, remove those.
1666 qualifier.layoutSet = TQualifier::layoutSetEnd;
1667 at->second.clearNewAssignments();
1668
1669 upgraded = true;
1670 }
1671 }
1672 }
1673 // If it's been upgraded to push_constant, then set the flag so when its traversed
1674 // in the next for loop, all references to this symbol will get their flag changed.
1675 // so it doesn't get a set/binding assigned to it.
1676 if (upgraded) {
1677 std::for_each(uniformVector.begin(), uniformVector.end(),
1678 [this](TVarLivePair& p) {
1679 if (p.first == autoPushConstantBlockName) {
1680 p.second.upgradedToPushConstant = true;
1681 }
1682 });
1683 }
1684 }
1685 for (size_t stage = 0; stage < EShLangCount; stage++) {
1686 if (intermediates[stage] != nullptr) {
1687 // traverse each stage, set new location to each input/output and unifom symbol, set new binding to
1688 // ubo, ssbo and opaque symbols. Assign push_constant upgrades as well.
1689 TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1690 std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) {
1691 auto at = pUniformVarMap[stage]->find(p.second.symbol->getAccessName());
1692 if (at != pUniformVarMap[stage]->end() && at->second.id == p.second.id){
1693 if (p.second.upgradedToPushConstant) {
1694 at->second.upgradedToPushConstant = true;
1695 } else {
1696 int resolvedBinding = at->second.newBinding;
1697 at->second = p.second;
1698 if (resolvedBinding > 0)
1699 at->second.newBinding = resolvedBinding;
1700 }
1701 }
1702 });
1703 TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage],
1704 *uniformResolve.uniformVarMap[stage]);
1705 intermediates[stage]->getTreeRoot()->traverse(&iter_iomap);
1706 }
1707 }
1708 return !hadError;
1709 } else {
1710 return false;
1711 }
1712 }
1713
1714 } // end namespace glslang
1715