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