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