1 //
2 // Copyright 2020 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include <algorithm>
8 #include <cstring>
9 #include <functional>
10 #include <numeric>
11 #include <unordered_map>
12 #include <unordered_set>
13
14 #include "compiler/translator/Compiler.h"
15 #include "compiler/translator/msl/AstHelpers.h"
16 #include "compiler/translator/msl/ModifyStruct.h"
17 #include "compiler/translator/msl/TranslatorMSL.h"
18
19 using namespace sh;
20
21 ////////////////////////////////////////////////////////////////////////////////
22
size() const23 size_t ModifiedStructMachineries::size() const
24 {
25 return ordering.size();
26 }
27
at(size_t index) const28 const ModifiedStructMachinery &ModifiedStructMachineries::at(size_t index) const
29 {
30 ASSERT(index < size());
31 const TStructure *s = ordering[index];
32 const ModifiedStructMachinery *m = find(*s);
33 ASSERT(m);
34 return *m;
35 }
36
find(const TStructure & s) const37 const ModifiedStructMachinery *ModifiedStructMachineries::find(const TStructure &s) const
38 {
39 auto iter = originalToMachinery.find(&s);
40 if (iter == originalToMachinery.end())
41 {
42 return nullptr;
43 }
44 return &iter->second;
45 }
46
insert(const TStructure & s,const ModifiedStructMachinery & machinery)47 void ModifiedStructMachineries::insert(const TStructure &s,
48 const ModifiedStructMachinery &machinery)
49 {
50 ASSERT(!find(s));
51 originalToMachinery[&s] = machinery;
52 ordering.push_back(&s);
53 }
54
55 ////////////////////////////////////////////////////////////////////////////////
56
57 namespace
58 {
59
Flatten(SymbolEnv & symbolEnv,TIntermTyped & node)60 TIntermTyped &Flatten(SymbolEnv &symbolEnv, TIntermTyped &node)
61 {
62 auto &type = node.getType();
63 ASSERT(type.isArray());
64
65 auto &retType = InnermostType(type);
66 retType.makeArray(1);
67
68 return symbolEnv.callFunctionOverload(Name("flatten"), retType, *new TIntermSequence{&node});
69 }
70
71 struct FlattenArray
72 {};
73
74 struct PathItem
75 {
76 enum class Type
77 {
78 Field, // Struct field indexing.
79 Index, // Array, vector, or matrix indexing.
80 FlattenArray, // Array of any rank -> pointer of innermost type.
81 };
82
PathItem__anon327efc6e0111::PathItem83 PathItem(const TField &field) : field(&field), type(Type::Field) {}
PathItem__anon327efc6e0111::PathItem84 PathItem(int index) : index(index), type(Type::Index) {}
PathItem__anon327efc6e0111::PathItem85 PathItem(unsigned index) : PathItem(static_cast<int>(index)) {}
PathItem__anon327efc6e0111::PathItem86 PathItem(FlattenArray flatten) : type(Type::FlattenArray) {}
87
88 union
89 {
90 const TField *field;
91 int index;
92 };
93 Type type;
94 };
95
BuildPathAccess(SymbolEnv & symbolEnv,const TVariable & var,const std::vector<PathItem> & path)96 TIntermTyped &BuildPathAccess(SymbolEnv &symbolEnv,
97 const TVariable &var,
98 const std::vector<PathItem> &path)
99 {
100 TIntermTyped *curr = new TIntermSymbol(&var);
101 for (const PathItem &item : path)
102 {
103 switch (item.type)
104 {
105 case PathItem::Type::Field:
106 curr = &AccessField(*curr, Name(*item.field));
107 break;
108 case PathItem::Type::Index:
109 curr = &AccessIndex(*curr, item.index);
110 break;
111 case PathItem::Type::FlattenArray:
112 {
113 curr = &Flatten(symbolEnv, *curr);
114 }
115 break;
116 }
117 }
118 return *curr;
119 }
120
121 ////////////////////////////////////////////////////////////////////////////////
122
123 using OriginalParam = const TVariable &;
124 using ModifiedParam = const TVariable &;
125
126 using OriginalAccess = TIntermTyped;
127 using ModifiedAccess = TIntermTyped;
128
129 struct Access
130 {
131 OriginalAccess &original;
132 ModifiedAccess &modified;
133
134 struct Env
135 {
136 const ConvertType type;
137 };
138 };
139
140 using ConversionFunc = std::function<Access(Access::Env &, OriginalAccess &, ModifiedAccess &)>;
141
142 class ConvertStructState : angle::NonCopyable
143 {
144 private:
145 struct ConversionInfo
146 {
147 ConversionFunc stdFunc;
148 const TFunction *astFunc;
149 std::vector<PathItem> pathItems;
150 Name modifiedFieldName;
151 };
152
153 public:
ConvertStructState(TCompiler & compiler,SymbolEnv & symbolEnv,IdGen & idGen,const ModifyStructConfig & config,ModifiedStructMachineries & outMachineries,const bool isUBO,const bool useAttributeAliasing)154 ConvertStructState(TCompiler &compiler,
155 SymbolEnv &symbolEnv,
156 IdGen &idGen,
157 const ModifyStructConfig &config,
158 ModifiedStructMachineries &outMachineries,
159 const bool isUBO,
160 const bool useAttributeAliasing)
161 : mCompiler(compiler),
162 config(config),
163 symbolEnv(symbolEnv),
164 modifiedFields(*new TFieldList()),
165 symbolTable(symbolEnv.symbolTable()),
166 idGen(idGen),
167 outMachineries(outMachineries),
168 isUBO(isUBO),
169 useAttributeAliasing(useAttributeAliasing)
170 {}
171
~ConvertStructState()172 ~ConvertStructState()
173 {
174 ASSERT(namePath.empty());
175 ASSERT(namePathSizes.empty());
176 }
177
publish(const TStructure & originalStruct,const Name & modifiedStructName)178 void publish(const TStructure &originalStruct, const Name &modifiedStructName)
179 {
180 const bool isOriginalToModified = config.convertType == ConvertType::OriginalToModified;
181
182 auto &modifiedStruct = *new TStructure(&symbolTable, modifiedStructName.rawName(),
183 &modifiedFields, modifiedStructName.symbolType());
184
185 auto &func = *new TFunction(
186 &symbolTable,
187 idGen.createNewName(isOriginalToModified ? "originalToModified" : "modifiedToOriginal")
188 .rawName(),
189 SymbolType::AngleInternal, new TType(TBasicType::EbtVoid), false);
190
191 OriginalParam originalParam =
192 CreateInstanceVariable(symbolTable, originalStruct, Name("original"));
193 ModifiedParam modifiedParam =
194 CreateInstanceVariable(symbolTable, modifiedStruct, Name("modified"));
195
196 symbolEnv.markAsReference(originalParam, AddressSpace::Thread);
197 symbolEnv.markAsReference(modifiedParam, config.externalAddressSpace);
198 if (isOriginalToModified)
199 {
200 func.addParameter(&originalParam);
201 func.addParameter(&modifiedParam);
202 }
203 else
204 {
205 func.addParameter(&modifiedParam);
206 func.addParameter(&originalParam);
207 }
208
209 TIntermBlock &body = *new TIntermBlock();
210
211 Access::Env env{config.convertType};
212
213 for (ConversionInfo &info : conversionInfos)
214 {
215 auto convert = [&](OriginalAccess &original, ModifiedAccess &modified) {
216 if (info.astFunc)
217 {
218 ASSERT(!info.stdFunc);
219 TIntermTyped &src = isOriginalToModified ? modified : original;
220 TIntermTyped &dest = isOriginalToModified ? original : modified;
221 body.appendStatement(TIntermAggregate::CreateFunctionCall(
222 *info.astFunc, new TIntermSequence{&dest, &src}));
223 }
224 else
225 {
226 ASSERT(info.stdFunc);
227 Access access = info.stdFunc(env, original, modified);
228 TIntermTyped &src = isOriginalToModified ? access.original : access.modified;
229 TIntermTyped &dest = isOriginalToModified ? access.modified : access.original;
230 body.appendStatement(new TIntermBinary(TOperator::EOpAssign, &dest, &src));
231 }
232 };
233 OriginalAccess *original = &BuildPathAccess(symbolEnv, originalParam, info.pathItems);
234 ModifiedAccess *modified = &AccessField(modifiedParam, info.modifiedFieldName);
235 if (useAttributeAliasing)
236 {
237 std::ostringstream aliasedName;
238 aliasedName << "ANGLE_ALIASED_" << info.modifiedFieldName;
239
240 TType *placeholderType = new TType(modified->getType());
241 placeholderType->setQualifier(EvqSpecConst);
242
243 modified = new TIntermSymbol(
244 new TVariable(&symbolTable, sh::ImmutableString(aliasedName.str()),
245 placeholderType, SymbolType::AngleInternal));
246 }
247 const TType ot = original->getType();
248 const TType mt = modified->getType();
249 ASSERT(ot.isArray() == mt.isArray());
250
251 // Clip distance output uses float[n] type, so the field must be assigned per-element
252 // when filling the modified struct. Explicit path name is used because original types
253 // are not available here.
254 if (ot.isArray() &&
255 (ot.getLayoutQualifier().matrixPacking == EmpRowMajor || ot != mt ||
256 info.modifiedFieldName == Name("gl_ClipDistance", SymbolType::BuiltIn)))
257 {
258 ASSERT(ot.getArraySizes() == mt.getArraySizes());
259 if (ot.isArrayOfArrays())
260 {
261 original = &Flatten(symbolEnv, *original);
262 modified = &Flatten(symbolEnv, *modified);
263 }
264 const int volume = static_cast<int>(ot.getArraySizeProduct());
265 for (int i = 0; i < volume; ++i)
266 {
267 if (i != 0)
268 {
269 original = original->deepCopy();
270 modified = modified->deepCopy();
271 }
272 OriginalAccess &o = AccessIndex(*original, i);
273 OriginalAccess &m = AccessIndex(*modified, i);
274 convert(o, m);
275 }
276 }
277 else
278 {
279 convert(*original, *modified);
280 }
281 }
282
283 auto *funcProto = new TIntermFunctionPrototype(&func);
284 auto *funcDef = new TIntermFunctionDefinition(funcProto, &body);
285
286 ModifiedStructMachinery machinery;
287 machinery.modifiedStruct = &modifiedStruct;
288 machinery.getConverter(config.convertType) = funcDef;
289
290 outMachineries.insert(originalStruct, machinery);
291 }
292
pushPath(PathItem const & item)293 void pushPath(PathItem const &item)
294 {
295 pathItems.push_back(item);
296
297 switch (item.type)
298 {
299 case PathItem::Type::Field:
300 pushNamePath(item.field->name());
301 break;
302
303 case PathItem::Type::Index:
304 pushNamePath(item.index);
305 break;
306
307 case PathItem::Type::FlattenArray:
308 namePathSizes.push_back(namePath.size());
309 break;
310 }
311 }
312
popPath()313 void popPath()
314 {
315 ASSERT(!namePath.empty());
316 ASSERT(!namePathSizes.empty());
317 namePath.resize(namePathSizes.back());
318 namePathSizes.pop_back();
319
320 ASSERT(!pathItems.empty());
321 pathItems.pop_back();
322 }
323
finalize(const bool allowPadding)324 void finalize(const bool allowPadding)
325 {
326 ASSERT(!finalized);
327 finalized = true;
328 introducePacking();
329 ASSERT(metalLayoutTotal == Layout::Identity());
330 // Only pad substructs. We don't want to pad the structure that contains all the UBOs, only
331 // individual UBOs.
332 if (allowPadding)
333 introducePadding();
334 }
335
generateModifiedFieldName(const TField & field,const TType & newType)336 Name generateModifiedFieldName(const TField &field, const TType &newType)
337 {
338 SymbolType type = field.symbolType();
339 if (pathItems.size() > 1)
340 {
341 // This is an input field that generates multiple modified fields. We cannot generate a
342 // new field name into "user" namespace, as user could choose a clashing name. Trust
343 // that we generate new names only for 1:N expansions.
344 type = SymbolType::AngleInternal;
345 }
346 return Name(namePath, type);
347 }
348
addModifiedField(const TField & field,TType & newType,TLayoutBlockStorage storage,TLayoutMatrixPacking packing,const AddressSpace * addressSpace)349 void addModifiedField(const TField &field,
350 TType &newType,
351 TLayoutBlockStorage storage,
352 TLayoutMatrixPacking packing,
353 const AddressSpace *addressSpace)
354 {
355 TLayoutQualifier layoutQualifier = newType.getLayoutQualifier();
356 layoutQualifier.blockStorage = storage;
357 layoutQualifier.matrixPacking = packing;
358 newType.setLayoutQualifier(layoutQualifier);
359
360 Name newName = generateModifiedFieldName(field, newType);
361 TField *modifiedField =
362 new TField(&newType, newName.rawName(), field.line(), newName.symbolType());
363 if (addressSpace)
364 {
365 symbolEnv.markAsPointer(*modifiedField, *addressSpace);
366 }
367 if (symbolEnv.isUBO(field))
368 {
369 symbolEnv.markAsUBO(*modifiedField);
370 }
371 modifiedFields.push_back(modifiedField);
372 }
373
addConversion(const ConversionFunc & func)374 void addConversion(const ConversionFunc &func)
375 {
376 ASSERT(!modifiedFields.empty());
377 conversionInfos.push_back({func, nullptr, pathItems, Name(*modifiedFields.back())});
378 }
379
addConversion(const TFunction & func)380 void addConversion(const TFunction &func)
381 {
382 ASSERT(!modifiedFields.empty());
383 conversionInfos.push_back({{}, &func, pathItems, Name(*modifiedFields.back())});
384 }
385
hasPacking() const386 bool hasPacking() const { return containsPacked; }
387
hasPadding() const388 bool hasPadding() const { return padFieldCount > 0; }
389
recurse(const TStructure & structure,ModifiedStructMachinery & outMachinery,const bool isUBORecurse)390 bool recurse(const TStructure &structure,
391 ModifiedStructMachinery &outMachinery,
392 const bool isUBORecurse)
393 {
394 const ModifiedStructMachinery *m = outMachineries.find(structure);
395 if (m == nullptr)
396 {
397 TranslatorMetalReflection *reflection = mtl::getTranslatorMetalReflection(&mCompiler);
398 reflection->addOriginalName(structure.uniqueId().get(), structure.name().data());
399 const Name name = idGen.createNewName(structure.name().data());
400 if (!TryCreateModifiedStruct(mCompiler, symbolEnv, idGen, config, structure, name,
401 outMachineries, isUBORecurse, config.allowPadding, false))
402 {
403 return false;
404 }
405 m = outMachineries.find(structure);
406 ASSERT(m);
407 }
408 outMachinery = *m;
409 return true;
410 }
411
getIsUBO() const412 bool getIsUBO() const { return isUBO; }
413
414 private:
addPadding(size_t padAmount,bool updateLayout)415 void addPadding(size_t padAmount, bool updateLayout)
416 {
417 if (padAmount == 0)
418 {
419 return;
420 }
421
422 const size_t begin = modifiedFields.size();
423
424 // Iteratively adding in scalar or vector padding because some struct types will not
425 // allow matrix or array members.
426 while (padAmount > 0)
427 {
428 TType *padType;
429 if (padAmount >= 16)
430 {
431 padAmount -= 16;
432 padType = new TType(TBasicType::EbtFloat, 4);
433 }
434 else if (padAmount >= 8)
435 {
436 padAmount -= 8;
437 padType = new TType(TBasicType::EbtFloat, 2);
438 }
439 else if (padAmount >= 4)
440 {
441 padAmount -= 4;
442 padType = new TType(TBasicType::EbtFloat);
443 }
444 else if (padAmount >= 2)
445 {
446 padAmount -= 2;
447 padType = new TType(TBasicType::EbtBool, 2);
448 }
449 else
450 {
451 ASSERT(padAmount == 1);
452 padAmount -= 1;
453 padType = new TType(TBasicType::EbtBool);
454 }
455
456 if (padType->getBasicType() != EbtBool)
457 {
458 padType->setPrecision(EbpLow);
459 }
460
461 if (updateLayout)
462 {
463 metalLayoutTotal += MetalLayoutOf(*padType);
464 }
465
466 const Name name = idGen.createNewName("pad");
467 modifiedFields.push_back(
468 new TField(padType, name.rawName(), kNoSourceLoc, name.symbolType()));
469 ++padFieldCount;
470 }
471
472 std::reverse(modifiedFields.begin() + begin, modifiedFields.end());
473 }
474
introducePacking()475 void introducePacking()
476 {
477 if (!config.allowPacking)
478 {
479 return;
480 }
481
482 auto setUnpackedStorage = [](TType &type) {
483 TLayoutBlockStorage storage = type.getLayoutQualifier().blockStorage;
484 switch (storage)
485 {
486 case TLayoutBlockStorage::EbsShared:
487 storage = TLayoutBlockStorage::EbsStd140;
488 break;
489 case TLayoutBlockStorage::EbsPacked:
490 storage = TLayoutBlockStorage::EbsStd430;
491 break;
492 case TLayoutBlockStorage::EbsStd140:
493 case TLayoutBlockStorage::EbsStd430:
494 case TLayoutBlockStorage::EbsUnspecified:
495 break;
496 }
497 SetBlockStorage(type, storage);
498 };
499
500 Layout glslLayoutTotal = Layout::Identity();
501 const size_t size = modifiedFields.size();
502
503 for (size_t i = 0; i < size; ++i)
504 {
505 TField &curr = *modifiedFields[i];
506 TType &currType = *curr.type();
507 const bool canBePacked = CanBePacked(currType);
508
509 auto dontPack = [&]() {
510 if (canBePacked)
511 {
512 setUnpackedStorage(currType);
513 }
514 glslLayoutTotal += GlslLayoutOf(currType);
515 };
516
517 if (!CanBePacked(currType))
518 {
519 dontPack();
520 continue;
521 }
522
523 const Layout packedGlslLayout = GlslLayoutOf(currType);
524 const TLayoutBlockStorage packedStorage = currType.getLayoutQualifier().blockStorage;
525 setUnpackedStorage(currType);
526 const Layout unpackedGlslLayout = GlslLayoutOf(currType);
527 SetBlockStorage(currType, packedStorage);
528
529 ASSERT(packedGlslLayout.sizeOf <= unpackedGlslLayout.sizeOf);
530 if (packedGlslLayout.sizeOf == unpackedGlslLayout.sizeOf)
531 {
532 dontPack();
533 continue;
534 }
535
536 const size_t j = i + 1;
537 if (j == size)
538 {
539 dontPack();
540 break;
541 }
542
543 const size_t pad = unpackedGlslLayout.sizeOf - packedGlslLayout.sizeOf;
544 const TField &next = *modifiedFields[j];
545 const Layout nextGlslLayout = GlslLayoutOf(*next.type());
546
547 if (pad < nextGlslLayout.sizeOf)
548 {
549 dontPack();
550 continue;
551 }
552
553 symbolEnv.markAsPacked(curr);
554 glslLayoutTotal += packedGlslLayout;
555 containsPacked = true;
556 }
557 }
558
introducePadding()559 void introducePadding()
560 {
561 if (!config.allowPadding)
562 {
563 return;
564 }
565
566 MetalLayoutOfConfig layoutConfig;
567 layoutConfig.disablePacking = !config.allowPacking;
568 layoutConfig.assumeStructsAreTailPadded = true;
569
570 TFieldList fields = std::move(modifiedFields);
571 ASSERT(!fields.empty()); // GLSL requires at least one member.
572
573 const TField *const first = fields.front();
574
575 for (TField *field : fields)
576 {
577 const TType &type = *field->type();
578
579 const Layout glslLayout = GlslLayoutOf(type);
580 const Layout metalLayout = MetalLayoutOf(type, layoutConfig);
581
582 size_t prePadAmount = 0;
583 if (glslLayout.alignOf > metalLayout.alignOf && field != first)
584 {
585 const size_t prePaddedSize = metalLayoutTotal.sizeOf;
586 metalLayoutTotal.requireAlignment(glslLayout.alignOf, true);
587 const size_t paddedSize = metalLayoutTotal.sizeOf;
588 prePadAmount = paddedSize - prePaddedSize;
589 metalLayoutTotal += metalLayout;
590 addPadding(prePadAmount, false); // Note: requireAlignment() already updated layout
591 }
592 else
593 {
594 metalLayoutTotal += metalLayout;
595 }
596
597 modifiedFields.push_back(field);
598
599 if (glslLayout.sizeOf > metalLayout.sizeOf && field != fields.back())
600 {
601 const bool updateLayout = true; // XXX: Correct?
602 const size_t padAmount = glslLayout.sizeOf - metalLayout.sizeOf;
603 addPadding(padAmount, updateLayout);
604 }
605 }
606 }
607
608 template <typename T>
pushNamePath(T && fieldName)609 void pushNamePath(T &&fieldName)
610 {
611 namePathSizes.push_back(namePath.size());
612 std::ostringstream str;
613 if (!namePath.empty())
614 {
615 str << std::move(namePath) << '_';
616 }
617 str << fieldName;
618 namePath = str.str();
619 }
620
621 public:
622 TCompiler &mCompiler;
623 const ModifyStructConfig &config;
624 SymbolEnv &symbolEnv;
625
626 private:
627 TFieldList &modifiedFields;
628 Layout metalLayoutTotal = Layout::Identity();
629 size_t padFieldCount = 0;
630 bool containsPacked = false;
631 bool finalized = false;
632
633 std::vector<PathItem> pathItems;
634
635 std::vector<size_t> namePathSizes;
636 std::string namePath;
637
638 std::vector<ConversionInfo> conversionInfos;
639 TSymbolTable &symbolTable;
640 IdGen &idGen;
641 ModifiedStructMachineries &outMachineries;
642 const bool isUBO;
643 const bool useAttributeAliasing;
644 };
645
646 ////////////////////////////////////////////////////////////////////////////////
647
648 using ModifyFunc = bool (*)(ConvertStructState &state,
649 const TField &field,
650 const TLayoutBlockStorage storage,
651 const TLayoutMatrixPacking packing);
652
653 bool ModifyRecursive(ConvertStructState &state,
654 const TField &field,
655 const TLayoutBlockStorage storage,
656 const TLayoutMatrixPacking packing);
657
IdentityModify(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)658 bool IdentityModify(ConvertStructState &state,
659 const TField &field,
660 const TLayoutBlockStorage storage,
661 const TLayoutMatrixPacking packing)
662 {
663 const TType &type = *field.type();
664 state.addModifiedField(field, CloneType(type), storage, packing, nullptr);
665 state.addConversion([=](Access::Env &, OriginalAccess &o, ModifiedAccess &m) {
666 return Access{o, m};
667 });
668 return false;
669 }
670
InlineStruct(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)671 bool InlineStruct(ConvertStructState &state,
672 const TField &field,
673 const TLayoutBlockStorage storage,
674 const TLayoutMatrixPacking packing)
675 {
676 const TType &type = *field.type();
677 const TStructure *substructure = state.symbolEnv.remap(type.getStruct());
678 if (!substructure)
679 {
680 return false;
681 }
682 if (type.isArray())
683 {
684 return false;
685 }
686 if (!state.config.inlineStruct(field))
687 {
688 return false;
689 }
690
691 const TFieldList &subfields = substructure->fields();
692 for (const TField *subfield : subfields)
693 {
694 const TType &subtype = *subfield->type();
695 const TLayoutBlockStorage substorage = Overlay(storage, subtype);
696 const TLayoutMatrixPacking subpacking = Overlay(packing, subtype);
697 ModifyRecursive(state, *subfield, substorage, subpacking);
698 }
699
700 return true;
701 }
702
RecurseStruct(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)703 bool RecurseStruct(ConvertStructState &state,
704 const TField &field,
705 const TLayoutBlockStorage storage,
706 const TLayoutMatrixPacking packing)
707 {
708 const TType &type = *field.type();
709 const TStructure *substructure = state.symbolEnv.remap(type.getStruct());
710 if (!substructure)
711 {
712 return false;
713 }
714 if (!state.config.recurseStruct(field))
715 {
716 return false;
717 }
718
719 ModifiedStructMachinery machinery;
720 if (!state.recurse(*substructure, machinery, state.getIsUBO()))
721 {
722 return false;
723 }
724
725 TType &newType = *new TType(machinery.modifiedStruct, false);
726 if (type.isArray())
727 {
728 newType.makeArrays(type.getArraySizes());
729 }
730
731 TIntermFunctionDefinition *converter = machinery.getConverter(state.config.convertType);
732 ASSERT(converter);
733
734 state.addModifiedField(field, newType, storage, packing, state.symbolEnv.isPointer(field));
735 if (state.symbolEnv.isPointer(field))
736 {
737 state.symbolEnv.removePointer(field);
738 }
739 state.addConversion(*converter->getFunction());
740
741 return true;
742 }
743
SplitMatrixColumns(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)744 bool SplitMatrixColumns(ConvertStructState &state,
745 const TField &field,
746 const TLayoutBlockStorage storage,
747 const TLayoutMatrixPacking packing)
748 {
749 const TType &type = *field.type();
750 if (!type.isMatrix())
751 {
752 return false;
753 }
754
755 if (!state.config.splitMatrixColumns(field))
756 {
757 return false;
758 }
759
760 const uint8_t cols = type.getCols();
761 TType &rowType = DropColumns(type);
762
763 for (uint8_t c = 0; c < cols; ++c)
764 {
765 state.pushPath(c);
766
767 state.addModifiedField(field, rowType, storage, packing, state.symbolEnv.isPointer(field));
768 if (state.symbolEnv.isPointer(field))
769 {
770 state.symbolEnv.removePointer(field);
771 }
772 state.addConversion([=](Access::Env &, OriginalAccess &o, ModifiedAccess &m) {
773 return Access{o, m};
774 });
775
776 state.popPath();
777 }
778
779 return true;
780 }
781
SaturateMatrixRows(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)782 bool SaturateMatrixRows(ConvertStructState &state,
783 const TField &field,
784 const TLayoutBlockStorage storage,
785 const TLayoutMatrixPacking packing)
786 {
787 const TType &type = *field.type();
788 if (!type.isMatrix())
789 {
790 return false;
791 }
792 const bool isRowMajor = type.getLayoutQualifier().matrixPacking == EmpRowMajor;
793 const uint8_t rows = type.getRows();
794 const uint8_t saturation = state.config.saturateMatrixRows(field);
795 if (saturation <= rows)
796 {
797 return false;
798 }
799
800 const uint8_t cols = type.getCols();
801 TType &satType = SetMatrixRowDim(type, saturation);
802 state.addModifiedField(field, satType, storage, packing, state.symbolEnv.isPointer(field));
803 if (state.symbolEnv.isPointer(field))
804 {
805 state.symbolEnv.removePointer(field);
806 }
807
808 for (uint8_t c = 0; c < cols; ++c)
809 {
810 for (uint8_t r = 0; r < rows; ++r)
811 {
812 state.addConversion([=](Access::Env &, OriginalAccess &o, ModifiedAccess &m) {
813 uint8_t firstModifiedIndex = isRowMajor ? r : c;
814 uint8_t secondModifiedIndex = isRowMajor ? c : r;
815 auto &o_ = AccessIndex(AccessIndex(o, c), r);
816 auto &m_ = AccessIndex(AccessIndex(m, firstModifiedIndex), secondModifiedIndex);
817 return Access{o_, m_};
818 });
819 }
820 }
821
822 return true;
823 }
824
TestBoolToUint(ConvertStructState & state,const TField & field)825 bool TestBoolToUint(ConvertStructState &state, const TField &field)
826 {
827 if (field.type()->getBasicType() != TBasicType::EbtBool)
828 {
829 return false;
830 }
831 if (!state.config.promoteBoolToUint(field))
832 {
833 return false;
834 }
835 return true;
836 }
837
ConvertBoolToUint(ConvertType convertType,OriginalAccess & o,ModifiedAccess & m)838 Access ConvertBoolToUint(ConvertType convertType, OriginalAccess &o, ModifiedAccess &m)
839 {
840 auto coerce = [](TIntermTyped &to, TIntermTyped &from) -> TIntermTyped & {
841 return *TIntermAggregate::CreateConstructor(to.getType(), new TIntermSequence{&from});
842 };
843 switch (convertType)
844 {
845 case ConvertType::OriginalToModified:
846 return Access{coerce(m, o), m};
847 case ConvertType::ModifiedToOriginal:
848 return Access{o, coerce(o, m)};
849 }
850 }
851
SaturateScalarOrVectorCommon(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing,const bool array)852 bool SaturateScalarOrVectorCommon(ConvertStructState &state,
853 const TField &field,
854 const TLayoutBlockStorage storage,
855 const TLayoutMatrixPacking packing,
856 const bool array)
857 {
858 const TType &type = *field.type();
859 if (type.isArray() != array)
860 {
861 return false;
862 }
863 if (!((type.isRank0() && HasScalarBasicType(type)) || type.isVector()))
864 {
865 return false;
866 }
867 const auto saturator =
868 array ? state.config.saturateScalarOrVectorArrays : state.config.saturateScalarOrVector;
869 const uint8_t dim = type.getNominalSize();
870 const uint8_t saturation = saturator(field);
871 if (saturation <= dim)
872 {
873 return false;
874 }
875
876 TType &satType = SetVectorDim(type, saturation);
877 const bool boolToUint = TestBoolToUint(state, field);
878 if (boolToUint)
879 {
880 satType.setBasicType(TBasicType::EbtUInt);
881 }
882 state.addModifiedField(field, satType, storage, packing, state.symbolEnv.isPointer(field));
883 if (state.symbolEnv.isPointer(field))
884 {
885 state.symbolEnv.removePointer(field);
886 }
887
888 for (uint8_t d = 0; d < dim; ++d)
889 {
890 state.addConversion([=](Access::Env &env, OriginalAccess &o, ModifiedAccess &m) {
891 auto &o_ = dim > 1 ? AccessIndex(o, d) : o;
892 auto &m_ = AccessIndex(m, d);
893 if (boolToUint)
894 {
895 return ConvertBoolToUint(env.type, o_, m_);
896 }
897 else
898 {
899 return Access{o_, m_};
900 }
901 });
902 }
903
904 return true;
905 }
906
SaturateScalarOrVectorArrays(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)907 bool SaturateScalarOrVectorArrays(ConvertStructState &state,
908 const TField &field,
909 const TLayoutBlockStorage storage,
910 const TLayoutMatrixPacking packing)
911 {
912 return SaturateScalarOrVectorCommon(state, field, storage, packing, true);
913 }
914
SaturateScalarOrVector(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)915 bool SaturateScalarOrVector(ConvertStructState &state,
916 const TField &field,
917 const TLayoutBlockStorage storage,
918 const TLayoutMatrixPacking packing)
919 {
920 return SaturateScalarOrVectorCommon(state, field, storage, packing, false);
921 }
922
PromoteBoolToUint(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)923 bool PromoteBoolToUint(ConvertStructState &state,
924 const TField &field,
925 const TLayoutBlockStorage storage,
926 const TLayoutMatrixPacking packing)
927 {
928 if (!TestBoolToUint(state, field))
929 {
930 return false;
931 }
932
933 auto &promotedType = CloneType(*field.type());
934 promotedType.setBasicType(TBasicType::EbtUInt);
935 state.addModifiedField(field, promotedType, storage, packing, state.symbolEnv.isPointer(field));
936 if (state.symbolEnv.isPointer(field))
937 {
938 state.symbolEnv.removePointer(field);
939 }
940
941 state.addConversion([=](Access::Env &env, OriginalAccess &o, ModifiedAccess &m) {
942 return ConvertBoolToUint(env.type, o, m);
943 });
944
945 return true;
946 }
947
ModifyCommon(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)948 bool ModifyCommon(ConvertStructState &state,
949 const TField &field,
950 const TLayoutBlockStorage storage,
951 const TLayoutMatrixPacking packing)
952 {
953 ModifyFunc funcs[] = {
954 InlineStruct, //
955 RecurseStruct, //
956 SplitMatrixColumns, //
957 SaturateMatrixRows, //
958 SaturateScalarOrVectorArrays, //
959 SaturateScalarOrVector, //
960 PromoteBoolToUint, //
961 };
962
963 for (ModifyFunc func : funcs)
964 {
965 if (func(state, field, storage, packing))
966 {
967 return true;
968 }
969 }
970
971 return IdentityModify(state, field, storage, packing);
972 }
973
InlineArray(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)974 bool InlineArray(ConvertStructState &state,
975 const TField &field,
976 const TLayoutBlockStorage storage,
977 const TLayoutMatrixPacking packing)
978 {
979 const TType &type = *field.type();
980 if (!type.isArray())
981 {
982 return false;
983 }
984 if (!state.config.inlineArray(field))
985 {
986 return false;
987 }
988
989 const unsigned volume = type.getArraySizeProduct();
990 const bool isMultiDim = type.isArrayOfArrays();
991
992 auto &innermostType = InnermostType(type);
993
994 if (isMultiDim)
995 {
996 state.pushPath(FlattenArray());
997 }
998
999 for (unsigned i = 0; i < volume; ++i)
1000 {
1001 state.pushPath(i);
1002 TType setType(innermostType);
1003 if (setType.getLayoutQualifier().locationsSpecified)
1004 {
1005 TLayoutQualifier qualifier(innermostType.getLayoutQualifier());
1006 qualifier.location = innermostType.getLayoutQualifier().location + i;
1007 qualifier.locationsSpecified = 1;
1008 setType.setLayoutQualifier(qualifier);
1009 }
1010 const TField innermostField(&setType, field.name(), field.line(), field.symbolType());
1011 ModifyCommon(state, innermostField, storage, packing);
1012 state.popPath();
1013 }
1014
1015 if (isMultiDim)
1016 {
1017 state.popPath();
1018 }
1019
1020 return true;
1021 }
1022
ModifyRecursive(ConvertStructState & state,const TField & field,const TLayoutBlockStorage storage,const TLayoutMatrixPacking packing)1023 bool ModifyRecursive(ConvertStructState &state,
1024 const TField &field,
1025 const TLayoutBlockStorage storage,
1026 const TLayoutMatrixPacking packing)
1027 {
1028 state.pushPath(field);
1029
1030 bool modified;
1031 if (InlineArray(state, field, storage, packing))
1032 {
1033 modified = true;
1034 }
1035 else
1036 {
1037 modified = ModifyCommon(state, field, storage, packing);
1038 }
1039
1040 state.popPath();
1041
1042 return modified;
1043 }
1044
1045 } // anonymous namespace
1046
1047 ////////////////////////////////////////////////////////////////////////////////
1048
TryCreateModifiedStruct(TCompiler & compiler,SymbolEnv & symbolEnv,IdGen & idGen,const ModifyStructConfig & config,const TStructure & originalStruct,const Name & modifiedStructName,ModifiedStructMachineries & outMachineries,const bool isUBO,const bool allowPadding,const bool useAttributeAliasing)1049 bool sh::TryCreateModifiedStruct(TCompiler &compiler,
1050 SymbolEnv &symbolEnv,
1051 IdGen &idGen,
1052 const ModifyStructConfig &config,
1053 const TStructure &originalStruct,
1054 const Name &modifiedStructName,
1055 ModifiedStructMachineries &outMachineries,
1056 const bool isUBO,
1057 const bool allowPadding,
1058 const bool useAttributeAliasing)
1059 {
1060 ConvertStructState state(compiler, symbolEnv, idGen, config, outMachineries, isUBO,
1061 useAttributeAliasing);
1062 size_t identicalFieldCount = 0;
1063
1064 const TFieldList &originalFields = originalStruct.fields();
1065 for (TField *originalField : originalFields)
1066 {
1067 const TType &originalType = *originalField->type();
1068 const TLayoutBlockStorage storage = Overlay(config.initialBlockStorage, originalType);
1069 const TLayoutMatrixPacking packing = Overlay(config.initialMatrixPacking, originalType);
1070 if (!ModifyRecursive(state, *originalField, storage, packing))
1071 {
1072 ++identicalFieldCount;
1073 }
1074 }
1075
1076 state.finalize(allowPadding);
1077
1078 if (identicalFieldCount == originalFields.size() && !state.hasPacking() &&
1079 !state.hasPadding() && !useAttributeAliasing)
1080 {
1081 return false;
1082 }
1083 state.publish(originalStruct, modifiedStructName);
1084
1085 return true;
1086 }
1087