• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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