1 //
2 // Copyright 2020 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 // The ValidateClipCullDistance function checks if the sum of array sizes for gl_ClipDistance and
7 // gl_CullDistance exceeds gl_MaxCombinedClipAndCullDistances
8 //
9
10 #include "ValidateClipCullDistance.h"
11
12 #include "compiler/translator/Diagnostics.h"
13 #include "compiler/translator/SymbolTable.h"
14 #include "compiler/translator/tree_util/IntermTraverse.h"
15 #include "compiler/translator/util.h"
16
17 namespace sh
18 {
19
20 namespace
21 {
22
error(const TIntermSymbol & symbol,const char * reason,TDiagnostics * diagnostics)23 void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
24 {
25 diagnostics->error(symbol.getLine(), reason, symbol.getName().data());
26 }
27
28 class ValidateClipCullDistanceTraverser : public TIntermTraverser
29 {
30 public:
31 ValidateClipCullDistanceTraverser();
32 void validate(TDiagnostics *diagnostics, const unsigned int maxCombinedClipAndCullDistances);
33
34 private:
35 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
36 bool visitBinary(Visit visit, TIntermBinary *node) override;
37
38 unsigned int mClipDistanceSize;
39 unsigned int mCullDistanceSize;
40
41 unsigned int mMaxClipDistanceIndex;
42 unsigned int mMaxCullDistanceIndex;
43
44 const TIntermSymbol *mClipDistance;
45 const TIntermSymbol *mCullDistance;
46 };
47
ValidateClipCullDistanceTraverser()48 ValidateClipCullDistanceTraverser::ValidateClipCullDistanceTraverser()
49 : TIntermTraverser(true, false, false),
50 mClipDistanceSize(0),
51 mCullDistanceSize(0),
52 mMaxClipDistanceIndex(0),
53 mMaxCullDistanceIndex(0),
54 mClipDistance(nullptr),
55 mCullDistance(nullptr)
56 {}
57
visitDeclaration(Visit visit,TIntermDeclaration * node)58 bool ValidateClipCullDistanceTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
59 {
60 const TIntermSequence &sequence = *(node->getSequence());
61
62 if (sequence.size() != 1)
63 {
64 return true;
65 }
66
67 const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
68 if (symbol == nullptr)
69 {
70 return true;
71 }
72
73 if (symbol->getName() == "gl_ClipDistance")
74 {
75 mClipDistanceSize = symbol->getOutermostArraySize();
76 mClipDistance = symbol;
77 }
78 else if (symbol->getName() == "gl_CullDistance")
79 {
80 mCullDistanceSize = symbol->getOutermostArraySize();
81 mCullDistance = symbol;
82 }
83
84 return true;
85 }
86
visitBinary(Visit visit,TIntermBinary * node)87 bool ValidateClipCullDistanceTraverser::visitBinary(Visit visit, TIntermBinary *node)
88 {
89 TOperator op = node->getOp();
90 if (op != EOpIndexDirect && op != EOpIndexIndirect)
91 {
92 return true;
93 }
94
95 TIntermSymbol *left = node->getLeft()->getAsSymbolNode();
96 if (!left)
97 {
98 return true;
99 }
100
101 ImmutableString varName(left->getName());
102 if (varName != "gl_ClipDistance" && varName != "gl_CullDistance")
103 {
104 return true;
105 }
106
107 const TConstantUnion *constIdx = node->getRight()->getConstantValue();
108 if (constIdx)
109 {
110 unsigned int idx = 0;
111 switch (constIdx->getType())
112 {
113 case EbtInt:
114 idx = constIdx->getIConst();
115 break;
116 case EbtUInt:
117 idx = constIdx->getUConst();
118 break;
119 case EbtFloat:
120 idx = static_cast<unsigned int>(constIdx->getFConst());
121 break;
122 case EbtBool:
123 idx = constIdx->getBConst() ? 1 : 0;
124 break;
125 default:
126 UNREACHABLE();
127 break;
128 }
129
130 if (varName == "gl_ClipDistance")
131 {
132 if (idx > mMaxClipDistanceIndex)
133 {
134 mMaxClipDistanceIndex = idx;
135 if (!mClipDistance)
136 {
137 mClipDistance = left;
138 }
139 }
140 }
141 else
142 {
143 ASSERT(varName == "gl_CullDistance");
144 if (idx > mMaxCullDistanceIndex)
145 {
146 mMaxCullDistanceIndex = idx;
147 if (!mCullDistance)
148 {
149 mCullDistance = left;
150 }
151 }
152 }
153 }
154
155 return true;
156 }
157
validate(TDiagnostics * diagnostics,const unsigned int maxCombinedClipAndCullDistances)158 void ValidateClipCullDistanceTraverser::validate(TDiagnostics *diagnostics,
159 const unsigned int maxCombinedClipAndCullDistances)
160 {
161 ASSERT(diagnostics);
162
163 unsigned int enabledClipDistances =
164 (mClipDistanceSize > 0 ? mClipDistanceSize
165 : (mClipDistance ? mMaxClipDistanceIndex + 1 : 0));
166 unsigned int enabledCullDistances =
167 (mCullDistanceSize > 0 ? mCullDistanceSize
168 : (mCullDistance ? mMaxCullDistanceIndex + 1 : 0));
169 unsigned int combinedClipAndCullDistances =
170 (enabledClipDistances > 0 && enabledCullDistances > 0
171 ? enabledClipDistances + enabledCullDistances
172 : 0);
173
174 if (combinedClipAndCullDistances > maxCombinedClipAndCullDistances)
175 {
176 const TIntermSymbol *greaterSymbol =
177 (enabledClipDistances >= enabledCullDistances ? mClipDistance : mCullDistance);
178
179 std::stringstream strstr = sh::InitializeStream<std::stringstream>();
180 strstr << "The sum of 'gl_ClipDistance' and 'gl_CullDistance' size is greater than "
181 "gl_MaxCombinedClipAndCullDistances ("
182 << combinedClipAndCullDistances << " > " << maxCombinedClipAndCullDistances << ")";
183 error(*greaterSymbol, strstr.str().c_str(), diagnostics);
184 }
185 }
186
187 } // anonymous namespace
188
ValidateClipCullDistance(TIntermBlock * root,TDiagnostics * diagnostics,const unsigned int maxCombinedClipAndCullDistances)189 bool ValidateClipCullDistance(TIntermBlock *root,
190 TDiagnostics *diagnostics,
191 const unsigned int maxCombinedClipAndCullDistances)
192 {
193 ValidateClipCullDistanceTraverser varyingValidator;
194 root->traverse(&varyingValidator);
195 int numErrorsBefore = diagnostics->numErrors();
196 varyingValidator.validate(diagnostics, maxCombinedClipAndCullDistances);
197 return (diagnostics->numErrors() == numErrorsBefore);
198 }
199
200 } // namespace sh
201