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