• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2002 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 // During parsing, all constant expressions are folded to constant union nodes. The expressions that
7 // have been folded may have had precision qualifiers, which should affect the precision of the
8 // consuming operation. If the folded constant union nodes are written to output as such they won't
9 // have any precision qualifiers, and their effect on the precision of the consuming operation is
10 // lost.
11 //
12 // RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants
13 // and hoists the constants outside the containing expression as precision qualified named variables
14 // in case that is required for correct precision propagation.
15 //
16 
17 #include "compiler/translator/tree_ops/RecordConstantPrecision.h"
18 
19 #include "compiler/translator/InfoSink.h"
20 #include "compiler/translator/tree_util/IntermNode_util.h"
21 #include "compiler/translator/tree_util/IntermTraverse.h"
22 
23 namespace sh
24 {
25 
26 namespace
27 {
28 
29 class RecordConstantPrecisionTraverser : public TIntermTraverser
30 {
31   public:
32     RecordConstantPrecisionTraverser(TSymbolTable *symbolTable);
33 
34     void visitConstantUnion(TIntermConstantUnion *node) override;
35 
36     void nextIteration();
37 
foundHigherPrecisionConstant() const38     bool foundHigherPrecisionConstant() const { return mFoundHigherPrecisionConstant; }
39 
40   protected:
41     bool operandAffectsParentOperationPrecision(TIntermTyped *operand);
42 
43     bool mFoundHigherPrecisionConstant;
44 };
45 
RecordConstantPrecisionTraverser(TSymbolTable * symbolTable)46 RecordConstantPrecisionTraverser::RecordConstantPrecisionTraverser(TSymbolTable *symbolTable)
47     : TIntermTraverser(true, false, true, symbolTable), mFoundHigherPrecisionConstant(false)
48 {}
49 
operandAffectsParentOperationPrecision(TIntermTyped * operand)50 bool RecordConstantPrecisionTraverser::operandAffectsParentOperationPrecision(TIntermTyped *operand)
51 {
52     if (getParentNode()->getAsCaseNode() || getParentNode()->getAsBlock())
53     {
54         return false;
55     }
56 
57     const TIntermBinary *parentAsBinary = getParentNode()->getAsBinaryNode();
58     if (parentAsBinary != nullptr)
59     {
60         // If the constant is assigned or is used to initialize a variable, or if it's an index,
61         // its precision has no effect.
62         switch (parentAsBinary->getOp())
63         {
64             case EOpInitialize:
65             case EOpAssign:
66             case EOpIndexDirect:
67             case EOpIndexDirectStruct:
68             case EOpIndexDirectInterfaceBlock:
69             case EOpIndexIndirect:
70                 return false;
71             default:
72                 break;
73         }
74 
75         TIntermTyped *otherOperand = parentAsBinary->getRight();
76         if (otherOperand == operand)
77         {
78             otherOperand = parentAsBinary->getLeft();
79         }
80         // If the precision of the other child is at least as high as the precision of the constant,
81         // the precision of the constant has no effect.
82         if (otherOperand->getAsConstantUnion() == nullptr &&
83             otherOperand->getPrecision() >= operand->getPrecision())
84         {
85             return false;
86         }
87     }
88 
89     TIntermAggregate *parentAsAggregate = getParentNode()->getAsAggregate();
90     if (parentAsAggregate != nullptr)
91     {
92         if (!parentAsAggregate->gotPrecisionFromChildren())
93         {
94             // This can be either:
95             // * a call to an user-defined function
96             // * a call to a texture function
97             // * some other kind of aggregate
98             // In any of these cases the constant precision has no effect.
99             return false;
100         }
101         if (parentAsAggregate->isConstructor() && parentAsAggregate->getBasicType() == EbtBool)
102         {
103             return false;
104         }
105         // If the precision of operands does affect the result, but the precision of any of the
106         // other children has a precision that's at least as high as the precision of the constant,
107         // the precision of the constant has no effect.
108         TIntermSequence *parameters = parentAsAggregate->getSequence();
109         for (TIntermNode *parameter : *parameters)
110         {
111             const TIntermTyped *typedParameter = parameter->getAsTyped();
112             if (parameter != operand && typedParameter != nullptr &&
113                 parameter->getAsConstantUnion() == nullptr &&
114                 typedParameter->getPrecision() >= operand->getPrecision())
115             {
116                 return false;
117             }
118         }
119     }
120     return true;
121 }
122 
visitConstantUnion(TIntermConstantUnion * node)123 void RecordConstantPrecisionTraverser::visitConstantUnion(TIntermConstantUnion *node)
124 {
125     if (mFoundHigherPrecisionConstant)
126         return;
127 
128     // If the constant has lowp or undefined precision, it can't increase the precision of consuming
129     // operations.
130     if (node->getPrecision() < EbpMedium)
131         return;
132 
133     // It's possible the node has no effect on the precision of the consuming expression, depending
134     // on the consuming expression, and the precision of the other parameters of the expression.
135     if (!operandAffectsParentOperationPrecision(node))
136         return;
137 
138     // Make the constant a precision-qualified named variable to make sure it affects the precision
139     // of the consuming expression.
140     TIntermDeclaration *variableDeclaration = nullptr;
141     TVariable *variable = DeclareTempVariable(mSymbolTable, node, EvqConst, &variableDeclaration);
142     insertStatementInParentBlock(variableDeclaration);
143     queueReplacement(CreateTempSymbolNode(variable), OriginalNode::IS_DROPPED);
144     mFoundHigherPrecisionConstant = true;
145 }
146 
nextIteration()147 void RecordConstantPrecisionTraverser::nextIteration()
148 {
149     mFoundHigherPrecisionConstant = false;
150 }
151 
152 }  // namespace
153 
RecordConstantPrecision(TCompiler * compiler,TIntermNode * root,TSymbolTable * symbolTable)154 bool RecordConstantPrecision(TCompiler *compiler, TIntermNode *root, TSymbolTable *symbolTable)
155 {
156     RecordConstantPrecisionTraverser traverser(symbolTable);
157     // Iterate as necessary, and reset the traverser between iterations.
158     do
159     {
160         traverser.nextIteration();
161         root->traverse(&traverser);
162         if (traverser.foundHigherPrecisionConstant())
163         {
164             if (!traverser.updateTree(compiler, root))
165             {
166                 return false;
167             }
168         }
169     } while (traverser.foundHigherPrecisionConstant());
170 
171     return true;
172 }
173 
174 }  // namespace sh
175