blob: 11740c3362d3568be7d93c1a7d676ae9aa7cdb85 [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,
34 bool multiview2,
35 TDiagnostics *diagnostics);
36
37 bool isValid() { return mValid; }
38
39 protected:
40 void visitSymbol(TIntermSymbol *node) override;
41 bool visitBinary(Visit visit, TIntermBinary *node) override;
42 bool visitUnary(Visit visit, TIntermUnary *node) override;
43 bool visitIfElse(Visit visit, TIntermIfElse *node) override;
44 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
45
46 private:
47 ValidateMultiviewTraverser(GLenum shaderType, bool multiview2, TDiagnostics *diagnostics);
48
49 static bool IsGLPosition(TIntermNode *node);
50 static bool IsGLViewIDOVR(TIntermNode *node);
51 static bool IsSimpleAssignmentToGLPositionX(TIntermBinary *node);
52 static bool IsSimpleAssignmentToGLPosition(TIntermBinary *node);
53
54 void validateAndTraverseViewIDConditionalBlock(TIntermBlock *block, const char *token);
55
56 bool mValid;
57 bool mMultiview2;
58 GLenum mShaderType;
59
60 bool mInsideViewIDConditional; // Only set if mMultiview2 is false.
61 bool mInsideRestrictedAssignment;
62 bool mGLPositionAllowed;
63 bool mViewIDOVRAllowed;
64
65 TDiagnostics *mDiagnostics;
66};
67
68bool ValidateMultiviewTraverser::validate(TIntermBlock *root,
69 GLenum shaderType,
70 bool multiview2,
71 TDiagnostics *diagnostics)
72{
73 ValidateMultiviewTraverser validate(shaderType, multiview2, diagnostics);
74 ASSERT(root);
75 root->traverse(&validate);
76 return validate.isValid();
77}
78
79ValidateMultiviewTraverser::ValidateMultiviewTraverser(GLenum shaderType,
80 bool multiview2,
81 TDiagnostics *diagnostics)
82 : TIntermTraverser(true, true, true),
83 mValid(true),
84 mMultiview2(multiview2),
85 mShaderType(shaderType),
86 mInsideViewIDConditional(false),
87 mInsideRestrictedAssignment(false),
88 mGLPositionAllowed(multiview2),
89 mViewIDOVRAllowed(multiview2),
90 mDiagnostics(diagnostics)
91{
92}
93
94bool ValidateMultiviewTraverser::IsGLPosition(TIntermNode *node)
95{
96 TIntermSymbol *symbolNode = node->getAsSymbolNode();
97 return (symbolNode && symbolNode->getName().getString() == "gl_Position");
98}
99
100bool ValidateMultiviewTraverser::IsGLViewIDOVR(TIntermNode *node)
101{
102 TIntermSymbol *symbolNode = node->getAsSymbolNode();
103 return (symbolNode && symbolNode->getName().getString() == "gl_ViewID_OVR");
104}
105
106bool ValidateMultiviewTraverser::IsSimpleAssignmentToGLPositionX(TIntermBinary *node)
107{
108 if (node->getOp() == EOpAssign)
109 {
110 TIntermSwizzle *leftAsSwizzle = node->getLeft()->getAsSwizzleNode();
111 if (leftAsSwizzle && IsGLPosition(leftAsSwizzle->getOperand()) &&
112 leftAsSwizzle->offsetsMatch(0))
113 {
114 return true;
115 }
116 TIntermBinary *leftAsBinary = node->getLeft()->getAsBinaryNode();
117 if (leftAsBinary && leftAsBinary->getOp() == EOpIndexDirect &&
118 IsGLPosition(leftAsBinary->getLeft()) &&
119 leftAsBinary->getRight()->getAsConstantUnion()->getIConst(0) == 0)
120 {
121 return true;
122 }
123 }
124 return false;
125}
126
127bool ValidateMultiviewTraverser::IsSimpleAssignmentToGLPosition(TIntermBinary *node)
128{
129 if (node->getOp() == EOpAssign)
130 {
131 if (IsGLPosition(node->getLeft()))
132 {
133 return true;
134 }
135 TIntermSwizzle *leftAsSwizzle = node->getLeft()->getAsSwizzleNode();
136 if (leftAsSwizzle && IsGLPosition(leftAsSwizzle->getOperand()))
137 {
138 return true;
139 }
140 TIntermBinary *leftAsBinary = node->getLeft()->getAsBinaryNode();
141 if (leftAsBinary && leftAsBinary->getOp() == EOpIndexDirect &&
142 IsGLPosition(leftAsBinary->getLeft()))
143 {
144 return true;
145 }
146 }
147 return false;
148}
149
150void ValidateMultiviewTraverser::visitSymbol(TIntermSymbol *node)
151{
152 if (IsGLPosition(node) && !mGLPositionAllowed)
153 {
154 ASSERT(!mMultiview2);
155 mDiagnostics->error(node->getLine(),
156 "Disallowed use of gl_Position when using OVR_multiview",
157 "gl_Position");
158 mValid = false;
159 }
160 else if (IsGLViewIDOVR(node) && !mViewIDOVRAllowed)
161 {
162 ASSERT(!mMultiview2);
163 mDiagnostics->error(node->getLine(),
164 "Disallowed use of gl_ViewID_OVR when using OVR_multiview",
165 "gl_ViewID_OVR");
166 mValid = false;
167 }
168 else if (!mMultiview2 && mShaderType == GL_FRAGMENT_SHADER)
169 {
170 std::array<const char *, 3> disallowedFragmentShaderSymbols{
171 {"gl_FragCoord", "gl_PointCoord", "gl_FrontFacing"}};
172 for (auto disallowedSymbol : disallowedFragmentShaderSymbols)
173 {
174 if (node->getSymbol() == disallowedSymbol)
175 {
176 mDiagnostics->error(
177 node->getLine(),
178 "Disallowed use of a built-in variable when using OVR_multiview",
179 disallowedSymbol);
180 mValid = false;
181 }
182 }
183 }
184}
185
186bool ValidateMultiviewTraverser::visitBinary(Visit visit, TIntermBinary *node)
187{
188 if (!mMultiview2 && mShaderType == GL_VERTEX_SHADER)
189 {
190 if (visit == PreVisit)
191 {
192 ASSERT(!mInsideViewIDConditional || mInsideRestrictedAssignment);
193 if (node->isAssignment())
194 {
195 if (mInsideRestrictedAssignment)
196 {
197 mDiagnostics->error(node->getLine(),
198 "Disallowed assignment inside assignment to gl_Position.x "
199 "when using OVR_multiview",
200 GetOperatorString(node->getOp()));
201 mValid = false;
202 }
203 else if (IsSimpleAssignmentToGLPositionX(node) &&
204 FindSymbolNode(node->getRight(), "gl_ViewID_OVR", EbtUInt))
205 {
206 if (!getParentNode()->getAsBlock())
207 {
208 mDiagnostics->error(node->getLine(),
209 "Disallowed use of assignment to gl_Position.x "
210 "when using OVR_multiview",
211 "=");
212 mValid = false;
213 }
214 mInsideRestrictedAssignment = true;
215 mViewIDOVRAllowed = true;
216 node->getRight()->traverse(this);
217 mInsideRestrictedAssignment = false;
218 mViewIDOVRAllowed = false;
219 return false;
220 }
221 else if (IsSimpleAssignmentToGLPosition(node))
222 {
223 node->getRight()->traverse(this);
224 return false;
225 }
226 }
227 }
228 }
229 return true;
230}
231
232bool ValidateMultiviewTraverser::visitUnary(Visit visit, TIntermUnary *node)
233{
234 if (visit == PreVisit && !mMultiview2 && mInsideRestrictedAssignment)
235 {
236 if (node->isAssignment())
237 {
238 mDiagnostics->error(node->getLine(),
239 "Disallowed unary operator inside assignment to gl_Position.x when "
240 "using OVR_multiview",
241 GetOperatorString(node->getOp()));
242 mValid = false;
243 }
244 }
245 return true;
246}
247
248void ValidateMultiviewTraverser::validateAndTraverseViewIDConditionalBlock(TIntermBlock *block,
249 const char *token)
250{
251 if (block->getSequence()->size() > 1)
252 {
253 mDiagnostics->error(block->getLine(),
254 "Only one assignment to gl_Position allowed inside if block dependent "
255 "on gl_ViewID_OVR when using OVR_multiview",
256 token);
257 mValid = false;
258 }
259 else if (block->getSequence()->size() == 1)
260 {
261 TIntermBinary *assignment = block->getSequence()->at(0)->getAsBinaryNode();
262 if (assignment && IsSimpleAssignmentToGLPositionX(assignment))
263 {
264 mInsideRestrictedAssignment = true;
265 assignment->getRight()->traverse(this);
266 mInsideRestrictedAssignment = false;
267 }
268 else
269 {
270 mDiagnostics->error(block->getLine(),
271 "Only one assignment to gl_Position.x allowed inside if block "
272 "dependent on gl_ViewID_OVR when using OVR_multiview",
273 token);
274 mValid = false;
275 }
276 }
277}
278
279bool ValidateMultiviewTraverser::visitIfElse(Visit visit, TIntermIfElse *node)
280{
281 if (!mMultiview2 && mShaderType == GL_VERTEX_SHADER)
282 {
283 ASSERT(visit == PreVisit);
284
285 // Check if the if statement refers to gl_ViewID_OVR and if it does so correctly
286 TIntermBinary *binaryCondition = node->getCondition()->getAsBinaryNode();
287 bool conditionIsAllowedComparisonWithViewID = false;
288 if (binaryCondition && binaryCondition->getOp() == EOpEqual)
289 {
290 conditionIsAllowedComparisonWithViewID =
291 IsGLViewIDOVR(binaryCondition->getLeft()) &&
292 binaryCondition->getRight()->getAsConstantUnion() &&
293 binaryCondition->getRight()->getQualifier() == EvqConst;
294 if (!conditionIsAllowedComparisonWithViewID)
295 {
296 conditionIsAllowedComparisonWithViewID =
297 IsGLViewIDOVR(binaryCondition->getRight()) &&
298 binaryCondition->getLeft()->getAsConstantUnion() &&
299 binaryCondition->getLeft()->getQualifier() == EvqConst;
300 }
301 }
302 if (conditionIsAllowedComparisonWithViewID)
303 {
304 mInsideViewIDConditional = true;
305 if (node->getTrueBlock())
306 {
307 validateAndTraverseViewIDConditionalBlock(node->getTrueBlock(), "if");
308 }
309 else
310 {
311 mDiagnostics->error(node->getLine(), "Expected assignment to gl_Position.x", "if");
312 }
313 if (node->getFalseBlock())
314 {
315 validateAndTraverseViewIDConditionalBlock(node->getFalseBlock(), "else");
316 }
317 mInsideViewIDConditional = false;
318 }
319 else
320 {
321 node->getCondition()->traverse(this);
322 if (node->getTrueBlock())
323 {
324 node->getTrueBlock()->traverse(this);
325 }
326 if (node->getFalseBlock())
327 {
328 node->getFalseBlock()->traverse(this);
329 }
330 }
331 return false;
332 }
333 return true;
334}
335
336bool ValidateMultiviewTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
337{
338 if (visit == PreVisit && !mMultiview2 && mInsideRestrictedAssignment)
339 {
340 // Check if the node is an user-defined function call or if an l-value is required, or if
341 // there are possible visible side effects, such as image writes.
342 if (node->getOp() == EOpFunctionCall)
343 {
344 if (node->isUserDefined())
345 {
346 mDiagnostics->error(node->getLine(),
347 "Disallowed user defined function call inside assignment to "
348 "gl_Position.x when using OVR_multiview",
349 GetOperatorString(node->getOp()));
350 mValid = false;
351 }
352 else if (TFunction::unmangleName(node->getFunctionSymbolInfo()->getName()) ==
353 "imageStore")
354 {
355 // TODO(oetuaho@nvidia.com): Record which built-in functions have side effects in
356 // the symbol info instead.
357 mDiagnostics->error(node->getLine(),
358 "Disallowed function call with side effects inside assignment "
359 "to gl_Position.x when using OVR_multiview",
360 GetOperatorString(node->getOp()));
361 mValid = false;
362 }
363 }
364 else if (node->getOp() == EOpModf)
365 {
366 // TODO(oetuaho@nvidia.com): It's quite hacky to hard-code modf - should maybe refactor
367 // out parameter detecting functionality in LValueTrackingTraverser so that it could be
368 // used here as well?
369 // LValueTrackingTraverser itself seems like a bad fit with the needs of this traverser.
370 mDiagnostics->error(node->getLine(),
371 "Disallowed use of a function with an out parameter inside "
372 "assignment to gl_Position.x when using OVR_multiview",
373 GetOperatorString(node->getOp()));
374 mValid = false;
375 }
376 }
377 return true;
378}
379
380} // anonymous namespace
381
382bool ValidateMultiviewWebGL(TIntermBlock *root,
383 GLenum shaderType,
384 bool multiview2,
385 TDiagnostics *diagnostics)
386{
387 if (shaderType == GL_VERTEX_SHADER && !FindSymbolNode(root, "gl_ViewID_OVR", EbtUInt))
388 {
389 return true;
390 }
391 return ValidateMultiviewTraverser::validate(root, shaderType, multiview2, diagnostics);
392}
393
394} // namespace sh