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 #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
37
38 #include "../Include/Common.h"
39 #include "../Include/InfoSink.h"
40
41 #include "gl_types.h"
42 #include "iomapper.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())
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->getName());
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->getName()); // 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->getName()] = 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 TVarLiveMap::const_iterator at = source->find(base->getName());
129 if (at == source->end())
130 return;
131
132 if (at->second.id != ent.id)
133 return;
134
135 if (at->second.newBinding != -1)
136 base->getWritableType().getQualifier().layoutBinding = at->second.newBinding;
137 if (at->second.newSet != -1)
138 base->getWritableType().getQualifier().layoutSet = at->second.newSet;
139 if (at->second.newLocation != -1)
140 base->getWritableType().getQualifier().layoutLocation = at->second.newLocation;
141 if (at->second.newComponent != -1)
142 base->getWritableType().getQualifier().layoutComponent = at->second.newComponent;
143 if (at->second.newIndex != -1)
144 base->getWritableType().getQualifier().layoutIndex = at->second.newIndex;
145 }
146
147 private:
148 const TVarLiveMap& inputList;
149 const TVarLiveMap& outputList;
150 const TVarLiveMap& uniformList;
151 };
152
153 struct TNotifyUniformAdaptor
154 {
155 EShLanguage stage;
156 TIoMapResolver& resolver;
TNotifyUniformAdaptorglslang::TNotifyUniformAdaptor157 inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
158 : stage(s)
159 , resolver(r)
160 {
161 }
162
operator ()glslang::TNotifyUniformAdaptor163 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
164 {
165 resolver.notifyBinding(stage, entKey.second);
166 }
167
168 private:
169 TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&) = delete;
170 };
171
172 struct TNotifyInOutAdaptor
173 {
174 EShLanguage stage;
175 TIoMapResolver& resolver;
TNotifyInOutAdaptorglslang::TNotifyInOutAdaptor176 inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
177 : stage(s)
178 , resolver(r)
179 {
180 }
181
operator ()glslang::TNotifyInOutAdaptor182 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
183 {
184 resolver.notifyInOut(stage, entKey.second);
185 }
186
187 private:
188 TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete;
189 };
190
191 struct TResolverUniformAdaptor {
TResolverUniformAdaptorglslang::TResolverUniformAdaptor192 TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
193 : stage(s)
194 , resolver(r)
195 , infoSink(i)
196 , error(e)
197 {
198 }
199
operator ()glslang::TResolverUniformAdaptor200 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
201 TVarEntryInfo& ent = entKey.second;
202 ent.newLocation = -1;
203 ent.newComponent = -1;
204 ent.newBinding = -1;
205 ent.newSet = -1;
206 ent.newIndex = -1;
207 const bool isValid = resolver.validateBinding(stage, ent);
208 if (isValid) {
209 resolver.resolveBinding(stage, ent);
210 resolver.resolveSet(stage, ent);
211 resolver.resolveUniformLocation(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.newSet != -1) {
222 if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
223 TString err = "mapped set out of range: " + entKey.first;
224
225 infoSink.info.message(EPrefixInternalError, err.c_str());
226 error = true;
227 }
228 }
229 } else {
230 TString errorMsg = "Invalid binding: " + entKey.first;
231 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
232 error = true;
233 }
234 }
235
setStageglslang::TResolverUniformAdaptor236 inline void setStage(EShLanguage s) { stage = s; }
237
238 EShLanguage stage;
239 TIoMapResolver& resolver;
240 TInfoSink& infoSink;
241 bool& error;
242
243 private:
244 TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete;
245 };
246
247 struct TResolverInOutAdaptor {
TResolverInOutAdaptorglslang::TResolverInOutAdaptor248 TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
249 : stage(s)
250 , resolver(r)
251 , infoSink(i)
252 , error(e)
253 {
254 }
255
operator ()glslang::TResolverInOutAdaptor256 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
257 {
258 TVarEntryInfo& ent = entKey.second;
259 ent.newLocation = -1;
260 ent.newComponent = -1;
261 ent.newBinding = -1;
262 ent.newSet = -1;
263 ent.newIndex = -1;
264 const bool isValid = resolver.validateInOut(stage, ent);
265 if (isValid) {
266 resolver.resolveInOutLocation(stage, ent);
267 resolver.resolveInOutComponent(stage, ent);
268 resolver.resolveInOutIndex(stage, ent);
269 } else {
270 TString errorMsg;
271 if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
272 errorMsg = "Invalid shader In/Out variable semantic: ";
273 errorMsg += ent.symbol->getType().getQualifier().semanticName;
274 } else {
275 errorMsg = "Invalid shader In/Out variable: ";
276 errorMsg += ent.symbol->getName();
277 }
278 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
279 error = true;
280 }
281 }
282
setStageglslang::TResolverInOutAdaptor283 inline void setStage(EShLanguage s) { stage = s; }
284
285 EShLanguage stage;
286 TIoMapResolver& resolver;
287 TInfoSink& infoSink;
288 bool& error;
289
290 private:
291 TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete;
292 };
293
294 // The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
295
296 struct TSymbolValidater
297 {
TSymbolValidaterglslang::TSymbolValidater298 TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
299 TVarLiveMap* uniform[EShLangCount], bool& hadError)
300 : preStage(EShLangCount)
301 , currentStage(EShLangCount)
302 , nextStage(EShLangCount)
303 , resolver(r)
304 , infoSink(i)
305 , hadError(hadError)
306 {
307 memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
308 memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
309 memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
310 }
311
operator ()glslang::TSymbolValidater312 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
313 TVarEntryInfo& ent1 = entKey.second;
314 TIntermSymbol* base = ent1.symbol;
315 const TType& type = ent1.symbol->getType();
316 const TString& name = entKey.first;
317 EShLanguage stage = ent1.stage;
318 TString mangleName1, mangleName2;
319 if (currentStage != stage) {
320 preStage = currentStage;
321 currentStage = stage;
322 nextStage = EShLangCount;
323 for (int i = currentStage + 1; i < EShLangCount; i++) {
324 if (inVarMaps[i] != nullptr) {
325 nextStage = static_cast<EShLanguage>(i);
326 break;
327 }
328 }
329 }
330
331 if (type.getQualifier().isArrayedIo(stage)) {
332 TType subType(type, 0);
333 subType.appendMangledName(mangleName1);
334 } else {
335 type.appendMangledName(mangleName1);
336 }
337
338 if (base->getQualifier().storage == EvqVaryingIn) {
339 // validate stage in;
340 if (preStage == EShLangCount)
341 return;
342 if (name == "gl_PerVertex")
343 return;
344 if (outVarMaps[preStage] != nullptr) {
345 auto ent2 = outVarMaps[preStage]->find(name);
346 if (ent2 != outVarMaps[preStage]->end()) {
347 if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) {
348 TType subType(ent2->second.symbol->getType(), 0);
349 subType.appendMangledName(mangleName2);
350 }
351 else {
352 ent2->second.symbol->getType().appendMangledName(mangleName2);
353 }
354 if (mangleName1 == mangleName2)
355 return;
356 else {
357 TString err = "Invalid In/Out variable type : " + entKey.first;
358 infoSink.info.message(EPrefixInternalError, err.c_str());
359 hadError = true;
360 }
361 }
362 return;
363 }
364 } else if (base->getQualifier().storage == EvqVaryingOut) {
365 // validate stage out;
366 if (nextStage == EShLangCount)
367 return;
368 if (name == "gl_PerVertex")
369 return;
370 if (outVarMaps[nextStage] != nullptr) {
371 auto ent2 = inVarMaps[nextStage]->find(name);
372 if (ent2 != inVarMaps[nextStage]->end()) {
373 if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
374 TType subType(ent2->second.symbol->getType(), 0);
375 subType.appendMangledName(mangleName2);
376 }
377 else {
378 ent2->second.symbol->getType().appendMangledName(mangleName2);
379 }
380 if (mangleName1 == mangleName2)
381 return;
382 else {
383 TString err = "Invalid In/Out variable type : " + entKey.first;
384 infoSink.info.message(EPrefixInternalError, err.c_str());
385 hadError = true;
386 }
387 }
388 return;
389 }
390 } else if (base->getQualifier().isUniformOrBuffer() && ! base->getQualifier().isPushConstant()) {
391 // validate uniform type;
392 for (int i = 0; i < EShLangCount; i++) {
393 if (i != currentStage && outVarMaps[i] != nullptr) {
394 auto ent2 = uniformVarMap[i]->find(name);
395 if (ent2 != uniformVarMap[i]->end()) {
396 ent2->second.symbol->getType().appendMangledName(mangleName2);
397 if (mangleName1 != mangleName2) {
398 TString err = "Invalid Uniform variable type : " + entKey.first;
399 infoSink.info.message(EPrefixInternalError, err.c_str());
400 hadError = true;
401 }
402 mangleName2.clear();
403 }
404 }
405 }
406 }
407 }
408 TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount];
409 // Use for mark pre stage, to get more interface symbol information.
410 EShLanguage preStage, currentStage, nextStage;
411 // Use for mark current shader stage for resolver
412 TIoMapResolver& resolver;
413 TInfoSink& infoSink;
414 bool& hadError;
415
416 private:
417 TSymbolValidater& operator=(TSymbolValidater&) = delete;
418 };
419
420 struct TSlotCollector {
TSlotCollectorglslang::TSlotCollector421 TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
422
operator ()glslang::TSlotCollector423 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
424 resolver.reserverStorageSlot(entKey.second, infoSink);
425 resolver.reserverResourceSlot(entKey.second, infoSink);
426 }
427 TIoMapResolver& resolver;
428 TInfoSink& infoSink;
429
430 private:
431 TSlotCollector& operator=(TSlotCollector&) = delete;
432 };
433
TDefaultIoResolverBase(const TIntermediate & intermediate)434 TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
435 : intermediate(intermediate)
436 , nextUniformLocation(intermediate.getUniformLocationBase())
437 , nextInputLocation(0)
438 , nextOutputLocation(0)
439 {
440 memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
441 }
442
getBaseBinding(TResourceType res,unsigned int set) const443 int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const {
444 return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set));
445 }
446
getResourceSetBinding() const447 const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding() const {
448 return intermediate.getResourceSetBinding();
449 }
450
doAutoBindingMapping() const451 bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
452
doAutoLocationMapping() const453 bool TDefaultIoResolverBase::doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
454
findSlot(int set,int slot)455 TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
456 return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
457 }
458
checkEmpty(int set,int slot)459 bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
460 TSlotSet::iterator at = findSlot(set, slot);
461 return ! (at != slots[set].end() && *at == slot);
462 }
463
reserveSlot(int set,int slot,int size)464 int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
465 TSlotSet::iterator at = findSlot(set, slot);
466 // tolerate aliasing, by not double-recording aliases
467 // (policy about appropriateness of the alias is higher up)
468 for (int i = 0; i < size; i++) {
469 if (at == slots[set].end() || *at != slot + i)
470 at = slots[set].insert(at, slot + i);
471 ++at;
472 }
473 return slot;
474 }
475
getFreeSlot(int set,int base,int size)476 int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
477 TSlotSet::iterator at = findSlot(set, base);
478 if (at == slots[set].end())
479 return reserveSlot(set, base, size);
480 // look for a big enough gap
481 for (; at != slots[set].end(); ++at) {
482 if (*at - base >= size)
483 break;
484 base = *at + 1;
485 }
486 return reserveSlot(set, base, size);
487 }
488
resolveSet(EShLanguage,TVarEntryInfo & ent)489 int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) {
490 const TType& type = ent.symbol->getType();
491 if (type.getQualifier().hasSet()) {
492 return ent.newSet = type.getQualifier().layoutSet;
493 }
494 // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
495 if (getResourceSetBinding().size() == 1) {
496 return ent.newSet = atoi(getResourceSetBinding()[0].c_str());
497 }
498 return ent.newSet = 0;
499 }
500
resolveUniformLocation(EShLanguage,TVarEntryInfo & ent)501 int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
502 const TType& type = ent.symbol->getType();
503 const char* name = ent.symbol->getName().c_str();
504 // kick out of not doing this
505 if (! doAutoLocationMapping()) {
506 return ent.newLocation = -1;
507 }
508 // no locations added if already present, a built-in variable, a block, or an opaque
509 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
510 type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
511 return ent.newLocation = -1;
512 }
513 // no locations on blocks of built-in variables
514 if (type.isStruct()) {
515 if (type.getStruct()->size() < 1) {
516 return ent.newLocation = -1;
517 }
518 if ((*type.getStruct())[0].type->isBuiltIn()) {
519 return ent.newLocation = -1;
520 }
521 }
522 int location = intermediate.getUniformLocationOverride(name);
523 if (location != -1) {
524 return ent.newLocation = location;
525 }
526 location = nextUniformLocation;
527 nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
528 return ent.newLocation = location;
529 }
530
resolveInOutLocation(EShLanguage stage,TVarEntryInfo & ent)531 int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
532 const TType& type = ent.symbol->getType();
533 // kick out of not doing this
534 if (! doAutoLocationMapping()) {
535 return ent.newLocation = -1;
536 }
537
538 // no locations added if already present, or a built-in variable
539 if (type.getQualifier().hasLocation() || type.isBuiltIn()) {
540 return ent.newLocation = -1;
541 }
542
543 // no locations on blocks of built-in variables
544 if (type.isStruct()) {
545 if (type.getStruct()->size() < 1) {
546 return ent.newLocation = -1;
547 }
548 if ((*type.getStruct())[0].type->isBuiltIn()) {
549 return ent.newLocation = -1;
550 }
551 }
552 // point to the right input or output location counter
553 int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
554 // Placeholder. This does not do proper cross-stage lining up, nor
555 // work with mixed location/no-location declarations.
556 int location = nextLocation;
557 int typeLocationSize;
558 // Don’t take into account the outer-most array if the stage’s
559 // interface is automatically an array.
560 typeLocationSize = computeTypeLocationSize(type, stage);
561 nextLocation += typeLocationSize;
562 return ent.newLocation = location;
563 }
564
resolveInOutComponent(EShLanguage,TVarEntryInfo & ent)565 int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
566 return ent.newComponent = -1;
567 }
568
resolveInOutIndex(EShLanguage,TVarEntryInfo & ent)569 int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; }
570
computeTypeLocationSize(const TType & type,EShLanguage stage)571 uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
572 int typeLocationSize;
573 // Don’t take into account the outer-most array if the stage’s
574 // interface is automatically an array.
575 if (type.getQualifier().isArrayedIo(stage)) {
576 TType elementType(type, 0);
577 typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
578 } else {
579 typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
580 }
581 return typeLocationSize;
582 }
583
584 //TDefaultGlslIoResolver
getResourceType(const glslang::TType & type)585 TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
586 if (isImageType(type)) {
587 return EResImage;
588 }
589 if (isTextureType(type)) {
590 return EResTexture;
591 }
592 if (isSsboType(type)) {
593 return EResSsbo;
594 }
595 if (isSamplerType(type)) {
596 return EResSampler;
597 }
598 if (isUboType(type)) {
599 return EResUbo;
600 }
601 return EResCount;
602 }
603
TDefaultGlslIoResolver(const TIntermediate & intermediate)604 TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
605 : TDefaultIoResolverBase(intermediate)
606 , preStage(EShLangCount)
607 , currentStage(EShLangCount)
608 { }
609
resolveInOutLocation(EShLanguage stage,TVarEntryInfo & ent)610 int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
611 const TType& type = ent.symbol->getType();
612 const TString& name = getAccessName(ent.symbol);
613 if (currentStage != stage) {
614 preStage = currentStage;
615 currentStage = stage;
616 }
617 // kick out of not doing this
618 if (! doAutoLocationMapping()) {
619 return ent.newLocation = -1;
620 }
621 // expand the location to each element if the symbol is a struct or array
622 if (type.getQualifier().hasLocation()) {
623 return ent.newLocation = type.getQualifier().layoutLocation;
624 }
625 // no locations added if already present, or a built-in variable
626 if (type.isBuiltIn()) {
627 return ent.newLocation = -1;
628 }
629 // no locations on blocks of built-in variables
630 if (type.isStruct()) {
631 if (type.getStruct()->size() < 1) {
632 return ent.newLocation = -1;
633 }
634 if ((*type.getStruct())[0].type->isBuiltIn()) {
635 return ent.newLocation = -1;
636 }
637 }
638 int typeLocationSize = computeTypeLocationSize(type, stage);
639 int location = type.getQualifier().layoutLocation;
640 bool hasLocation = false;
641 EShLanguage keyStage(EShLangCount);
642 TStorageQualifier storage;
643 storage = EvqInOut;
644 if (type.getQualifier().isPipeInput()) {
645 // If this symbol is a input, search pre stage's out
646 keyStage = preStage;
647 }
648 if (type.getQualifier().isPipeOutput()) {
649 // If this symbol is a output, search next stage's in
650 keyStage = currentStage;
651 }
652 // The in/out in current stage is not declared with location, but it is possible declared
653 // with explicit location in other stages, find the storageSlotMap firstly to check whether
654 // the in/out has location
655 int resourceKey = buildStorageKey(keyStage, storage);
656 if (! storageSlotMap[resourceKey].empty()) {
657 TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name);
658 if (iter != storageSlotMap[resourceKey].end()) {
659 // If interface resource be found, set it has location and this symbol's new location
660 // equal the symbol's explicit location declaration in pre or next stage.
661 //
662 // vs: out vec4 a;
663 // fs: layout(..., location = 3,...) in vec4 a;
664 hasLocation = true;
665 location = iter->second;
666 // if we want deal like that:
667 // vs: layout(location=4) out vec4 a;
668 // out vec4 b;
669 //
670 // fs: in vec4 a;
671 // layout(location = 4) in vec4 b;
672 // we need retraverse the map.
673 }
674 if (! hasLocation) {
675 // If interface resource note found, It's mean the location in two stage are both implicit declarat.
676 // So we should find a new slot for this interface.
677 //
678 // vs: out vec4 a;
679 // fs: in vec4 a;
680 location = getFreeSlot(resourceKey, 0, typeLocationSize);
681 storageSlotMap[resourceKey][name] = location;
682 }
683 } else {
684 // the first interface declarated in a program.
685 TVarSlotMap varSlotMap;
686 location = getFreeSlot(resourceKey, 0, typeLocationSize);
687 varSlotMap[name] = location;
688 storageSlotMap[resourceKey] = varSlotMap;
689 }
690 //Update location
691 return ent.newLocation = location;
692 }
693
resolveUniformLocation(EShLanguage,TVarEntryInfo & ent)694 int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
695 const TType& type = ent.symbol->getType();
696 const TString& name = getAccessName(ent.symbol);
697 // kick out of not doing this
698 if (! doAutoLocationMapping()) {
699 return ent.newLocation = -1;
700 }
701 // expand the location to each element if the symbol is a struct or array
702 if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) {
703 return ent.newLocation = type.getQualifier().layoutLocation;
704 } else {
705 // no locations added if already present, a built-in variable, a block, or an opaque
706 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
707 type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
708 return ent.newLocation = -1;
709 }
710 // no locations on blocks of built-in variables
711 if (type.isStruct()) {
712 if (type.getStruct()->size() < 1) {
713 return ent.newLocation = -1;
714 }
715 if ((*type.getStruct())[0].type->isBuiltIn()) {
716 return ent.newLocation = -1;
717 }
718 }
719 }
720 int location = intermediate.getUniformLocationOverride(name.c_str());
721 if (location != -1) {
722 return ent.newLocation = location;
723 }
724
725 int size = TIntermediate::computeTypeUniformLocationSize(type);
726
727 // The uniform in current stage is not declared with location, but it is possible declared
728 // with explicit location in other stages, find the storageSlotMap firstly to check whether
729 // the uniform has location
730 bool hasLocation = false;
731 int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
732 TVarSlotMap& slotMap = storageSlotMap[resourceKey];
733 // Check dose shader program has uniform resource
734 if (! slotMap.empty()) {
735 // If uniform resource not empty, try find a same name uniform
736 TVarSlotMap::iterator iter = slotMap.find(name);
737 if (iter != slotMap.end()) {
738 // If uniform resource be found, set it has location and this symbol's new location
739 // equal the uniform's explicit location declaration in other stage.
740 //
741 // vs: uniform vec4 a;
742 // fs: layout(..., location = 3,...) uniform vec4 a;
743 hasLocation = true;
744 location = iter->second;
745 }
746 if (! hasLocation) {
747 // No explicit location declaration in other stage.
748 // So we should find a new slot for this uniform.
749 //
750 // vs: uniform vec4 a;
751 // fs: uniform vec4 a;
752 location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
753 storageSlotMap[resourceKey][name] = location;
754 }
755 } else {
756 // the first uniform declaration in a program.
757 TVarSlotMap varSlotMap;
758 location = getFreeSlot(resourceKey, 0, size);
759 varSlotMap[name] = location;
760 storageSlotMap[resourceKey] = varSlotMap;
761 }
762 return ent.newLocation = location;
763 }
764
resolveBinding(EShLanguage,TVarEntryInfo & ent)765 int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) {
766 const TType& type = ent.symbol->getType();
767 const TString& name = getAccessName(ent.symbol);
768 // On OpenGL arrays of opaque types take a separate binding for each element
769 int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
770 TResourceType resource = getResourceType(type);
771 // don't need to handle uniform symbol, it will be handled in resolveUniformLocation
772 if (resource == EResUbo && type.getBasicType() != EbtBlock) {
773 return ent.newBinding = -1;
774 }
775 // There is no 'set' qualifier in OpenGL shading language, each resource has its own
776 // binding name space, so remap the 'set' to resource type which make each resource
777 // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
778 int set = resource;
779 if (resource < EResCount) {
780 if (type.getQualifier().hasBinding()) {
781 ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
782 return ent.newBinding;
783 } else if (ent.live && doAutoBindingMapping()) {
784 // The resource in current stage is not declared with binding, but it is possible declared
785 // with explicit binding in other stages, find the resourceSlotMap firstly to check whether
786 // the resource has binding, don't need to allocate if it already has a binding
787 bool hasBinding = false;
788 if (! resourceSlotMap[resource].empty()) {
789 TVarSlotMap::iterator iter = resourceSlotMap[resource].find(name);
790 if (iter != resourceSlotMap[resource].end()) {
791 hasBinding = true;
792 ent.newBinding = iter->second;
793 }
794 }
795 if (! hasBinding) {
796 TVarSlotMap varSlotMap;
797 // find free slot, the caller did make sure it passes all vars with binding
798 // first and now all are passed that do not have a binding and needs one
799 int binding = getFreeSlot(resource, getBaseBinding(resource, set), numBindings);
800 varSlotMap[name] = binding;
801 resourceSlotMap[resource] = varSlotMap;
802 ent.newBinding = binding;
803 }
804 return ent.newBinding;
805 }
806 }
807 return ent.newBinding = -1;
808 }
809
beginResolve(EShLanguage stage)810 void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
811 // reset stage state
812 if (stage == EShLangCount)
813 preStage = currentStage = stage;
814 // update stage state
815 else if (currentStage != stage) {
816 preStage = currentStage;
817 currentStage = stage;
818 }
819 }
820
endResolve(EShLanguage)821 void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
822 // TODO nothing
823 }
824
beginCollect(EShLanguage stage)825 void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
826 // reset stage state
827 if (stage == EShLangCount)
828 preStage = currentStage = stage;
829 // update stage state
830 else if (currentStage != stage) {
831 preStage = currentStage;
832 currentStage = stage;
833 }
834 }
835
endCollect(EShLanguage)836 void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
837 // TODO nothing
838 }
839
reserverStorageSlot(TVarEntryInfo & ent,TInfoSink & infoSink)840 void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
841 const TType& type = ent.symbol->getType();
842 const TString& name = getAccessName(ent.symbol);
843 TStorageQualifier storage = type.getQualifier().storage;
844 EShLanguage stage(EShLangCount);
845 switch (storage) {
846 case EvqUniform:
847 if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) {
848 //
849 // Reserve the slots for the uniforms who has explicit location
850 int storageKey = buildStorageKey(EShLangCount, EvqUniform);
851 int location = type.getQualifier().layoutLocation;
852 TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
853 TVarSlotMap::iterator iter = varSlotMap.find(name);
854 if (iter == varSlotMap.end()) {
855 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
856 reserveSlot(storageKey, location, numLocations);
857 varSlotMap[name] = location;
858 } else {
859 // Allocate location by name for OpenGL driver, so the uniform in different
860 // stages should be declared with the same location
861 if (iter->second != location) {
862 TString errorMsg = "Invalid location: " + name;
863 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
864 hasError = true;
865 }
866 }
867 }
868 break;
869 case EvqVaryingIn:
870 case EvqVaryingOut:
871 //
872 // Reserve the slots for the inout who has explicit location
873 if (type.getQualifier().hasLocation()) {
874 stage = storage == EvqVaryingIn ? preStage : stage;
875 stage = storage == EvqVaryingOut ? currentStage : stage;
876 int storageKey = buildStorageKey(stage, EvqInOut);
877 int location = type.getQualifier().layoutLocation;
878 TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
879 TVarSlotMap::iterator iter = varSlotMap.find(name);
880 if (iter == varSlotMap.end()) {
881 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
882 reserveSlot(storageKey, location, numLocations);
883 varSlotMap[name] = location;
884 } else {
885 // Allocate location by name for OpenGL driver, so the uniform in different
886 // stages should be declared with the same location
887 if (iter->second != location) {
888 TString errorMsg = "Invalid location: " + name;
889 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
890 hasError = true;
891 }
892 }
893 }
894 break;
895 default:
896 break;
897 }
898 }
899
reserverResourceSlot(TVarEntryInfo & ent,TInfoSink & infoSink)900 void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
901 const TType& type = ent.symbol->getType();
902 const TString& name = getAccessName(ent.symbol);
903 int resource = getResourceType(type);
904 if (type.getQualifier().hasBinding()) {
905 TVarSlotMap& varSlotMap = resourceSlotMap[resource];
906 TVarSlotMap::iterator iter = varSlotMap.find(name);
907 int binding = type.getQualifier().layoutBinding;
908 if (iter == varSlotMap.end()) {
909 // Reserve the slots for the ubo, ssbo and opaques who has explicit binding
910 int numBindings = type.isSizedArray() ? type.getCumulativeArraySize() : 1;
911 varSlotMap[name] = binding;
912 reserveSlot(resource, binding, numBindings);
913 } else {
914 // Allocate binding by name for OpenGL driver, so the resource in different
915 // stages should be declared with the same binding
916 if (iter->second != binding) {
917 TString errorMsg = "Invalid binding: " + name;
918 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
919 hasError = true;
920 }
921 }
922 }
923 }
924
getAccessName(const TIntermSymbol * symbol)925 const TString& TDefaultGlslIoResolver::getAccessName(const TIntermSymbol* symbol)
926 {
927 return symbol->getBasicType() == EbtBlock ?
928 symbol->getType().getTypeName() :
929 symbol->getName();
930 }
931
932 //TDefaultGlslIoResolver end
933
934 /*
935 * Basic implementation of glslang::TIoMapResolver that replaces the
936 * previous offset behavior.
937 * It does the same, uses the offsets for the corresponding uniform
938 * types. Also respects the EOptionAutoMapBindings flag and binds
939 * them if needed.
940 */
941 /*
942 * Default resolver
943 */
944 struct TDefaultIoResolver : public TDefaultIoResolverBase {
TDefaultIoResolverglslang::TDefaultIoResolver945 TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
946
validateBindingglslang::TDefaultIoResolver947 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
948
getResourceTypeglslang::TDefaultIoResolver949 TResourceType getResourceType(const glslang::TType& type) override {
950 if (isImageType(type)) {
951 return EResImage;
952 }
953 if (isTextureType(type)) {
954 return EResTexture;
955 }
956 if (isSsboType(type)) {
957 return EResSsbo;
958 }
959 if (isSamplerType(type)) {
960 return EResSampler;
961 }
962 if (isUboType(type)) {
963 return EResUbo;
964 }
965 return EResCount;
966 }
967
resolveBindingglslang::TDefaultIoResolver968 int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
969 const TType& type = ent.symbol->getType();
970 const int set = getLayoutSet(type);
971 // On OpenGL arrays of opaque types take a seperate binding for each element
972 int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
973 TResourceType resource = getResourceType(type);
974 if (resource < EResCount) {
975 if (type.getQualifier().hasBinding()) {
976 return ent.newBinding = reserveSlot(
977 set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
978 } else if (ent.live && doAutoBindingMapping()) {
979 // find free slot, the caller did make sure it passes all vars with binding
980 // first and now all are passed that do not have a binding and needs one
981 return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings);
982 }
983 }
984 return ent.newBinding = -1;
985 }
986 };
987
988 #ifdef ENABLE_HLSL
989 /********************************************************************************
990 The following IO resolver maps types in HLSL register space, as follows:
991
992 t - for shader resource views (SRV)
993 TEXTURE1D
994 TEXTURE1DARRAY
995 TEXTURE2D
996 TEXTURE2DARRAY
997 TEXTURE3D
998 TEXTURECUBE
999 TEXTURECUBEARRAY
1000 TEXTURE2DMS
1001 TEXTURE2DMSARRAY
1002 STRUCTUREDBUFFER
1003 BYTEADDRESSBUFFER
1004 BUFFER
1005 TBUFFER
1006
1007 s - for samplers
1008 SAMPLER
1009 SAMPLER1D
1010 SAMPLER2D
1011 SAMPLER3D
1012 SAMPLERCUBE
1013 SAMPLERSTATE
1014 SAMPLERCOMPARISONSTATE
1015
1016 u - for unordered access views (UAV)
1017 RWBYTEADDRESSBUFFER
1018 RWSTRUCTUREDBUFFER
1019 APPENDSTRUCTUREDBUFFER
1020 CONSUMESTRUCTUREDBUFFER
1021 RWBUFFER
1022 RWTEXTURE1D
1023 RWTEXTURE1DARRAY
1024 RWTEXTURE2D
1025 RWTEXTURE2DARRAY
1026 RWTEXTURE3D
1027
1028 b - for constant buffer views (CBV)
1029 CBUFFER
1030 CONSTANTBUFFER
1031 ********************************************************************************/
1032 struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
TDefaultHlslIoResolverglslang::TDefaultHlslIoResolver1033 TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1034
validateBindingglslang::TDefaultHlslIoResolver1035 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1036
getResourceTypeglslang::TDefaultHlslIoResolver1037 TResourceType getResourceType(const glslang::TType& type) override {
1038 if (isUavType(type)) {
1039 return EResUav;
1040 }
1041 if (isSrvType(type)) {
1042 return EResTexture;
1043 }
1044 if (isSamplerType(type)) {
1045 return EResSampler;
1046 }
1047 if (isUboType(type)) {
1048 return EResUbo;
1049 }
1050 return EResCount;
1051 }
1052
resolveBindingglslang::TDefaultHlslIoResolver1053 int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
1054 const TType& type = ent.symbol->getType();
1055 const int set = getLayoutSet(type);
1056 TResourceType resource = getResourceType(type);
1057 if (resource < EResCount) {
1058 if (type.getQualifier().hasBinding()) {
1059 return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding);
1060 } else if (ent.live && doAutoBindingMapping()) {
1061 // find free slot, the caller did make sure it passes all vars with binding
1062 // first and now all are passed that do not have a binding and needs one
1063 return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set));
1064 }
1065 }
1066 return ent.newBinding = -1;
1067 }
1068 };
1069 #endif
1070
1071 // Map I/O variables to provided offsets, and make bindings for
1072 // unbound but live variables.
1073 //
1074 // Returns false if the input is too malformed to do this.
addStage(EShLanguage stage,TIntermediate & intermediate,TInfoSink & infoSink,TIoMapResolver * resolver)1075 bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1076 bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
1077 intermediate.getAutoMapLocations();
1078 // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1079 // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1080 for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1081 somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1082 intermediate.hasShiftBindingForSet(TResourceType(res));
1083 }
1084 if (! somethingToDo && resolver == nullptr)
1085 return true;
1086 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
1087 return false;
1088 TIntermNode* root = intermediate.getTreeRoot();
1089 if (root == nullptr)
1090 return false;
1091 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1092 TDefaultIoResolver defaultResolver(intermediate);
1093 #ifdef ENABLE_HLSL
1094 TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1095 if (resolver == nullptr) {
1096 // TODO: use a passed in IO mapper for this
1097 if (intermediate.usingHlslIoMapping())
1098 resolver = &defaultHlslResolver;
1099 else
1100 resolver = &defaultResolver;
1101 }
1102 resolver->addStage(stage);
1103 #else
1104 resolver = &defaultResolver;
1105 #endif
1106
1107 TVarLiveMap inVarMap, outVarMap, uniformVarMap;
1108 TVarLiveVector inVector, outVector, uniformVector;
1109 TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
1110 TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
1111 root->traverse(&iter_binding_all);
1112 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1113 while (! iter_binding_live.destinations.empty()) {
1114 TIntermNode* destination = iter_binding_live.destinations.back();
1115 iter_binding_live.destinations.pop_back();
1116 destination->traverse(&iter_binding_live);
1117 }
1118
1119 // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
1120 std::for_each(inVarMap.begin(), inVarMap.end(),
1121 [&inVector](TVarLivePair p) { inVector.push_back(p); });
1122 std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1123 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1124 });
1125 std::for_each(outVarMap.begin(), outVarMap.end(),
1126 [&outVector](TVarLivePair p) { outVector.push_back(p); });
1127 std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1128 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1129 });
1130 std::for_each(uniformVarMap.begin(), uniformVarMap.end(),
1131 [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); });
1132 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1133 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1134 });
1135 bool hadError = false;
1136 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1137 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1138 TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError);
1139 TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
1140 resolver->beginNotifications(stage);
1141 std::for_each(inVector.begin(), inVector.end(), inOutNotify);
1142 std::for_each(outVector.begin(), outVector.end(), inOutNotify);
1143 std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
1144 resolver->endNotifications(stage);
1145 resolver->beginResolve(stage);
1146 std::for_each(inVector.begin(), inVector.end(), inOutResolve);
1147 std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
1148 auto at = inVarMap.find(p.second.symbol->getName());
1149 if (at != inVarMap.end())
1150 at->second = p.second;
1151 });
1152 std::for_each(outVector.begin(), outVector.end(), inOutResolve);
1153 std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
1154 auto at = outVarMap.find(p.second.symbol->getName());
1155 if (at != outVarMap.end())
1156 at->second = p.second;
1157 });
1158 std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
1159 std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
1160 auto at = uniformVarMap.find(p.second.symbol->getName());
1161 if (at != uniformVarMap.end())
1162 at->second = p.second;
1163 });
1164 resolver->endResolve(stage);
1165 if (!hadError) {
1166 TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
1167 root->traverse(&iter_iomap);
1168 }
1169 return !hadError;
1170 }
1171
1172 // Map I/O variables to provided offsets, and make bindings for
1173 // unbound but live variables.
1174 //
1175 // Returns false if the input is too malformed to do this.
addStage(EShLanguage stage,TIntermediate & intermediate,TInfoSink & infoSink,TIoMapResolver * resolver)1176 bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1177
1178 bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
1179 intermediate.getAutoMapLocations();
1180 // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1181 // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1182 for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1183 somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1184 intermediate.hasShiftBindingForSet(TResourceType(res));
1185 }
1186 if (! somethingToDo && resolver == nullptr) {
1187 return true;
1188 }
1189 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) {
1190 return false;
1191 }
1192 TIntermNode* root = intermediate.getTreeRoot();
1193 if (root == nullptr) {
1194 return false;
1195 }
1196 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1197 TDefaultGlslIoResolver defaultResolver(intermediate);
1198 if (resolver == nullptr) {
1199 resolver = &defaultResolver;
1200 }
1201 resolver->addStage(stage);
1202 inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap();
1203 TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
1204 *uniformVarMap[stage]);
1205 TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage],
1206 *uniformVarMap[stage]);
1207 root->traverse(&iter_binding_all);
1208 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1209 while (! iter_binding_live.destinations.empty()) {
1210 TIntermNode* destination = iter_binding_live.destinations.back();
1211 iter_binding_live.destinations.pop_back();
1212 destination->traverse(&iter_binding_live);
1213 }
1214
1215 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1216 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1217 // Resolve current stage input symbol location with previous stage output here,
1218 // uniform symbol, ubo, ssbo and opaque symbols are per-program resource,
1219 // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap()
1220 resolver->beginNotifications(stage);
1221 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify);
1222 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify);
1223 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify);
1224 resolver->endNotifications(stage);
1225 TSlotCollector slotCollector(*resolver, infoSink);
1226 resolver->beginCollect(stage);
1227 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector);
1228 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector);
1229 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector);
1230 resolver->endCollect(stage);
1231 intermediates[stage] = &intermediate;
1232 return !hadError;
1233 }
1234
doMap(TIoMapResolver * resolver,TInfoSink & infoSink)1235 bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
1236 resolver->endResolve(EShLangCount);
1237 if (!hadError) {
1238 //Resolve uniform location, ubo/ssbo/opaque bindings across stages
1239 TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, infoSink, hadError);
1240 TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
1241 TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps, outVarMaps, uniformVarMap, hadError);
1242 TVarLiveVector uniformVector;
1243 resolver->beginResolve(EShLangCount);
1244 for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
1245 if (inVarMaps[stage] != nullptr) {
1246 inOutResolve.setStage(EShLanguage(stage));
1247 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), symbolValidater);
1248 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutResolve);
1249 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), symbolValidater);
1250 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutResolve);
1251 }
1252 if (uniformVarMap[stage] != nullptr) {
1253 uniformResolve.setStage(EShLanguage(stage));
1254 // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
1255 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(),
1256 [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); });
1257 }
1258 }
1259 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1260 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1261 });
1262 std::for_each(uniformVector.begin(), uniformVector.end(), symbolValidater);
1263 std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
1264 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1265 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1266 });
1267 resolver->endResolve(EShLangCount);
1268 for (size_t stage = 0; stage < EShLangCount; stage++) {
1269 if (intermediates[stage] != nullptr) {
1270 // traverse each stage, set new location to each input/output and unifom symbol, set new binding to
1271 // ubo, ssbo and opaque symbols
1272 TVarLiveMap** pUniformVarMap = uniformVarMap;
1273 std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) {
1274 auto at = pUniformVarMap[stage]->find(p.second.symbol->getName());
1275 if (at != pUniformVarMap[stage]->end())
1276 at->second = p.second;
1277 });
1278 TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage],
1279 *uniformVarMap[stage]);
1280 intermediates[stage]->getTreeRoot()->traverse(&iter_iomap);
1281 }
1282 }
1283 return !hadError;
1284 } else {
1285 return false;
1286 }
1287 }
1288
1289 } // end namespace glslang
1290
1291 #endif // !GLSLANG_WEB && !GLSLANG_ANGLE
1292