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