diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp new file mode 100644 index 0000000000..219a6b31fc --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp @@ -0,0 +1,673 @@ +// +// Copyright 2018 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. +// +// RewriteStructSamplers: Extract samplers from structs. +// + +#include "compiler/translator/tree_ops/RewriteStructSamplers.h" + +#include "compiler/translator/ImmutableStringBuilder.h" +#include "compiler/translator/SymbolTable.h" +#include "compiler/translator/tree_util/IntermNode_util.h" +#include "compiler/translator/tree_util/IntermTraverse.h" + +namespace sh +{ +namespace +{ + +// Used to map one structure type to another (one where the samplers are removed). +struct StructureData +{ + // The structure this was replaced with. If nullptr, it means the structure is removed (because + // it had all samplers). + const TStructure *modified; + // Indexed by the field index of original structure, to get the field index of the modified + // structure. For example: + // + // struct Original + // { + // sampler2D s1; + // vec4 f1; + // sampler2D s2; + // sampler2D s3; + // vec4 f2; + // }; + // + // struct Modified + // { + // vec4 f1; + // vec4 f2; + // }; + // + // fieldMap: + // 0 -> Invalid + // 1 -> 0 + // 2 -> Invalid + // 3 -> Invalid + // 4 -> 1 + // + TVector<int> fieldMap; +}; + +using StructureMap = angle::HashMap<const TStructure *, StructureData>; +using StructureUniformMap = angle::HashMap<const TVariable *, const TVariable *>; +using ExtractedSamplerMap = angle::HashMap<std::string, const TVariable *>; + +TIntermTyped *RewriteModifiedStructFieldSelectionExpression( + TCompiler *compiler, + TIntermBinary *node, + const StructureMap &structureMap, + const StructureUniformMap &structureUniformMap, + const ExtractedSamplerMap &extractedSamplers); + +TIntermTyped *RewriteExpressionVisitBinaryHelper(TCompiler *compiler, + TIntermBinary *node, + const StructureMap &structureMap, + const StructureUniformMap &structureUniformMap, + const ExtractedSamplerMap &extractedSamplers) +{ + // Only interested in EOpIndexDirectStruct binary nodes. + if (node->getOp() != EOpIndexDirectStruct) + { + return nullptr; + } + + const TStructure *structure = node->getLeft()->getType().getStruct(); + ASSERT(structure); + + // If the result of the index is not a sampler and the struct is not replaced, there's nothing + // to do. + if (!node->getType().isSampler() && structureMap.find(structure) == structureMap.end()) + { + return nullptr; + } + + // Otherwise, replace the whole expression such that: + // + // - if sampler, it's indexed with whatever indices the parent structs were indexed with, + // - otherwise, the chain of field selections is rewritten by modifying the base uniform so all + // the intermediate nodes would have the correct type (and therefore fields). + ASSERT(structureMap.find(structure) != structureMap.end()); + + return RewriteModifiedStructFieldSelectionExpression(compiler, node, structureMap, + structureUniformMap, extractedSamplers); +} + +// Given an expression, this traverser calculates a new expression where sampler-in-structs are +// replaced with their extracted ones, and field indices are adjusted for the rest of the fields. +// In particular, this is run on the right node of EOpIndexIndirect binary nodes, so that the +// expression in the index gets a chance to go through this transformation. +class RewriteExpressionTraverser final : public TIntermTraverser +{ + public: + explicit RewriteExpressionTraverser(TCompiler *compiler, + const StructureMap &structureMap, + const StructureUniformMap &structureUniformMap, + const ExtractedSamplerMap &extractedSamplers) + : TIntermTraverser(true, false, false), + mCompiler(compiler), + mStructureMap(structureMap), + mStructureUniformMap(structureUniformMap), + mExtractedSamplers(extractedSamplers) + {} + + bool visitBinary(Visit visit, TIntermBinary *node) override + { + TIntermTyped *rewritten = RewriteExpressionVisitBinaryHelper( + mCompiler, node, mStructureMap, mStructureUniformMap, mExtractedSamplers); + + if (rewritten == nullptr) + { + return true; + } + + queueReplacement(rewritten, OriginalNode::IS_DROPPED); + + // Don't iterate as the expression is rewritten. + return false; + } + + void visitSymbol(TIntermSymbol *node) override + { + // It's impossible to reach here with a symbol that needs replacement. + // MonomorphizeUnsupportedFunctions makes sure that whole structs containing + // samplers are not passed to functions, so any instance of the struct uniform is + // necessarily indexed right away. visitBinary should have already taken care of it. + ASSERT(mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end()); + } + + private: + TCompiler *mCompiler; + + // See RewriteStructSamplersTraverser. + const StructureMap &mStructureMap; + const StructureUniformMap &mStructureUniformMap; + const ExtractedSamplerMap &mExtractedSamplers; +}; + +// Rewrite the index of an EOpIndexIndirect expression. The root can never need replacing, because +// it cannot be a sampler itself or of a struct type. +void RewriteIndexExpression(TCompiler *compiler, + TIntermTyped *expression, + const StructureMap &structureMap, + const StructureUniformMap &structureUniformMap, + const ExtractedSamplerMap &extractedSamplers) +{ + RewriteExpressionTraverser traverser(compiler, structureMap, structureUniformMap, + extractedSamplers); + expression->traverse(&traverser); + bool valid = traverser.updateTree(compiler, expression); + ASSERT(valid); +} + +// Given an expression such as the following: +// +// EOpIndexDirectStruct (sampler) +// / \ +// EOpIndex* field index +// / \ +// EOpIndexDirectStruct index 2 +// / \ +// EOpIndex* field index +// / \ +// EOpIndexDirectStruct index 1 +// / \ +// Uniform Struct field index +// +// produces: +// +// EOpIndex* +// / \ +// EOpIndex* index 2 +// / \ +// sampler index 1 +// +// Alternatively, if the expression is as such: +// +// EOpIndexDirectStruct +// / \ +// (modified struct type) EOpIndex* field index +// / \ +// EOpIndexDirectStruct index 2 +// / \ +// EOpIndex* field index +// / \ +// EOpIndexDirectStruct index 1 +// / \ +// Uniform Struct field index +// +// produces: +// +// EOpIndexDirectStruct +// / \ +// EOpIndex* mapped field index +// / \ +// EOpIndexDirectStruct index 2 +// / \ +// EOpIndex* mapped field index +// / \ +// EOpIndexDirectStruct index 1 +// / \ +// Uniform Struct mapped field index +// +TIntermTyped *RewriteModifiedStructFieldSelectionExpression( + TCompiler *compiler, + TIntermBinary *node, + const StructureMap &structureMap, + const StructureUniformMap &structureUniformMap, + const ExtractedSamplerMap &extractedSamplers) +{ + ASSERT(node->getOp() == EOpIndexDirectStruct); + + const bool isSampler = node->getType().isSampler(); + + TIntermSymbol *baseUniform = nullptr; + std::string samplerName; + + TVector<TIntermBinary *> indexNodeStack; + + // Iterate once and build the name of the sampler. + TIntermBinary *iter = node; + while (baseUniform == nullptr) + { + indexNodeStack.push_back(iter); + baseUniform = iter->getLeft()->getAsSymbolNode(); + + if (isSampler) + { + if (iter->getOp() == EOpIndexDirectStruct) + { + // When indexed into a struct, get the field name instead and construct the sampler + // name. + samplerName.insert(0, iter->getIndexStructFieldName().data()); + samplerName.insert(0, "_"); + } + + if (baseUniform) + { + // If left is a symbol, we have reached the end of the chain. Use the struct name + // to finish building the name of the sampler. + samplerName.insert(0, baseUniform->variable().name().data()); + } + } + + iter = iter->getLeft()->getAsBinaryNode(); + } + + TIntermTyped *rewritten = nullptr; + + if (isSampler) + { + ASSERT(extractedSamplers.find(samplerName) != extractedSamplers.end()); + rewritten = new TIntermSymbol(extractedSamplers.at(samplerName)); + } + else + { + const TVariable *baseUniformVar = &baseUniform->variable(); + ASSERT(structureUniformMap.find(baseUniformVar) != structureUniformMap.end()); + rewritten = new TIntermSymbol(structureUniformMap.at(baseUniformVar)); + } + + // Iterate again and build the expression from bottom up. + for (auto it = indexNodeStack.rbegin(); it != indexNodeStack.rend(); ++it) + { + TIntermBinary *indexNode = *it; + + switch (indexNode->getOp()) + { + case EOpIndexDirectStruct: + if (!isSampler) + { + // Remap the field. + const TStructure *structure = indexNode->getLeft()->getType().getStruct(); + ASSERT(structureMap.find(structure) != structureMap.end()); + + TIntermConstantUnion *asConstantUnion = + indexNode->getRight()->getAsConstantUnion(); + ASSERT(asConstantUnion); + + const int fieldIndex = asConstantUnion->getIConst(0); + ASSERT(fieldIndex < + static_cast<int>(structureMap.at(structure).fieldMap.size())); + + const int mappedFieldIndex = structureMap.at(structure).fieldMap[fieldIndex]; + + rewritten = new TIntermBinary(EOpIndexDirectStruct, rewritten, + CreateIndexNode(mappedFieldIndex)); + } + break; + + case EOpIndexDirect: + rewritten = new TIntermBinary(EOpIndexDirect, rewritten, indexNode->getRight()); + break; + + case EOpIndexIndirect: + { + // Run RewriteExpressionTraverser on the right node. It may itself be an expression + // with a sampler inside that needs to be rewritten, or simply use a field of a + // struct that's remapped. + TIntermTyped *indexExpression = indexNode->getRight(); + RewriteIndexExpression(compiler, indexExpression, structureMap, structureUniformMap, + extractedSamplers); + rewritten = new TIntermBinary(EOpIndexIndirect, rewritten, indexExpression); + break; + } + + default: + UNREACHABLE(); + break; + } + } + + return rewritten; +} + +class RewriteStructSamplersTraverser final : public TIntermTraverser +{ + public: + explicit RewriteStructSamplersTraverser(TCompiler *compiler, TSymbolTable *symbolTable) + : TIntermTraverser(true, false, false, symbolTable), + mCompiler(compiler), + mRemovedUniformsCount(0) + {} + + int removedUniformsCount() const { return mRemovedUniformsCount; } + + // Each struct sampler declaration is stripped of its samplers. New uniforms are added for each + // stripped struct sampler. + bool visitDeclaration(Visit visit, TIntermDeclaration *decl) override + { + if (!mInGlobalScope) + { + return true; + } + + const TIntermSequence &sequence = *(decl->getSequence()); + TIntermTyped *declarator = sequence.front()->getAsTyped(); + const TType &type = declarator->getType(); + + if (!type.isStructureContainingSamplers()) + { + return false; + } + + TIntermSequence newSequence; + + if (type.isStructSpecifier()) + { + // If this is just a struct definition (not a uniform variable declaration of a + // struct type), just remove the samplers. They are not instantiated yet. + const TStructure *structure = type.getStruct(); + ASSERT(structure && mStructureMap.find(structure) == mStructureMap.end()); + + stripStructSpecifierSamplers(structure, &newSequence); + } + else + { + const TStructure *structure = type.getStruct(); + + // If the structure is defined at the same time, create the mapping to the stripped + // version first. + if (mStructureMap.find(structure) == mStructureMap.end()) + { + stripStructSpecifierSamplers(structure, &newSequence); + } + + // Then, extract the samplers from the struct and create global-scope variables instead. + TIntermSymbol *asSymbol = declarator->getAsSymbolNode(); + ASSERT(asSymbol); + const TVariable &variable = asSymbol->variable(); + ASSERT(variable.symbolType() != SymbolType::Empty); + + extractStructSamplerUniforms(variable, structure, &newSequence); + } + + mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), decl, + std::move(newSequence)); + + return false; + } + + // Same implementation as in RewriteExpressionTraverser. That traverser cannot replace root. + bool visitBinary(Visit visit, TIntermBinary *node) override + { + TIntermTyped *rewritten = RewriteExpressionVisitBinaryHelper( + mCompiler, node, mStructureMap, mStructureUniformMap, mExtractedSamplers); + + if (rewritten == nullptr) + { + return true; + } + + queueReplacement(rewritten, OriginalNode::IS_DROPPED); + + // Don't iterate as the expression is rewritten. + return false; + } + + // Same implementation as in RewriteExpressionTraverser. That traverser cannot replace root. + void visitSymbol(TIntermSymbol *node) override + { + ASSERT(mStructureUniformMap.find(&node->variable()) == mStructureUniformMap.end()); + } + + private: + // Removes all samplers from a struct specifier. + void stripStructSpecifierSamplers(const TStructure *structure, TIntermSequence *newSequence) + { + TFieldList *newFieldList = new TFieldList; + ASSERT(structure->containsSamplers()); + + // Add this struct to the struct map + ASSERT(mStructureMap.find(structure) == mStructureMap.end()); + StructureData *modifiedData = &mStructureMap[structure]; + + modifiedData->modified = nullptr; + modifiedData->fieldMap.resize(structure->fields().size(), std::numeric_limits<int>::max()); + + for (size_t fieldIndex = 0; fieldIndex < structure->fields().size(); ++fieldIndex) + { + const TField *field = structure->fields()[fieldIndex]; + const TType &fieldType = *field->type(); + + // If the field is a sampler, or a struct that's entirely removed, skip it. + if (!fieldType.isSampler() && !isRemovedStructType(fieldType)) + { + TType *newType = nullptr; + + // Otherwise, if it's a struct that's replaced, create a new field of the replaced + // type. + if (fieldType.isStructureContainingSamplers()) + { + const TStructure *fieldStruct = fieldType.getStruct(); + ASSERT(mStructureMap.find(fieldStruct) != mStructureMap.end()); + + const TStructure *modifiedStruct = mStructureMap[fieldStruct].modified; + ASSERT(modifiedStruct); + + newType = new TType(modifiedStruct, true); + if (fieldType.isArray()) + { + newType->makeArrays(fieldType.getArraySizes()); + } + } + else + { + // If not, duplicate the field as is. + newType = new TType(fieldType); + } + + // Record the mapping of the field indices, so future EOpIndexDirectStruct's into + // this struct can be fixed up. + modifiedData->fieldMap[fieldIndex] = static_cast<int>(newFieldList->size()); + + TField *newField = + new TField(newType, field->name(), field->line(), field->symbolType()); + newFieldList->push_back(newField); + } + } + + // Prune empty structs. + if (newFieldList->empty()) + { + return; + } + + // Declare a new struct with the same name and the new fields. + modifiedData->modified = + new TStructure(mSymbolTable, structure->name(), newFieldList, structure->symbolType()); + TType *newStructType = new TType(modifiedData->modified, true); + TVariable *newStructVar = + new TVariable(mSymbolTable, kEmptyImmutableString, newStructType, SymbolType::Empty); + TIntermSymbol *newStructRef = new TIntermSymbol(newStructVar); + + TIntermDeclaration *structDecl = new TIntermDeclaration; + structDecl->appendDeclarator(newStructRef); + + newSequence->push_back(structDecl); + } + + // Returns true if the type is a struct that was removed because we extracted all the members. + bool isRemovedStructType(const TType &type) const + { + const TStructure *structure = type.getStruct(); + if (structure == nullptr) + { + // Not a struct + return false; + } + + // A struct is removed if it is in the map, but doesn't have a replacement struct. + auto iter = mStructureMap.find(structure); + return iter != mStructureMap.end() && iter->second.modified == nullptr; + } + + // Removes samplers from struct uniforms. For each sampler removed also adds a new globally + // defined sampler uniform. + void extractStructSamplerUniforms(const TVariable &variable, + const TStructure *structure, + TIntermSequence *newSequence) + { + ASSERT(structure->containsSamplers()); + ASSERT(mStructureMap.find(structure) != mStructureMap.end()); + + const TType &type = variable.getType(); + enterArray(type); + + for (const TField *field : structure->fields()) + { + extractFieldSamplers(variable.name().data(), field, newSequence); + } + + // If there's a replacement structure (because there are non-sampler fields in the struct), + // add a declaration with that type. + const TStructure *modified = mStructureMap[structure].modified; + if (modified != nullptr) + { + TType *newType = new TType(modified, false); + if (type.isArray()) + { + newType->makeArrays(type.getArraySizes()); + } + newType->setQualifier(EvqUniform); + const TVariable *newVariable = + new TVariable(mSymbolTable, variable.name(), newType, variable.symbolType()); + + TIntermDeclaration *newDecl = new TIntermDeclaration(); + newDecl->appendDeclarator(new TIntermSymbol(newVariable)); + + newSequence->push_back(newDecl); + + ASSERT(mStructureUniformMap.find(&variable) == mStructureUniformMap.end()); + mStructureUniformMap[&variable] = newVariable; + } + else + { + mRemovedUniformsCount++; + } + + exitArray(type); + } + + // Extracts samplers from a field of a struct. Works with nested structs and arrays. + void extractFieldSamplers(const std::string &prefix, + const TField *field, + TIntermSequence *newSequence) + { + const TType &fieldType = *field->type(); + if (fieldType.isSampler() || fieldType.isStructureContainingSamplers()) + { + std::string newPrefix = prefix + "_" + field->name().data(); + + if (fieldType.isSampler()) + { + extractSampler(newPrefix, fieldType, newSequence); + } + else + { + enterArray(fieldType); + const TStructure *structure = fieldType.getStruct(); + for (const TField *nestedField : structure->fields()) + { + extractFieldSamplers(newPrefix, nestedField, newSequence); + } + exitArray(fieldType); + } + } + } + + void GenerateArraySizesFromStack(TVector<unsigned int> *sizesOut) + { + sizesOut->reserve(mArraySizeStack.size()); + + for (auto it = mArraySizeStack.rbegin(); it != mArraySizeStack.rend(); ++it) + { + sizesOut->push_back(*it); + } + } + + // Extracts a sampler from a struct. Declares the new extracted sampler. + void extractSampler(const std::string &newName, + const TType &fieldType, + TIntermSequence *newSequence) + { + ASSERT(fieldType.isSampler()); + + TType *newType = new TType(fieldType); + + // Add array dimensions accumulated so far due to struct arrays. Note that to support + // nested arrays, mArraySizeStack has the outermost size in the front. |makeArrays| thus + // expects this in reverse order. + TVector<unsigned int> parentArraySizes; + GenerateArraySizesFromStack(&parentArraySizes); + newType->makeArrays(parentArraySizes); + + ImmutableStringBuilder nameBuilder(newName.size() + 1); + nameBuilder << newName; + + newType->setQualifier(EvqUniform); + TVariable *newVariable = + new TVariable(mSymbolTable, nameBuilder, newType, SymbolType::AngleInternal); + TIntermSymbol *newSymbol = new TIntermSymbol(newVariable); + + TIntermDeclaration *samplerDecl = new TIntermDeclaration; + samplerDecl->appendDeclarator(newSymbol); + + newSequence->push_back(samplerDecl); + + // TODO: Use a temp name instead of generating a name as currently done. There is no + // guarantee that these generated names cannot clash. Create a mapping from the previous + // name to the name assigned to the temp variable so ShaderVariable::mappedName can be + // updated post-transformation. http://anglebug.com/4301 + ASSERT(mExtractedSamplers.find(newName) == mExtractedSamplers.end()); + mExtractedSamplers[newName] = newVariable; + } + + void enterArray(const TType &arrayType) + { + const TSpan<const unsigned int> &arraySizes = arrayType.getArraySizes(); + for (auto it = arraySizes.rbegin(); it != arraySizes.rend(); ++it) + { + unsigned int arraySize = *it; + mArraySizeStack.push_back(arraySize); + } + } + + void exitArray(const TType &arrayType) + { + mArraySizeStack.resize(mArraySizeStack.size() - arrayType.getNumArraySizes()); + } + + TCompiler *mCompiler; + int mRemovedUniformsCount; + + // Map structures with samplers to ones that have their samplers removed. + StructureMap mStructureMap; + + // Map uniform variables of structure type that are replaced with another variable. + StructureUniformMap mStructureUniformMap; + + // Map a constructed sampler name to its variable. Used to replace an expression that uses this + // sampler with the extracted one. + ExtractedSamplerMap mExtractedSamplers; + + // A stack of array sizes. Used to figure out the array dimensions of the extracted sampler, + // for example when it's nested in an array of structs in an array of structs. + TVector<unsigned int> mArraySizeStack; +}; +} // anonymous namespace + +bool RewriteStructSamplers(TCompiler *compiler, + TIntermBlock *root, + TSymbolTable *symbolTable, + int *removedUniformsCountOut) +{ + RewriteStructSamplersTraverser traverser(compiler, symbolTable); + root->traverse(&traverser); + *removedUniformsCountOut = traverser.removedUniformsCount(); + return traverser.updateTree(compiler, root); +} +} // namespace sh |