• 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/tree_util/IntermNode_util.h"
15 #include "compiler/translator/tree_util/IntermTraverse.h"
16 
17 namespace sh
18 {
19 
20 namespace
21 {
22 
23 class Traverser : public TIntermTraverser
24 {
25   public:
26     ANGLE_NO_DISCARD static bool Apply(TCompiler *compiler,
27                                        TIntermNode *root,
28                                        const TSymbolTable &symbolTable,
29                                        TIntermBinary *flipXY,
30                                        TIntermTyped *fragRotation);
31 
32   private:
33     Traverser(TIntermBinary *flipXY, TIntermTyped *fragRotation, TSymbolTable *symbolTable);
34     bool visitUnary(Visit visit, TIntermUnary *node) override;
35 
36     TIntermBinary *mFlipXY      = nullptr;
37     TIntermTyped *mFragRotation = nullptr;
38 };
39 
Traverser(TIntermBinary * flipXY,TIntermTyped * fragRotation,TSymbolTable * symbolTable)40 Traverser::Traverser(TIntermBinary *flipXY, TIntermTyped *fragRotation, TSymbolTable *symbolTable)
41     : TIntermTraverser(true, false, false, symbolTable),
42       mFlipXY(flipXY),
43       mFragRotation(fragRotation)
44 {}
45 
46 // static
Apply(TCompiler * compiler,TIntermNode * root,const TSymbolTable & symbolTable,TIntermBinary * flipXY,TIntermTyped * fragRotation)47 bool Traverser::Apply(TCompiler *compiler,
48                       TIntermNode *root,
49                       const TSymbolTable &symbolTable,
50                       TIntermBinary *flipXY,
51                       TIntermTyped *fragRotation)
52 {
53     TSymbolTable *pSymbolTable = const_cast<TSymbolTable *>(&symbolTable);
54     Traverser traverser(flipXY, fragRotation, pSymbolTable);
55     root->traverse(&traverser);
56     return traverser.updateTree(compiler, root);
57 }
58 
visitUnary(Visit visit,TIntermUnary * node)59 bool Traverser::visitUnary(Visit visit, TIntermUnary *node)
60 {
61     // Decide if the node represents a call to dFdx() or dFdy()
62     if ((node->getOp() != EOpDFdx) && (node->getOp() != EOpDFdy))
63     {
64         return true;
65     }
66 
67     // Prior to supporting Android pre-rotation, dFdy() needed to be multiplied by mFlipXY.y:
68     //
69     //   correctedDfdy(operand) = dFdy(operand) * mFlipXY.y
70     //
71     // For Android pre-rotation, both dFdx() and dFdy() need to be "rotated" and multiplied by
72     // mFlipXY.  "Rotation" means to swap them for 90 and 270 degrees, or to not swap them for 0
73     // and 180 degrees.  This rotation is accomplished with mFragRotation, which is a 2x2 matrix
74     // used for fragment shader rotation.  The 1st half (a vec2 that is either (1,0) or (0,1)) is
75     // used for rewriting dFdx() and the 2nd half (either (0,1) or (1,0)) is used for rewriting
76     // dFdy().  Otherwise, the formula for the rewrite is the same:
77     //
78     //     result = ((dFdx(operand) * (mFragRotation[half] * mFlipXY).x) +
79     //               (dFdy(operand) * (mFragRotation[half] * mFlipXY).y))
80     //
81     // For dFdx(), half is 0 (the 1st half).  For dFdy(), half is 1 (the 2nd half).  Depending on
82     // the rotation, mFragRotation[half] will cause either dFdx(operand) or dFdy(operand) to be
83     // zeroed-out.  That effectively means that the above code results in the following for 0 and
84     // 180 degrees:
85     //
86     //   correctedDfdx(operand) = dFdx(operand) * mFlipXY.x
87     //   correctedDfdy(operand) = dFdy(operand) * mFlipXY.y
88     //
89     // and the following for 90 and 270 degrees:
90     //
91     //   correctedDfdx(operand) = dFdy(operand) * mFlipXY.y
92     //   correctedDfdy(operand) = dFdx(operand) * mFlipXY.x
93     //
94     // TODO(ianelliott): Look at the performance of this approach and potentially optimize it
95     // http://anglebug.com/4678
96 
97     // Get a vec2 with the correct half of ANGLEUniforms.fragRotation
98     TIntermBinary *halfRotationMat = nullptr;
99     if (node->getOp() == EOpDFdx)
100     {
101         halfRotationMat =
102             new TIntermBinary(EOpIndexDirect, mFragRotation->deepCopy(), CreateIndexNode(0));
103     }
104     else
105     {
106         halfRotationMat =
107             new TIntermBinary(EOpIndexDirect, mFragRotation->deepCopy(), CreateIndexNode(1));
108     }
109 
110     // Multiply halfRotationMat by ANGLEUniforms.flipXY and store in a temporary variable
111     TIntermBinary *rotatedFlipXY = new TIntermBinary(EOpMul, mFlipXY->deepCopy(), halfRotationMat);
112     const TType *vec2Type        = StaticType::GetBasic<EbtFloat, 2>();
113     TIntermSymbol *tmpRotFlipXY  = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec2Type));
114     TIntermSequence *tmpDecl     = new TIntermSequence;
115     tmpDecl->push_back(CreateTempInitDeclarationNode(&tmpRotFlipXY->variable(), rotatedFlipXY));
116     insertStatementsInParentBlock(*tmpDecl);
117 
118     // Get the .x and .y swizzles to use as multipliers
119     TVector<int> swizzleOffsetX = {0};
120     TVector<int> swizzleOffsetY = {1};
121     TIntermSwizzle *multiplierX = new TIntermSwizzle(tmpRotFlipXY, swizzleOffsetX);
122     TIntermSwizzle *multiplierY = new TIntermSwizzle(tmpRotFlipXY->deepCopy(), swizzleOffsetY);
123 
124     // Get the results of dFdx(operand) and dFdy(operand), and multiply them by the swizzles
125     TIntermTyped *operand = node->getOperand();
126     TIntermUnary *dFdx    = new TIntermUnary(EOpDFdx, operand->deepCopy(), node->getFunction());
127     TIntermUnary *dFdy    = new TIntermUnary(EOpDFdy, operand->deepCopy(), node->getFunction());
128     size_t objectSize     = node->getType().getObjectSize();
129     TOperator multiplyOp  = (objectSize == 1) ? EOpMul : EOpVectorTimesScalar;
130     TIntermBinary *rotatedFlippedDfdx = new TIntermBinary(multiplyOp, dFdx, multiplierX);
131     TIntermBinary *rotatedFlippedDfdy = new TIntermBinary(multiplyOp, dFdy, multiplierY);
132 
133     // Sum them together into the result:
134     TIntermBinary *correctedResult =
135         new TIntermBinary(EOpAdd, rotatedFlippedDfdx, rotatedFlippedDfdy);
136 
137     // Replace the old dFdx() or dFdy() node with the new node that contains the corrected value
138     queueReplacement(correctedResult, OriginalNode::IS_DROPPED);
139 
140     return true;
141 }
142 
143 }  // anonymous namespace
144 
RewriteDfdy(TCompiler * compiler,TIntermNode * root,const TSymbolTable & symbolTable,int shaderVersion,TIntermBinary * flipXY,TIntermTyped * fragRotation)145 bool RewriteDfdy(TCompiler *compiler,
146                  TIntermNode *root,
147                  const TSymbolTable &symbolTable,
148                  int shaderVersion,
149                  TIntermBinary *flipXY,
150                  TIntermTyped *fragRotation)
151 {
152     // dFdy is only valid in GLSL 3.0 and later.
153     if (shaderVersion < 300)
154         return true;
155 
156     return Traverser::Apply(compiler, root, symbolTable, flipXY, fragRotation);
157 }
158 
159 }  // namespace sh
160