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 // Don't clamp indirect indices on unsized arrays in buffer blocks. They are covered by the
52 // relevant robust access behavior of the backend.
53 if (leftType.isUnsizedArray())
54 {
55 return true;
56 }
57
58 // On GLSL es 100, clamp is only defined for float, so float arguments are used.
59 //
60 // However, float clamp is unconditionally emitted to workaround driver bugs with integer
61 // clamp on Qualcomm. http://crbug.com/1217167
62 //
63 // const bool useFloatClamp = mCompiler->getShaderVersion() == 100;
64 const bool useFloatClamp = true;
65
66 TIntermConstantUnion *zero = createClampValue(0, useFloatClamp);
67 TIntermTyped *max;
68
69 if (leftType.isArray())
70 {
71 max = createClampValue(static_cast<int>(leftType.getOutermostArraySize()) - 1,
72 useFloatClamp);
73 }
74 else
75 {
76 ASSERT(leftType.isVector() || leftType.isMatrix());
77 max = createClampValue(leftType.getNominalSize() - 1, useFloatClamp);
78 }
79
80 TIntermTyped *index = node->getRight();
81 // If the index node is not an int (i.e. it's a uint), or a float (if using float clamp),
82 // cast it.
83 const TBasicType requiredBasicType = useFloatClamp ? EbtFloat : EbtInt;
84 if (rightType.getBasicType() != requiredBasicType)
85 {
86 const TType *clampType = useFloatClamp ? StaticType::GetBasic<EbtFloat, EbpHigh>()
87 : StaticType::GetBasic<EbtInt, EbpHigh>();
88 TIntermSequence constructorArgs = {index};
89 index = TIntermAggregate::CreateConstructor(*clampType, &constructorArgs);
90 }
91
92 // min(gl_PointSize, maxPointSize)
93 TIntermSequence args;
94 args.push_back(index);
95 args.push_back(zero);
96 args.push_back(max);
97 TIntermTyped *clamped =
98 CreateBuiltInFunctionCallNode("clamp", &args, *mSymbolTable, useFloatClamp ? 100 : 300);
99
100 // Cast back to int if float clamp was used.
101 if (useFloatClamp)
102 {
103 TIntermSequence constructorArgs = {clamped};
104 clamped = TIntermAggregate::CreateConstructor(*StaticType::GetBasic<EbtInt, EbpHigh>(),
105 &constructorArgs);
106 }
107
108 // Replace the right node (the index) with the clamped result.
109 queueReplacementWithParent(node, node->getRight(), clamped, OriginalNode::IS_DROPPED);
110
111 // Don't recurse as left and right nodes are already processed.
112 return false;
113 }
114
115 private:
createClampValue(int value,bool useFloat)116 TIntermConstantUnion *createClampValue(int value, bool useFloat)
117 {
118 if (useFloat)
119 {
120 return CreateFloatNode(static_cast<float>(value), EbpHigh);
121 }
122 return CreateIndexNode(value);
123 }
124
125 TCompiler *mCompiler;
126 };
127 } // anonymous namespace
128
ClampIndirectIndices(TCompiler * compiler,TIntermNode * root,TSymbolTable * symbolTable)129 bool ClampIndirectIndices(TCompiler *compiler, TIntermNode *root, TSymbolTable *symbolTable)
130 {
131 ClampIndirectIndicesTraverser traverser(compiler, symbolTable);
132 root->traverse(&traverser);
133 return traverser.updateTree(compiler, root);
134 }
135
136 } // namespace sh
137