• 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/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