blob: 12e849681fa87fff791d0c719313e367fb7f4253 [file] [log] [blame]
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include <algorithm>
#include <cstring>
#include <functional>
#include <numeric>
#include <unordered_map>
#include <unordered_set>
#include "compiler/translator/Compiler.h"
#include "compiler/translator/ImmutableStringBuilder.h"
#include "compiler/translator/msl/AstHelpers.h"
#include "compiler/translator/msl/ModifyStruct.h"
#include "compiler/translator/msl/TranslatorMSL.h"
using namespace sh;
////////////////////////////////////////////////////////////////////////////////
size_t ModifiedStructMachineries::size() const
{
return ordering.size();
}
const ModifiedStructMachinery &ModifiedStructMachineries::at(size_t index) const
{
ASSERT(index < size());
const TStructure *s = ordering[index];
const ModifiedStructMachinery *m = find(*s);
ASSERT(m);
return *m;
}
const ModifiedStructMachinery *ModifiedStructMachineries::find(const TStructure &s) const
{
auto iter = originalToMachinery.find(&s);
if (iter == originalToMachinery.end())
{
return nullptr;
}
return &iter->second;
}
void ModifiedStructMachineries::insert(const TStructure &s,
const ModifiedStructMachinery &machinery)
{
ASSERT(!find(s));
originalToMachinery[&s] = machinery;
ordering.push_back(&s);
}
////////////////////////////////////////////////////////////////////////////////
namespace
{
TIntermTyped &Flatten(SymbolEnv &symbolEnv, TIntermTyped &node)
{
auto &type = node.getType();
ASSERT(type.isArray());
auto &retType = InnermostType(type);
retType.makeArray(1);
return symbolEnv.callFunctionOverload(Name("flatten"), retType, *new TIntermSequence{&node});
}
struct FlattenArray
{};
struct PathItem
{
enum class Type
{
Field, // Struct field indexing.
Index, // Array, vector, or matrix indexing.
FlattenArray, // Array of any rank -> pointer of innermost type.
};
PathItem(const TField &field) : field(&field), type(Type::Field) {}
PathItem(int index) : index(index), type(Type::Index) {}
PathItem(unsigned index) : PathItem(static_cast<int>(index)) {}
PathItem(FlattenArray flatten) : type(Type::FlattenArray) {}
union
{
const TField *field;
int index;
};
Type type;
};
TIntermTyped &BuildPathAccess(SymbolEnv &symbolEnv,
const TVariable &var,
const std::vector<PathItem> &path)
{
TIntermTyped *curr = new TIntermSymbol(&var);
for (const PathItem &item : path)
{
switch (item.type)
{
case PathItem::Type::Field:
curr = &AccessField(*curr, Name(*item.field));
break;
case PathItem::Type::Index:
curr = &AccessIndex(*curr, item.index);
break;
case PathItem::Type::FlattenArray:
{
curr = &Flatten(symbolEnv, *curr);
}
break;
}
}
return *curr;
}
////////////////////////////////////////////////////////////////////////////////
using OriginalParam = const TVariable &;
using ModifiedParam = const TVariable &;
using OriginalAccess = TIntermTyped;
using ModifiedAccess = TIntermTyped;
struct Access
{
OriginalAccess &original;
ModifiedAccess &modified;
struct Env
{
const ConvertType type;
};
};
using ConversionFunc = std::function<Access(Access::Env &, OriginalAccess &, ModifiedAccess &)>;
class ConvertStructState : angle::NonCopyable
{
private:
struct ConversionInfo
{
ConversionFunc stdFunc;
const TFunction *astFunc;
std::vector<PathItem> pathItems;
Name modifiedFieldName;
};
public:
ConvertStructState(TCompiler &compiler,
SymbolEnv &symbolEnv,
IdGen &idGen,
const ModifyStructConfig &config,
ModifiedStructMachineries &outMachineries,
const bool isUBO,
const bool useAttributeAliasing)
: mCompiler(compiler),
config(config),
symbolEnv(symbolEnv),
modifiedFields(*new TFieldList()),
symbolTable(symbolEnv.symbolTable()),
idGen(idGen),
outMachineries(outMachineries),
isUBO(isUBO),
useAttributeAliasing(useAttributeAliasing)
{}
~ConvertStructState()
{
}
void publish(const TStructure &originalStruct, const Name &modifiedStructName)
{
const bool isOriginalToModified = config.convertType == ConvertType::OriginalToModified;
auto &modifiedStruct = *new TStructure(&symbolTable, modifiedStructName.rawName(),
&modifiedFields, modifiedStructName.symbolType());
auto &func = *new TFunction(
&symbolTable,
idGen.createNewName(isOriginalToModified ? "originalToModified" : "modifiedToOriginal")
.rawName(),
SymbolType::AngleInternal, new TType(TBasicType::EbtVoid), false);
OriginalParam originalParam =
CreateInstanceVariable(symbolTable, originalStruct, Name("original"));
ModifiedParam modifiedParam =
CreateInstanceVariable(symbolTable, modifiedStruct, Name("modified"));
symbolEnv.markAsReference(originalParam, AddressSpace::Thread);
symbolEnv.markAsReference(modifiedParam, config.externalAddressSpace);
if (isOriginalToModified)
{
func.addParameter(&originalParam);
func.addParameter(&modifiedParam);
}
else
{
func.addParameter(&modifiedParam);
func.addParameter(&originalParam);
}
TIntermBlock &body = *new TIntermBlock();
Access::Env env{config.convertType};
for (ConversionInfo &info : conversionInfos)
{
auto convert = [&](OriginalAccess &original, ModifiedAccess &modified) {
if (info.astFunc)
{
ASSERT(!info.stdFunc);
TIntermTyped &src = isOriginalToModified ? modified : original;
TIntermTyped &dest = isOriginalToModified ? original : modified;
body.appendStatement(TIntermAggregate::CreateFunctionCall(
*info.astFunc, new TIntermSequence{&dest, &src}));
}
else
{
ASSERT(info.stdFunc);
Access access = info.stdFunc(env, original, modified);
TIntermTyped &src = isOriginalToModified ? access.original : access.modified;
TIntermTyped &dest = isOriginalToModified ? access.modified : access.original;
body.appendStatement(new TIntermBinary(TOperator::EOpAssign, &dest, &src));
}
};
OriginalAccess *original = &BuildPathAccess(symbolEnv, originalParam, info.pathItems);
ModifiedAccess *modified = &AccessField(modifiedParam, info.modifiedFieldName);
if (useAttributeAliasing)
{
std::ostringstream aliasedName;
aliasedName << "ANGLE_ALIASED_" << info.modifiedFieldName;
TType *placeholderType = new TType(modified->getType());
placeholderType->setQualifier(EvqSpecConst);
modified = new TIntermSymbol(
new TVariable(&symbolTable, sh::ImmutableString(aliasedName.str()),
placeholderType, SymbolType::AngleInternal));
}
const TType ot = original->getType();
const TType mt = modified->getType();
ASSERT(ot.isArray() == mt.isArray());
// Clip distance output uses float[n] type, so the field must be assigned per-element
// when filling the modified struct. Explicit path name is used because original types
// are not available here.
if (ot.isArray() &&
(ot.getLayoutQualifier().matrixPacking == EmpRowMajor || ot != mt ||
info.modifiedFieldName == Name("gl_ClipDistance", SymbolType::BuiltIn)))
{
ASSERT(ot.getArraySizes() == mt.getArraySizes());
if (ot.isArrayOfArrays())
{
original = &Flatten(symbolEnv, *original);
modified = &Flatten(symbolEnv, *modified);
}
const int volume = static_cast<int>(ot.getArraySizeProduct());
for (int i = 0; i < volume; ++i)
{
if (i != 0)
{
original = original->deepCopy();
modified = modified->deepCopy();
}
OriginalAccess &o = AccessIndex(*original, i);
OriginalAccess &m = AccessIndex(*modified, i);
convert(o, m);
}
}
else
{
convert(*original, *modified);
}
}
auto *funcProto = new TIntermFunctionPrototype(&func);
auto *funcDef = new TIntermFunctionDefinition(funcProto, &body);
ModifiedStructMachinery machinery;
machinery.modifiedStruct = &modifiedStruct;
machinery.getConverter(config.convertType) = funcDef;
outMachineries.insert(originalStruct, machinery);
}
ImmutableString rootFieldName() const
{
if (!pathItems.empty())
{
if (pathItems[0].type == PathItem::Type::Field)
{
return pathItems[0].field->name();
}
}
UNREACHABLE();
return kEmptyImmutableString;
}
void pushPath(PathItem const &item) { pathItems.push_back(item); }
void popPath()
{
ASSERT(!pathItems.empty());
pathItems.pop_back();
if (pathItems.empty())
{
// Next push will start a new root output variable to linearize.
mSubfieldIndex = 0;
}
}
void finalize(const bool allowPadding)
{
ASSERT(!finalized);
finalized = true;
introducePacking();
ASSERT(metalLayoutTotal == Layout::Identity());
// Only pad substructs. We don't want to pad the structure that contains all the UBOs, only
// individual UBOs.
if (allowPadding)
introducePadding();
}
void addModifiedField(const TField &field,
TType &newType,
TLayoutBlockStorage storage,
TLayoutMatrixPacking packing,
const AddressSpace *addressSpace)
{
TLayoutQualifier layoutQualifier = newType.getLayoutQualifier();
layoutQualifier.blockStorage = storage;
layoutQualifier.matrixPacking = packing;
newType.setLayoutQualifier(layoutQualifier);
sh::ImmutableString newName = field.name();
sh::SymbolType newSymbolType = field.symbolType();
if (pathItems.size() > 1)
{
// Current state is linearizing a root input field into multiple modified fields. The
// new fields need unique names. Generate the new names into AngleInternal namespace.
// The user could choose a clashing name in UserDefined namespace.
newSymbolType = SymbolType::AngleInternal;
// The user specified root field name is currently used as the basis for the MSL vs-fs
// interface matching. The field linearization itself is deterministic, so subfield
// index is sufficient to define all the entries in MSL interface in all the compatible
// VS and FS MSL programs.
newName = BuildConcatenatedImmutableString(rootFieldName(), '_', mSubfieldIndex);
++mSubfieldIndex;
}
TField *modifiedField = new TField(&newType, newName, field.line(), newSymbolType);
if (addressSpace)
{
symbolEnv.markAsPointer(*modifiedField, *addressSpace);
}
if (symbolEnv.isUBO(field))
{
symbolEnv.markAsUBO(*modifiedField);
}
modifiedFields.push_back(modifiedField);
}
void addConversion(const ConversionFunc &func)
{
ASSERT(!modifiedFields.empty());
conversionInfos.push_back({func, nullptr, pathItems, Name(*modifiedFields.back())});
}
void addConversion(const TFunction &func)
{
ASSERT(!modifiedFields.empty());
conversionInfos.push_back({{}, &func, pathItems, Name(*modifiedFields.back())});
}
bool hasPacking() const { return containsPacked; }
bool hasPadding() const { return padFieldCount > 0; }
bool recurse(const TStructure &structure,
ModifiedStructMachinery &outMachinery,
const bool isUBORecurse)
{
const ModifiedStructMachinery *m = outMachineries.find(structure);
if (m == nullptr)
{
TranslatorMetalReflection *reflection = mtl::getTranslatorMetalReflection(&mCompiler);
reflection->addOriginalName(structure.uniqueId().get(), structure.name().data());
const Name name = idGen.createNewName(structure.name().data());
if (!TryCreateModifiedStruct(mCompiler, symbolEnv, idGen, config, structure, name,
outMachineries, isUBORecurse, config.allowPadding, false))
{
return false;
}
m = outMachineries.find(structure);
ASSERT(m);
}
outMachinery = *m;
return true;
}
bool getIsUBO() const { return isUBO; }
private:
void addPadding(size_t padAmount, bool updateLayout)
{
if (padAmount == 0)
{
return;
}
const size_t begin = modifiedFields.size();
// Iteratively adding in scalar or vector padding because some struct types will not
// allow matrix or array members.
while (padAmount > 0)
{
TType *padType;
if (padAmount >= 16)
{
padAmount -= 16;
padType = new TType(TBasicType::EbtFloat, 4);
}
else if (padAmount >= 8)
{
padAmount -= 8;
padType = new TType(TBasicType::EbtFloat, 2);
}
else if (padAmount >= 4)
{
padAmount -= 4;
padType = new TType(TBasicType::EbtFloat);
}
else if (padAmount >= 2)
{
padAmount -= 2;
padType = new TType(TBasicType::EbtBool, 2);
}
else
{
ASSERT(padAmount == 1);
padAmount -= 1;
padType = new TType(TBasicType::EbtBool);
}
if (padType->getBasicType() != EbtBool)
{
padType->setPrecision(EbpLow);
}
if (updateLayout)
{
metalLayoutTotal += MetalLayoutOf(*padType);
}
const Name name = idGen.createNewName("pad");
modifiedFields.push_back(
new TField(padType, name.rawName(), kNoSourceLoc, name.symbolType()));
++padFieldCount;
}
std::reverse(modifiedFields.begin() + begin, modifiedFields.end());
}
void introducePacking()
{
if (!config.allowPacking)
{
return;
}
auto setUnpackedStorage = [](TType &type) {
TLayoutBlockStorage storage = type.getLayoutQualifier().blockStorage;
switch (storage)
{
case TLayoutBlockStorage::EbsShared:
storage = TLayoutBlockStorage::EbsStd140;
break;
case TLayoutBlockStorage::EbsPacked:
storage = TLayoutBlockStorage::EbsStd430;
break;
case TLayoutBlockStorage::EbsStd140:
case TLayoutBlockStorage::EbsStd430:
case TLayoutBlockStorage::EbsUnspecified:
break;
}
SetBlockStorage(type, storage);
};
Layout glslLayoutTotal = Layout::Identity();
const size_t size = modifiedFields.size();
for (size_t i = 0; i < size; ++i)
{
TField &curr = *modifiedFields[i];
TType &currType = *curr.type();
const bool canBePacked = CanBePacked(currType);
auto dontPack = [&]() {
if (canBePacked)
{
setUnpackedStorage(currType);
}
glslLayoutTotal += GlslLayoutOf(currType);
};
if (!CanBePacked(currType))
{
dontPack();
continue;
}
const Layout packedGlslLayout = GlslLayoutOf(currType);
const TLayoutBlockStorage packedStorage = currType.getLayoutQualifier().blockStorage;
setUnpackedStorage(currType);
const Layout unpackedGlslLayout = GlslLayoutOf(currType);
SetBlockStorage(currType, packedStorage);
ASSERT(packedGlslLayout.sizeOf <= unpackedGlslLayout.sizeOf);
if (packedGlslLayout.sizeOf == unpackedGlslLayout.sizeOf)
{
dontPack();
continue;
}
const size_t j = i + 1;
if (j == size)
{
dontPack();
break;
}
const size_t pad = unpackedGlslLayout.sizeOf - packedGlslLayout.sizeOf;
const TField &next = *modifiedFields[j];
const Layout nextGlslLayout = GlslLayoutOf(*next.type());
if (pad < nextGlslLayout.sizeOf)
{
dontPack();
continue;
}
symbolEnv.markAsPacked(curr);
glslLayoutTotal += packedGlslLayout;
containsPacked = true;
}
}
void introducePadding()
{
if (!config.allowPadding)
{
return;
}
MetalLayoutOfConfig layoutConfig;
layoutConfig.disablePacking = !config.allowPacking;
layoutConfig.assumeStructsAreTailPadded = true;
TFieldList fields = std::move(modifiedFields);
ASSERT(!fields.empty()); // GLSL requires at least one member.
const TField *const first = fields.front();
for (TField *field : fields)
{
const TType &type = *field->type();
const Layout glslLayout = GlslLayoutOf(type);
const Layout metalLayout = MetalLayoutOf(type, layoutConfig);
size_t prePadAmount = 0;
if (glslLayout.alignOf > metalLayout.alignOf && field != first)
{
const size_t prePaddedSize = metalLayoutTotal.sizeOf;
metalLayoutTotal.requireAlignment(glslLayout.alignOf, true);
const size_t paddedSize = metalLayoutTotal.sizeOf;
prePadAmount = paddedSize - prePaddedSize;
metalLayoutTotal += metalLayout;
addPadding(prePadAmount, false); // Note: requireAlignment() already updated layout
}
else
{
metalLayoutTotal += metalLayout;
}
modifiedFields.push_back(field);
if (glslLayout.sizeOf > metalLayout.sizeOf && field != fields.back())
{
const bool updateLayout = true; // XXX: Correct?
const size_t padAmount = glslLayout.sizeOf - metalLayout.sizeOf;
addPadding(padAmount, updateLayout);
}
}
}
public:
TCompiler &mCompiler;
const ModifyStructConfig &config;
SymbolEnv &symbolEnv;
private:
TFieldList &modifiedFields;
Layout metalLayoutTotal = Layout::Identity();
size_t padFieldCount = 0;
bool containsPacked = false;
bool finalized = false;
std::vector<PathItem> pathItems;
int mSubfieldIndex = 0;
std::vector<ConversionInfo> conversionInfos;
TSymbolTable &symbolTable;
IdGen &idGen;
ModifiedStructMachineries &outMachineries;
const bool isUBO;
const bool useAttributeAliasing;
};
////////////////////////////////////////////////////////////////////////////////
using ModifyFunc = bool (*)(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing);
bool ModifyRecursive(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing);
bool IdentityModify(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
const TType &type = *field.type();
state.addModifiedField(field, CloneType(type), storage, packing, nullptr);
state.addConversion([=](Access::Env &, OriginalAccess &o, ModifiedAccess &m) {
return Access{o, m};
});
return false;
}
bool InlineStruct(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
const TType &type = *field.type();
const TStructure *substructure = state.symbolEnv.remap(type.getStruct());
if (!substructure)
{
return false;
}
if (type.isArray())
{
return false;
}
if (!state.config.inlineStruct(field))
{
return false;
}
const TFieldList &subfields = substructure->fields();
for (const TField *subfield : subfields)
{
const TType &subtype = *subfield->type();
const TLayoutBlockStorage substorage = Overlay(storage, subtype);
const TLayoutMatrixPacking subpacking = Overlay(packing, subtype);
ModifyRecursive(state, *subfield, substorage, subpacking);
}
return true;
}
bool RecurseStruct(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
const TType &type = *field.type();
const TStructure *substructure = state.symbolEnv.remap(type.getStruct());
if (!substructure)
{
return false;
}
if (!state.config.recurseStruct(field))
{
return false;
}
ModifiedStructMachinery machinery;
if (!state.recurse(*substructure, machinery, state.getIsUBO()))
{
return false;
}
TType &newType = *new TType(machinery.modifiedStruct, false);
if (type.isArray())
{
newType.makeArrays(type.getArraySizes());
}
TIntermFunctionDefinition *converter = machinery.getConverter(state.config.convertType);
ASSERT(converter);
state.addModifiedField(field, newType, storage, packing, state.symbolEnv.isPointer(field));
if (state.symbolEnv.isPointer(field))
{
state.symbolEnv.removePointer(field);
}
state.addConversion(*converter->getFunction());
return true;
}
bool SplitMatrixColumns(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
const TType &type = *field.type();
if (!type.isMatrix())
{
return false;
}
if (!state.config.splitMatrixColumns(field))
{
return false;
}
const uint8_t cols = type.getCols();
TType &rowType = DropColumns(type);
for (uint8_t c = 0; c < cols; ++c)
{
state.pushPath(c);
state.addModifiedField(field, rowType, storage, packing, state.symbolEnv.isPointer(field));
if (state.symbolEnv.isPointer(field))
{
state.symbolEnv.removePointer(field);
}
state.addConversion([=](Access::Env &, OriginalAccess &o, ModifiedAccess &m) {
return Access{o, m};
});
state.popPath();
}
return true;
}
bool SaturateMatrixRows(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
const TType &type = *field.type();
if (!type.isMatrix())
{
return false;
}
const bool isRowMajor = type.getLayoutQualifier().matrixPacking == EmpRowMajor;
const uint8_t rows = type.getRows();
const uint8_t saturation = state.config.saturateMatrixRows(field);
if (saturation <= rows)
{
return false;
}
const uint8_t cols = type.getCols();
TType &satType = SetMatrixRowDim(type, saturation);
state.addModifiedField(field, satType, storage, packing, state.symbolEnv.isPointer(field));
if (state.symbolEnv.isPointer(field))
{
state.symbolEnv.removePointer(field);
}
for (uint8_t c = 0; c < cols; ++c)
{
for (uint8_t r = 0; r < rows; ++r)
{
state.addConversion([=](Access::Env &, OriginalAccess &o, ModifiedAccess &m) {
uint8_t firstModifiedIndex = isRowMajor ? r : c;
uint8_t secondModifiedIndex = isRowMajor ? c : r;
auto &o_ = AccessIndex(AccessIndex(o, c), r);
auto &m_ = AccessIndex(AccessIndex(m, firstModifiedIndex), secondModifiedIndex);
return Access{o_, m_};
});
}
}
return true;
}
bool TestBoolToUint(ConvertStructState &state, const TField &field)
{
if (field.type()->getBasicType() != TBasicType::EbtBool)
{
return false;
}
if (!state.config.promoteBoolToUint(field))
{
return false;
}
return true;
}
Access ConvertBoolToUint(ConvertType convertType, OriginalAccess &o, ModifiedAccess &m)
{
auto coerce = [](TIntermTyped &to, TIntermTyped &from) -> TIntermTyped & {
return *TIntermAggregate::CreateConstructor(to.getType(), new TIntermSequence{&from});
};
switch (convertType)
{
case ConvertType::OriginalToModified:
return Access{coerce(m, o), m};
case ConvertType::ModifiedToOriginal:
return Access{o, coerce(o, m)};
}
}
bool SaturateScalarOrVectorCommon(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing,
const bool array)
{
const TType &type = *field.type();
if (type.isArray() != array)
{
return false;
}
if (!((type.isRank0() && HasScalarBasicType(type)) || type.isVector()))
{
return false;
}
const auto saturator =
array ? state.config.saturateScalarOrVectorArrays : state.config.saturateScalarOrVector;
const uint8_t dim = type.getNominalSize();
const uint8_t saturation = saturator(field);
if (saturation <= dim)
{
return false;
}
TType &satType = SetVectorDim(type, saturation);
const bool boolToUint = TestBoolToUint(state, field);
if (boolToUint)
{
satType.setBasicType(TBasicType::EbtUInt);
}
state.addModifiedField(field, satType, storage, packing, state.symbolEnv.isPointer(field));
if (state.symbolEnv.isPointer(field))
{
state.symbolEnv.removePointer(field);
}
for (uint8_t d = 0; d < dim; ++d)
{
state.addConversion([=](Access::Env &env, OriginalAccess &o, ModifiedAccess &m) {
auto &o_ = dim > 1 ? AccessIndex(o, d) : o;
auto &m_ = AccessIndex(m, d);
if (boolToUint)
{
return ConvertBoolToUint(env.type, o_, m_);
}
else
{
return Access{o_, m_};
}
});
}
return true;
}
bool SaturateScalarOrVectorArrays(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
return SaturateScalarOrVectorCommon(state, field, storage, packing, true);
}
bool SaturateScalarOrVector(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
return SaturateScalarOrVectorCommon(state, field, storage, packing, false);
}
bool PromoteBoolToUint(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
if (!TestBoolToUint(state, field))
{
return false;
}
auto &promotedType = CloneType(*field.type());
promotedType.setBasicType(TBasicType::EbtUInt);
state.addModifiedField(field, promotedType, storage, packing, state.symbolEnv.isPointer(field));
if (state.symbolEnv.isPointer(field))
{
state.symbolEnv.removePointer(field);
}
state.addConversion([=](Access::Env &env, OriginalAccess &o, ModifiedAccess &m) {
return ConvertBoolToUint(env.type, o, m);
});
return true;
}
bool ModifyCommon(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
ModifyFunc funcs[] = {
InlineStruct, //
RecurseStruct, //
SplitMatrixColumns, //
SaturateMatrixRows, //
SaturateScalarOrVectorArrays, //
SaturateScalarOrVector, //
PromoteBoolToUint, //
};
for (ModifyFunc func : funcs)
{
if (func(state, field, storage, packing))
{
return true;
}
}
return IdentityModify(state, field, storage, packing);
}
bool InlineArray(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
const TType &type = *field.type();
if (!type.isArray())
{
return false;
}
if (!state.config.inlineArray(field))
{
return false;
}
const unsigned volume = type.getArraySizeProduct();
const bool isMultiDim = type.isArrayOfArrays();
auto &innermostType = InnermostType(type);
if (isMultiDim)
{
state.pushPath(FlattenArray());
}
for (unsigned i = 0; i < volume; ++i)
{
state.pushPath(i);
TType setType(innermostType);
if (setType.getLayoutQualifier().locationsSpecified)
{
TLayoutQualifier qualifier(innermostType.getLayoutQualifier());
qualifier.location = innermostType.getLayoutQualifier().location + i;
qualifier.locationsSpecified = 1;
setType.setLayoutQualifier(qualifier);
}
const TField innermostField(&setType, field.name(), field.line(), field.symbolType());
ModifyCommon(state, innermostField, storage, packing);
state.popPath();
}
if (isMultiDim)
{
state.popPath();
}
return true;
}
bool ModifyRecursive(ConvertStructState &state,
const TField &field,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking packing)
{
state.pushPath(field);
bool modified;
if (InlineArray(state, field, storage, packing))
{
modified = true;
}
else
{
modified = ModifyCommon(state, field, storage, packing);
}
state.popPath();
return modified;
}
} // anonymous namespace
////////////////////////////////////////////////////////////////////////////////
bool sh::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)
{
ConvertStructState state(compiler, symbolEnv, idGen, config, outMachineries, isUBO,
useAttributeAliasing);
size_t identicalFieldCount = 0;
const TFieldList &originalFields = originalStruct.fields();
for (TField *originalField : originalFields)
{
const TType &originalType = *originalField->type();
const TLayoutBlockStorage storage = Overlay(config.initialBlockStorage, originalType);
const TLayoutMatrixPacking packing = Overlay(config.initialMatrixPacking, originalType);
if (!ModifyRecursive(state, *originalField, storage, packing))
{
++identicalFieldCount;
}
}
state.finalize(allowPadding);
if (identicalFieldCount == originalFields.size() && !state.hasPacking() &&
!state.hasPadding() && !useAttributeAliasing)
{
return false;
}
state.publish(originalStruct, modifiedStructName);
return true;
}