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