summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp
blob: 5233a29f1957306cb964f474109cff4b2213df66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//
// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// During parsing, all constant expressions are folded to constant union nodes. The expressions that
// have been folded may have had precision qualifiers, which should affect the precision of the
// consuming operation. If the folded constant union nodes are written to output as such they won't
// have any precision qualifiers, and their effect on the precision of the consuming operation is
// lost.
//
// RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants
// and hoists the constants outside the containing expression as precision qualified named variables
// in case that is required for correct precision propagation.
//

#include "compiler/translator/RecordConstantPrecision.h"

#include "compiler/translator/InfoSink.h"
#include "compiler/translator/IntermTraverse.h"

namespace sh
{

namespace
{

class RecordConstantPrecisionTraverser : public TIntermTraverser
{
  public:
    RecordConstantPrecisionTraverser(TSymbolTable *symbolTable);

    void visitConstantUnion(TIntermConstantUnion *node) override;

    void nextIteration();

    bool foundHigherPrecisionConstant() const { return mFoundHigherPrecisionConstant; }
  protected:
    bool operandAffectsParentOperationPrecision(TIntermTyped *operand);

    bool mFoundHigherPrecisionConstant;
};

RecordConstantPrecisionTraverser::RecordConstantPrecisionTraverser(TSymbolTable *symbolTable)
    : TIntermTraverser(true, false, true, symbolTable), mFoundHigherPrecisionConstant(false)
{
}

bool RecordConstantPrecisionTraverser::operandAffectsParentOperationPrecision(TIntermTyped *operand)
{
    if (getParentNode()->getAsCaseNode() || getParentNode()->getAsBlock())
    {
        return false;
    }

    const TIntermBinary *parentAsBinary = getParentNode()->getAsBinaryNode();
    if (parentAsBinary != nullptr)
    {
        // If the constant is assigned or is used to initialize a variable, or if it's an index,
        // its precision has no effect.
        switch (parentAsBinary->getOp())
        {
            case EOpInitialize:
            case EOpAssign:
            case EOpIndexDirect:
            case EOpIndexDirectStruct:
            case EOpIndexDirectInterfaceBlock:
            case EOpIndexIndirect:
                return false;
            default:
                break;
        }

        TIntermTyped *otherOperand = parentAsBinary->getRight();
        if (otherOperand == operand)
        {
            otherOperand = parentAsBinary->getLeft();
        }
        // If the precision of the other child is at least as high as the precision of the constant,
        // the precision of the constant has no effect.
        if (otherOperand->getAsConstantUnion() == nullptr &&
            otherOperand->getPrecision() >= operand->getPrecision())
        {
            return false;
        }
    }

    TIntermAggregate *parentAsAggregate = getParentNode()->getAsAggregate();
    if (parentAsAggregate != nullptr)
    {
        if (!parentAsAggregate->gotPrecisionFromChildren())
        {
            // This can be either:
            // * a call to an user-defined function
            // * a call to a texture function
            // * some other kind of aggregate
            // In any of these cases the constant precision has no effect.
            return false;
        }
        if (parentAsAggregate->isConstructor() && parentAsAggregate->getBasicType() == EbtBool)
        {
            return false;
        }
        // If the precision of operands does affect the result, but the precision of any of the
        // other children has a precision that's at least as high as the precision of the constant,
        // the precision of the constant has no effect.
        TIntermSequence *parameters = parentAsAggregate->getSequence();
        for (TIntermNode *parameter : *parameters)
        {
            const TIntermTyped *typedParameter = parameter->getAsTyped();
            if (parameter != operand && typedParameter != nullptr &&
                parameter->getAsConstantUnion() == nullptr &&
                typedParameter->getPrecision() >= operand->getPrecision())
            {
                return false;
            }
        }
    }
    return true;
}

void RecordConstantPrecisionTraverser::visitConstantUnion(TIntermConstantUnion *node)
{
    if (mFoundHigherPrecisionConstant)
        return;

    // If the constant has lowp or undefined precision, it can't increase the precision of consuming
    // operations.
    if (node->getPrecision() < EbpMedium)
        return;

    // It's possible the node has no effect on the precision of the consuming expression, depending
    // on the consuming expression, and the precision of the other parameters of the expression.
    if (!operandAffectsParentOperationPrecision(node))
        return;

    // Make the constant a precision-qualified named variable to make sure it affects the precision
    // of the consuming expression.
    TIntermSequence insertions;
    insertions.push_back(createTempInitDeclaration(node, EvqConst));
    insertStatementsInParentBlock(insertions);
    queueReplacement(createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);
    mFoundHigherPrecisionConstant = true;
}

void RecordConstantPrecisionTraverser::nextIteration()
{
    nextTemporaryId();
    mFoundHigherPrecisionConstant = false;
}

}  // namespace

void RecordConstantPrecision(TIntermNode *root, TSymbolTable *symbolTable)
{
    RecordConstantPrecisionTraverser traverser(symbolTable);
    // Iterate as necessary, and reset the traverser between iterations.
    do
    {
        traverser.nextIteration();
        root->traverse(&traverser);
        if (traverser.foundHigherPrecisionConstant())
            traverser.updateTree();
    } while (traverser.foundHigherPrecisionConstant());
}

}  // namespace sh