// // 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: Traverser(TSymbolTable *symbolTable, SpecConst *specConst, const DriverUniform *driverUniforms); private: bool visitAggregate(Visit visit, TIntermAggregate *node) override; SpecConst *mSpecConst = nullptr; const DriverUniform *mDriverUniforms = nullptr; }; Traverser::Traverser(TSymbolTable *symbolTable, SpecConst *specConst, const DriverUniform *driverUniforms) : TIntermTraverser(true, false, false, symbolTable), mSpecConst(specConst), mDriverUniforms(driverUniforms) {} bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node) { // Decide if the node represents a call to dFdx() or dFdy() if (node->getOp() != EOpDFdx && node->getOp() != EOpDFdy) { return true; } const bool isDFdx = node->getOp() == EOpDFdx; // Two transformations are done on dFdx and dFdy: // // - If pre-rotation is applied, dFdx and dFdy may need to swap their axis based on the degree // of rotation. dFdx becomes dFdy if rotation is 90 or 270 degrees. Similarly, dFdy becomes // dFdx. // - The result is potentially negated. This could be due to viewport y-flip or pre-rotation. // // Accordingly, there are two variables controlling the above transformations: // // - Rotation: A vec2 that is either (0, 1) or (1, 0). dFdx and dFdy are replaced with: // // dFdx * Rotation.x + dFdy * Rotation.y // // - Scale: A vec2 with -1 or 1 for either x or y components. The previous result is multiplied // by this. // // Together, the above operations account for the combinations of 4 possible rotations and // y-flip. // 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, *mSymbolTable, 300); TIntermTyped *dFdy = CreateBuiltInUnaryFunctionCallNode("dFdy", operand->deepCopy(), *mSymbolTable, 300); // Get rotation multiplier TIntermTyped *swapXY = mSpecConst->getSwapXY(); if (swapXY == nullptr) { swapXY = mDriverUniforms->getSwapXY(); } TIntermTyped *swapXMultiplier = MakeSwapXMultiplier(swapXY); TIntermTyped *swapYMultiplier = MakeSwapYMultiplier(swapXY->deepCopy()); // Get flip multiplier TIntermTyped *flipXY = mDriverUniforms->getFlipXY(mSymbolTable, DriverUniformFlip::Fragment); // Multiply the flip and rotation multipliers TIntermTyped *xMultiplier = new TIntermBinary(EOpMul, isDFdx ? swapXMultiplier : swapYMultiplier, (new TIntermSwizzle(flipXY->deepCopy(), {0}))->fold(nullptr)); TIntermTyped *yMultiplier = new TIntermBinary(EOpMul, isDFdx ? swapYMultiplier : swapXMultiplier, (new TIntermSwizzle(flipXY->deepCopy(), {1}))->fold(nullptr)); const TOperator mulOp = dFdx->getType().isVector() ? EOpVectorTimesScalar : EOpMul; TIntermTyped *rotatedFlippedDfdx = new TIntermBinary(mulOp, dFdx, xMultiplier); TIntermTyped *rotatedFlippedDfdy = new TIntermBinary(mulOp, dFdy, yMultiplier); // Sum them together into the result TIntermBinary *rotatedFlippedResult = new TIntermBinary(EOpAdd, rotatedFlippedDfdx, rotatedFlippedDfdy); // Replace the old dFdx() or dFdy() node with the new node that contains the corrected value // // Note the following bugs (anglebug.com/7346): // // - Side effects of operand are duplicated with the above // - If the direct child of this node is itself dFdx/y, its queueReplacement will not be // effective as the parent is also replaced. queueReplacement(rotatedFlippedResult, OriginalNode::IS_DROPPED); return true; } } // anonymous namespace bool RewriteDfdy(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable, int shaderVersion, SpecConst *specConst, const DriverUniform *driverUniforms) { // dFdx/dFdy is only valid in GLSL 3.0 and later. if (shaderVersion < 300) { return true; } Traverser traverser(symbolTable, specConst, driverUniforms); root->traverse(&traverser); return traverser.updateTree(compiler, root); } } // namespace sh