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