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 "iomapper.h"
39 #include "LiveTraverser.h"
40 #include "localintermediate.h"
41
42 #include "gl_types.h"
43
44 #include <unordered_set>
45 #include <unordered_map>
46
47 //
48 // Map IO bindings.
49 //
50 // High-level algorithm for one stage:
51 //
52 // 1. Traverse all code (live+dead) to find the explicitly provided bindings.
53 //
54 // 2. Traverse (just) the live code to determine which non-provided bindings
55 // require auto-numbering. We do not auto-number dead ones.
56 //
57 // 3. Traverse all the code to apply the bindings:
58 // a. explicitly given bindings are offset according to their type
59 // b. implicit live bindings are auto-numbered into the holes, using
60 // any open binding slot.
61 // c. implicit dead bindings are left un-bound.
62 //
63
64
65 namespace glslang {
66
67 struct TVarEntryInfo
68 {
69 int id;
70 TIntermSymbol* symbol;
71 bool live;
72 int newBinding;
73 int newSet;
74 int newLocation;
75 int newComponent;
76 int newIndex;
77
78 struct TOrderById
79 {
operator ()glslang::TVarEntryInfo::TOrderById80 inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r)
81 {
82 return l.id < r.id;
83 }
84 };
85
86 struct TOrderByPriority
87 {
88 // ordering:
89 // 1) has both binding and set
90 // 2) has binding but no set
91 // 3) has no binding but set
92 // 4) has no binding and no set
operator ()glslang::TVarEntryInfo::TOrderByPriority93 inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r)
94 {
95 const TQualifier& lq = l.symbol->getQualifier();
96 const TQualifier& rq = r.symbol->getQualifier();
97
98 // simple rules:
99 // has binding gives 2 points
100 // has set gives 1 point
101 // who has the most points is more important.
102 int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
103 int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
104
105 if (lPoints == rPoints)
106 return l.id < r.id;
107 return lPoints > rPoints;
108 }
109 };
110 };
111
112
113
114 typedef std::vector<TVarEntryInfo> TVarLiveMap;
115
116 class TVarGatherTraverser : public TLiveTraverser
117 {
118 public:
TVarGatherTraverser(const TIntermediate & i,bool traverseDeadCode,TVarLiveMap & inList,TVarLiveMap & outList,TVarLiveMap & uniformList)119 TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList)
120 : TLiveTraverser(i, traverseDeadCode, true, true, false)
121 , inputList(inList)
122 , outputList(outList)
123 , uniformList(uniformList)
124 {
125 }
126
127
visitSymbol(TIntermSymbol * base)128 virtual void visitSymbol(TIntermSymbol* base)
129 {
130 TVarLiveMap* target = nullptr;
131 if (base->getQualifier().storage == EvqVaryingIn)
132 target = &inputList;
133 else if (base->getQualifier().storage == EvqVaryingOut)
134 target = &outputList;
135 else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().layoutPushConstant)
136 target = &uniformList;
137
138 if (target) {
139 TVarEntryInfo ent = { base->getId(), base, !traverseAll };
140 TVarLiveMap::iterator at = std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
141 if (at != target->end() && at->id == ent.id)
142 at->live = at->live || !traverseAll; // update live state
143 else
144 target->insert(at, ent);
145 }
146 }
147
148 private:
149 TVarLiveMap& inputList;
150 TVarLiveMap& outputList;
151 TVarLiveMap& uniformList;
152 };
153
154 class TVarSetTraverser : public TLiveTraverser
155 {
156 public:
TVarSetTraverser(const TIntermediate & i,const TVarLiveMap & inList,const TVarLiveMap & outList,const TVarLiveMap & uniformList)157 TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList)
158 : TLiveTraverser(i, true, true, true, false)
159 , inputList(inList)
160 , outputList(outList)
161 , uniformList(uniformList)
162 {
163 }
164
165
visitSymbol(TIntermSymbol * base)166 virtual void visitSymbol(TIntermSymbol* base)
167 {
168 const TVarLiveMap* source;
169 if (base->getQualifier().storage == EvqVaryingIn)
170 source = &inputList;
171 else if (base->getQualifier().storage == EvqVaryingOut)
172 source = &outputList;
173 else if (base->getQualifier().isUniformOrBuffer())
174 source = &uniformList;
175 else
176 return;
177
178 TVarEntryInfo ent = { base->getId() };
179 TVarLiveMap::const_iterator at = std::lower_bound(source->begin(), source->end(), ent, TVarEntryInfo::TOrderById());
180 if (at == source->end())
181 return;
182
183 if (at->id != ent.id)
184 return;
185
186 if (at->newBinding != -1)
187 base->getWritableType().getQualifier().layoutBinding = at->newBinding;
188 if (at->newSet != -1)
189 base->getWritableType().getQualifier().layoutSet = at->newSet;
190 if (at->newLocation != -1)
191 base->getWritableType().getQualifier().layoutLocation = at->newLocation;
192 if (at->newComponent != -1)
193 base->getWritableType().getQualifier().layoutComponent = at->newComponent;
194 if (at->newIndex != -1)
195 base->getWritableType().getQualifier().layoutIndex = at->newIndex;
196 }
197
198 private:
199 const TVarLiveMap& inputList;
200 const TVarLiveMap& outputList;
201 const TVarLiveMap& uniformList;
202 };
203
204 struct TNotifyUniformAdaptor
205 {
206 EShLanguage stage;
207 TIoMapResolver& resolver;
TNotifyUniformAdaptorglslang::TNotifyUniformAdaptor208 inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
209 : stage(s)
210 , resolver(r)
211 {
212 }
operator ()glslang::TNotifyUniformAdaptor213 inline void operator()(TVarEntryInfo& ent)
214 {
215 resolver.notifyBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
216 }
217 private:
218 TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&);
219 };
220
221 struct TNotifyInOutAdaptor
222 {
223 EShLanguage stage;
224 TIoMapResolver& resolver;
TNotifyInOutAdaptorglslang::TNotifyInOutAdaptor225 inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
226 : stage(s)
227 , resolver(r)
228 {
229 }
operator ()glslang::TNotifyInOutAdaptor230 inline void operator()(TVarEntryInfo& ent)
231 {
232 resolver.notifyInOut(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
233 }
234 private:
235 TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&);
236 };
237
238 struct TResolverUniformAdaptor
239 {
TResolverUniformAdaptorglslang::TResolverUniformAdaptor240 TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
241 : stage(s)
242 , resolver(r)
243 , infoSink(i)
244 , error(e)
245 , intermediate(interm)
246 {
247 }
248
operator ()glslang::TResolverUniformAdaptor249 inline void operator()(TVarEntryInfo& ent)
250 {
251 ent.newLocation = -1;
252 ent.newComponent = -1;
253 ent.newBinding = -1;
254 ent.newSet = -1;
255 ent.newIndex = -1;
256 const bool isValid = resolver.validateBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
257 ent.live);
258 if (isValid) {
259 ent.newBinding = resolver.resolveBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
260 ent.live);
261 ent.newSet = resolver.resolveSet(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
262 ent.newLocation = resolver.resolveUniformLocation(stage, ent.symbol->getName().c_str(),
263 ent.symbol->getType(), ent.live);
264
265 if (ent.newBinding != -1) {
266 if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
267 TString err = "mapped binding out of range: " + ent.symbol->getName();
268
269 infoSink.info.message(EPrefixInternalError, err.c_str());
270 error = true;
271 }
272 }
273 if (ent.newSet != -1) {
274 if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
275 TString err = "mapped set out of range: " + ent.symbol->getName();
276
277 infoSink.info.message(EPrefixInternalError, err.c_str());
278 error = true;
279 }
280 }
281 } else {
282 TString errorMsg = "Invalid binding: " + ent.symbol->getName();
283 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
284 error = true;
285 }
286 }
287
288 EShLanguage stage;
289 TIoMapResolver& resolver;
290 TInfoSink& infoSink;
291 bool& error;
292 TIntermediate& intermediate;
293
294 private:
295 TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&);
296 };
297
298 struct TResolverInOutAdaptor
299 {
TResolverInOutAdaptorglslang::TResolverInOutAdaptor300 TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
301 : stage(s)
302 , resolver(r)
303 , infoSink(i)
304 , error(e)
305 , intermediate(interm)
306 {
307 }
308
operator ()glslang::TResolverInOutAdaptor309 inline void operator()(TVarEntryInfo& ent)
310 {
311 ent.newLocation = -1;
312 ent.newComponent = -1;
313 ent.newBinding = -1;
314 ent.newSet = -1;
315 ent.newIndex = -1;
316 const bool isValid = resolver.validateInOut(stage,
317 ent.symbol->getName().c_str(),
318 ent.symbol->getType(),
319 ent.live);
320 if (isValid) {
321 ent.newLocation = resolver.resolveInOutLocation(stage,
322 ent.symbol->getName().c_str(),
323 ent.symbol->getType(),
324 ent.live);
325 ent.newComponent = resolver.resolveInOutComponent(stage,
326 ent.symbol->getName().c_str(),
327 ent.symbol->getType(),
328 ent.live);
329 ent.newIndex = resolver.resolveInOutIndex(stage,
330 ent.symbol->getName().c_str(),
331 ent.symbol->getType(),
332 ent.live);
333 } else {
334 TString errorMsg;
335 if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
336 errorMsg = "Invalid shader In/Out variable semantic: ";
337 errorMsg += ent.symbol->getType().getQualifier().semanticName;
338 } else {
339 errorMsg = "Invalid shader In/Out variable: ";
340 errorMsg += ent.symbol->getName();
341 }
342 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
343 error = true;
344 }
345 }
346
347 EShLanguage stage;
348 TIoMapResolver& resolver;
349 TInfoSink& infoSink;
350 bool& error;
351 TIntermediate& intermediate;
352
353 private:
354 TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&);
355 };
356
357 // Base class for shared TIoMapResolver services, used by several derivations.
358 struct TDefaultIoResolverBase : public glslang::TIoMapResolver
359 {
TDefaultIoResolverBaseglslang::TDefaultIoResolverBase360 TDefaultIoResolverBase(const TIntermediate &intermediate) :
361 intermediate(intermediate),
362 nextUniformLocation(intermediate.getUniformLocationBase()),
363 nextInputLocation(0),
364 nextOutputLocation(0)
365 { }
366
getBaseBindingglslang::TDefaultIoResolverBase367 int getBaseBinding(TResourceType res, unsigned int set) const {
368 return selectBaseBinding(intermediate.getShiftBinding(res),
369 intermediate.getShiftBindingForSet(res, set));
370 }
371
getResourceSetBindingglslang::TDefaultIoResolverBase372 const std::vector<std::string>& getResourceSetBinding() const { return intermediate.getResourceSetBinding(); }
373
doAutoBindingMappingglslang::TDefaultIoResolverBase374 bool doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
doAutoLocationMappingglslang::TDefaultIoResolverBase375 bool doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
376
377 typedef std::vector<int> TSlotSet;
378 typedef std::unordered_map<int, TSlotSet> TSlotSetMap;
379 TSlotSetMap slots;
380
findSlotglslang::TDefaultIoResolverBase381 TSlotSet::iterator findSlot(int set, int slot)
382 {
383 return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
384 }
385
checkEmptyglslang::TDefaultIoResolverBase386 bool checkEmpty(int set, int slot)
387 {
388 TSlotSet::iterator at = findSlot(set, slot);
389 return !(at != slots[set].end() && *at == slot);
390 }
391
reserveSlotglslang::TDefaultIoResolverBase392 int reserveSlot(int set, int slot, int size = 1)
393 {
394 TSlotSet::iterator at = findSlot(set, slot);
395
396 // tolerate aliasing, by not double-recording aliases
397 // (policy about appropriateness of the alias is higher up)
398 for (int i = 0; i < size; i++) {
399 if (at == slots[set].end() || *at != slot + i)
400 at = slots[set].insert(at, slot + i);
401 ++at;
402 }
403
404 return slot;
405 }
406
getFreeSlotglslang::TDefaultIoResolverBase407 int getFreeSlot(int set, int base, int size = 1)
408 {
409 TSlotSet::iterator at = findSlot(set, base);
410 if (at == slots[set].end())
411 return reserveSlot(set, base, size);
412
413 // look for a big enough gap
414 for (; at != slots[set].end(); ++at) {
415 if (*at - base >= size)
416 break;
417 base = *at + 1;
418 }
419 return reserveSlot(set, base, size);
420 }
421
422 virtual bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override = 0;
423
424 virtual int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override = 0;
425
resolveSetglslang::TDefaultIoResolverBase426 int resolveSet(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
427 {
428 if (type.getQualifier().hasSet())
429 return type.getQualifier().layoutSet;
430
431 // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
432 if (getResourceSetBinding().size() == 1)
433 return atoi(getResourceSetBinding()[0].c_str());
434
435 return 0;
436 }
resolveUniformLocationglslang::TDefaultIoResolverBase437 int resolveUniformLocation(EShLanguage /*stage*/, const char* name, const glslang::TType& type, bool /*is_live*/) override
438 {
439 // kick out of not doing this
440 if (!doAutoLocationMapping())
441 return -1;
442
443 // no locations added if already present, a built-in variable, a block, or an opaque
444 if (type.getQualifier().hasLocation() || type.isBuiltIn() ||
445 type.getBasicType() == EbtBlock ||
446 type.getBasicType() == EbtAtomicUint ||
447 (type.containsOpaque() && intermediate.getSpv().openGl == 0))
448 return -1;
449
450 // no locations on blocks of built-in variables
451 if (type.isStruct()) {
452 if (type.getStruct()->size() < 1)
453 return -1;
454 if ((*type.getStruct())[0].type->isBuiltIn())
455 return -1;
456 }
457
458 int location = intermediate.getUniformLocationOverride(name);
459 if (location != -1)
460 return location;
461
462 location = nextUniformLocation;
463
464 nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
465
466 return location;
467 }
validateInOutglslang::TDefaultIoResolverBase468 bool validateInOut(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
469 {
470 return true;
471 }
resolveInOutLocationglslang::TDefaultIoResolverBase472 int resolveInOutLocation(EShLanguage stage, const char* /*name*/, const TType& type, bool /*is_live*/) override
473 {
474 // kick out of not doing this
475 if (!doAutoLocationMapping())
476 return -1;
477
478 // no locations added if already present, or a built-in variable
479 if (type.getQualifier().hasLocation() || type.isBuiltIn())
480 return -1;
481
482 // no locations on blocks of built-in variables
483 if (type.isStruct()) {
484 if (type.getStruct()->size() < 1)
485 return -1;
486 if ((*type.getStruct())[0].type->isBuiltIn())
487 return -1;
488 }
489
490 // point to the right input or output location counter
491 int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
492
493 // Placeholder. This does not do proper cross-stage lining up, nor
494 // work with mixed location/no-location declarations.
495 int location = nextLocation;
496 int typeLocationSize;
497 // Don’t take into account the outer-most array if the stage’s
498 // interface is automatically an array.
499 if (type.getQualifier().isArrayedIo(stage)) {
500 TType elementType(type, 0);
501 typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
502 } else {
503 typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
504 }
505 nextLocation += typeLocationSize;
506
507 return location;
508 }
resolveInOutComponentglslang::TDefaultIoResolverBase509 int resolveInOutComponent(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
510 {
511 return -1;
512 }
resolveInOutIndexglslang::TDefaultIoResolverBase513 int resolveInOutIndex(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
514 {
515 return -1;
516 }
517
notifyBindingglslang::TDefaultIoResolverBase518 void notifyBinding(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
notifyInOutglslang::TDefaultIoResolverBase519 void notifyInOut(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
endNotificationsglslang::TDefaultIoResolverBase520 void endNotifications(EShLanguage) override {}
beginNotificationsglslang::TDefaultIoResolverBase521 void beginNotifications(EShLanguage) override {}
beginResolveglslang::TDefaultIoResolverBase522 void beginResolve(EShLanguage) override {}
endResolveglslang::TDefaultIoResolverBase523 void endResolve(EShLanguage) override {}
524
525 protected:
526 TDefaultIoResolverBase(TDefaultIoResolverBase&);
527 TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&);
528
529 const TIntermediate &intermediate;
530 int nextUniformLocation;
531 int nextInputLocation;
532 int nextOutputLocation;
533
534 // Return descriptor set specific base if there is one, and the generic base otherwise.
selectBaseBindingglslang::TDefaultIoResolverBase535 int selectBaseBinding(int base, int descriptorSetBase) const {
536 return descriptorSetBase != -1 ? descriptorSetBase : base;
537 }
538
getLayoutSetglslang::TDefaultIoResolverBase539 static int getLayoutSet(const glslang::TType& type) {
540 if (type.getQualifier().hasSet())
541 return type.getQualifier().layoutSet;
542 else
543 return 0;
544 }
545
isSamplerTypeglslang::TDefaultIoResolverBase546 static bool isSamplerType(const glslang::TType& type) {
547 return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler();
548 }
549
isTextureTypeglslang::TDefaultIoResolverBase550 static bool isTextureType(const glslang::TType& type) {
551 return (type.getBasicType() == glslang::EbtSampler &&
552 (type.getSampler().isTexture() || type.getSampler().isSubpass()));
553 }
554
isUboTypeglslang::TDefaultIoResolverBase555 static bool isUboType(const glslang::TType& type) {
556 return type.getQualifier().storage == EvqUniform;
557 }
558 };
559
560 /*
561 * Basic implementation of glslang::TIoMapResolver that replaces the
562 * previous offset behavior.
563 * It does the same, uses the offsets for the corresponding uniform
564 * types. Also respects the EOptionAutoMapBindings flag and binds
565 * them if needed.
566 */
567 /*
568 * Default resolver
569 */
570 struct TDefaultIoResolver : public TDefaultIoResolverBase
571 {
TDefaultIoResolverglslang::TDefaultIoResolver572 TDefaultIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
573
validateBindingglslang::TDefaultIoResolver574 bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
575 {
576 return true;
577 }
578
resolveBindingglslang::TDefaultIoResolver579 int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
580 {
581 const int set = getLayoutSet(type);
582 // On OpenGL arrays of opaque types take a seperate binding for each element
583 int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
584
585 if (type.getQualifier().hasBinding()) {
586 if (isImageType(type))
587 return reserveSlot(set, getBaseBinding(EResImage, set) + type.getQualifier().layoutBinding, numBindings);
588
589 if (isTextureType(type))
590 return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding, numBindings);
591
592 if (isSsboType(type))
593 return reserveSlot(set, getBaseBinding(EResSsbo, set) + type.getQualifier().layoutBinding, numBindings);
594
595 if (isSamplerType(type))
596 return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding, numBindings);
597
598 if (isUboType(type))
599 return reserveSlot(set, getBaseBinding(EResUbo, set) + type.getQualifier().layoutBinding, numBindings);
600 } else if (is_live && doAutoBindingMapping()) {
601 // find free slot, the caller did make sure it passes all vars with binding
602 // first and now all are passed that do not have a binding and needs one
603
604 if (isImageType(type))
605 return getFreeSlot(set, getBaseBinding(EResImage, set), numBindings);
606
607 if (isTextureType(type))
608 return getFreeSlot(set, getBaseBinding(EResTexture, set), numBindings);
609
610 if (isSsboType(type))
611 return getFreeSlot(set, getBaseBinding(EResSsbo, set), numBindings);
612
613 if (isSamplerType(type))
614 return getFreeSlot(set, getBaseBinding(EResSampler, set), numBindings);
615
616 if (isUboType(type))
617 return getFreeSlot(set, getBaseBinding(EResUbo, set), numBindings);
618 }
619
620 return -1;
621 }
622
623 protected:
isImageTypeglslang::TDefaultIoResolver624 static bool isImageType(const glslang::TType& type) {
625 return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage();
626 }
627
isSsboTypeglslang::TDefaultIoResolver628 static bool isSsboType(const glslang::TType& type) {
629 return type.getQualifier().storage == EvqBuffer;
630 }
631 };
632
633 /********************************************************************************
634 The following IO resolver maps types in HLSL register space, as follows:
635
636 t - for shader resource views (SRV)
637 TEXTURE1D
638 TEXTURE1DARRAY
639 TEXTURE2D
640 TEXTURE2DARRAY
641 TEXTURE3D
642 TEXTURECUBE
643 TEXTURECUBEARRAY
644 TEXTURE2DMS
645 TEXTURE2DMSARRAY
646 STRUCTUREDBUFFER
647 BYTEADDRESSBUFFER
648 BUFFER
649 TBUFFER
650
651 s - for samplers
652 SAMPLER
653 SAMPLER1D
654 SAMPLER2D
655 SAMPLER3D
656 SAMPLERCUBE
657 SAMPLERSTATE
658 SAMPLERCOMPARISONSTATE
659
660 u - for unordered access views (UAV)
661 RWBYTEADDRESSBUFFER
662 RWSTRUCTUREDBUFFER
663 APPENDSTRUCTUREDBUFFER
664 CONSUMESTRUCTUREDBUFFER
665 RWBUFFER
666 RWTEXTURE1D
667 RWTEXTURE1DARRAY
668 RWTEXTURE2D
669 RWTEXTURE2DARRAY
670 RWTEXTURE3D
671
672 b - for constant buffer views (CBV)
673 CBUFFER
674 CONSTANTBUFFER
675 ********************************************************************************/
676 struct TDefaultHlslIoResolver : public TDefaultIoResolverBase
677 {
TDefaultHlslIoResolverglslang::TDefaultHlslIoResolver678 TDefaultHlslIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
679
validateBindingglslang::TDefaultHlslIoResolver680 bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
681 {
682 return true;
683 }
684
resolveBindingglslang::TDefaultHlslIoResolver685 int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
686 {
687 const int set = getLayoutSet(type);
688
689 if (type.getQualifier().hasBinding()) {
690 if (isUavType(type))
691 return reserveSlot(set, getBaseBinding(EResUav, set) + type.getQualifier().layoutBinding);
692
693 if (isSrvType(type))
694 return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding);
695
696 if (isSamplerType(type))
697 return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding);
698
699 if (isUboType(type))
700 return reserveSlot(set, getBaseBinding(EResUbo, set) + type.getQualifier().layoutBinding);
701 } else if (is_live && doAutoBindingMapping()) {
702 // find free slot, the caller did make sure it passes all vars with binding
703 // first and now all are passed that do not have a binding and needs one
704
705 if (isUavType(type))
706 return getFreeSlot(set, getBaseBinding(EResUav, set));
707
708 if (isSrvType(type))
709 return getFreeSlot(set, getBaseBinding(EResTexture, set));
710
711 if (isSamplerType(type))
712 return getFreeSlot(set, getBaseBinding(EResSampler, set));
713
714 if (isUboType(type))
715 return getFreeSlot(set, getBaseBinding(EResUbo, set));
716 }
717
718 return -1;
719 }
720
721 protected:
722 // Return true if this is a SRV (shader resource view) type:
isSrvTypeglslang::TDefaultHlslIoResolver723 static bool isSrvType(const glslang::TType& type) {
724 return isTextureType(type) || type.getQualifier().storage == EvqBuffer;
725 }
726
727 // Return true if this is a UAV (unordered access view) type:
isUavTypeglslang::TDefaultHlslIoResolver728 static bool isUavType(const glslang::TType& type) {
729 if (type.getQualifier().readonly)
730 return false;
731
732 return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) ||
733 (type.getQualifier().storage == EvqBuffer);
734 }
735 };
736
737
738 // Map I/O variables to provided offsets, and make bindings for
739 // unbound but live variables.
740 //
741 // Returns false if the input is too malformed to do this.
addStage(EShLanguage stage,TIntermediate & intermediate,TInfoSink & infoSink,TIoMapResolver * resolver)742 bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSink &infoSink, TIoMapResolver *resolver)
743 {
744 bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
745 intermediate.getAutoMapBindings() ||
746 intermediate.getAutoMapLocations();
747
748 for (int res = 0; res < EResCount; ++res) {
749 somethingToDo = somethingToDo ||
750 (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
751 intermediate.hasShiftBindingForSet(TResourceType(res));
752 }
753
754 if (!somethingToDo && resolver == nullptr)
755 return true;
756
757 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
758 return false;
759
760 TIntermNode* root = intermediate.getTreeRoot();
761 if (root == nullptr)
762 return false;
763
764 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
765 TDefaultIoResolver defaultResolver(intermediate);
766 TDefaultHlslIoResolver defaultHlslResolver(intermediate);
767
768 if (resolver == nullptr) {
769 // TODO: use a passed in IO mapper for this
770 if (intermediate.usingHlslIoMapping())
771 resolver = &defaultHlslResolver;
772 else
773 resolver = &defaultResolver;
774 }
775
776 TVarLiveMap inVarMap, outVarMap, uniformVarMap;
777 TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
778 TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
779
780 root->traverse(&iter_binding_all);
781 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
782
783 while (!iter_binding_live.functions.empty()) {
784 TIntermNode* function = iter_binding_live.functions.back();
785 iter_binding_live.functions.pop_back();
786 function->traverse(&iter_binding_live);
787 }
788
789 // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
790 std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderByPriority());
791
792 bool hadError = false;
793 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
794 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
795 TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError, intermediate);
796 TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError, intermediate);
797 resolver->beginNotifications(stage);
798 std::for_each(inVarMap.begin(), inVarMap.end(), inOutNotify);
799 std::for_each(outVarMap.begin(), outVarMap.end(), inOutNotify);
800 std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformNotify);
801 resolver->endNotifications(stage);
802 resolver->beginResolve(stage);
803 std::for_each(inVarMap.begin(), inVarMap.end(), inOutResolve);
804 std::for_each(outVarMap.begin(), outVarMap.end(), inOutResolve);
805 std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformResolve);
806 resolver->endResolve(stage);
807
808 if (!hadError) {
809 // sort by id again, so we can use lower bound to find entries
810 std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderById());
811 TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
812 root->traverse(&iter_iomap);
813 }
814
815 return !hadError;
816 }
817
818 } // end namespace glslang
819