RewriteDfdy.cpp (5192B)
1 // 2 // Copyright 2019 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // Implementation of dFdy viewport transformation. 7 // See header for more info. 8 9 #include "compiler/translator/tree_ops/RewriteDfdy.h" 10 11 #include "common/angleutils.h" 12 #include "compiler/translator/SymbolTable.h" 13 #include "compiler/translator/TranslatorVulkan.h" 14 #include "compiler/translator/tree_util/DriverUniform.h" 15 #include "compiler/translator/tree_util/IntermNode_util.h" 16 #include "compiler/translator/tree_util/IntermTraverse.h" 17 #include "compiler/translator/tree_util/SpecializationConstant.h" 18 19 namespace sh 20 { 21 22 namespace 23 { 24 25 class Traverser : public TIntermTraverser 26 { 27 public: 28 Traverser(TSymbolTable *symbolTable, SpecConst *specConst, const DriverUniform *driverUniforms); 29 30 private: 31 bool visitAggregate(Visit visit, TIntermAggregate *node) override; 32 33 SpecConst *mSpecConst = nullptr; 34 const DriverUniform *mDriverUniforms = nullptr; 35 }; 36 37 Traverser::Traverser(TSymbolTable *symbolTable, 38 SpecConst *specConst, 39 const DriverUniform *driverUniforms) 40 : TIntermTraverser(true, false, false, symbolTable), 41 mSpecConst(specConst), 42 mDriverUniforms(driverUniforms) 43 {} 44 45 bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node) 46 { 47 // Decide if the node represents a call to dFdx() or dFdy() 48 if (node->getOp() != EOpDFdx && node->getOp() != EOpDFdy) 49 { 50 return true; 51 } 52 53 const bool isDFdx = node->getOp() == EOpDFdx; 54 55 // Two transformations are done on dFdx and dFdy: 56 // 57 // - If pre-rotation is applied, dFdx and dFdy may need to swap their axis based on the degree 58 // of rotation. dFdx becomes dFdy if rotation is 90 or 270 degrees. Similarly, dFdy becomes 59 // dFdx. 60 // - The result is potentially negated. This could be due to viewport y-flip or pre-rotation. 61 // 62 // Accordingly, there are two variables controlling the above transformations: 63 // 64 // - Rotation: A vec2 that is either (0, 1) or (1, 0). dFdx and dFdy are replaced with: 65 // 66 // dFdx * Rotation.x + dFdy * Rotation.y 67 // 68 // - Scale: A vec2 with -1 or 1 for either x or y components. The previous result is multiplied 69 // by this. 70 // 71 // Together, the above operations account for the combinations of 4 possible rotations and 72 // y-flip. 73 74 // Get the results of dFdx(operand) and dFdy(operand), and multiply them by the swizzles 75 TIntermTyped *operand = node->getChildNode(0)->getAsTyped(); 76 77 TIntermTyped *dFdx = CreateBuiltInUnaryFunctionCallNode("dFdx", operand, *mSymbolTable, 300); 78 TIntermTyped *dFdy = 79 CreateBuiltInUnaryFunctionCallNode("dFdy", operand->deepCopy(), *mSymbolTable, 300); 80 81 // Get rotation multiplier 82 TIntermTyped *swapXY = mSpecConst->getSwapXY(); 83 if (swapXY == nullptr) 84 { 85 swapXY = mDriverUniforms->getSwapXY(); 86 } 87 88 TIntermTyped *swapXMultiplier = MakeSwapXMultiplier(swapXY); 89 TIntermTyped *swapYMultiplier = MakeSwapYMultiplier(swapXY->deepCopy()); 90 91 // Get flip multiplier 92 TIntermTyped *flipXY = mDriverUniforms->getFlipXY(mSymbolTable, DriverUniformFlip::Fragment); 93 94 // Multiply the flip and rotation multipliers 95 TIntermTyped *xMultiplier = 96 new TIntermBinary(EOpMul, isDFdx ? swapXMultiplier : swapYMultiplier, 97 (new TIntermSwizzle(flipXY->deepCopy(), {0}))->fold(nullptr)); 98 TIntermTyped *yMultiplier = 99 new TIntermBinary(EOpMul, isDFdx ? swapYMultiplier : swapXMultiplier, 100 (new TIntermSwizzle(flipXY->deepCopy(), {1}))->fold(nullptr)); 101 102 const TOperator mulOp = dFdx->getType().isVector() ? EOpVectorTimesScalar : EOpMul; 103 TIntermTyped *rotatedFlippedDfdx = new TIntermBinary(mulOp, dFdx, xMultiplier); 104 TIntermTyped *rotatedFlippedDfdy = new TIntermBinary(mulOp, dFdy, yMultiplier); 105 106 // Sum them together into the result 107 TIntermBinary *rotatedFlippedResult = 108 new TIntermBinary(EOpAdd, rotatedFlippedDfdx, rotatedFlippedDfdy); 109 110 // Replace the old dFdx() or dFdy() node with the new node that contains the corrected value 111 // 112 // Note the following bugs (anglebug.com/7346): 113 // 114 // - Side effects of operand are duplicated with the above 115 // - If the direct child of this node is itself dFdx/y, its queueReplacement will not be 116 // effective as the parent is also replaced. 117 queueReplacement(rotatedFlippedResult, OriginalNode::IS_DROPPED); 118 119 return true; 120 } 121 } // anonymous namespace 122 123 bool RewriteDfdy(TCompiler *compiler, 124 TIntermBlock *root, 125 TSymbolTable *symbolTable, 126 int shaderVersion, 127 SpecConst *specConst, 128 const DriverUniform *driverUniforms) 129 { 130 // dFdx/dFdy is only valid in GLSL 3.0 and later. 131 if (shaderVersion < 300) 132 { 133 return true; 134 } 135 136 Traverser traverser(symbolTable, specConst, driverUniforms); 137 root->traverse(&traverser); 138 return traverser.updateTree(compiler, root); 139 } 140 141 } // namespace sh