• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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