| // |
| // 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. |
| // |
| // Implementation of dFdy viewport transformation. |
| // See header for more info. |
| |
| #include "compiler/translator/tree_ops/RewriteDfdy.h" |
| |
| #include "common/angleutils.h" |
| #include "compiler/translator/SymbolTable.h" |
| #include "compiler/translator/TranslatorVulkan.h" |
| #include "compiler/translator/tree_util/DriverUniform.h" |
| #include "compiler/translator/tree_util/IntermNode_util.h" |
| #include "compiler/translator/tree_util/IntermTraverse.h" |
| #include "compiler/translator/tree_util/SpecializationConstant.h" |
| |
| namespace sh |
| { |
| |
| namespace |
| { |
| |
| class Traverser : public TIntermTraverser |
| { |
| public: |
| ANGLE_NO_DISCARD static bool Apply(TCompiler *compiler, |
| ShCompileOptions compileOptions, |
| TIntermNode *root, |
| const TSymbolTable &symbolTable, |
| SpecConst *specConst, |
| const DriverUniform *driverUniforms); |
| |
| private: |
| Traverser(TSymbolTable *symbolTable, |
| ShCompileOptions compileOptions, |
| SpecConst *specConst, |
| const DriverUniform *driverUniforms); |
| bool visitAggregate(Visit visit, TIntermAggregate *node) override; |
| |
| bool visitAggregateWithRotation(Visit visit, TIntermAggregate *node); |
| bool visitAggregateWithoutRotation(Visit visit, TIntermAggregate *node); |
| |
| SpecConst *mRotationSpecConst = nullptr; |
| const DriverUniform *mDriverUniforms = nullptr; |
| bool mUsePreRotation = false; |
| }; |
| |
| Traverser::Traverser(TSymbolTable *symbolTable, |
| ShCompileOptions compileOptions, |
| SpecConst *specConst, |
| const DriverUniform *driverUniforms) |
| : TIntermTraverser(true, false, false, symbolTable), |
| mRotationSpecConst(specConst), |
| mDriverUniforms(driverUniforms), |
| mUsePreRotation((compileOptions & SH_ADD_PRE_ROTATION) != 0) |
| {} |
| |
| // static |
| bool Traverser::Apply(TCompiler *compiler, |
| ShCompileOptions compileOptions, |
| TIntermNode *root, |
| const TSymbolTable &symbolTable, |
| SpecConst *specConst, |
| const DriverUniform *driverUniforms) |
| { |
| TSymbolTable *pSymbolTable = const_cast<TSymbolTable *>(&symbolTable); |
| Traverser traverser(pSymbolTable, compileOptions, specConst, driverUniforms); |
| root->traverse(&traverser); |
| return traverser.updateTree(compiler, root); |
| } |
| |
| bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node) |
| { |
| if (mUsePreRotation) |
| { |
| return visitAggregateWithRotation(visit, node); |
| } |
| return visitAggregateWithoutRotation(visit, node); |
| } |
| |
| bool Traverser::visitAggregateWithRotation(Visit visit, TIntermAggregate *node) |
| { |
| // Decide if the node represents a call to dFdx() or dFdy() |
| if ((node->getOp() != EOpDFdx) && (node->getOp() != EOpDFdy)) |
| { |
| return true; |
| } |
| |
| // Prior to supporting Android pre-rotation, dFdy() needed to be multiplied by mFlipXY.y: |
| // |
| // correctedDfdy(operand) = dFdy(operand) * mFlipXY.y |
| // |
| // For Android pre-rotation, both dFdx() and dFdy() need to be "rotated" and multiplied by |
| // mFlipXY. "Rotation" means to swap them for 90 and 270 degrees, or to not swap them for 0 |
| // and 180 degrees. This rotation is accomplished with mFragRotation, which is a 2x2 matrix |
| // used for fragment shader rotation. The 1st half (a vec2 that is either (1,0) or (0,1)) is |
| // used for rewriting dFdx() and the 2nd half (either (0,1) or (1,0)) is used for rewriting |
| // dFdy(). Otherwise, the formula for the rewrite is the same: |
| // |
| // result = ((dFdx(operand) * (mFragRotation[half] * mFlipXY).x) + |
| // (dFdy(operand) * (mFragRotation[half] * mFlipXY).y)) |
| // |
| // For dFdx(), half is 0 (the 1st half). For dFdy(), half is 1 (the 2nd half). Depending on |
| // the rotation, mFragRotation[half] will cause either dFdx(operand) or dFdy(operand) to be |
| // zeroed-out. That effectively means that the above code results in the following for 0 and |
| // 180 degrees: |
| // |
| // correctedDfdx(operand) = dFdx(operand) * mFlipXY.x |
| // correctedDfdy(operand) = dFdy(operand) * mFlipXY.y |
| // |
| // and the following for 90 and 270 degrees: |
| // |
| // correctedDfdx(operand) = dFdy(operand) * mFlipXY.y |
| // correctedDfdy(operand) = dFdx(operand) * mFlipXY.x |
| |
| TIntermTyped *multiplierX; |
| TIntermTyped *multiplierY; |
| if (node->getOp() == EOpDFdx) |
| { |
| multiplierX = mRotationSpecConst->getMultiplierXForDFdx(); |
| multiplierY = mRotationSpecConst->getMultiplierYForDFdx(); |
| } |
| else |
| { |
| multiplierX = mRotationSpecConst->getMultiplierXForDFdy(); |
| multiplierY = mRotationSpecConst->getMultiplierYForDFdy(); |
| } |
| |
| if (!multiplierX) |
| { |
| ASSERT(!multiplierY); |
| TIntermTyped *flipXY = mDriverUniforms->getFlipXYRef(); |
| TIntermTyped *fragRotation = mDriverUniforms->getFragRotationMatrixRef(); |
| |
| // Get a vec2 with the correct half of ANGLEUniforms.fragRotation |
| TIntermBinary *halfRotationMat = nullptr; |
| if (node->getOp() == EOpDFdx) |
| { |
| halfRotationMat = new TIntermBinary(EOpIndexDirect, fragRotation, CreateIndexNode(0)); |
| } |
| else |
| { |
| halfRotationMat = new TIntermBinary(EOpIndexDirect, fragRotation, CreateIndexNode(1)); |
| } |
| |
| // Multiply halfRotationMat by ANGLEUniforms.flipXY and store in a temporary variable |
| TIntermBinary *rotatedFlipXY = new TIntermBinary(EOpMul, flipXY, halfRotationMat); |
| const TType *vec2Type = &rotatedFlipXY->getType(); |
| TIntermSymbol *tmpRotFlipXY = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec2Type)); |
| TIntermSequence tmpDecl; |
| tmpDecl.push_back(CreateTempInitDeclarationNode(&tmpRotFlipXY->variable(), rotatedFlipXY)); |
| insertStatementsInParentBlock(tmpDecl); |
| |
| // Get the .x and .y swizzles to use as multipliers |
| TVector<int> swizzleOffsetX = {0}; |
| TVector<int> swizzleOffsetY = {1}; |
| multiplierX = new TIntermSwizzle(tmpRotFlipXY, swizzleOffsetX); |
| multiplierY = new TIntermSwizzle(tmpRotFlipXY->deepCopy(), swizzleOffsetY); |
| } |
| |
| // Get the results of dFdx(operand) and dFdy(operand), and multiply them by the swizzles |
| TIntermTyped *operand = node->getChildNode(0)->getAsTyped(); |
| |
| TIntermTyped *dFdx = |
| CreateBuiltInUnaryFunctionCallNode("dFdx", operand->deepCopy(), *mSymbolTable, 300); |
| TIntermTyped *dFdy = |
| CreateBuiltInUnaryFunctionCallNode("dFdy", operand->deepCopy(), *mSymbolTable, 300); |
| |
| size_t objectSize = node->getType().getObjectSize(); |
| TOperator multiplyOp = (objectSize == 1) ? EOpMul : EOpVectorTimesScalar; |
| TIntermBinary *rotatedFlippedDfdx = new TIntermBinary(multiplyOp, dFdx, multiplierX); |
| TIntermBinary *rotatedFlippedDfdy = new TIntermBinary(multiplyOp, dFdy, multiplierY); |
| |
| // Sum them together into the result: |
| TIntermBinary *correctedResult = |
| new TIntermBinary(EOpAdd, rotatedFlippedDfdx, rotatedFlippedDfdy); |
| |
| // Replace the old dFdx() or dFdy() node with the new node that contains the corrected value |
| queueReplacement(correctedResult, OriginalNode::IS_DROPPED); |
| |
| return true; |
| } |
| |
| bool Traverser::visitAggregateWithoutRotation(Visit visit, TIntermAggregate *node) |
| { |
| // Decide if the node represents a call to dFdy() |
| if (node->getOp() != EOpDFdy) |
| { |
| return true; |
| } |
| |
| // Copy the dFdy node so we can replace it with the corrected value |
| TIntermAggregate *newDfdy = node->deepCopy()->getAsAggregate(); |
| |
| size_t objectSize = node->getType().getObjectSize(); |
| TOperator multiplyOp = (objectSize == 1) ? EOpMul : EOpVectorTimesScalar; |
| |
| TIntermTyped *flipY = mRotationSpecConst->getFlipY(); |
| if (!flipY) |
| { |
| TIntermTyped *flipXY = mDriverUniforms->getFlipXYRef(); |
| flipY = new TIntermBinary(EOpIndexDirect, flipXY, CreateIndexNode(1)); |
| } |
| |
| // Correct dFdy()'s value: |
| // (dFdy() * mFlipXY.y) |
| TIntermBinary *correctedDfdy = new TIntermBinary(multiplyOp, newDfdy, flipY); |
| |
| // Replace the old dFdy node with the new node that contains the corrected value |
| queueReplacement(correctedDfdy, OriginalNode::IS_DROPPED); |
| |
| return true; |
| } |
| } // anonymous namespace |
| |
| bool RewriteDfdy(TCompiler *compiler, |
| ShCompileOptions compileOptions, |
| TIntermNode *root, |
| const TSymbolTable &symbolTable, |
| int shaderVersion, |
| SpecConst *specConst, |
| const DriverUniform *driverUniforms) |
| { |
| // dFdy is only valid in GLSL 3.0 and later. |
| if (shaderVersion < 300) |
| return true; |
| |
| return Traverser::Apply(compiler, compileOptions, root, symbolTable, specConst, driverUniforms); |
| } |
| |
| } // namespace sh |