// // 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/StaticType.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(&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 = StaticType::GetTemporary(); 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 swizzleOffsetX = {0}; TVector 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