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