summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp
diff options
context:
space:
mode:
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.cpp673
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