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/StaticType.h"
13 #include "compiler/translator/SymbolTable.h"
14 #include "compiler/translator/TranslatorVulkan.h"
15 #include "compiler/translator/tree_util/DriverUniform.h"
16 #include "compiler/translator/tree_util/IntermNode_util.h"
17 #include "compiler/translator/tree_util/IntermTraverse.h"
18 #include "compiler/translator/tree_util/SpecializationConstant.h"
19
20 namespace sh
21 {
22
23 namespace
24 {
25
26 class Traverser : public TIntermTraverser
27 {
28 public:
29 ANGLE_NO_DISCARD static bool Apply(TCompiler *compiler,
30 ShCompileOptions compileOptions,
31 TIntermNode *root,
32 const TSymbolTable &symbolTable,
33 SpecConst *specConst,
34 const DriverUniform *driverUniforms);
35
36 private:
37 Traverser(TSymbolTable *symbolTable,
38 ShCompileOptions compileOptions,
39 SpecConst *specConst,
40 const DriverUniform *driverUniforms);
41 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
42
43 bool visitAggregateWithRotation(Visit visit, TIntermAggregate *node);
44 bool visitAggregateWithoutRotation(Visit visit, TIntermAggregate *node);
45
46 SpecConst *mRotationSpecConst = nullptr;
47 const DriverUniform *mDriverUniforms = nullptr;
48 bool mUsePreRotation = false;
49 };
50
Traverser(TSymbolTable * symbolTable,ShCompileOptions compileOptions,SpecConst * specConst,const DriverUniform * driverUniforms)51 Traverser::Traverser(TSymbolTable *symbolTable,
52 ShCompileOptions compileOptions,
53 SpecConst *specConst,
54 const DriverUniform *driverUniforms)
55 : TIntermTraverser(true, false, false, symbolTable),
56 mRotationSpecConst(specConst),
57 mDriverUniforms(driverUniforms),
58 mUsePreRotation((compileOptions & SH_ADD_PRE_ROTATION) != 0)
59 {}
60
61 // static
Apply(TCompiler * compiler,ShCompileOptions compileOptions,TIntermNode * root,const TSymbolTable & symbolTable,SpecConst * specConst,const DriverUniform * driverUniforms)62 bool Traverser::Apply(TCompiler *compiler,
63 ShCompileOptions compileOptions,
64 TIntermNode *root,
65 const TSymbolTable &symbolTable,
66 SpecConst *specConst,
67 const DriverUniform *driverUniforms)
68 {
69 TSymbolTable *pSymbolTable = const_cast<TSymbolTable *>(&symbolTable);
70 Traverser traverser(pSymbolTable, compileOptions, specConst, driverUniforms);
71 root->traverse(&traverser);
72 return traverser.updateTree(compiler, root);
73 }
74
visitAggregate(Visit visit,TIntermAggregate * node)75 bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
76 {
77 if (mUsePreRotation)
78 {
79 return visitAggregateWithRotation(visit, node);
80 }
81 return visitAggregateWithoutRotation(visit, node);
82 }
83
visitAggregateWithRotation(Visit visit,TIntermAggregate * node)84 bool Traverser::visitAggregateWithRotation(Visit visit, TIntermAggregate *node)
85 {
86 // Decide if the node represents a call to dFdx() or dFdy()
87 if ((node->getOp() != EOpDFdx) && (node->getOp() != EOpDFdy))
88 {
89 return true;
90 }
91
92 // Prior to supporting Android pre-rotation, dFdy() needed to be multiplied by mFlipXY.y:
93 //
94 // correctedDfdy(operand) = dFdy(operand) * mFlipXY.y
95 //
96 // For Android pre-rotation, both dFdx() and dFdy() need to be "rotated" and multiplied by
97 // mFlipXY. "Rotation" means to swap them for 90 and 270 degrees, or to not swap them for 0
98 // and 180 degrees. This rotation is accomplished with mFragRotation, which is a 2x2 matrix
99 // used for fragment shader rotation. The 1st half (a vec2 that is either (1,0) or (0,1)) is
100 // used for rewriting dFdx() and the 2nd half (either (0,1) or (1,0)) is used for rewriting
101 // dFdy(). Otherwise, the formula for the rewrite is the same:
102 //
103 // result = ((dFdx(operand) * (mFragRotation[half] * mFlipXY).x) +
104 // (dFdy(operand) * (mFragRotation[half] * mFlipXY).y))
105 //
106 // For dFdx(), half is 0 (the 1st half). For dFdy(), half is 1 (the 2nd half). Depending on
107 // the rotation, mFragRotation[half] will cause either dFdx(operand) or dFdy(operand) to be
108 // zeroed-out. That effectively means that the above code results in the following for 0 and
109 // 180 degrees:
110 //
111 // correctedDfdx(operand) = dFdx(operand) * mFlipXY.x
112 // correctedDfdy(operand) = dFdy(operand) * mFlipXY.y
113 //
114 // and the following for 90 and 270 degrees:
115 //
116 // correctedDfdx(operand) = dFdy(operand) * mFlipXY.y
117 // correctedDfdy(operand) = dFdx(operand) * mFlipXY.x
118
119 TIntermTyped *multiplierX;
120 TIntermTyped *multiplierY;
121 if (node->getOp() == EOpDFdx)
122 {
123 multiplierX = mRotationSpecConst->getMultiplierXForDFdx();
124 multiplierY = mRotationSpecConst->getMultiplierYForDFdx();
125 }
126 else
127 {
128 multiplierX = mRotationSpecConst->getMultiplierXForDFdy();
129 multiplierY = mRotationSpecConst->getMultiplierYForDFdy();
130 }
131
132 if (!multiplierX)
133 {
134 ASSERT(!multiplierY);
135 TIntermTyped *flipXY = mDriverUniforms->getFlipXYRef();
136 TIntermTyped *fragRotation = mDriverUniforms->getFragRotationMatrixRef();
137
138 // Get a vec2 with the correct half of ANGLEUniforms.fragRotation
139 TIntermBinary *halfRotationMat = nullptr;
140 if (node->getOp() == EOpDFdx)
141 {
142 halfRotationMat = new TIntermBinary(EOpIndexDirect, fragRotation, CreateIndexNode(0));
143 }
144 else
145 {
146 halfRotationMat = new TIntermBinary(EOpIndexDirect, fragRotation, CreateIndexNode(1));
147 }
148
149 // Multiply halfRotationMat by ANGLEUniforms.flipXY and store in a temporary variable
150 TIntermBinary *rotatedFlipXY = new TIntermBinary(EOpMul, flipXY, halfRotationMat);
151 const TType *vec2Type = StaticType::GetTemporary<EbtFloat, 2>();
152 TIntermSymbol *tmpRotFlipXY = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec2Type));
153 TIntermSequence tmpDecl;
154 tmpDecl.push_back(CreateTempInitDeclarationNode(&tmpRotFlipXY->variable(), rotatedFlipXY));
155 insertStatementsInParentBlock(tmpDecl);
156
157 // Get the .x and .y swizzles to use as multipliers
158 TVector<int> swizzleOffsetX = {0};
159 TVector<int> swizzleOffsetY = {1};
160 multiplierX = new TIntermSwizzle(tmpRotFlipXY, swizzleOffsetX);
161 multiplierY = new TIntermSwizzle(tmpRotFlipXY->deepCopy(), swizzleOffsetY);
162 }
163
164 // Get the results of dFdx(operand) and dFdy(operand), and multiply them by the swizzles
165 TIntermTyped *operand = node->getChildNode(0)->getAsTyped();
166
167 TIntermTyped *dFdx =
168 CreateBuiltInUnaryFunctionCallNode("dFdx", operand->deepCopy(), *mSymbolTable, 300);
169 TIntermTyped *dFdy =
170 CreateBuiltInUnaryFunctionCallNode("dFdy", operand->deepCopy(), *mSymbolTable, 300);
171
172 size_t objectSize = node->getType().getObjectSize();
173 TOperator multiplyOp = (objectSize == 1) ? EOpMul : EOpVectorTimesScalar;
174 TIntermBinary *rotatedFlippedDfdx = new TIntermBinary(multiplyOp, dFdx, multiplierX);
175 TIntermBinary *rotatedFlippedDfdy = new TIntermBinary(multiplyOp, dFdy, multiplierY);
176
177 // Sum them together into the result:
178 TIntermBinary *correctedResult =
179 new TIntermBinary(EOpAdd, rotatedFlippedDfdx, rotatedFlippedDfdy);
180
181 // Replace the old dFdx() or dFdy() node with the new node that contains the corrected value
182 queueReplacement(correctedResult, OriginalNode::IS_DROPPED);
183
184 return true;
185 }
186
visitAggregateWithoutRotation(Visit visit,TIntermAggregate * node)187 bool Traverser::visitAggregateWithoutRotation(Visit visit, TIntermAggregate *node)
188 {
189 // Decide if the node represents a call to dFdy()
190 if (node->getOp() != EOpDFdy)
191 {
192 return true;
193 }
194
195 // Copy the dFdy node so we can replace it with the corrected value
196 TIntermAggregate *newDfdy = node->deepCopy()->getAsAggregate();
197
198 size_t objectSize = node->getType().getObjectSize();
199 TOperator multiplyOp = (objectSize == 1) ? EOpMul : EOpVectorTimesScalar;
200
201 TIntermTyped *flipY = mRotationSpecConst->getFlipY();
202 if (!flipY)
203 {
204 TIntermTyped *flipXY = mDriverUniforms->getFlipXYRef();
205 flipY = new TIntermBinary(EOpIndexDirect, flipXY, CreateIndexNode(1));
206 }
207
208 // Correct dFdy()'s value:
209 // (dFdy() * mFlipXY.y)
210 TIntermBinary *correctedDfdy = new TIntermBinary(multiplyOp, newDfdy, flipY);
211
212 // Replace the old dFdy node with the new node that contains the corrected value
213 queueReplacement(correctedDfdy, OriginalNode::IS_DROPPED);
214
215 return true;
216 }
217 } // anonymous namespace
218
RewriteDfdy(TCompiler * compiler,ShCompileOptions compileOptions,TIntermNode * root,const TSymbolTable & symbolTable,int shaderVersion,SpecConst * specConst,const DriverUniform * driverUniforms)219 bool RewriteDfdy(TCompiler *compiler,
220 ShCompileOptions compileOptions,
221 TIntermNode *root,
222 const TSymbolTable &symbolTable,
223 int shaderVersion,
224 SpecConst *specConst,
225 const DriverUniform *driverUniforms)
226 {
227 // dFdy is only valid in GLSL 3.0 and later.
228 if (shaderVersion < 300)
229 return true;
230
231 return Traverser::Apply(compiler, compileOptions, root, symbolTable, specConst, driverUniforms);
232 }
233
234 } // namespace sh
235