summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp')
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp420
1 files changed, 420 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp
new file mode 100644
index 0000000000..709f394878
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp
@@ -0,0 +1,420 @@
+//
+// 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.
+//
+// RewriteExpressionsWithShaderStorageBlock rewrites the expressions that contain shader storage
+// block calls into several simple ones that can be easily handled in the HLSL translator. After the
+// AST pass, all ssbo related blocks will be like below:
+// ssbo_access_chain = ssbo_access_chain;
+// ssbo_access_chain = expr_no_ssbo;
+// lvalue_no_ssbo = ssbo_access_chain;
+//
+
+#include "compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.h"
+
+#include "compiler/translator/Symbol.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+namespace
+{
+
+bool IsIncrementOrDecrementOperator(TOperator op)
+{
+ switch (op)
+ {
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsCompoundAssignment(TOperator op)
+{
+ switch (op)
+ {
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ case EOpDivAssign:
+ case EOpIModAssign:
+ case EOpBitShiftLeftAssign:
+ case EOpBitShiftRightAssign:
+ case EOpBitwiseAndAssign:
+ case EOpBitwiseXorAssign:
+ case EOpBitwiseOrAssign:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// EOpIndexDirect, EOpIndexIndirect, EOpIndexDirectStruct, EOpIndexDirectInterfaceBlock belong to
+// operators in SSBO access chain.
+bool IsReadonlyBinaryOperatorNotInSSBOAccessChain(TOperator op)
+{
+ switch (op)
+ {
+ case EOpComma:
+ case EOpAdd:
+ case EOpSub:
+ case EOpMul:
+ case EOpDiv:
+ case EOpIMod:
+ case EOpBitShiftLeft:
+ case EOpBitShiftRight:
+ case EOpBitwiseAnd:
+ case EOpBitwiseXor:
+ case EOpBitwiseOr:
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ case EOpVectorTimesScalar:
+ case EOpMatrixTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesMatrix:
+ case EOpLogicalOr:
+ case EOpLogicalXor:
+ case EOpLogicalAnd:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool HasSSBOAsFunctionArgument(TIntermSequence *arguments)
+{
+ for (TIntermNode *arg : *arguments)
+ {
+ TIntermTyped *typedArg = arg->getAsTyped();
+ if (IsInShaderStorageBlock(typedArg))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+class RewriteExpressionsWithShaderStorageBlockTraverser : public TIntermTraverser
+{
+ public:
+ RewriteExpressionsWithShaderStorageBlockTraverser(TSymbolTable *symbolTable);
+ void nextIteration();
+ bool foundSSBO() const { return mFoundSSBO; }
+
+ private:
+ bool visitBinary(Visit, TIntermBinary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+
+ TIntermSymbol *insertInitStatementAndReturnTempSymbol(TIntermTyped *node,
+ TIntermSequence *insertions);
+
+ bool mFoundSSBO;
+};
+
+RewriteExpressionsWithShaderStorageBlockTraverser::
+ RewriteExpressionsWithShaderStorageBlockTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, true, false, symbolTable), mFoundSSBO(false)
+{}
+
+TIntermSymbol *
+RewriteExpressionsWithShaderStorageBlockTraverser::insertInitStatementAndReturnTempSymbol(
+ TIntermTyped *node,
+ TIntermSequence *insertions)
+{
+ TIntermDeclaration *variableDeclaration;
+ TVariable *tempVariable =
+ DeclareTempVariable(mSymbolTable, node, EvqTemporary, &variableDeclaration);
+
+ insertions->push_back(variableDeclaration);
+ return CreateTempSymbolNode(tempVariable);
+}
+
+bool RewriteExpressionsWithShaderStorageBlockTraverser::visitBinary(Visit visit,
+ TIntermBinary *node)
+{
+ // Make sure that the expression is caculated from left to right.
+ if (visit != InVisit)
+ {
+ return true;
+ }
+
+ if (mFoundSSBO)
+ {
+ return false;
+ }
+
+ bool rightSSBO = IsInShaderStorageBlock(node->getRight());
+ bool leftSSBO = IsInShaderStorageBlock(node->getLeft());
+ if (!leftSSBO && !rightSSBO)
+ {
+ return true;
+ }
+
+ // case 1: Compound assigment operator
+ // original:
+ // lssbo += expr;
+ // new:
+ // var rvalue = expr;
+ // var temp = lssbo;
+ // temp += rvalue;
+ // lssbo = temp;
+ //
+ // original:
+ // lvalue_no_ssbo += rssbo;
+ // new:
+ // var rvalue = rssbo;
+ // lvalue_no_ssbo += rvalue;
+ if (IsCompoundAssignment(node->getOp()))
+ {
+ mFoundSSBO = true;
+ TIntermSequence insertions;
+ TIntermTyped *rightNode =
+ insertInitStatementAndReturnTempSymbol(node->getRight(), &insertions);
+ if (leftSSBO)
+ {
+ TIntermSymbol *tempSymbol =
+ insertInitStatementAndReturnTempSymbol(node->getLeft()->deepCopy(), &insertions);
+ TIntermBinary *tempCompoundOperate =
+ new TIntermBinary(node->getOp(), tempSymbol->deepCopy(), rightNode->deepCopy());
+ insertions.push_back(tempCompoundOperate);
+ insertStatementsInParentBlock(insertions);
+
+ TIntermBinary *assignTempValueToSSBO =
+ new TIntermBinary(EOpAssign, node->getLeft(), tempSymbol->deepCopy());
+ queueReplacement(assignTempValueToSSBO, OriginalNode::IS_DROPPED);
+ }
+ else
+ {
+ insertStatementsInParentBlock(insertions);
+ TIntermBinary *compoundAssignRValueToLValue =
+ new TIntermBinary(node->getOp(), node->getLeft(), rightNode->deepCopy());
+ queueReplacement(compoundAssignRValueToLValue, OriginalNode::IS_DROPPED);
+ }
+ }
+ // case 2: Readonly binary operator
+ // original:
+ // ssbo0 + ssbo1 + ssbo2;
+ // new:
+ // var temp0 = ssbo0;
+ // var temp1 = ssbo1;
+ // var temp2 = ssbo2;
+ // temp0 + temp1 + temp2;
+ else if (IsReadonlyBinaryOperatorNotInSSBOAccessChain(node->getOp()) && (leftSSBO || rightSSBO))
+ {
+ mFoundSSBO = true;
+ TIntermTyped *rightNode = node->getRight();
+ TIntermTyped *leftNode = node->getLeft();
+ TIntermSequence insertions;
+ if (rightSSBO)
+ {
+ rightNode = insertInitStatementAndReturnTempSymbol(node->getRight(), &insertions);
+ }
+ if (leftSSBO)
+ {
+ leftNode = insertInitStatementAndReturnTempSymbol(node->getLeft(), &insertions);
+ }
+
+ insertStatementsInParentBlock(insertions);
+ TIntermBinary *newExpr =
+ new TIntermBinary(node->getOp(), leftNode->deepCopy(), rightNode->deepCopy());
+ queueReplacement(newExpr, OriginalNode::IS_DROPPED);
+ }
+ return !mFoundSSBO;
+}
+
+// case 3: ssbo as the argument of aggregate type
+// original:
+// foo(ssbo);
+// new:
+// var tempArg = ssbo;
+// foo(tempArg);
+// ssbo = tempArg; (Optional based on whether ssbo is an out|input argument)
+//
+// original:
+// foo(ssbo) * expr;
+// new:
+// var tempArg = ssbo;
+// var tempReturn = foo(tempArg);
+// ssbo = tempArg; (Optional based on whether ssbo is an out|input argument)
+// tempReturn * expr;
+bool RewriteExpressionsWithShaderStorageBlockTraverser::visitAggregate(Visit visit,
+ TIntermAggregate *node)
+{
+ // Make sure that visitAggregate is only executed once for same node.
+ if (visit != PreVisit)
+ {
+ return true;
+ }
+
+ if (mFoundSSBO)
+ {
+ return false;
+ }
+
+ // We still need to process the ssbo as the non-first argument of atomic memory functions.
+ if (BuiltInGroup::IsAtomicMemory(node->getOp()) &&
+ IsInShaderStorageBlock((*node->getSequence())[0]->getAsTyped()))
+ {
+ return true;
+ }
+
+ if (!HasSSBOAsFunctionArgument(node->getSequence()))
+ {
+ return true;
+ }
+
+ mFoundSSBO = true;
+ TIntermSequence insertions;
+ TIntermSequence readBackToSSBOs;
+ TIntermSequence *originalArguments = node->getSequence();
+ for (size_t i = 0; i < node->getChildCount(); ++i)
+ {
+ TIntermTyped *ssboArgument = (*originalArguments)[i]->getAsTyped();
+ if (IsInShaderStorageBlock(ssboArgument))
+ {
+ TIntermSymbol *argumentCopy =
+ insertInitStatementAndReturnTempSymbol(ssboArgument, &insertions);
+ if (node->getFunction() != nullptr)
+ {
+ TQualifier qual = node->getFunction()->getParam(i)->getType().getQualifier();
+ if (qual == EvqParamInOut || qual == EvqParamOut)
+ {
+ TIntermBinary *readBackToSSBO = new TIntermBinary(
+ EOpAssign, ssboArgument->deepCopy(), argumentCopy->deepCopy());
+ readBackToSSBOs.push_back(readBackToSSBO);
+ }
+ }
+ node->replaceChildNode(ssboArgument, argumentCopy);
+ }
+ }
+
+ TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+ if (parentBlock)
+ {
+ // Aggregate node is as a single sentence.
+ insertions.push_back(node);
+ if (!readBackToSSBOs.empty())
+ {
+ insertions.insert(insertions.end(), readBackToSSBOs.begin(), readBackToSSBOs.end());
+ }
+ mMultiReplacements.emplace_back(parentBlock, node, std::move(insertions));
+ }
+ else
+ {
+ // Aggregate node is inside an expression.
+ TIntermSymbol *tempSymbol = insertInitStatementAndReturnTempSymbol(node, &insertions);
+ if (!readBackToSSBOs.empty())
+ {
+ insertions.insert(insertions.end(), readBackToSSBOs.begin(), readBackToSSBOs.end());
+ }
+ insertStatementsInParentBlock(insertions);
+ queueReplacement(tempSymbol->deepCopy(), OriginalNode::IS_DROPPED);
+ }
+
+ return false;
+}
+
+bool RewriteExpressionsWithShaderStorageBlockTraverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ if (mFoundSSBO)
+ {
+ return false;
+ }
+
+ if (!IsInShaderStorageBlock(node->getOperand()))
+ {
+ return true;
+ }
+
+ // .length() is processed in OutputHLSL.
+ if (node->getOp() == EOpArrayLength)
+ {
+ return true;
+ }
+
+ mFoundSSBO = true;
+
+ // case 4: ssbo as the operand of ++/--
+ // original:
+ // ++ssbo * expr;
+ // new:
+ // var temp1 = ssbo;
+ // var temp2 = ++temp1;
+ // ssbo = temp1;
+ // temp2 * expr;
+ if (IsIncrementOrDecrementOperator(node->getOp()))
+ {
+ TIntermSequence insertions;
+ TIntermSymbol *temp1 =
+ insertInitStatementAndReturnTempSymbol(node->getOperand(), &insertions);
+ TIntermUnary *newUnary = new TIntermUnary(node->getOp(), temp1->deepCopy(), nullptr);
+ TIntermSymbol *temp2 = insertInitStatementAndReturnTempSymbol(newUnary, &insertions);
+ TIntermBinary *readBackToSSBO =
+ new TIntermBinary(EOpAssign, node->getOperand()->deepCopy(), temp1->deepCopy());
+ insertions.push_back(readBackToSSBO);
+ insertStatementsInParentBlock(insertions);
+ queueReplacement(temp2->deepCopy(), OriginalNode::IS_DROPPED);
+ }
+ // case 5: ssbo as the operand of readonly unary operator
+ // original:
+ // ~ssbo * expr;
+ // new:
+ // var temp = ssbo;
+ // ~temp * expr;
+ else
+ {
+ TIntermSequence insertions;
+ TIntermSymbol *temp =
+ insertInitStatementAndReturnTempSymbol(node->getOperand(), &insertions);
+ insertStatementsInParentBlock(insertions);
+ node->replaceChildNode(node->getOperand(), temp->deepCopy());
+ }
+ return false;
+}
+
+void RewriteExpressionsWithShaderStorageBlockTraverser::nextIteration()
+{
+ mFoundSSBO = false;
+}
+
+} // anonymous namespace
+
+bool RewriteExpressionsWithShaderStorageBlock(TCompiler *compiler,
+ TIntermNode *root,
+ TSymbolTable *symbolTable)
+{
+ RewriteExpressionsWithShaderStorageBlockTraverser traverser(symbolTable);
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundSSBO())
+ {
+ if (!traverser.updateTree(compiler, root))
+ {
+ return false;
+ }
+ }
+ } while (traverser.foundSSBO());
+
+ return true;
+}
+} // namespace sh