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