1 //
2 // Copyright 2021 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 // ClampIndirectIndices.h: Add clamp to the indirect indices used on arrays.
7 //
8 
9 #include "compiler/translator/tree_ops/ClampIndirectIndices.h"
10 
11 #include "compiler/translator/Compiler.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 namespace
20 {
21 // Traverser that finds EOpIndexIndirect nodes and applies a clamp to their right-hand side
22 // expression.
23 class ClampIndirectIndicesTraverser : public TIntermTraverser
24 {
25   public:
ClampIndirectIndicesTraverser(TCompiler * compiler,TSymbolTable * symbolTable)26     ClampIndirectIndicesTraverser(TCompiler *compiler, TSymbolTable *symbolTable)
27         : TIntermTraverser(true, false, false, symbolTable), mCompiler(compiler)
28     {}
29 
visitBinary(Visit visit,TIntermBinary * node)30     bool visitBinary(Visit visit, TIntermBinary *node) override
31     {
32         ASSERT(visit == PreVisit);
33 
34         // Only interested in EOpIndexIndirect nodes.
35         if (node->getOp() != EOpIndexIndirect)
36         {
37             return true;
38         }
39 
40         // Apply the transformation to the left and right nodes
41         bool valid = ClampIndirectIndices(mCompiler, node->getLeft(), mSymbolTable);
42         ASSERT(valid);
43         valid = ClampIndirectIndices(mCompiler, node->getRight(), mSymbolTable);
44         ASSERT(valid);
45 
46         // Generate clamp(right, 0, N), where N is the size of the array being indexed minus 1.  If
47         // the array is runtime-sized, the length() method is called on it.
48         const TType &leftType  = node->getLeft()->getType();
49         const TType &rightType = node->getRight()->getType();
50 
51         // On GLSL es 100, clamp is only defined for float, so float arguments are used.
52         //
53         // However, float clamp is unconditionally emitted to workaround driver bugs with integer
54         // clamp on Qualcomm.  http://crbug.com/1217167
55         //
56         // const bool useFloatClamp = mCompiler->getShaderVersion() == 100;
57         const bool useFloatClamp = true;
58 
59         TIntermConstantUnion *zero = createClampValue(0, useFloatClamp);
60         TIntermTyped *max;
61 
62         if (leftType.isUnsizedArray())
63         {
64             // Unsized arrays are an ES3.1 feature, so integer clamp should be available already.
65             max = new TIntermUnary(EOpArrayLength, node->getLeft(), nullptr);
66             max = new TIntermBinary(EOpSub, max, CreateIndexNode(1));
67             if (useFloatClamp)
68             {
69                 TIntermSequence constructorArgs = {max};
70                 max = TIntermAggregate::CreateConstructor(*StaticType::GetBasic<EbtFloat>(),
71                                                           &constructorArgs);
72             }
73         }
74         else if (leftType.isArray())
75         {
76             max = createClampValue(static_cast<int>(leftType.getOutermostArraySize()) - 1,
77                                    useFloatClamp);
78         }
79         else
80         {
81             ASSERT(leftType.isVector() || leftType.isMatrix());
82             max = createClampValue(leftType.getNominalSize() - 1, useFloatClamp);
83         }
84 
85         TIntermTyped *index = node->getRight();
86         // If the index node is not an int (i.e. it's a uint), or a float (if using float clamp),
87         // cast it.
88         const TBasicType requiredBasicType = useFloatClamp ? EbtFloat : EbtInt;
89         if (rightType.getBasicType() != requiredBasicType)
90         {
91             const TType *clampType =
92                 useFloatClamp ? StaticType::GetBasic<EbtFloat>() : StaticType::GetBasic<EbtInt>();
93             TIntermSequence constructorArgs = {index};
94             index = TIntermAggregate::CreateConstructor(*clampType, &constructorArgs);
95         }
96 
97         // min(gl_PointSize, maxPointSize)
98         TIntermSequence args;
99         args.push_back(index);
100         args.push_back(zero);
101         args.push_back(max);
102         TIntermTyped *clamped =
103             CreateBuiltInFunctionCallNode("clamp", &args, *mSymbolTable, useFloatClamp ? 100 : 300);
104 
105         // Cast back to int if float clamp was used.
106         if (useFloatClamp)
107         {
108             TIntermSequence constructorArgs = {clamped};
109             clamped = TIntermAggregate::CreateConstructor(*StaticType::GetBasic<EbtInt>(),
110                                                           &constructorArgs);
111         }
112 
113         // Replace the right node (the index) with the clamped result.
114         queueReplacementWithParent(node, node->getRight(), clamped, OriginalNode::IS_DROPPED);
115 
116         // Don't recurse as left and right nodes are already processed.
117         return false;
118     }
119 
120   private:
createClampValue(int value,bool useFloat)121     TIntermConstantUnion *createClampValue(int value, bool useFloat)
122     {
123         if (useFloat)
124         {
125             return CreateFloatNode(static_cast<float>(value));
126         }
127         return CreateIndexNode(value);
128     }
129 
130     TCompiler *mCompiler;
131 };
132 }  // anonymous namespace
133 
ClampIndirectIndices(TCompiler * compiler,TIntermNode * root,TSymbolTable * symbolTable)134 bool ClampIndirectIndices(TCompiler *compiler, TIntermNode *root, TSymbolTable *symbolTable)
135 {
136     ClampIndirectIndicesTraverser traverser(compiler, symbolTable);
137     root->traverse(&traverser);
138     return traverser.updateTree(compiler, root);
139 }
140 
141 }  // namespace sh
142