Source code

Revision control

Copy as Markdown

Other Tools

//
// Copyright 2019 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.
//
// RewriteAtomicCounters: Emulate atomic counter buffers with storage buffers.
//
#include "compiler/translator/tree_ops/RewriteArrayOfArrayOfOpaqueUniforms.h"
#include "compiler/translator/Compiler.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"
#include "compiler/translator/tree_util/ReplaceVariable.h"
namespace sh
{
namespace
{
struct UniformData
{
// Corresponding to an array of array of opaque uniform variable, this is the flattened variable
// that is replacing it.
const TVariable *flattened;
// Assume a general case of array declaration with N dimensions:
//
// uniform type u[Dn]..[D2][D1];
//
// Let's define
//
// Pn = D(n-1)*...*D2*D1
//
// In that case, we have:
//
// u[In] = ac + In*Pn
// u[In][I(n-1)] = ac + In*Pn + I(n-1)*P(n-1)
// u[In]...[Ii] = ac + In*Pn + ... + Ii*Pi
//
// This array contains Pi. Note that the like TType::mArraySizes, the last element is the
// outermost dimension. Element 0 is necessarily 1.
TVector<unsigned int> mSubArraySizes;
};
using UniformMap = angle::HashMap<const TVariable *, UniformData>;
TIntermTyped *RewriteArrayOfArraySubscriptExpression(TCompiler *compiler,
TIntermBinary *node,
const UniformMap &uniformMap);
// Given an expression, this traverser calculates a new expression where array of array of opaque
// uniforms are replaced with their flattened ones. 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 UniformMap &uniformMap)
: TIntermTraverser(true, false, false), mCompiler(compiler), mUniformMap(uniformMap)
{}
bool visitBinary(Visit visit, TIntermBinary *node) override
{
TIntermTyped *rewritten =
RewriteArrayOfArraySubscriptExpression(mCompiler, node, mUniformMap);
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
{
// We cannot reach here for an opaque uniform that is being replaced. visitBinary should
// have taken care of it.
ASSERT(!IsOpaqueType(node->getType().getBasicType()) ||
mUniformMap.find(&node->variable()) == mUniformMap.end());
}
private:
TCompiler *mCompiler;
const UniformMap &mUniformMap;
};
// Rewrite the index of an EOpIndexIndirect expression. The root can never need replacing, because
// it cannot be an opaque uniform itself.
void RewriteIndexExpression(TCompiler *compiler,
TIntermTyped *expression,
const UniformMap &uniformMap)
{
RewriteExpressionTraverser traverser(compiler, uniformMap);
expression->traverse(&traverser);
bool valid = traverser.updateTree(compiler, expression);
ASSERT(valid);
}
// Given an expression such as the following:
//
// EOpIndex(In)Direct (opaque uniform)
// / \
// EOpIndex(In)Direct I1
// / \
// ... I2
// /
// EOpIndex(In)Direct
// / \
// uniform In
//
// produces:
//
// EOpIndex(In)Direct
// / \
// uniform In*Pn + ... + I2*P2 + I1*P1
//
TIntermTyped *RewriteArrayOfArraySubscriptExpression(TCompiler *compiler,
TIntermBinary *node,
const UniformMap &uniformMap)
{
// Only interested in opaque uniforms.
if (!IsOpaqueType(node->getType().getBasicType()))
{
return nullptr;
}
TIntermSymbol *opaqueUniform = nullptr;
// Iterate once and find the opaque uniform that's being indexed.
TIntermBinary *iter = node;
while (opaqueUniform == nullptr)
{
ASSERT(iter->getOp() == EOpIndexDirect || iter->getOp() == EOpIndexIndirect);
opaqueUniform = iter->getLeft()->getAsSymbolNode();
iter = iter->getLeft()->getAsBinaryNode();
}
// If not being replaced, there's nothing to do.
auto flattenedIter = uniformMap.find(&opaqueUniform->variable());
if (flattenedIter == uniformMap.end())
{
return nullptr;
}
const UniformData &data = flattenedIter->second;
// Iterate again and build the index expression. The index expression constitutes the sum of
// the variable indices plus a constant offset calculated from the constant indices. For
// example, smplr[1][x][2][y] will have an index of x*P3 + y*P1 + c, where c = (1*P4 + 2*P2).
unsigned int constantOffset = 0;
TIntermTyped *variableIndex = nullptr;
// Since the opaque uniforms are fully subscripted, we know exactly how many EOpIndex* nodes
// there should be.
for (size_t dimIndex = 0; dimIndex < data.mSubArraySizes.size(); ++dimIndex)
{
ASSERT(node);
unsigned int subArraySize = data.mSubArraySizes[dimIndex];
switch (node->getOp())
{
case EOpIndexDirect:
// Accumulate the constant index.
constantOffset +=
node->getRight()->getAsConstantUnion()->getIConst(0) * subArraySize;
break;
case EOpIndexIndirect:
{
// Run RewriteExpressionTraverser on the right node. It may itself be an expression
// with an array of array of opaque uniform inside that needs to be rewritten.
TIntermTyped *indexExpression = node->getRight();
RewriteIndexExpression(compiler, indexExpression, uniformMap);
// Scale and accumulate.
if (subArraySize != 1)
{
indexExpression =
new TIntermBinary(EOpMul, indexExpression, CreateIndexNode(subArraySize));
}
if (variableIndex == nullptr)
{
variableIndex = indexExpression;
}
else
{
variableIndex = new TIntermBinary(EOpAdd, variableIndex, indexExpression);
}
break;
}
default:
UNREACHABLE();
break;
}
node = node->getLeft()->getAsBinaryNode();
}
// Add the two accumulated indices together.
TIntermTyped *index = nullptr;
if (constantOffset == 0 && variableIndex != nullptr)
{
// No constant offset, but there's variable offset. Take that as offset.
index = variableIndex;
}
else
{
// Either the constant offset is non zero, or there's no variable offset (so constant 0
// should be used).
index = CreateIndexNode(constantOffset);
if (variableIndex)
{
index = new TIntermBinary(EOpAdd, index, variableIndex);
}
}
// Create an index into the flattened uniform.
TOperator op = variableIndex ? EOpIndexIndirect : EOpIndexDirect;
return new TIntermBinary(op, new TIntermSymbol(data.flattened), index);
}
// Traverser that takes:
//
// uniform sampler/image/atomic_uint u[N][M]..
//
// and transforms it to:
//
// uniform sampler/image/atomic_uint u[N * M * ..]
//
// MonomorphizeUnsupportedFunctions makes it impossible for this array to be partially
// subscripted, or passed as argument to a function unsubscripted. This means that every encounter
// of this uniform can be expected to be fully subscripted.
//
class RewriteArrayOfArrayOfOpaqueUniformsTraverser : public TIntermTraverser
{
public:
RewriteArrayOfArrayOfOpaqueUniformsTraverser(TCompiler *compiler, TSymbolTable *symbolTable)
: TIntermTraverser(true, false, false, symbolTable), mCompiler(compiler)
{}
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
{
if (!mInGlobalScope)
{
return true;
}
const TIntermSequence &sequence = *(node->getSequence());
TIntermTyped *variable = sequence.front()->getAsTyped();
const TType &type = variable->getType();
bool isOpaqueUniform =
type.getQualifier() == EvqUniform && IsOpaqueType(type.getBasicType());
// Only interested in array of array of opaque uniforms.
if (!isOpaqueUniform || !type.isArrayOfArrays())
{
return false;
}
// Opaque uniforms cannot have initializers, so the declaration must necessarily be a
// symbol.
TIntermSymbol *symbol = variable->getAsSymbolNode();
ASSERT(symbol != nullptr);
const TVariable *uniformVariable = &symbol->variable();
// Create an entry in the map.
ASSERT(mUniformMap.find(uniformVariable) == mUniformMap.end());
UniformData &data = mUniformMap[uniformVariable];
// Calculate the accumulated dimension products. See UniformData::mSubArraySizes.
const TSpan<const unsigned int> &arraySizes = type.getArraySizes();
mUniformMap[uniformVariable].mSubArraySizes.resize(arraySizes.size());
unsigned int runningProduct = 1;
for (size_t dimension = 0; dimension < arraySizes.size(); ++dimension)
{
data.mSubArraySizes[dimension] = runningProduct;
runningProduct *= arraySizes[dimension];
}
// Create a replacement variable with the array flattened.
TType *newType = new TType(type);
newType->toArrayBaseType();
newType->makeArray(runningProduct);
data.flattened = new TVariable(mSymbolTable, uniformVariable->name(), newType,
uniformVariable->symbolType());
TIntermDeclaration *decl = new TIntermDeclaration;
decl->appendDeclarator(new TIntermSymbol(data.flattened));
queueReplacement(decl, OriginalNode::IS_DROPPED);
return false;
}
bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
{
// As an optimization, don't bother inspecting functions if there aren't any opaque uniforms
// to replace.
return !mUniformMap.empty();
}
// Same implementation as in RewriteExpressionTraverser. That traverser cannot replace root.
bool visitBinary(Visit visit, TIntermBinary *node) override
{
TIntermTyped *rewritten =
RewriteArrayOfArraySubscriptExpression(mCompiler, node, mUniformMap);
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
{
ASSERT(!IsOpaqueType(node->getType().getBasicType()) ||
mUniformMap.find(&node->variable()) == mUniformMap.end());
}
private:
TCompiler *mCompiler;
UniformMap mUniformMap;
};
} // anonymous namespace
bool RewriteArrayOfArrayOfOpaqueUniforms(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable)
{
RewriteArrayOfArrayOfOpaqueUniformsTraverser traverser(compiler, symbolTable);
root->traverse(&traverser);
return traverser.updateTree(compiler, root);
}
} // namespace sh