blob: 169a32e91601d1d8207094714ddb5012409fab73 [file] [log] [blame]
Olli Etuaho09b04a22016-12-15 13:30:26 +00001//
2// Copyright (c) 2002-2016 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// ValidateMultiviewWebGL.cpp:
7// Validate the AST according to rules in the WEBGL_multiview extension spec. Only covers those
8// rules not already covered in ParseContext.
9//
10
11#include "compiler/translator/ValidateMultiviewWebGL.h"
12
13#include <array>
14
15#include "angle_gl.h"
16#include "compiler/translator/Diagnostics.h"
17#include "compiler/translator/FindSymbolNode.h"
18#include "compiler/translator/IntermNode.h"
19#include "compiler/translator/SymbolTable.h"
20
21namespace sh
22{
23
24namespace
25{
26
27class ValidateMultiviewTraverser : public TIntermTraverser
28{
29 public:
30 // Check for errors and write error messages to diagnostics. Returns true if there are no
31 // errors.
32 static bool validate(TIntermBlock *root,
33 GLenum shaderType,
Olli Etuaho01d0ad02017-01-22 14:51:23 -080034 const TSymbolTable &symbolTable,
35 int shaderVersion,
Olli Etuaho09b04a22016-12-15 13:30:26 +000036 bool multiview2,
37 TDiagnostics *diagnostics);
38
39 bool isValid() { return mValid; }
40
41 protected:
42 void visitSymbol(TIntermSymbol *node) override;
43 bool visitBinary(Visit visit, TIntermBinary *node) override;
44 bool visitUnary(Visit visit, TIntermUnary *node) override;
45 bool visitIfElse(Visit visit, TIntermIfElse *node) override;
46 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
47
48 private:
Olli Etuaho01d0ad02017-01-22 14:51:23 -080049 ValidateMultiviewTraverser(GLenum shaderType,
50 const TSymbolTable &symbolTable,
51 int shaderVersion,
52 bool multiview2,
53 TDiagnostics *diagnostics);
Olli Etuaho09b04a22016-12-15 13:30:26 +000054
55 static bool IsGLPosition(TIntermNode *node);
56 static bool IsGLViewIDOVR(TIntermNode *node);
57 static bool IsSimpleAssignmentToGLPositionX(TIntermBinary *node);
58 static bool IsSimpleAssignmentToGLPosition(TIntermBinary *node);
59
60 void validateAndTraverseViewIDConditionalBlock(TIntermBlock *block, const char *token);
61
62 bool mValid;
63 bool mMultiview2;
64 GLenum mShaderType;
Olli Etuaho01d0ad02017-01-22 14:51:23 -080065 const TSymbolTable &mSymbolTable;
66 const int mShaderVersion;
Olli Etuaho09b04a22016-12-15 13:30:26 +000067
68 bool mInsideViewIDConditional; // Only set if mMultiview2 is false.
69 bool mInsideRestrictedAssignment;
70 bool mGLPositionAllowed;
71 bool mViewIDOVRAllowed;
72
73 TDiagnostics *mDiagnostics;
74};
75
76bool ValidateMultiviewTraverser::validate(TIntermBlock *root,
77 GLenum shaderType,
Olli Etuaho01d0ad02017-01-22 14:51:23 -080078 const TSymbolTable &symbolTable,
79 int shaderVersion,
Olli Etuaho09b04a22016-12-15 13:30:26 +000080 bool multiview2,
81 TDiagnostics *diagnostics)
82{
Olli Etuaho01d0ad02017-01-22 14:51:23 -080083 ValidateMultiviewTraverser validate(shaderType, symbolTable, shaderVersion, multiview2,
84 diagnostics);
Olli Etuaho09b04a22016-12-15 13:30:26 +000085 ASSERT(root);
86 root->traverse(&validate);
87 return validate.isValid();
88}
89
90ValidateMultiviewTraverser::ValidateMultiviewTraverser(GLenum shaderType,
Olli Etuaho01d0ad02017-01-22 14:51:23 -080091 const TSymbolTable &symbolTable,
92 int shaderVersion,
Olli Etuaho09b04a22016-12-15 13:30:26 +000093 bool multiview2,
94 TDiagnostics *diagnostics)
95 : TIntermTraverser(true, true, true),
96 mValid(true),
97 mMultiview2(multiview2),
98 mShaderType(shaderType),
Olli Etuaho01d0ad02017-01-22 14:51:23 -080099 mSymbolTable(symbolTable),
100 mShaderVersion(shaderVersion),
Olli Etuaho09b04a22016-12-15 13:30:26 +0000101 mInsideViewIDConditional(false),
102 mInsideRestrictedAssignment(false),
103 mGLPositionAllowed(multiview2),
104 mViewIDOVRAllowed(multiview2),
105 mDiagnostics(diagnostics)
106{
107}
108
109bool ValidateMultiviewTraverser::IsGLPosition(TIntermNode *node)
110{
111 TIntermSymbol *symbolNode = node->getAsSymbolNode();
112 return (symbolNode && symbolNode->getName().getString() == "gl_Position");
113}
114
115bool ValidateMultiviewTraverser::IsGLViewIDOVR(TIntermNode *node)
116{
117 TIntermSymbol *symbolNode = node->getAsSymbolNode();
118 return (symbolNode && symbolNode->getName().getString() == "gl_ViewID_OVR");
119}
120
121bool ValidateMultiviewTraverser::IsSimpleAssignmentToGLPositionX(TIntermBinary *node)
122{
123 if (node->getOp() == EOpAssign)
124 {
125 TIntermSwizzle *leftAsSwizzle = node->getLeft()->getAsSwizzleNode();
126 if (leftAsSwizzle && IsGLPosition(leftAsSwizzle->getOperand()) &&
127 leftAsSwizzle->offsetsMatch(0))
128 {
129 return true;
130 }
131 TIntermBinary *leftAsBinary = node->getLeft()->getAsBinaryNode();
132 if (leftAsBinary && leftAsBinary->getOp() == EOpIndexDirect &&
133 IsGLPosition(leftAsBinary->getLeft()) &&
134 leftAsBinary->getRight()->getAsConstantUnion()->getIConst(0) == 0)
135 {
136 return true;
137 }
138 }
139 return false;
140}
141
142bool ValidateMultiviewTraverser::IsSimpleAssignmentToGLPosition(TIntermBinary *node)
143{
144 if (node->getOp() == EOpAssign)
145 {
146 if (IsGLPosition(node->getLeft()))
147 {
148 return true;
149 }
150 TIntermSwizzle *leftAsSwizzle = node->getLeft()->getAsSwizzleNode();
151 if (leftAsSwizzle && IsGLPosition(leftAsSwizzle->getOperand()))
152 {
153 return true;
154 }
155 TIntermBinary *leftAsBinary = node->getLeft()->getAsBinaryNode();
156 if (leftAsBinary && leftAsBinary->getOp() == EOpIndexDirect &&
157 IsGLPosition(leftAsBinary->getLeft()))
158 {
159 return true;
160 }
161 }
162 return false;
163}
164
165void ValidateMultiviewTraverser::visitSymbol(TIntermSymbol *node)
166{
167 if (IsGLPosition(node) && !mGLPositionAllowed)
168 {
169 ASSERT(!mMultiview2);
170 mDiagnostics->error(node->getLine(),
171 "Disallowed use of gl_Position when using OVR_multiview",
172 "gl_Position");
173 mValid = false;
174 }
175 else if (IsGLViewIDOVR(node) && !mViewIDOVRAllowed)
176 {
177 ASSERT(!mMultiview2);
178 mDiagnostics->error(node->getLine(),
179 "Disallowed use of gl_ViewID_OVR when using OVR_multiview",
180 "gl_ViewID_OVR");
181 mValid = false;
182 }
183 else if (!mMultiview2 && mShaderType == GL_FRAGMENT_SHADER)
184 {
185 std::array<const char *, 3> disallowedFragmentShaderSymbols{
186 {"gl_FragCoord", "gl_PointCoord", "gl_FrontFacing"}};
187 for (auto disallowedSymbol : disallowedFragmentShaderSymbols)
188 {
189 if (node->getSymbol() == disallowedSymbol)
190 {
191 mDiagnostics->error(
192 node->getLine(),
193 "Disallowed use of a built-in variable when using OVR_multiview",
194 disallowedSymbol);
195 mValid = false;
196 }
197 }
198 }
199}
200
201bool ValidateMultiviewTraverser::visitBinary(Visit visit, TIntermBinary *node)
202{
203 if (!mMultiview2 && mShaderType == GL_VERTEX_SHADER)
204 {
205 if (visit == PreVisit)
206 {
207 ASSERT(!mInsideViewIDConditional || mInsideRestrictedAssignment);
208 if (node->isAssignment())
209 {
210 if (mInsideRestrictedAssignment)
211 {
212 mDiagnostics->error(node->getLine(),
213 "Disallowed assignment inside assignment to gl_Position.x "
214 "when using OVR_multiview",
215 GetOperatorString(node->getOp()));
216 mValid = false;
217 }
218 else if (IsSimpleAssignmentToGLPositionX(node) &&
219 FindSymbolNode(node->getRight(), "gl_ViewID_OVR", EbtUInt))
220 {
221 if (!getParentNode()->getAsBlock())
222 {
223 mDiagnostics->error(node->getLine(),
224 "Disallowed use of assignment to gl_Position.x "
225 "when using OVR_multiview",
226 "=");
227 mValid = false;
228 }
229 mInsideRestrictedAssignment = true;
230 mViewIDOVRAllowed = true;
231 node->getRight()->traverse(this);
232 mInsideRestrictedAssignment = false;
233 mViewIDOVRAllowed = false;
234 return false;
235 }
236 else if (IsSimpleAssignmentToGLPosition(node))
237 {
238 node->getRight()->traverse(this);
239 return false;
240 }
241 }
242 }
243 }
244 return true;
245}
246
247bool ValidateMultiviewTraverser::visitUnary(Visit visit, TIntermUnary *node)
248{
249 if (visit == PreVisit && !mMultiview2 && mInsideRestrictedAssignment)
250 {
251 if (node->isAssignment())
252 {
253 mDiagnostics->error(node->getLine(),
254 "Disallowed unary operator inside assignment to gl_Position.x when "
255 "using OVR_multiview",
256 GetOperatorString(node->getOp()));
257 mValid = false;
258 }
259 }
260 return true;
261}
262
263void ValidateMultiviewTraverser::validateAndTraverseViewIDConditionalBlock(TIntermBlock *block,
264 const char *token)
265{
266 if (block->getSequence()->size() > 1)
267 {
268 mDiagnostics->error(block->getLine(),
269 "Only one assignment to gl_Position allowed inside if block dependent "
270 "on gl_ViewID_OVR when using OVR_multiview",
271 token);
272 mValid = false;
273 }
274 else if (block->getSequence()->size() == 1)
275 {
276 TIntermBinary *assignment = block->getSequence()->at(0)->getAsBinaryNode();
277 if (assignment && IsSimpleAssignmentToGLPositionX(assignment))
278 {
279 mInsideRestrictedAssignment = true;
280 assignment->getRight()->traverse(this);
281 mInsideRestrictedAssignment = false;
282 }
283 else
284 {
285 mDiagnostics->error(block->getLine(),
286 "Only one assignment to gl_Position.x allowed inside if block "
287 "dependent on gl_ViewID_OVR when using OVR_multiview",
288 token);
289 mValid = false;
290 }
291 }
292}
293
294bool ValidateMultiviewTraverser::visitIfElse(Visit visit, TIntermIfElse *node)
295{
296 if (!mMultiview2 && mShaderType == GL_VERTEX_SHADER)
297 {
298 ASSERT(visit == PreVisit);
299
300 // Check if the if statement refers to gl_ViewID_OVR and if it does so correctly
301 TIntermBinary *binaryCondition = node->getCondition()->getAsBinaryNode();
302 bool conditionIsAllowedComparisonWithViewID = false;
303 if (binaryCondition && binaryCondition->getOp() == EOpEqual)
304 {
305 conditionIsAllowedComparisonWithViewID =
306 IsGLViewIDOVR(binaryCondition->getLeft()) &&
307 binaryCondition->getRight()->getAsConstantUnion() &&
308 binaryCondition->getRight()->getQualifier() == EvqConst;
309 if (!conditionIsAllowedComparisonWithViewID)
310 {
311 conditionIsAllowedComparisonWithViewID =
312 IsGLViewIDOVR(binaryCondition->getRight()) &&
313 binaryCondition->getLeft()->getAsConstantUnion() &&
314 binaryCondition->getLeft()->getQualifier() == EvqConst;
315 }
316 }
317 if (conditionIsAllowedComparisonWithViewID)
318 {
319 mInsideViewIDConditional = true;
320 if (node->getTrueBlock())
321 {
322 validateAndTraverseViewIDConditionalBlock(node->getTrueBlock(), "if");
323 }
324 else
325 {
326 mDiagnostics->error(node->getLine(), "Expected assignment to gl_Position.x", "if");
327 }
328 if (node->getFalseBlock())
329 {
330 validateAndTraverseViewIDConditionalBlock(node->getFalseBlock(), "else");
331 }
332 mInsideViewIDConditional = false;
333 }
334 else
335 {
336 node->getCondition()->traverse(this);
337 if (node->getTrueBlock())
338 {
339 node->getTrueBlock()->traverse(this);
340 }
341 if (node->getFalseBlock())
342 {
343 node->getFalseBlock()->traverse(this);
344 }
345 }
346 return false;
347 }
348 return true;
349}
350
351bool ValidateMultiviewTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
352{
353 if (visit == PreVisit && !mMultiview2 && mInsideRestrictedAssignment)
354 {
355 // Check if the node is an user-defined function call or if an l-value is required, or if
356 // there are possible visible side effects, such as image writes.
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800357 if (node->getOp() == EOpCallFunctionInAST)
Olli Etuaho09b04a22016-12-15 13:30:26 +0000358 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800359 mDiagnostics->error(node->getLine(),
360 "Disallowed user defined function call inside assignment to "
361 "gl_Position.x when using OVR_multiview",
362 GetOperatorString(node->getOp()));
363 mValid = false;
364 }
365 else if (node->getOp() == EOpCallBuiltInFunction &&
Olli Etuahoec9232b2017-03-27 17:01:37 +0300366 node->getFunctionSymbolInfo()->getName() == "imageStore")
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800367 {
368 // TODO(oetuaho@nvidia.com): Record which built-in functions have side effects in
369 // the symbol info instead.
370 mDiagnostics->error(node->getLine(),
371 "Disallowed function call with side effects inside assignment "
372 "to gl_Position.x when using OVR_multiview",
373 GetOperatorString(node->getOp()));
374 mValid = false;
Olli Etuaho09b04a22016-12-15 13:30:26 +0000375 }
Olli Etuaho01d0ad02017-01-22 14:51:23 -0800376 else if (!node->isConstructor())
Olli Etuaho09b04a22016-12-15 13:30:26 +0000377 {
Olli Etuahof2209f72017-04-01 12:45:55 +0300378 TFunction *builtInFunc = static_cast<TFunction *>(
379 mSymbolTable.findBuiltIn(node->getSymbolTableMangledName(), mShaderVersion));
Olli Etuaho01d0ad02017-01-22 14:51:23 -0800380 for (size_t paramIndex = 0u; paramIndex < builtInFunc->getParamCount(); ++paramIndex)
381 {
382 TQualifier qualifier = builtInFunc->getParam(paramIndex).type->getQualifier();
383 if (qualifier == EvqOut || qualifier == EvqInOut)
384 {
385 mDiagnostics->error(node->getLine(),
386 "Disallowed use of a function with an out parameter inside "
387 "assignment to gl_Position.x when using OVR_multiview",
388 GetOperatorString(node->getOp()));
389 mValid = false;
390 }
391 }
Olli Etuaho09b04a22016-12-15 13:30:26 +0000392 }
393 }
394 return true;
395}
396
397} // anonymous namespace
398
399bool ValidateMultiviewWebGL(TIntermBlock *root,
400 GLenum shaderType,
Olli Etuaho01d0ad02017-01-22 14:51:23 -0800401 const TSymbolTable &symbolTable,
402 int shaderVersion,
Olli Etuaho09b04a22016-12-15 13:30:26 +0000403 bool multiview2,
404 TDiagnostics *diagnostics)
405{
406 if (shaderType == GL_VERTEX_SHADER && !FindSymbolNode(root, "gl_ViewID_OVR", EbtUInt))
407 {
408 return true;
409 }
Olli Etuaho01d0ad02017-01-22 14:51:23 -0800410 return ValidateMultiviewTraverser::validate(root, shaderType, symbolTable, shaderVersion,
411 multiview2, diagnostics);
Olli Etuaho09b04a22016-12-15 13:30:26 +0000412}
413
414} // namespace sh