summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceClipCullDistanceVariable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceClipCullDistanceVariable.cpp')
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceClipCullDistanceVariable.cpp591
1 files changed, 591 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceClipCullDistanceVariable.cpp b/gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceClipCullDistanceVariable.cpp
new file mode 100644
index 0000000000..a8379f48f1
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceClipCullDistanceVariable.cpp
@@ -0,0 +1,591 @@
+//
+// 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.
+//
+// ReplaceClipCullDistanceVariable.cpp: Find any references to gl_ClipDistance or gl_CullDistance
+// and replace it with ANGLEClipDistance or ANGLECullDistance.
+//
+
+#include "compiler/translator/tree_util/ReplaceClipCullDistanceVariable.h"
+
+#include "common/bitset_utils.h"
+#include "common/debug.h"
+#include "common/utilities.h"
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/tree_util/BuiltIn.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+#include "compiler/translator/tree_util/ReplaceVariable.h"
+#include "compiler/translator/tree_util/RunAtTheBeginningOfShader.h"
+#include "compiler/translator/tree_util/RunAtTheEndOfShader.h"
+
+namespace sh
+{
+namespace
+{
+
+using ClipCullDistanceIdxSet = angle::BitSet<32>;
+
+typedef TIntermNode *AssignFunc(const unsigned int index,
+ TIntermSymbol *left,
+ TIntermSymbol *right,
+ const TIntermTyped *enableFlags);
+
+template <typename Variable>
+const Variable *FindVariable(const std::vector<Variable> &mVars, const ImmutableString &name)
+{
+ for (const Variable &var : mVars)
+ {
+ if (name == var.instanceName)
+ {
+ return &var;
+ }
+ }
+
+ return nullptr;
+}
+
+// Traverse the tree and collect the redeclaration and all constant index references of
+// gl_ClipDistance/gl_CullDistance
+class GLClipCullDistanceReferenceTraverser : public TIntermTraverser
+{
+ public:
+ GLClipCullDistanceReferenceTraverser(const TIntermSymbol **redeclaredSymOut,
+ bool *nonConstIdxUsedOut,
+ unsigned int *maxConstIdxOut,
+ ClipCullDistanceIdxSet *constIndicesOut,
+ TQualifier targetQualifier)
+ : TIntermTraverser(true, false, false),
+ mRedeclaredSym(redeclaredSymOut),
+ mUseNonConstClipCullDistanceIndex(nonConstIdxUsedOut),
+ mMaxConstClipCullDistanceIndex(maxConstIdxOut),
+ mConstClipCullDistanceIndices(constIndicesOut),
+ mTargetQualifier(targetQualifier)
+ {
+ *mRedeclaredSym = nullptr;
+ *mUseNonConstClipCullDistanceIndex = false;
+ *mMaxConstClipCullDistanceIndex = 0;
+ mConstClipCullDistanceIndices->reset();
+ }
+
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
+ {
+ // If gl_ClipDistance/gl_CullDistance is redeclared, we need to collect its information
+ const TIntermSequence &sequence = *(node->getSequence());
+
+ if (sequence.size() != 1)
+ {
+ return true;
+ }
+
+ TIntermSymbol *variable = sequence.front()->getAsSymbolNode();
+ if (variable == nullptr || variable->getType().getQualifier() != mTargetQualifier)
+ {
+ return true;
+ }
+
+ *mRedeclaredSym = variable->getAsSymbolNode();
+
+ return true;
+ }
+
+ bool visitBinary(Visit visit, TIntermBinary *node) override
+ {
+ TOperator op = node->getOp();
+ if (op != EOpIndexDirect && op != EOpIndexIndirect)
+ {
+ return true;
+ }
+
+ // gl_ClipDistance / gl_CullDistance
+ TIntermTyped *left = node->getLeft()->getAsTyped();
+ if (!left)
+ {
+ return true;
+ }
+
+ ASSERT(op == EOpIndexDirect || op == EOpIndexIndirect);
+
+ TIntermSymbol *clipCullDistance = left->getAsSymbolNode();
+ if (!clipCullDistance)
+ {
+ return true;
+ }
+ if (clipCullDistance->getType().getQualifier() != mTargetQualifier)
+ {
+ return true;
+ }
+
+ const TConstantUnion *constIdx = node->getRight()->getConstantValue();
+ if (!constIdx)
+ {
+ *mUseNonConstClipCullDistanceIndex = true;
+ }
+ else
+ {
+ unsigned int idx = 0;
+ switch (constIdx->getType())
+ {
+ case EbtInt:
+ idx = constIdx->getIConst();
+ break;
+ case EbtUInt:
+ idx = constIdx->getUConst();
+ break;
+ case EbtFloat:
+ idx = static_cast<unsigned int>(constIdx->getFConst());
+ break;
+ case EbtBool:
+ idx = constIdx->getBConst() ? 1 : 0;
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ ASSERT(idx < mConstClipCullDistanceIndices->size());
+ mConstClipCullDistanceIndices->set(idx);
+
+ *mMaxConstClipCullDistanceIndex = std::max(*mMaxConstClipCullDistanceIndex, idx);
+ }
+
+ return true;
+ }
+
+ private:
+ const TIntermSymbol **mRedeclaredSym;
+ // Flag indicating whether there is at least one reference of gl_ClipDistance with non-constant
+ // index
+ bool *mUseNonConstClipCullDistanceIndex;
+ // Max constant index that is used to reference gl_ClipDistance
+ unsigned int *mMaxConstClipCullDistanceIndex;
+ // List of constant index reference of gl_ClipDistance
+ ClipCullDistanceIdxSet *mConstClipCullDistanceIndices;
+ // Qualifier for gl_ClipDistance/gl_CullDistance
+ const TQualifier mTargetQualifier;
+};
+
+// Replace all symbolic occurrences of given variables except one symbol.
+class ReplaceVariableExceptOneTraverser : public TIntermTraverser
+{
+ public:
+ ReplaceVariableExceptOneTraverser(const TVariable *toBeReplaced,
+ const TIntermTyped *replacement,
+ const TIntermSymbol *exception)
+ : TIntermTraverser(true, false, false),
+ mToBeReplaced(toBeReplaced),
+ mException(exception),
+ mReplacement(replacement)
+ {}
+
+ void visitSymbol(TIntermSymbol *node) override
+ {
+ if (&node->variable() == mToBeReplaced && node != mException)
+ {
+ queueReplacement(mReplacement->deepCopy(), OriginalNode::IS_DROPPED);
+ }
+ }
+
+ private:
+ const TVariable *const mToBeReplaced;
+ const TIntermSymbol *const mException;
+ const TIntermTyped *const mReplacement;
+};
+
+TIntermNode *simpleAssignFunc(const unsigned int index,
+ TIntermSymbol *leftSymbol,
+ TIntermSymbol *rightSymbol,
+ const TIntermTyped * /*enableFlags*/)
+{
+ // leftSymbol[index] = rightSymbol[index]
+ // E.g., ANGLEClipDistance[index] = gl_ClipDistance[index]
+ TIntermBinary *left =
+ new TIntermBinary(EOpIndexDirect, leftSymbol->deepCopy(), CreateIndexNode(index));
+ TIntermBinary *right =
+ new TIntermBinary(EOpIndexDirect, rightSymbol->deepCopy(), CreateIndexNode(index));
+
+ return new TIntermBinary(EOpAssign, left, right);
+}
+
+// This is only used for gl_ClipDistance
+TIntermNode *assignFuncWithEnableFlags(const unsigned int index,
+ TIntermSymbol *leftSymbol,
+ TIntermSymbol *rightSymbol,
+ const TIntermTyped *enableFlags)
+{
+ // if (ANGLEUniforms.clipDistancesEnabled & (0x1 << index))
+ // gl_ClipDistance[index] = ANGLEClipDistance[index];
+ // else
+ // gl_ClipDistance[index] = 0;
+ TIntermConstantUnion *bitMask = CreateUIntNode(0x1 << index);
+ TIntermBinary *bitwiseAnd = new TIntermBinary(EOpBitwiseAnd, enableFlags->deepCopy(), bitMask);
+ TIntermBinary *nonZero = new TIntermBinary(EOpNotEqual, bitwiseAnd, CreateUIntNode(0));
+
+ TIntermBinary *left =
+ new TIntermBinary(EOpIndexDirect, leftSymbol->deepCopy(), CreateIndexNode(index));
+ TIntermBinary *right =
+ new TIntermBinary(EOpIndexDirect, rightSymbol->deepCopy(), CreateIndexNode(index));
+ TIntermBinary *assignment = new TIntermBinary(EOpAssign, left, right);
+ TIntermBlock *trueBlock = new TIntermBlock();
+ trueBlock->appendStatement(assignment);
+
+ TIntermBinary *zeroAssignment =
+ new TIntermBinary(EOpAssign, left->deepCopy(), CreateFloatNode(0, EbpMedium));
+ TIntermBlock *falseBlock = new TIntermBlock();
+ falseBlock->appendStatement(zeroAssignment);
+
+ return new TIntermIfElse(nonZero, trueBlock, falseBlock);
+}
+
+class ReplaceClipCullDistanceAssignments : angle::NonCopyable
+{
+ public:
+ ReplaceClipCullDistanceAssignments(TCompiler *compiler,
+ TIntermBlock *root,
+ TSymbolTable *symbolTable,
+ const TVariable *glClipCullDistanceVar,
+ const TIntermSymbol *redeclaredGlClipDistance,
+ const ImmutableString &angleVarName)
+ : mCompiler(compiler),
+ mRoot(root),
+ mSymbolTable(symbolTable),
+ mGlVar(glClipCullDistanceVar),
+ mRedeclaredGLVar(redeclaredGlClipDistance),
+ mANGLEVarName(angleVarName)
+ {
+ mEnabledDistances = 0;
+ }
+
+ unsigned int getEnabledClipCullDistance(const bool useNonConstIndex,
+ const unsigned int maxConstIndex);
+ const TVariable *declareANGLEVariable(const TVariable *originalVariable);
+ bool assignOriginalValueToANGLEVariable(const GLenum shaderType);
+ bool assignANGLEValueToOriginalVariable(const GLenum shaderType,
+ const bool isRedeclared,
+ const TIntermTyped *enableFlags,
+ const ClipCullDistanceIdxSet *constIndices);
+
+ private:
+ bool assignOriginalValueToANGLEVariableImpl();
+ bool assignANGLEValueToOriginalVariableImpl(const bool isRedeclared,
+ const TIntermTyped *enableFlags,
+ const ClipCullDistanceIdxSet *constIndices,
+ AssignFunc assignFunc);
+
+ // Common variables for replacing gl_Clip/CullDistances with ANGLEClip/CullDistances
+ TCompiler *mCompiler;
+ TIntermBlock *mRoot;
+ TSymbolTable *mSymbolTable;
+
+ const TVariable *mGlVar;
+ const TIntermSymbol *mRedeclaredGLVar;
+ const ImmutableString mANGLEVarName;
+
+ unsigned int mEnabledDistances;
+ const TVariable *mANGLEVar;
+};
+
+unsigned int ReplaceClipCullDistanceAssignments::getEnabledClipCullDistance(
+ const bool useNonConstIndex,
+ const unsigned int maxConstIndex)
+{
+ if (mRedeclaredGLVar)
+ {
+ // If array is redeclared by user, use that redeclared size.
+ mEnabledDistances = mRedeclaredGLVar->getType().getOutermostArraySize();
+ }
+ else if (!useNonConstIndex)
+ {
+ ASSERT(maxConstIndex < mGlVar->getType().getOutermostArraySize());
+ // Only use constant index, then use max array index used.
+ mEnabledDistances = maxConstIndex + 1;
+ }
+
+ return mEnabledDistances;
+}
+
+const TVariable *ReplaceClipCullDistanceAssignments::declareANGLEVariable(
+ const TVariable *originalVariable)
+{
+ ASSERT(mEnabledDistances > 0);
+
+ TType *clipCullDistanceType = new TType(originalVariable->getType());
+ clipCullDistanceType->setQualifier(EvqGlobal);
+ clipCullDistanceType->toArrayBaseType();
+ clipCullDistanceType->makeArray(mEnabledDistances);
+
+ mANGLEVar =
+ new TVariable(mSymbolTable, mANGLEVarName, clipCullDistanceType, SymbolType::AngleInternal);
+
+ TIntermSymbol *clipCullDistanceDeclarator = new TIntermSymbol(mANGLEVar);
+ TIntermDeclaration *clipCullDistanceDecl = new TIntermDeclaration;
+ clipCullDistanceDecl->appendDeclarator(clipCullDistanceDeclarator);
+
+ // Must declare ANGLEClipdistance/ANGLECullDistance before any function, since
+ // gl_ClipDistance/gl_CullDistance might be accessed within a function declared before main.
+ mRoot->insertStatement(0, clipCullDistanceDecl);
+
+ return mANGLEVar;
+}
+
+bool ReplaceClipCullDistanceAssignments::assignOriginalValueToANGLEVariableImpl()
+{
+ ASSERT(mEnabledDistances > 0);
+
+ TIntermBlock *readBlock = new TIntermBlock;
+ TIntermSymbol *glClipCullDistanceSymbol = new TIntermSymbol(mGlVar);
+ TIntermSymbol *clipCullDistanceSymbol = new TIntermSymbol(mANGLEVar);
+
+ for (unsigned int i = 0; i < mEnabledDistances; i++)
+ {
+ readBlock->appendStatement(
+ simpleAssignFunc(i, clipCullDistanceSymbol, glClipCullDistanceSymbol, nullptr));
+ }
+
+ return RunAtTheBeginningOfShader(mCompiler, mRoot, readBlock);
+}
+
+bool ReplaceClipCullDistanceAssignments::assignANGLEValueToOriginalVariableImpl(
+ const bool isRedeclared,
+ const TIntermTyped *enableFlags,
+ const ClipCullDistanceIdxSet *constIndices,
+ AssignFunc assignFunc)
+{
+ ASSERT(mEnabledDistances > 0);
+
+ TIntermBlock *assignBlock = new TIntermBlock;
+ TIntermSymbol *glClipCullDistanceSymbol = new TIntermSymbol(mGlVar);
+ TIntermSymbol *clipCullDistanceSymbol = new TIntermSymbol(mANGLEVar);
+
+ // The array size is decided by either redeclaring the variable or accessing the variable with a
+ // integral constant index. And this size is the count of the enabled value. So, if the index
+ // which is greater than the array size, is used to access the variable, this access will be
+ // ignored.
+ if (isRedeclared || !constIndices)
+ {
+ for (unsigned int i = 0; i < mEnabledDistances; ++i)
+ {
+ assignBlock->appendStatement(
+ assignFunc(i, glClipCullDistanceSymbol, clipCullDistanceSymbol, enableFlags));
+ }
+ }
+ else
+ {
+ // Assign ANGLEClip/CullDistance[i]'s value to gl_Clip/CullDistance[i] if i is in the
+ // constant indices list. Those elements whose index is not in the constant index list will
+ // be zeroise for initialization.
+ for (unsigned int i = 0; i < mEnabledDistances; ++i)
+ {
+ if (constIndices->test(i))
+ {
+ assignBlock->appendStatement(
+ assignFunc(i, glClipCullDistanceSymbol, clipCullDistanceSymbol, enableFlags));
+ }
+ else
+ {
+ // gl_Clip/CullDistance[i] = 0;
+ TIntermBinary *left = new TIntermBinary(
+ EOpIndexDirect, glClipCullDistanceSymbol->deepCopy(), CreateIndexNode(i));
+ TIntermBinary *zeroAssignment =
+ new TIntermBinary(EOpAssign, left, CreateFloatNode(0, EbpMedium));
+ assignBlock->appendStatement(zeroAssignment);
+ }
+ }
+ }
+
+ return RunAtTheEndOfShader(mCompiler, mRoot, assignBlock, mSymbolTable);
+}
+
+[[nodiscard]] bool ReplaceClipCullDistanceAssignments::assignOriginalValueToANGLEVariable(
+ const GLenum shaderType)
+{
+ switch (shaderType)
+ {
+ case GL_VERTEX_SHADER:
+ // Vertex shader can use gl_Clip/CullDistance as a output only
+ break;
+ case GL_FRAGMENT_SHADER:
+ {
+ // These shader types can use gl_Clip/CullDistance as input
+ if (!assignOriginalValueToANGLEVariableImpl())
+ {
+ return false;
+ }
+ break;
+ }
+ default:
+ {
+ UNREACHABLE();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+[[nodiscard]] bool ReplaceClipCullDistanceAssignments::assignANGLEValueToOriginalVariable(
+ const GLenum shaderType,
+ const bool isRedeclared,
+ const TIntermTyped *enableFlags,
+ const ClipCullDistanceIdxSet *constIndices)
+{
+ switch (shaderType)
+ {
+ case GL_VERTEX_SHADER:
+ {
+ // Vertex shader can use gl_Clip/CullDistance as output.
+ // If the enabled gl_Clip/CullDistances are not initialized, results are undefined.
+ // EXT_clip_cull_distance spec :
+ // The shader must also set all values in gl_ClipDistance that have been enabled via the
+ // OpenGL ES API, or results are undefined. Values written into gl_ClipDistance for
+ // planes that are not enabled have no effect.
+ // ...
+ // Shaders writing gl_CullDistance must write all enabled distances, or culling results
+ // are undefined.
+ if (!assignANGLEValueToOriginalVariableImpl(
+ isRedeclared, enableFlags, constIndices,
+ enableFlags ? assignFuncWithEnableFlags : simpleAssignFunc))
+ {
+ return false;
+ }
+ break;
+ }
+ case GL_FRAGMENT_SHADER:
+ // Fragment shader can use gl_Clip/CullDistance as input only
+ break;
+ default:
+ {
+ UNREACHABLE();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Common code to transform gl_ClipDistance and gl_CullDistance. Comments reference
+// gl_ClipDistance, but are also applicable to gl_CullDistance.
+[[nodiscard]] bool ReplaceClipCullDistanceAssignmentsImpl(
+ TCompiler *compiler,
+ TIntermBlock *root,
+ TSymbolTable *symbolTable,
+ const GLenum shaderType,
+ const TIntermTyped *clipDistanceEnableFlags,
+ const char *builtInName,
+ const char *replacementName,
+ TQualifier builtInQualifier)
+{
+ // Collect all constant index references of gl_ClipDistance
+ ImmutableString name(builtInName);
+ ClipCullDistanceIdxSet constIndices;
+ bool useNonConstIndex = false;
+ const TIntermSymbol *redeclaredBuiltIn = nullptr;
+ unsigned int maxConstIndex = 0;
+ GLClipCullDistanceReferenceTraverser indexTraverser(
+ &redeclaredBuiltIn, &useNonConstIndex, &maxConstIndex, &constIndices, builtInQualifier);
+ root->traverse(&indexTraverser);
+ if (!useNonConstIndex && constIndices.none())
+ {
+ // No references of gl_ClipDistance
+ return true;
+ }
+
+ // Retrieve gl_ClipDistance variable reference
+ // Search user redeclared gl_ClipDistance first
+ const TVariable *builtInVar = nullptr;
+ if (redeclaredBuiltIn)
+ {
+ builtInVar = &redeclaredBuiltIn->variable();
+ }
+ else
+ {
+ // User defined not found, find in built-in table
+ builtInVar = static_cast<const TVariable *>(
+ symbolTable->findBuiltIn(name, compiler->getShaderVersion()));
+ }
+ if (!builtInVar)
+ {
+ return false;
+ }
+
+ ReplaceClipCullDistanceAssignments replacementUtils(compiler, root, symbolTable, builtInVar,
+ redeclaredBuiltIn,
+ ImmutableString(replacementName));
+
+ // Declare a global variable substituting gl_ClipDistance
+ unsigned int enabledClipDistances =
+ replacementUtils.getEnabledClipCullDistance(useNonConstIndex, maxConstIndex);
+ if (!enabledClipDistances)
+ {
+ // Spec :
+ // The gl_ClipDistance array is predeclared as unsized and must be explicitly sized by the
+ // shader either redeclaring it with a size or implicitly sized by indexing it only with
+ // integral constant expressions.
+ return false;
+ }
+
+ const TVariable *replacementVar = replacementUtils.declareANGLEVariable(builtInVar);
+
+ // Replace gl_ClipDistance reference with ANGLEClipDistance, except the declaration
+ ReplaceVariableExceptOneTraverser replaceTraverser(builtInVar,
+ new TIntermSymbol(replacementVar),
+ /** exception */ redeclaredBuiltIn);
+ root->traverse(&replaceTraverser);
+ if (!replaceTraverser.updateTree(compiler, root))
+ {
+ return false;
+ }
+
+ // Read gl_ClipDistance to ANGLEClipDistance for getting a original data
+ if (!replacementUtils.assignOriginalValueToANGLEVariable(shaderType))
+ {
+ return false;
+ }
+
+ // Reassign ANGLEClipDistance to gl_ClipDistance but ignore those that are disabled
+ const bool isRedeclared = redeclaredBuiltIn != nullptr;
+ if (!replacementUtils.assignANGLEValueToOriginalVariable(
+ shaderType, isRedeclared, clipDistanceEnableFlags, &constIndices))
+ {
+ return false;
+ }
+
+ // If not redeclared, replace the built-in with one that is appropriately sized
+ if (!isRedeclared)
+ {
+ TType *resizedType = new TType(builtInVar->getType());
+ resizedType->setArraySize(0, enabledClipDistances);
+
+ TVariable *resizedVar = new TVariable(symbolTable, name, resizedType, SymbolType::BuiltIn);
+
+ return ReplaceVariable(compiler, root, builtInVar, resizedVar);
+ }
+
+ return true;
+}
+
+} // anonymous namespace
+
+[[nodiscard]] bool ReplaceClipDistanceAssignments(TCompiler *compiler,
+ TIntermBlock *root,
+ TSymbolTable *symbolTable,
+ const GLenum shaderType,
+ const TIntermTyped *clipDistanceEnableFlags)
+{
+ return ReplaceClipCullDistanceAssignmentsImpl(compiler, root, symbolTable, shaderType,
+ clipDistanceEnableFlags, "gl_ClipDistance",
+ "ANGLEClipDistance", EvqClipDistance);
+}
+
+[[nodiscard]] bool ReplaceCullDistanceAssignments(TCompiler *compiler,
+ TIntermBlock *root,
+ TSymbolTable *symbolTable,
+ const GLenum shaderType)
+{
+ return ReplaceClipCullDistanceAssignmentsImpl(compiler, root, symbolTable, shaderType, nullptr,
+ "gl_CullDistance", "ANGLECullDistance",
+ EvqCullDistance);
+}
+
+} // namespace sh