1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES Utilities
3 * ------------------------------------------------
4 *
5 * Copyright 2015 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Shader .test file utilities.
22 *//*--------------------------------------------------------------------*/
23
24 #include "gluShaderLibrary.hpp"
25
26 #include "tcuStringTemplate.hpp"
27 #include "tcuResource.hpp"
28 #include "tcuTestLog.hpp"
29
30 #include "deStringUtil.hpp"
31 #include "deUniquePtr.hpp"
32 #include "deFilePath.hpp"
33
34 #include "glwEnums.hpp"
35
36 #include <sstream>
37 #include <map>
38 #include <cstdlib>
39
40 #if 0
41 # define PARSE_DBG(X) printf X
42 #else
43 # define PARSE_DBG(X) DE_NULL_STATEMENT
44 #endif
45
46 namespace glu
47 {
48 namespace sl
49 {
50
51 using namespace tcu;
52
53 using std::vector;
54 using std::string;
55 using std::map;
56 using std::ostringstream;
57 using std::pair;
58 using de::UniquePtr;
59
60 // Specification
61
isValid(const ValueBlock & block)62 bool isValid (const ValueBlock& block)
63 {
64 for (size_t storageNdx = 0; storageNdx < 3; ++storageNdx)
65 {
66 const vector<Value>& values = storageNdx == 0 ? block.inputs :
67 storageNdx == 1 ? block.outputs :
68 block.uniforms;
69 const size_t refArrayLen = values.empty() ? 0 : (values[0].elements.size() / (size_t)values[0].type.getScalarSize());
70
71 for (size_t valNdx = 0; valNdx < values.size(); ++valNdx)
72 {
73 const Value& value = values[valNdx];
74
75 if (!value.type.isBasicType())
76 {
77 print("ERROR: Value '%s' is of unsupported type!\n", value.name.c_str());
78 return false;
79 }
80
81 if (value.elements.size() != refArrayLen*(size_t)value.type.getScalarSize())
82 {
83 print("ERROR: Value '%s' has invalid number of scalars!\n", value.name.c_str());
84 return false;
85 }
86 }
87 }
88
89 return true;
90 }
91
isValid(const ShaderCaseSpecification & spec)92 bool isValid (const ShaderCaseSpecification& spec)
93 {
94 const deUint32 vtxFragMask = (1u << SHADERTYPE_VERTEX)
95 | (1u << SHADERTYPE_FRAGMENT);
96 const deUint32 tessCtrlEvalMask = (1u << SHADERTYPE_TESSELLATION_CONTROL)
97 | (1u << SHADERTYPE_TESSELLATION_EVALUATION);
98 const deUint32 supportedStageMask = vtxFragMask | tessCtrlEvalMask
99 | (1u << SHADERTYPE_GEOMETRY);
100 const bool isSeparable = !spec.programs.empty() && spec.programs[0].sources.separable;
101
102 if (spec.programs.empty())
103 {
104 print("ERROR: No programs specified!\n");
105 return false;
106 }
107
108 if (isCapabilityRequired(CAPABILITY_FULL_GLSL_ES_100_SUPPORT, spec))
109 {
110 if (spec.targetVersion != GLSL_VERSION_100_ES)
111 {
112 print("ERROR: Full GLSL ES 1.00 support requested for other GLSL version!\n");
113 return false;
114 }
115
116 if (spec.expectResult != EXPECT_PASS &&
117 spec.expectResult != EXPECT_VALIDATION_FAIL &&
118 spec.expectResult != EXPECT_BUILD_SUCCESSFUL)
119 {
120 print("ERROR: Full GLSL ES 1.00 support doesn't make sense when expecting compile/link failure!\n");
121 return false;
122 }
123 }
124
125 if (!de::inBounds(spec.caseType, (CaseType)0, CASETYPE_LAST))
126 {
127 print("ERROR: Invalid case type!\n");
128 return false;
129 }
130
131 if (!de::inBounds(spec.expectResult, (ExpectResult)0, EXPECT_LAST))
132 {
133 print("ERROR: Invalid expected result!\n");
134 return false;
135 }
136
137 if (!isValid(spec.values))
138 return false;
139
140 if (!spec.values.inputs.empty() && !spec.values.outputs.empty() &&
141 spec.values.inputs[0].elements.size() / spec.values.inputs[0].type.getScalarSize() != spec.values.outputs[0].elements.size() / spec.values.outputs[0].type.getScalarSize())
142 {
143 print("ERROR: Number of input and output elements don't match!\n");
144 return false;
145 }
146
147 if (isSeparable)
148 {
149 deUint32 usedStageMask = 0u;
150
151 if (spec.caseType != CASETYPE_COMPLETE)
152 {
153 print("ERROR: Separable shaders supported only for complete cases!\n");
154 return false;
155 }
156
157 for (size_t progNdx = 0; progNdx < spec.programs.size(); ++progNdx)
158 {
159 for (int shaderStageNdx = 0; shaderStageNdx < SHADERTYPE_LAST; ++shaderStageNdx)
160 {
161 const deUint32 curStageMask = (1u << shaderStageNdx);
162
163 if (supportedStageMask & curStageMask)
164 {
165 const bool hasShader = !spec.programs[progNdx].sources.sources[shaderStageNdx].empty();
166 const bool isEnabled = (spec.programs[progNdx].activeStages & curStageMask) != 0;
167
168 if (hasShader != isEnabled)
169 {
170 print("ERROR: Inconsistent source/enable for shader stage %s!\n", getShaderTypeName((ShaderType)shaderStageNdx));
171 return false;
172 }
173
174 if (hasShader && (usedStageMask & curStageMask) != 0)
175 {
176 print("ERROR: Stage %s enabled on multiple programs!\n", getShaderTypeName((ShaderType)shaderStageNdx));
177 return false;
178 }
179
180 if (isEnabled)
181 usedStageMask |= curStageMask;
182 }
183 else if (!spec.programs[progNdx].sources.sources[shaderStageNdx].empty())
184 {
185 print("ERROR: Source specified for unsupported shader stage %s!\n", getShaderTypeName((ShaderType)shaderStageNdx));
186 return false;
187 }
188 }
189 }
190
191 if ((usedStageMask & vtxFragMask) != vtxFragMask)
192 {
193 print("ERROR: Vertex and fragment shaders are mandatory!\n");
194 return false;
195 }
196
197 if ((usedStageMask & tessCtrlEvalMask) != 0 && (usedStageMask & tessCtrlEvalMask) != tessCtrlEvalMask)
198 {
199 print("ERROR: Both tessellation control and eval shaders must be either enabled or disabled!\n");
200 return false;
201 }
202 }
203 else
204 {
205 const bool hasVertex = !spec.programs[0].sources.sources[SHADERTYPE_VERTEX].empty();
206 const bool hasFragment = !spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].empty();
207
208 if (spec.programs.size() != 1)
209 {
210 print("ERROR: Only cases using separable programs can have multiple programs!\n");
211 return false;
212 }
213
214 if (spec.caseType == CASETYPE_VERTEX_ONLY && (!hasVertex || hasFragment))
215 {
216 print("ERROR: Vertex-only case must have only vertex shader!\n");
217 return false;
218 }
219
220 if (spec.caseType == CASETYPE_FRAGMENT_ONLY && (hasVertex || !hasFragment))
221 {
222 print("ERROR: Fragment-only case must have only fragment shader!\n");
223 return false;
224 }
225
226 if (spec.caseType == CASETYPE_COMPLETE && (!hasVertex || !hasFragment))
227 {
228 print("ERROR: Complete case must have at least vertex and fragment shaders\n");
229 return false;
230 }
231 }
232
233 return true;
234 }
235
isCapabilityRequired(CapabilityFlag capabilityFlag,const ShaderCaseSpecification & spec)236 bool isCapabilityRequired(CapabilityFlag capabilityFlag, const ShaderCaseSpecification& spec)
237 {
238 std::vector<RequiredCapability>::const_iterator currRequirement = spec.requiredCaps.begin();
239 while (currRequirement != spec.requiredCaps.end())
240 {
241 if ((currRequirement->type == CAPABILITY_FLAG) && (currRequirement->flagName == capabilityFlag))
242 return true;
243 ++currRequirement;
244 }
245
246 return false;
247 }
248
249 // Parser
250
251 static const glu::GLSLVersion DEFAULT_GLSL_VERSION = glu::GLSL_VERSION_100_ES;
252
isWhitespace(char c)253 DE_INLINE deBool isWhitespace (char c)
254 {
255 return (c == ' ') || (c == '\t') || (c == '\r') || (c == '\n');
256 }
257
isEOL(char c)258 DE_INLINE deBool isEOL (char c)
259 {
260 return (c == '\r') || (c == '\n');
261 }
262
isNumeric(char c)263 DE_INLINE deBool isNumeric (char c)
264 {
265 return deInRange32(c, '0', '9');
266 }
267
isAlpha(char c)268 DE_INLINE deBool isAlpha (char c)
269 {
270 return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z');
271 }
272
isCaseNameChar(char c)273 DE_INLINE deBool isCaseNameChar (char c)
274 {
275 return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z') || deInRange32(c, '0', '9') || (c == '_') || (c == '-') || (c == '.');
276 }
277
278 class ShaderParser
279 {
280 public:
281 ShaderParser (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory);
282 ~ShaderParser (void);
283
284 vector<tcu::TestNode*> parse (void);
285
286 private:
287 enum Token
288 {
289 TOKEN_INVALID = 0,
290 TOKEN_EOF,
291 TOKEN_STRING,
292 TOKEN_SHADER_SOURCE,
293
294 TOKEN_INT_LITERAL,
295 TOKEN_FLOAT_LITERAL,
296
297 // identifiers
298 TOKEN_IDENTIFIER,
299 TOKEN_TRUE,
300 TOKEN_FALSE,
301 TOKEN_DESC,
302 TOKEN_EXPECT,
303 TOKEN_GROUP,
304 TOKEN_CASE,
305 TOKEN_END,
306 TOKEN_OUTPUT_COLOR,
307 TOKEN_FORMAT,
308 TOKEN_VALUES,
309 TOKEN_BOTH,
310 TOKEN_VERTEX,
311 TOKEN_FRAGMENT,
312 TOKEN_UNIFORM,
313 TOKEN_INPUT,
314 TOKEN_OUTPUT,
315 TOKEN_FLOAT,
316 TOKEN_FLOAT_VEC2,
317 TOKEN_FLOAT_VEC3,
318 TOKEN_FLOAT_VEC4,
319 TOKEN_FLOAT_MAT2,
320 TOKEN_FLOAT_MAT2X3,
321 TOKEN_FLOAT_MAT2X4,
322 TOKEN_FLOAT_MAT3X2,
323 TOKEN_FLOAT_MAT3,
324 TOKEN_FLOAT_MAT3X4,
325 TOKEN_FLOAT_MAT4X2,
326 TOKEN_FLOAT_MAT4X3,
327 TOKEN_FLOAT_MAT4,
328 TOKEN_INT,
329 TOKEN_INT_VEC2,
330 TOKEN_INT_VEC3,
331 TOKEN_INT_VEC4,
332 TOKEN_UINT,
333 TOKEN_UINT_VEC2,
334 TOKEN_UINT_VEC3,
335 TOKEN_UINT_VEC4,
336 TOKEN_BOOL,
337 TOKEN_BOOL_VEC2,
338 TOKEN_BOOL_VEC3,
339 TOKEN_BOOL_VEC4,
340 TOKEN_VERSION,
341 TOKEN_TESSELLATION_CONTROL,
342 TOKEN_TESSELLATION_EVALUATION,
343 TOKEN_GEOMETRY,
344 TOKEN_REQUIRE,
345 TOKEN_IN,
346 TOKEN_IMPORT,
347 TOKEN_PIPELINE_PROGRAM,
348 TOKEN_ACTIVE_STAGES,
349
350 // symbols
351 TOKEN_ASSIGN,
352 TOKEN_PLUS,
353 TOKEN_MINUS,
354 TOKEN_COMMA,
355 TOKEN_VERTICAL_BAR,
356 TOKEN_SEMI_COLON,
357 TOKEN_LEFT_PAREN,
358 TOKEN_RIGHT_PAREN,
359 TOKEN_LEFT_BRACKET,
360 TOKEN_RIGHT_BRACKET,
361 TOKEN_LEFT_BRACE,
362 TOKEN_RIGHT_BRACE,
363 TOKEN_GREATER,
364
365 TOKEN_LAST
366 };
367
368 void parseError (const std::string& errorStr);
369 float parseFloatLiteral (const char* str);
370 int parseIntLiteral (const char* str);
371 string parseStringLiteral (const char* str);
372 string parseShaderSource (const char* str);
373 void advanceToken (void);
374 void advanceToken (Token assumed);
375 void assumeToken (Token token);
376 DataType mapDataTypeToken (Token token);
377 const char* getTokenName (Token token);
378 deUint32 getShaderStageLiteralFlag (void);
379 deUint32 getGLEnumFromName (const std::string& enumName);
380
381 void parseValueElement (DataType dataType, Value& result);
382 void parseValue (ValueBlock& valueBlock);
383 void parseValueBlock (ValueBlock& valueBlock);
384 deUint32 parseShaderStageList (void);
385 void parseRequirement (vector<RequiredCapability> &requiredCaps, vector<RequiredExtension> &requiredExts);
386 void parseExpectResult (ExpectResult& expectResult);
387 void parseFormat (DataType& format);
388 void parseGLSLVersion (glu::GLSLVersion& version);
389 void parsePipelineProgram (ProgramSpecification& program);
390 void parseShaderCase (vector<tcu::TestNode*>& shaderNodeList);
391 void parseShaderGroup (vector<tcu::TestNode*>& shaderNodeList);
392 void parseImport (vector<tcu::TestNode*>& shaderNodeList);
393
394 const tcu::Archive& m_archive;
395 const string m_filename;
396 ShaderCaseFactory* const m_caseFactory;
397
398 UniquePtr<tcu::Resource> m_resource;
399 vector<char> m_input;
400
401 const char* m_curPtr;
402 Token m_curToken;
403 std::string m_curTokenStr;
404 };
405
ShaderParser(const tcu::Archive & archive,const string & filename,ShaderCaseFactory * caseFactroy)406 ShaderParser::ShaderParser (const tcu::Archive& archive, const string& filename, ShaderCaseFactory* caseFactroy)
407 : m_archive (archive)
408 , m_filename (filename)
409 , m_caseFactory (caseFactroy)
410 , m_resource (archive.getResource(m_filename.c_str()))
411 , m_curPtr (DE_NULL)
412 , m_curToken (TOKEN_LAST)
413 {
414 }
415
~ShaderParser(void)416 ShaderParser::~ShaderParser (void)
417 {
418 }
419
parseError(const std::string & errorStr)420 void ShaderParser::parseError (const std::string& errorStr)
421 {
422 string atStr = string(m_curPtr, 80);
423 throw tcu::InternalError((string("Parser error: ") + errorStr + " near '" + atStr + " ...'").c_str(), DE_NULL, __FILE__, __LINE__);
424 }
425
parseFloatLiteral(const char * str)426 float ShaderParser::parseFloatLiteral (const char* str)
427 {
428 return (float)atof(str);
429 }
430
parseIntLiteral(const char * str)431 int ShaderParser::parseIntLiteral (const char* str)
432 {
433 return atoi(str);
434 }
435
parseStringLiteral(const char * str)436 string ShaderParser::parseStringLiteral (const char* str)
437 {
438 const char* p = str;
439 char endChar = *p++;
440 ostringstream o;
441
442 while (*p != endChar && *p)
443 {
444 if (*p == '\\')
445 {
446 switch (p[1])
447 {
448 case 0: DE_ASSERT(DE_FALSE); break;
449 case 'n': o << '\n'; break;
450 case 't': o << '\t'; break;
451 default: o << p[1]; break;
452 }
453
454 p += 2;
455 }
456 else
457 o << *p++;
458 }
459
460 return o.str();
461 }
462
removeExtraIndentation(const string & source)463 static string removeExtraIndentation (const string& source)
464 {
465 // Detect indentation from first line.
466 int numIndentChars = 0;
467 for (int ndx = 0; ndx < (int)source.length() && isWhitespace(source[ndx]); ndx++)
468 numIndentChars += source[ndx] == '\t' ? 4 : 1;
469
470 // Process all lines and remove preceding indentation.
471 ostringstream processed;
472 {
473 bool atLineStart = true;
474 int indentCharsOmitted = 0;
475
476 for (int pos = 0; pos < (int)source.length(); pos++)
477 {
478 char c = source[pos];
479
480 if (atLineStart && indentCharsOmitted < numIndentChars && (c == ' ' || c == '\t'))
481 {
482 indentCharsOmitted += c == '\t' ? 4 : 1;
483 }
484 else if (isEOL(c))
485 {
486 if (source[pos] == '\r' && source[pos+1] == '\n')
487 {
488 pos += 1;
489 processed << '\n';
490 }
491 else
492 processed << c;
493
494 atLineStart = true;
495 indentCharsOmitted = 0;
496 }
497 else
498 {
499 processed << c;
500 atLineStart = false;
501 }
502 }
503 }
504
505 return processed.str();
506 }
507
parseShaderSource(const char * str)508 string ShaderParser::parseShaderSource (const char* str)
509 {
510 const char* p = str+2;
511 ostringstream o;
512
513 // Eat first empty line from beginning.
514 while (*p == ' ') p++;
515 if (*p == '\r') p++;
516 if (*p == '\n') p++;
517
518 while ((p[0] != '"') || (p[1] != '"'))
519 {
520 if (*p == '\\')
521 {
522 switch (p[1])
523 {
524 case 0: DE_ASSERT(DE_FALSE); break;
525 case 'n': o << '\n'; break;
526 case 't': o << '\t'; break;
527 default: o << p[1]; break;
528 }
529
530 p += 2;
531 }
532 else
533 o << *p++;
534 }
535
536 return removeExtraIndentation(o.str());
537 }
538
advanceToken(void)539 void ShaderParser::advanceToken (void)
540 {
541 // Skip old token.
542 m_curPtr += m_curTokenStr.length();
543
544 // Reset token (for safety).
545 m_curToken = TOKEN_INVALID;
546 m_curTokenStr = "";
547
548 // Eat whitespace & comments while they last.
549 for (;;)
550 {
551 while (isWhitespace(*m_curPtr))
552 m_curPtr++;
553
554 // Check for EOL comment.
555 if (*m_curPtr == '#')
556 {
557 while (*m_curPtr && !isEOL(*m_curPtr))
558 m_curPtr++;
559 }
560 else
561 break;
562 }
563
564 if (!*m_curPtr)
565 {
566 m_curToken = TOKEN_EOF;
567 m_curTokenStr = "<EOF>";
568 }
569 else if (isAlpha(*m_curPtr))
570 {
571 struct Named
572 {
573 const char* str;
574 Token token;
575 };
576
577 static const Named s_named[] =
578 {
579 { "true", TOKEN_TRUE },
580 { "false", TOKEN_FALSE },
581 { "desc", TOKEN_DESC },
582 { "expect", TOKEN_EXPECT },
583 { "group", TOKEN_GROUP },
584 { "case", TOKEN_CASE },
585 { "end", TOKEN_END },
586 { "output_color", TOKEN_OUTPUT_COLOR },
587 { "format", TOKEN_FORMAT },
588 { "values", TOKEN_VALUES },
589 { "both", TOKEN_BOTH },
590 { "vertex", TOKEN_VERTEX },
591 { "fragment", TOKEN_FRAGMENT },
592 { "uniform", TOKEN_UNIFORM },
593 { "input", TOKEN_INPUT },
594 { "output", TOKEN_OUTPUT },
595 { "float", TOKEN_FLOAT },
596 { "vec2", TOKEN_FLOAT_VEC2 },
597 { "vec3", TOKEN_FLOAT_VEC3 },
598 { "vec4", TOKEN_FLOAT_VEC4 },
599 { "mat2", TOKEN_FLOAT_MAT2 },
600 { "mat2x3", TOKEN_FLOAT_MAT2X3 },
601 { "mat2x4", TOKEN_FLOAT_MAT2X4 },
602 { "mat3x2", TOKEN_FLOAT_MAT3X2 },
603 { "mat3", TOKEN_FLOAT_MAT3 },
604 { "mat3x4", TOKEN_FLOAT_MAT3X4 },
605 { "mat4x2", TOKEN_FLOAT_MAT4X2 },
606 { "mat4x3", TOKEN_FLOAT_MAT4X3 },
607 { "mat4", TOKEN_FLOAT_MAT4 },
608 { "int", TOKEN_INT },
609 { "ivec2", TOKEN_INT_VEC2 },
610 { "ivec3", TOKEN_INT_VEC3 },
611 { "ivec4", TOKEN_INT_VEC4 },
612 { "uint", TOKEN_UINT },
613 { "uvec2", TOKEN_UINT_VEC2 },
614 { "uvec3", TOKEN_UINT_VEC3 },
615 { "uvec4", TOKEN_UINT_VEC4 },
616 { "bool", TOKEN_BOOL },
617 { "bvec2", TOKEN_BOOL_VEC2 },
618 { "bvec3", TOKEN_BOOL_VEC3 },
619 { "bvec4", TOKEN_BOOL_VEC4 },
620 { "version", TOKEN_VERSION },
621 { "tessellation_control", TOKEN_TESSELLATION_CONTROL },
622 { "tessellation_evaluation", TOKEN_TESSELLATION_EVALUATION },
623 { "geometry", TOKEN_GEOMETRY },
624 { "require", TOKEN_REQUIRE },
625 { "in", TOKEN_IN },
626 { "import", TOKEN_IMPORT },
627 { "pipeline_program", TOKEN_PIPELINE_PROGRAM },
628 { "active_stages", TOKEN_ACTIVE_STAGES },
629 };
630
631 const char* end = m_curPtr + 1;
632 while (isCaseNameChar(*end))
633 end++;
634 m_curTokenStr = string(m_curPtr, end - m_curPtr);
635
636 m_curToken = TOKEN_IDENTIFIER;
637
638 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_named); ndx++)
639 {
640 if (m_curTokenStr == s_named[ndx].str)
641 {
642 m_curToken = s_named[ndx].token;
643 break;
644 }
645 }
646 }
647 else if (isNumeric(*m_curPtr))
648 {
649 /* \todo [2010-03-31 petri] Hex? */
650 const char* p = m_curPtr;
651 while (isNumeric(*p))
652 p++;
653 if (*p == '.')
654 {
655 p++;
656 while (isNumeric(*p))
657 p++;
658
659 if (*p == 'e' || *p == 'E')
660 {
661 p++;
662 if (*p == '+' || *p == '-')
663 p++;
664 DE_ASSERT(isNumeric(*p));
665 while (isNumeric(*p))
666 p++;
667 }
668
669 m_curToken = TOKEN_FLOAT_LITERAL;
670 m_curTokenStr = string(m_curPtr, p - m_curPtr);
671 }
672 else
673 {
674 m_curToken = TOKEN_INT_LITERAL;
675 m_curTokenStr = string(m_curPtr, p - m_curPtr);
676 }
677 }
678 else if (*m_curPtr == '"' && m_curPtr[1] == '"')
679 {
680 const char* p = m_curPtr + 2;
681
682 while ((p[0] != '"') || (p[1] != '"'))
683 {
684 DE_ASSERT(*p);
685 if (*p == '\\')
686 {
687 DE_ASSERT(p[1] != 0);
688 p += 2;
689 }
690 else
691 p++;
692 }
693 p += 2;
694
695 m_curToken = TOKEN_SHADER_SOURCE;
696 m_curTokenStr = string(m_curPtr, (int)(p - m_curPtr));
697 }
698 else if (*m_curPtr == '"' || *m_curPtr == '\'')
699 {
700 char endChar = *m_curPtr;
701 const char* p = m_curPtr + 1;
702
703 while (*p != endChar)
704 {
705 DE_ASSERT(*p);
706 if (*p == '\\')
707 {
708 DE_ASSERT(p[1] != 0);
709 p += 2;
710 }
711 else
712 p++;
713 }
714 p++;
715
716 m_curToken = TOKEN_STRING;
717 m_curTokenStr = string(m_curPtr, (int)(p - m_curPtr));
718 }
719 else
720 {
721 struct SimpleToken
722 {
723 const char* str;
724 Token token;
725 };
726
727 static const SimpleToken s_simple[] =
728 {
729 { "=", TOKEN_ASSIGN },
730 { "+", TOKEN_PLUS },
731 { "-", TOKEN_MINUS },
732 { ",", TOKEN_COMMA },
733 { "|", TOKEN_VERTICAL_BAR },
734 { ";", TOKEN_SEMI_COLON },
735 { "(", TOKEN_LEFT_PAREN },
736 { ")", TOKEN_RIGHT_PAREN },
737 { "[", TOKEN_LEFT_BRACKET },
738 { "]", TOKEN_RIGHT_BRACKET },
739 { "{", TOKEN_LEFT_BRACE },
740 { "}", TOKEN_RIGHT_BRACE },
741 { ">", TOKEN_GREATER },
742 };
743
744 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_simple); ndx++)
745 {
746 if (strncmp(s_simple[ndx].str, m_curPtr, strlen(s_simple[ndx].str)) == 0)
747 {
748 m_curToken = s_simple[ndx].token;
749 m_curTokenStr = s_simple[ndx].str;
750 return;
751 }
752 }
753
754 // Otherwise invalid token.
755 m_curToken = TOKEN_INVALID;
756 m_curTokenStr = *m_curPtr;
757 }
758 }
759
advanceToken(Token assumed)760 void ShaderParser::advanceToken (Token assumed)
761 {
762 assumeToken(assumed);
763 advanceToken();
764 }
765
assumeToken(Token token)766 void ShaderParser::assumeToken (Token token)
767 {
768 if (m_curToken != token)
769 parseError((string("unexpected token '") + m_curTokenStr + "', expecting '" + getTokenName(token) + "'").c_str());
770 DE_TEST_ASSERT(m_curToken == token);
771 }
772
mapDataTypeToken(Token token)773 DataType ShaderParser::mapDataTypeToken (Token token)
774 {
775 switch (token)
776 {
777 case TOKEN_FLOAT: return TYPE_FLOAT;
778 case TOKEN_FLOAT_VEC2: return TYPE_FLOAT_VEC2;
779 case TOKEN_FLOAT_VEC3: return TYPE_FLOAT_VEC3;
780 case TOKEN_FLOAT_VEC4: return TYPE_FLOAT_VEC4;
781 case TOKEN_FLOAT_MAT2: return TYPE_FLOAT_MAT2;
782 case TOKEN_FLOAT_MAT2X3: return TYPE_FLOAT_MAT2X3;
783 case TOKEN_FLOAT_MAT2X4: return TYPE_FLOAT_MAT2X4;
784 case TOKEN_FLOAT_MAT3X2: return TYPE_FLOAT_MAT3X2;
785 case TOKEN_FLOAT_MAT3: return TYPE_FLOAT_MAT3;
786 case TOKEN_FLOAT_MAT3X4: return TYPE_FLOAT_MAT3X4;
787 case TOKEN_FLOAT_MAT4X2: return TYPE_FLOAT_MAT4X2;
788 case TOKEN_FLOAT_MAT4X3: return TYPE_FLOAT_MAT4X3;
789 case TOKEN_FLOAT_MAT4: return TYPE_FLOAT_MAT4;
790 case TOKEN_INT: return TYPE_INT;
791 case TOKEN_INT_VEC2: return TYPE_INT_VEC2;
792 case TOKEN_INT_VEC3: return TYPE_INT_VEC3;
793 case TOKEN_INT_VEC4: return TYPE_INT_VEC4;
794 case TOKEN_UINT: return TYPE_UINT;
795 case TOKEN_UINT_VEC2: return TYPE_UINT_VEC2;
796 case TOKEN_UINT_VEC3: return TYPE_UINT_VEC3;
797 case TOKEN_UINT_VEC4: return TYPE_UINT_VEC4;
798 case TOKEN_BOOL: return TYPE_BOOL;
799 case TOKEN_BOOL_VEC2: return TYPE_BOOL_VEC2;
800 case TOKEN_BOOL_VEC3: return TYPE_BOOL_VEC3;
801 case TOKEN_BOOL_VEC4: return TYPE_BOOL_VEC4;
802 default: return TYPE_INVALID;
803 }
804 }
805
getTokenName(Token token)806 const char* ShaderParser::getTokenName (Token token)
807 {
808 switch (token)
809 {
810 case TOKEN_INVALID: return "<invalid>";
811 case TOKEN_EOF: return "<eof>";
812 case TOKEN_STRING: return "<string>";
813 case TOKEN_SHADER_SOURCE: return "source";
814
815 case TOKEN_INT_LITERAL: return "<int>";
816 case TOKEN_FLOAT_LITERAL: return "<float>";
817
818 // identifiers
819 case TOKEN_IDENTIFIER: return "<identifier>";
820 case TOKEN_TRUE: return "true";
821 case TOKEN_FALSE: return "false";
822 case TOKEN_DESC: return "desc";
823 case TOKEN_EXPECT: return "expect";
824 case TOKEN_GROUP: return "group";
825 case TOKEN_CASE: return "case";
826 case TOKEN_END: return "end";
827 case TOKEN_VALUES: return "values";
828 case TOKEN_BOTH: return "both";
829 case TOKEN_VERTEX: return "vertex";
830 case TOKEN_FRAGMENT: return "fragment";
831 case TOKEN_TESSELLATION_CONTROL: return "tessellation_control";
832 case TOKEN_TESSELLATION_EVALUATION: return "tessellation_evaluation";
833 case TOKEN_GEOMETRY: return "geometry";
834 case TOKEN_REQUIRE: return "require";
835 case TOKEN_UNIFORM: return "uniform";
836 case TOKEN_INPUT: return "input";
837 case TOKEN_OUTPUT: return "output";
838 case TOKEN_FLOAT: return "float";
839 case TOKEN_FLOAT_VEC2: return "vec2";
840 case TOKEN_FLOAT_VEC3: return "vec3";
841 case TOKEN_FLOAT_VEC4: return "vec4";
842 case TOKEN_FLOAT_MAT2: return "mat2";
843 case TOKEN_FLOAT_MAT2X3: return "mat2x3";
844 case TOKEN_FLOAT_MAT2X4: return "mat2x4";
845 case TOKEN_FLOAT_MAT3X2: return "mat3x2";
846 case TOKEN_FLOAT_MAT3: return "mat3";
847 case TOKEN_FLOAT_MAT3X4: return "mat3x4";
848 case TOKEN_FLOAT_MAT4X2: return "mat4x2";
849 case TOKEN_FLOAT_MAT4X3: return "mat4x3";
850 case TOKEN_FLOAT_MAT4: return "mat4";
851 case TOKEN_INT: return "int";
852 case TOKEN_INT_VEC2: return "ivec2";
853 case TOKEN_INT_VEC3: return "ivec3";
854 case TOKEN_INT_VEC4: return "ivec4";
855 case TOKEN_UINT: return "uint";
856 case TOKEN_UINT_VEC2: return "uvec2";
857 case TOKEN_UINT_VEC3: return "uvec3";
858 case TOKEN_UINT_VEC4: return "uvec4";
859 case TOKEN_BOOL: return "bool";
860 case TOKEN_BOOL_VEC2: return "bvec2";
861 case TOKEN_BOOL_VEC3: return "bvec3";
862 case TOKEN_BOOL_VEC4: return "bvec4";
863 case TOKEN_IN: return "in";
864 case TOKEN_IMPORT: return "import";
865 case TOKEN_PIPELINE_PROGRAM: return "pipeline_program";
866 case TOKEN_ACTIVE_STAGES: return "active_stages";
867
868 case TOKEN_ASSIGN: return "=";
869 case TOKEN_PLUS: return "+";
870 case TOKEN_MINUS: return "-";
871 case TOKEN_COMMA: return ",";
872 case TOKEN_VERTICAL_BAR: return "|";
873 case TOKEN_SEMI_COLON: return ";";
874 case TOKEN_LEFT_PAREN: return "(";
875 case TOKEN_RIGHT_PAREN: return ")";
876 case TOKEN_LEFT_BRACKET: return "[";
877 case TOKEN_RIGHT_BRACKET: return "]";
878 case TOKEN_LEFT_BRACE: return "{";
879 case TOKEN_RIGHT_BRACE: return "}";
880 case TOKEN_GREATER: return ">";
881
882 default: return "<unknown>";
883 }
884 }
885
getShaderStageLiteralFlag(void)886 deUint32 ShaderParser::getShaderStageLiteralFlag (void)
887 {
888 switch (m_curToken)
889 {
890 case TOKEN_VERTEX: return (1 << glu::SHADERTYPE_VERTEX);
891 case TOKEN_FRAGMENT: return (1 << glu::SHADERTYPE_FRAGMENT);
892 case TOKEN_GEOMETRY: return (1 << glu::SHADERTYPE_GEOMETRY);
893 case TOKEN_TESSELLATION_CONTROL: return (1 << glu::SHADERTYPE_TESSELLATION_CONTROL);
894 case TOKEN_TESSELLATION_EVALUATION: return (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION);
895
896 default:
897 parseError(std::string() + "invalid shader stage name, got " + m_curTokenStr);
898 return 0;
899 }
900 }
901
getGLEnumFromName(const std::string & enumName)902 deUint32 ShaderParser::getGLEnumFromName (const std::string& enumName)
903 {
904 static const struct
905 {
906 const char* name;
907 deUint32 value;
908 } names[] =
909 {
910 { "GL_MAX_VERTEX_IMAGE_UNIFORMS", GL_MAX_VERTEX_IMAGE_UNIFORMS },
911 { "GL_MAX_VERTEX_ATOMIC_COUNTERS", GL_MAX_VERTEX_ATOMIC_COUNTERS },
912 { "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS", GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS },
913 { "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS", GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS },
914 };
915
916 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(names); ++ndx)
917 if (names[ndx].name == enumName)
918 return names[ndx].value;
919
920 parseError(std::string() + "unknown enum name, got " + enumName);
921 return 0;
922 }
923
parseValueElement(DataType expectedDataType,Value & result)924 void ShaderParser::parseValueElement (DataType expectedDataType, Value& result)
925 {
926 DataType scalarType = getDataTypeScalarType(expectedDataType);
927 int scalarSize = getDataTypeScalarSize(expectedDataType);
928
929 /* \todo [2010-04-19 petri] Support arrays. */
930 Value::Element elems[16];
931
932 if (scalarSize > 1)
933 {
934 DE_ASSERT(mapDataTypeToken(m_curToken) == expectedDataType);
935 advanceToken(); // data type (float, vec2, etc.)
936 advanceToken(TOKEN_LEFT_PAREN);
937 }
938
939 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
940 {
941 if (scalarType == TYPE_FLOAT)
942 {
943 float signMult = 1.0f;
944 if (m_curToken == TOKEN_MINUS)
945 {
946 signMult = -1.0f;
947 advanceToken();
948 }
949
950 assumeToken(TOKEN_FLOAT_LITERAL);
951 elems[scalarNdx].float32 = signMult * parseFloatLiteral(m_curTokenStr.c_str());
952 advanceToken(TOKEN_FLOAT_LITERAL);
953 }
954 else if (scalarType == TYPE_INT || scalarType == TYPE_UINT)
955 {
956 int signMult = 1;
957 if (m_curToken == TOKEN_MINUS)
958 {
959 signMult = -1;
960 advanceToken();
961 }
962
963 assumeToken(TOKEN_INT_LITERAL);
964 elems[scalarNdx].int32 = signMult * parseIntLiteral(m_curTokenStr.c_str());
965 advanceToken(TOKEN_INT_LITERAL);
966 }
967 else
968 {
969 DE_ASSERT(scalarType == TYPE_BOOL);
970 elems[scalarNdx].bool32 = (m_curToken == TOKEN_TRUE);
971 if (m_curToken != TOKEN_TRUE && m_curToken != TOKEN_FALSE)
972 parseError(string("unexpected token, expecting bool: " + m_curTokenStr));
973 advanceToken(); // true/false
974 }
975
976 if (scalarNdx != (scalarSize - 1))
977 advanceToken(TOKEN_COMMA);
978 }
979
980 if (scalarSize > 1)
981 advanceToken(TOKEN_RIGHT_PAREN);
982
983 // Store results.
984 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
985 result.elements.push_back(elems[scalarNdx]);
986 }
987
parseValue(ValueBlock & valueBlock)988 void ShaderParser::parseValue (ValueBlock& valueBlock)
989 {
990 PARSE_DBG((" parseValue()\n"));
991
992 // Parsed results.
993 vector<Value>* dstBlock = DE_NULL;
994 DataType basicType = TYPE_LAST;
995 std::string valueName;
996
997 // Parse storage.
998 if (m_curToken == TOKEN_UNIFORM)
999 dstBlock = &valueBlock.uniforms;
1000 else if (m_curToken == TOKEN_INPUT)
1001 dstBlock = &valueBlock.inputs;
1002 else if (m_curToken == TOKEN_OUTPUT)
1003 dstBlock = &valueBlock.outputs;
1004 else
1005 parseError(string("unexpected token encountered when parsing value classifier"));
1006 advanceToken();
1007
1008 // Parse data type.
1009 basicType = mapDataTypeToken(m_curToken);
1010 if (basicType == TYPE_INVALID)
1011 parseError(string("unexpected token when parsing value data type: " + m_curTokenStr));
1012 advanceToken();
1013
1014 // Parse value name.
1015 if (m_curToken == TOKEN_IDENTIFIER || m_curToken == TOKEN_STRING)
1016 {
1017 if (m_curToken == TOKEN_IDENTIFIER)
1018 valueName = m_curTokenStr;
1019 else
1020 valueName = parseStringLiteral(m_curTokenStr.c_str());
1021 }
1022 else
1023 parseError(string("unexpected token when parsing value name: " + m_curTokenStr));
1024 advanceToken();
1025
1026 // Parse assignment operator.
1027 advanceToken(TOKEN_ASSIGN);
1028
1029 {
1030 Value value;
1031 value.name = valueName;
1032 value.type = VarType(basicType, PRECISION_LAST);
1033 dstBlock->push_back(value);
1034 }
1035
1036 // Parse actual value.
1037 if (m_curToken == TOKEN_LEFT_BRACKET) // value list
1038 {
1039 int arrayLength = 0; // \todo [2015-08-03 pyry] Currently unused
1040
1041 advanceToken(TOKEN_LEFT_BRACKET);
1042
1043 for (;;)
1044 {
1045 parseValueElement(basicType, dstBlock->back());
1046 arrayLength++;
1047
1048 if (m_curToken == TOKEN_RIGHT_BRACKET)
1049 break;
1050 else if (m_curToken == TOKEN_VERTICAL_BAR)
1051 {
1052 advanceToken();
1053 continue;
1054 }
1055 else
1056 parseError(string("unexpected token in value element array: " + m_curTokenStr));
1057 }
1058
1059 advanceToken(TOKEN_RIGHT_BRACKET);
1060 }
1061 else // single elements
1062 {
1063 parseValueElement(basicType, dstBlock->back());
1064 }
1065
1066 advanceToken(TOKEN_SEMI_COLON); // end of declaration
1067 }
1068
parseValueBlock(ValueBlock & valueBlock)1069 void ShaderParser::parseValueBlock (ValueBlock& valueBlock)
1070 {
1071 PARSE_DBG((" parseValueBlock()\n"));
1072 advanceToken(TOKEN_VALUES);
1073 advanceToken(TOKEN_LEFT_BRACE);
1074
1075 for (;;)
1076 {
1077 if (m_curToken == TOKEN_UNIFORM || m_curToken == TOKEN_INPUT || m_curToken == TOKEN_OUTPUT)
1078 parseValue(valueBlock);
1079 else if (m_curToken == TOKEN_RIGHT_BRACE)
1080 break;
1081 else
1082 parseError(string("unexpected token when parsing a value block: " + m_curTokenStr));
1083 }
1084
1085 advanceToken(TOKEN_RIGHT_BRACE);
1086 }
1087
parseShaderStageList(void)1088 deUint32 ShaderParser::parseShaderStageList (void)
1089 {
1090 deUint32 mask = 0;
1091
1092 assumeToken(TOKEN_LEFT_BRACE);
1093
1094 // don't allow 0-sized lists
1095 advanceToken();
1096 mask |= getShaderStageLiteralFlag();
1097 advanceToken();
1098
1099 for (;;)
1100 {
1101 if (m_curToken == TOKEN_RIGHT_BRACE)
1102 break;
1103 else if (m_curToken == TOKEN_COMMA)
1104 {
1105 deUint32 stageFlag;
1106 advanceToken();
1107
1108 stageFlag = getShaderStageLiteralFlag();
1109 if (stageFlag & mask)
1110 parseError(string("stage already set in the shader stage set: " + m_curTokenStr));
1111
1112 mask |= stageFlag;
1113 advanceToken();
1114 }
1115 else
1116 parseError(string("invalid shader stage set token: " + m_curTokenStr));
1117 }
1118 advanceToken(TOKEN_RIGHT_BRACE);
1119
1120 return mask;
1121 }
1122
parseRequirement(vector<RequiredCapability> & requiredCaps,vector<RequiredExtension> & requiredExts)1123 void ShaderParser::parseRequirement (vector<RequiredCapability>& requiredCaps, vector<RequiredExtension>& requiredExts)
1124 {
1125 PARSE_DBG((" parseRequirement()\n"));
1126
1127 advanceToken();
1128 assumeToken(TOKEN_IDENTIFIER);
1129
1130 if (m_curTokenStr == "extension")
1131 {
1132 std::vector<std::string> anyExtensionStringList;
1133 deUint32 affectedCasesFlags = -1; // by default all stages
1134
1135 advanceToken();
1136 assumeToken(TOKEN_LEFT_BRACE);
1137
1138 advanceToken();
1139 assumeToken(TOKEN_STRING);
1140
1141 anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str()));
1142 advanceToken();
1143
1144 for (;;)
1145 {
1146 if (m_curToken == TOKEN_RIGHT_BRACE)
1147 break;
1148 else if (m_curToken == TOKEN_VERTICAL_BAR)
1149 {
1150 advanceToken();
1151 assumeToken(TOKEN_STRING);
1152
1153 anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str()));
1154 advanceToken();
1155 }
1156 else
1157 parseError(string("invalid extension list token: " + m_curTokenStr));
1158 }
1159 advanceToken(TOKEN_RIGHT_BRACE);
1160
1161 if (m_curToken == TOKEN_IN)
1162 {
1163 advanceToken();
1164 affectedCasesFlags = parseShaderStageList();
1165 }
1166
1167 requiredExts.push_back(RequiredExtension(anyExtensionStringList, affectedCasesFlags));
1168 }
1169 else if (m_curTokenStr == "limit")
1170 {
1171 deUint32 limitEnum;
1172 int limitValue;
1173
1174 advanceToken();
1175
1176 assumeToken(TOKEN_STRING);
1177 limitEnum = getGLEnumFromName(parseStringLiteral(m_curTokenStr.c_str()));
1178 advanceToken();
1179
1180 assumeToken(TOKEN_GREATER);
1181 advanceToken();
1182
1183 assumeToken(TOKEN_INT_LITERAL);
1184 limitValue = parseIntLiteral(m_curTokenStr.c_str());
1185 advanceToken();
1186
1187 requiredCaps.push_back(RequiredCapability(limitEnum, limitValue));
1188 }
1189 else if (m_curTokenStr == "full_glsl_es_100_support")
1190 {
1191 advanceToken();
1192
1193 requiredCaps.push_back(RequiredCapability(CAPABILITY_FULL_GLSL_ES_100_SUPPORT));
1194 }
1195 else if (m_curTokenStr == "only_glsl_es_100_support")
1196 {
1197 advanceToken();
1198
1199 requiredCaps.push_back(RequiredCapability(CAPABILITY_ONLY_GLSL_ES_100_SUPPORT));
1200 }
1201 else if (m_curTokenStr == "exactly_one_draw_buffer")
1202 {
1203 advanceToken();
1204
1205 requiredCaps.push_back(RequiredCapability(CAPABILITY_EXACTLY_ONE_DRAW_BUFFER));
1206 }
1207 else
1208 parseError(string("invalid requirement value: " + m_curTokenStr));
1209 }
1210
parseExpectResult(ExpectResult & expectResult)1211 void ShaderParser::parseExpectResult (ExpectResult& expectResult)
1212 {
1213 assumeToken(TOKEN_IDENTIFIER);
1214
1215 if (m_curTokenStr == "pass")
1216 expectResult = EXPECT_PASS;
1217 else if (m_curTokenStr == "compile_fail")
1218 expectResult = EXPECT_COMPILE_FAIL;
1219 else if (m_curTokenStr == "link_fail")
1220 expectResult = EXPECT_LINK_FAIL;
1221 else if (m_curTokenStr == "compile_or_link_fail")
1222 expectResult = EXPECT_COMPILE_LINK_FAIL;
1223 else if (m_curTokenStr == "validation_fail")
1224 expectResult = EXPECT_VALIDATION_FAIL;
1225 else if (m_curTokenStr == "build_successful")
1226 expectResult = EXPECT_BUILD_SUCCESSFUL;
1227 else
1228 parseError(string("invalid expected result value: " + m_curTokenStr));
1229
1230 advanceToken();
1231 }
1232
parseFormat(DataType & format)1233 void ShaderParser::parseFormat (DataType& format)
1234 {
1235 format = mapDataTypeToken(m_curToken);
1236 advanceToken();
1237 }
1238
parseGLSLVersion(glu::GLSLVersion & version)1239 void ShaderParser::parseGLSLVersion (glu::GLSLVersion& version)
1240 {
1241 int versionNum = 0;
1242 std::string postfix = "";
1243
1244 assumeToken(TOKEN_INT_LITERAL);
1245 versionNum = parseIntLiteral(m_curTokenStr.c_str());
1246 advanceToken();
1247
1248 if (m_curToken == TOKEN_IDENTIFIER)
1249 {
1250 postfix = m_curTokenStr;
1251 advanceToken();
1252 }
1253
1254 DE_STATIC_ASSERT(glu::GLSL_VERSION_LAST == 15);
1255
1256 if (versionNum == 100 && postfix == "es") version = glu::GLSL_VERSION_100_ES;
1257 else if (versionNum == 300 && postfix == "es") version = glu::GLSL_VERSION_300_ES;
1258 else if (versionNum == 310 && postfix == "es") version = glu::GLSL_VERSION_310_ES;
1259 else if (versionNum == 320 && postfix == "es") version = glu::GLSL_VERSION_320_ES;
1260 else if (versionNum == 130) version = glu::GLSL_VERSION_130;
1261 else if (versionNum == 140) version = glu::GLSL_VERSION_140;
1262 else if (versionNum == 150) version = glu::GLSL_VERSION_150;
1263 else if (versionNum == 330) version = glu::GLSL_VERSION_330;
1264 else if (versionNum == 400) version = glu::GLSL_VERSION_400;
1265 else if (versionNum == 410) version = glu::GLSL_VERSION_410;
1266 else if (versionNum == 420) version = glu::GLSL_VERSION_420;
1267 else if (versionNum == 430) version = glu::GLSL_VERSION_430;
1268 else if (versionNum == 440) version = glu::GLSL_VERSION_440;
1269 else if (versionNum == 450) version = glu::GLSL_VERSION_450;
1270 else if (versionNum == 460) version = glu::GLSL_VERSION_460;
1271 else
1272 parseError("Unknown GLSL version");
1273 }
1274
parsePipelineProgram(ProgramSpecification & program)1275 void ShaderParser::parsePipelineProgram (ProgramSpecification& program)
1276 {
1277 advanceToken(TOKEN_PIPELINE_PROGRAM);
1278
1279 for (;;)
1280 {
1281 if (m_curToken == TOKEN_END)
1282 break;
1283 else if (m_curToken == TOKEN_ACTIVE_STAGES)
1284 {
1285 advanceToken();
1286 program.activeStages = parseShaderStageList();
1287 }
1288 else if (m_curToken == TOKEN_REQUIRE)
1289 {
1290 vector<RequiredCapability> dummyCaps;
1291 size_t size = program.requiredExtensions.size();
1292 parseRequirement(dummyCaps, program.requiredExtensions);
1293
1294 if (size == program.requiredExtensions.size())
1295 parseError("only extension requirements are allowed inside pipeline program");
1296 }
1297 else if (m_curToken == TOKEN_VERTEX ||
1298 m_curToken == TOKEN_FRAGMENT ||
1299 m_curToken == TOKEN_TESSELLATION_CONTROL ||
1300 m_curToken == TOKEN_TESSELLATION_EVALUATION ||
1301 m_curToken == TOKEN_GEOMETRY)
1302 {
1303 const Token token = m_curToken;
1304 string source;
1305
1306 advanceToken();
1307 assumeToken(TOKEN_SHADER_SOURCE);
1308 source = parseShaderSource(m_curTokenStr.c_str());
1309 advanceToken();
1310
1311 switch (token)
1312 {
1313 case TOKEN_VERTEX: program.sources.sources[SHADERTYPE_VERTEX].push_back(source); break;
1314 case TOKEN_FRAGMENT: program.sources.sources[SHADERTYPE_FRAGMENT].push_back(source); break;
1315 case TOKEN_TESSELLATION_CONTROL: program.sources.sources[SHADERTYPE_TESSELLATION_CONTROL].push_back(source); break;
1316 case TOKEN_TESSELLATION_EVALUATION: program.sources.sources[SHADERTYPE_TESSELLATION_EVALUATION].push_back(source); break;
1317 case TOKEN_GEOMETRY: program.sources.sources[SHADERTYPE_GEOMETRY].push_back(source); break;
1318 default:
1319 parseError(DE_FALSE);
1320 }
1321 }
1322 else
1323 parseError(string("invalid pipeline program value: " + m_curTokenStr));
1324 }
1325 advanceToken(TOKEN_END);
1326
1327 if (program.activeStages == 0)
1328 parseError("program pipeline object must have active stages");
1329 }
1330
parseShaderCase(vector<tcu::TestNode * > & shaderNodeList)1331 void ShaderParser::parseShaderCase (vector<tcu::TestNode*>& shaderNodeList)
1332 {
1333 // Parse 'case'.
1334 PARSE_DBG((" parseShaderCase()\n"));
1335 advanceToken(TOKEN_CASE);
1336
1337 // Parse case name.
1338 string caseName = m_curTokenStr;
1339 advanceToken(); // \note [pyry] All token types are allowed here.
1340
1341 // \todo [pyry] Optimize by parsing most stuff directly to ShaderCaseSpecification
1342
1343 // Setup case.
1344 GLSLVersion version = DEFAULT_GLSL_VERSION;
1345 ExpectResult expectResult = EXPECT_PASS;
1346 OutputType outputType = OUTPUT_RESULT;
1347 DataType format = TYPE_LAST;
1348 string description;
1349 string bothSource;
1350 vector<string> vertexSources;
1351 vector<string> fragmentSources;
1352 vector<string> tessellationCtrlSources;
1353 vector<string> tessellationEvalSources;
1354 vector<string> geometrySources;
1355 ValueBlock valueBlock;
1356 bool valueBlockSeen = false;
1357 vector<RequiredCapability> requiredCaps;
1358 vector<RequiredExtension> requiredExts;
1359 vector<ProgramSpecification> pipelinePrograms;
1360
1361 for (;;)
1362 {
1363 if (m_curToken == TOKEN_END)
1364 break;
1365 else if (m_curToken == TOKEN_DESC)
1366 {
1367 advanceToken();
1368 assumeToken(TOKEN_STRING);
1369 description = parseStringLiteral(m_curTokenStr.c_str());
1370 advanceToken();
1371 }
1372 else if (m_curToken == TOKEN_EXPECT)
1373 {
1374 advanceToken();
1375 parseExpectResult(expectResult);
1376 }
1377 else if (m_curToken == TOKEN_OUTPUT_COLOR)
1378 {
1379 outputType = OUTPUT_COLOR;
1380 advanceToken();
1381 parseFormat(format);
1382 }
1383 else if (m_curToken == TOKEN_VALUES)
1384 {
1385 if (valueBlockSeen)
1386 parseError("multiple value blocks");
1387 parseValueBlock(valueBlock);
1388 valueBlockSeen = true;
1389 }
1390 else if (m_curToken == TOKEN_BOTH ||
1391 m_curToken == TOKEN_VERTEX ||
1392 m_curToken == TOKEN_FRAGMENT ||
1393 m_curToken == TOKEN_TESSELLATION_CONTROL ||
1394 m_curToken == TOKEN_TESSELLATION_EVALUATION ||
1395 m_curToken == TOKEN_GEOMETRY)
1396 {
1397 const Token token = m_curToken;
1398 string source;
1399
1400 advanceToken();
1401 assumeToken(TOKEN_SHADER_SOURCE);
1402 source = parseShaderSource(m_curTokenStr.c_str());
1403 advanceToken();
1404
1405 switch (token)
1406 {
1407 case TOKEN_VERTEX: vertexSources.push_back(source); break;
1408 case TOKEN_FRAGMENT: fragmentSources.push_back(source); break;
1409 case TOKEN_TESSELLATION_CONTROL: tessellationCtrlSources.push_back(source); break;
1410 case TOKEN_TESSELLATION_EVALUATION: tessellationEvalSources.push_back(source); break;
1411 case TOKEN_GEOMETRY: geometrySources.push_back(source); break;
1412 case TOKEN_BOTH:
1413 {
1414 if (!bothSource.empty())
1415 parseError("multiple 'both' blocks");
1416 bothSource = source;
1417 break;
1418 }
1419
1420 default:
1421 parseError(DE_FALSE);
1422 }
1423 }
1424 else if (m_curToken == TOKEN_VERSION)
1425 {
1426 advanceToken();
1427 parseGLSLVersion(version);
1428 }
1429 else if (m_curToken == TOKEN_REQUIRE)
1430 {
1431 parseRequirement(requiredCaps, requiredExts);
1432 }
1433 else if (m_curToken == TOKEN_PIPELINE_PROGRAM)
1434 {
1435 ProgramSpecification pipelineProgram;
1436 parsePipelineProgram(pipelineProgram);
1437 pipelineProgram.sources.separable = true;
1438 pipelinePrograms.push_back(pipelineProgram);
1439 }
1440 else
1441 parseError(string("unexpected token while parsing shader case: " + m_curTokenStr));
1442 }
1443
1444 advanceToken(TOKEN_END); // case end
1445
1446 if (!bothSource.empty())
1447 {
1448 if (!vertexSources.empty() ||
1449 !fragmentSources.empty() ||
1450 !tessellationCtrlSources.empty() ||
1451 !tessellationEvalSources.empty() ||
1452 !geometrySources.empty() ||
1453 !pipelinePrograms.empty())
1454 {
1455 parseError("'both' cannot be mixed with other shader stages");
1456 }
1457
1458 // vertex
1459 {
1460 ShaderCaseSpecification spec;
1461 spec.caseType = CASETYPE_VERTEX_ONLY;
1462 spec.expectResult = expectResult;
1463 spec.targetVersion = version;
1464 spec.requiredCaps = requiredCaps;
1465 spec.values = valueBlock;
1466
1467 spec.programs.resize(1);
1468 spec.programs[0].sources << VertexSource(bothSource);
1469 spec.programs[0].requiredExtensions = requiredExts;
1470
1471 shaderNodeList.push_back(m_caseFactory->createCase(caseName + "_vertex", description, ShaderCaseSpecification(spec)));
1472 }
1473
1474 // fragment
1475 {
1476 ShaderCaseSpecification spec;
1477 spec.caseType = CASETYPE_FRAGMENT_ONLY;
1478 spec.expectResult = expectResult;
1479 spec.targetVersion = version;
1480 spec.requiredCaps = requiredCaps;
1481 spec.values = valueBlock;
1482
1483 spec.programs.resize(1);
1484 spec.programs[0].sources << FragmentSource(bothSource);
1485 spec.programs[0].requiredExtensions = requiredExts;
1486
1487 shaderNodeList.push_back(m_caseFactory->createCase(caseName + "_fragment", description, ShaderCaseSpecification(spec)));
1488 }
1489 }
1490 else if (pipelinePrograms.empty())
1491 {
1492 ShaderCaseSpecification spec;
1493 spec.caseType = CASETYPE_COMPLETE;
1494 spec.expectResult = expectResult;
1495 spec.outputType = outputType;
1496 spec.outputFormat = format;
1497 spec.targetVersion = version;
1498 spec.requiredCaps = requiredCaps;
1499 spec.values = valueBlock;
1500
1501 spec.programs.resize(1);
1502 spec.programs[0].sources.sources[SHADERTYPE_VERTEX].swap(vertexSources);
1503 spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].swap(fragmentSources);
1504 spec.programs[0].sources.sources[SHADERTYPE_TESSELLATION_CONTROL].swap(tessellationCtrlSources);
1505 spec.programs[0].sources.sources[SHADERTYPE_TESSELLATION_EVALUATION].swap(tessellationEvalSources);
1506 spec.programs[0].sources.sources[SHADERTYPE_GEOMETRY].swap(geometrySources);
1507 spec.programs[0].requiredExtensions.swap(requiredExts);
1508
1509 shaderNodeList.push_back(m_caseFactory->createCase(caseName, description, ShaderCaseSpecification(spec)));
1510 }
1511 else
1512 {
1513 if (!vertexSources.empty() ||
1514 !fragmentSources.empty() ||
1515 !tessellationCtrlSources.empty() ||
1516 !tessellationEvalSources.empty() ||
1517 !geometrySources.empty())
1518 {
1519 parseError("pipeline programs cannot be mixed with complete programs");
1520 }
1521
1522 if (!requiredExts.empty())
1523 parseError("global extension requirements cannot be mixed with pipeline programs");
1524
1525 // Pipeline case, multiple programs
1526 {
1527 ShaderCaseSpecification spec;
1528 spec.caseType = CASETYPE_COMPLETE;
1529 spec.expectResult = expectResult;
1530 spec.targetVersion = version;
1531 spec.requiredCaps = requiredCaps;
1532 spec.values = valueBlock;
1533
1534 spec.programs.swap(pipelinePrograms);
1535
1536 shaderNodeList.push_back(m_caseFactory->createCase(caseName, description, ShaderCaseSpecification(spec)));
1537 }
1538 }
1539 }
1540
parseShaderGroup(vector<tcu::TestNode * > & shaderNodeList)1541 void ShaderParser::parseShaderGroup (vector<tcu::TestNode*>& shaderNodeList)
1542 {
1543 // Parse 'case'.
1544 PARSE_DBG((" parseShaderGroup()\n"));
1545 advanceToken(TOKEN_GROUP);
1546
1547 // Parse case name.
1548 string name = m_curTokenStr;
1549 advanceToken(); // \note [pyry] We don't want to check token type here (for instance to allow "uniform") group.
1550
1551 // Parse description.
1552 assumeToken(TOKEN_STRING);
1553 string description = parseStringLiteral(m_curTokenStr.c_str());
1554 advanceToken(TOKEN_STRING);
1555
1556 std::vector<tcu::TestNode*> children;
1557
1558 // Parse group children.
1559 for (;;)
1560 {
1561 if (m_curToken == TOKEN_END)
1562 break;
1563 else if (m_curToken == TOKEN_GROUP)
1564 parseShaderGroup(children);
1565 else if (m_curToken == TOKEN_CASE)
1566 parseShaderCase(children);
1567 else if (m_curToken == TOKEN_IMPORT)
1568 parseImport(children);
1569 else
1570 parseError(string("unexpected token while parsing shader group: " + m_curTokenStr));
1571 }
1572
1573 advanceToken(TOKEN_END); // group end
1574
1575 // Create group node.
1576 tcu::TestCaseGroup* groupNode = m_caseFactory->createGroup(name, description, children);
1577 shaderNodeList.push_back(groupNode);
1578 }
1579
parseImport(vector<tcu::TestNode * > & shaderNodeList)1580 void ShaderParser::parseImport (vector<tcu::TestNode*>& shaderNodeList)
1581 {
1582 std::string importFileName;
1583
1584 advanceToken(TOKEN_IMPORT);
1585
1586 assumeToken(TOKEN_STRING);
1587 importFileName = parseStringLiteral(m_curTokenStr.c_str());
1588 advanceToken(TOKEN_STRING);
1589
1590 {
1591 ShaderParser subParser (m_archive, de::FilePath::join(de::FilePath(m_filename).getDirName(), importFileName).getPath(), m_caseFactory);
1592 const vector<tcu::TestNode*> importedCases = subParser.parse();
1593
1594 // \todo [2015-08-03 pyry] Not exception safe
1595 shaderNodeList.insert(shaderNodeList.end(), importedCases.begin(), importedCases.end());
1596 }
1597 }
1598
parse(void)1599 vector<tcu::TestNode*> ShaderParser::parse (void)
1600 {
1601 const int dataLen = m_resource->getSize();
1602
1603 m_input.resize(dataLen+1);
1604 m_resource->setPosition(0);
1605 m_resource->read((deUint8*)&m_input[0], dataLen);
1606 m_input[dataLen] = '\0';
1607
1608 // Initialize parser.
1609 m_curPtr = &m_input[0];
1610 m_curToken = TOKEN_INVALID;
1611 m_curTokenStr = "";
1612 advanceToken();
1613
1614 vector<tcu::TestNode*> nodeList;
1615
1616 // Parse all cases.
1617 PARSE_DBG(("parse()\n"));
1618 for (;;)
1619 {
1620 if (m_curToken == TOKEN_CASE)
1621 parseShaderCase(nodeList);
1622 else if (m_curToken == TOKEN_GROUP)
1623 parseShaderGroup(nodeList);
1624 else if (m_curToken == TOKEN_IMPORT)
1625 parseImport(nodeList);
1626 else if (m_curToken == TOKEN_EOF)
1627 break;
1628 else
1629 parseError(string("invalid token encountered at main level: '") + m_curTokenStr + "'");
1630 }
1631
1632 assumeToken(TOKEN_EOF);
1633 // printf(" parsed %d test cases.\n", caseList.size());
1634 return nodeList;
1635 }
1636
parseFile(const tcu::Archive & archive,const std::string & filename,ShaderCaseFactory * caseFactory)1637 std::vector<tcu::TestNode*> parseFile (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory)
1638 {
1639 sl::ShaderParser parser (archive, filename, caseFactory);
1640
1641 return parser.parse();
1642 }
1643
1644 // Execution utilities
1645
dumpValue(tcu::TestLog & log,const Value & val,const char * storageName,int arrayNdx)1646 static void dumpValue (tcu::TestLog& log, const Value& val, const char* storageName, int arrayNdx)
1647 {
1648 const char* const valueName = val.name.c_str();
1649 const DataType dataType = val.type.getBasicType();
1650 int scalarSize = getDataTypeScalarSize(dataType);
1651 ostringstream result;
1652
1653 result << " " << storageName << " ";
1654
1655 result << getDataTypeName(dataType) << " " << valueName << ":";
1656
1657 if (isDataTypeScalar(dataType))
1658 result << " ";
1659 if (isDataTypeVector(dataType))
1660 result << " [ ";
1661 else if (isDataTypeMatrix(dataType))
1662 result << "\n";
1663
1664 if (isDataTypeScalarOrVector(dataType))
1665 {
1666 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1667 {
1668 int elemNdx = arrayNdx;
1669 const Value::Element& e = val.elements[elemNdx*scalarSize + scalarNdx];
1670 result << ((scalarNdx != 0) ? ", " : "");
1671
1672 if (isDataTypeFloatOrVec(dataType))
1673 result << e.float32;
1674 else if (isDataTypeIntOrIVec(dataType))
1675 result << e.int32;
1676 else if (isDataTypeUintOrUVec(dataType))
1677 result << (deUint32)e.int32;
1678 else if (isDataTypeBoolOrBVec(dataType))
1679 result << (e.bool32 ? "true" : "false");
1680 }
1681 }
1682 else if (isDataTypeMatrix(dataType))
1683 {
1684 int numRows = getDataTypeMatrixNumRows(dataType);
1685 int numCols = getDataTypeMatrixNumColumns(dataType);
1686 for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
1687 {
1688 result << " [ ";
1689 for (int colNdx = 0; colNdx < numCols; colNdx++)
1690 {
1691 int elemNdx = arrayNdx;
1692 float v = val.elements[elemNdx*scalarSize + rowNdx*numCols + colNdx].float32;
1693 result << ((colNdx==0) ? "" : ", ") << v;
1694 }
1695 result << " ]\n";
1696 }
1697 }
1698
1699 if (isDataTypeScalar(dataType))
1700 result << "\n";
1701 else if (isDataTypeVector(dataType))
1702 result << " ]\n";
1703
1704 log << TestLog::Message << result.str() << TestLog::EndMessage;
1705 }
1706
dumpValues(tcu::TestLog & log,const vector<Value> & values,const char * storageName,int arrayNdx)1707 static void dumpValues (tcu::TestLog& log, const vector<Value>& values, const char* storageName, int arrayNdx)
1708 {
1709 for (size_t valNdx = 0; valNdx < values.size(); valNdx++)
1710 dumpValue(log, values[valNdx], storageName, arrayNdx);
1711 }
1712
dumpValues(tcu::TestLog & log,const ValueBlock & values,int arrayNdx)1713 void dumpValues (tcu::TestLog& log, const ValueBlock& values, int arrayNdx)
1714 {
1715 dumpValues(log, values.inputs, "input", arrayNdx);
1716 dumpValues(log, values.outputs, "expected", arrayNdx);
1717 dumpValues(log, values.uniforms, "uniform", arrayNdx);
1718 }
1719
generateExtensionStatements(std::ostringstream & buf,const std::vector<RequiredExtension> & extensions,glu::ShaderType type)1720 static void generateExtensionStatements (std::ostringstream& buf, const std::vector<RequiredExtension>& extensions, glu::ShaderType type)
1721 {
1722 for (size_t ndx = 0; ndx < extensions.size(); ++ndx)
1723 {
1724 DE_ASSERT(extensions[ndx].effectiveStages != 0u &&
1725 extensions[ndx].alternatives.size() == 1);
1726
1727 if ((extensions[ndx].effectiveStages & (1u << (deUint32)type)) != 0)
1728 buf << "#extension " << extensions[ndx].alternatives[0] << " : require\n";
1729 }
1730 }
1731
1732 // Injects #extension XXX : require lines after the last preprocessor directive in the shader code. Does not support line continuations
injectExtensionRequirements(const std::string & baseCode,const std::vector<RequiredExtension> & extensions,glu::ShaderType shaderType)1733 std::string injectExtensionRequirements (const std::string& baseCode, const std::vector<RequiredExtension>& extensions, glu::ShaderType shaderType)
1734 {
1735 std::istringstream baseCodeBuf (baseCode);
1736 std::ostringstream resultBuf;
1737 std::string line;
1738 bool firstNonPreprocessorLine = true;
1739 std::ostringstream extStr;
1740
1741 generateExtensionStatements(extStr, extensions, shaderType);
1742
1743 // skip if no requirements
1744 if (extStr.str().empty())
1745 return baseCode;
1746
1747 while (std::getline(baseCodeBuf, line))
1748 {
1749 // begins with '#'?
1750 const std::string::size_type firstNonWhitespace = line.find_first_not_of("\t ");
1751 const bool isPreprocessorDirective = (firstNonWhitespace != std::string::npos && line.at(firstNonWhitespace) == '#');
1752
1753 // Inject #extensions
1754 if (!isPreprocessorDirective && firstNonPreprocessorLine)
1755 {
1756 firstNonPreprocessorLine = false;
1757 resultBuf << extStr.str();
1758 }
1759
1760 resultBuf << line << "\n";
1761 }
1762
1763 return resultBuf.str();
1764 }
1765
genCompareFunctions(ostringstream & stream,const ValueBlock & valueBlock,bool useFloatTypes)1766 void genCompareFunctions (ostringstream& stream, const ValueBlock& valueBlock, bool useFloatTypes)
1767 {
1768 bool cmpTypeFound[TYPE_LAST];
1769 for (int i = 0; i < TYPE_LAST; i++)
1770 cmpTypeFound[i] = false;
1771
1772 for (size_t valueNdx = 0; valueNdx < valueBlock.outputs.size(); valueNdx++)
1773 {
1774 const Value& val = valueBlock.outputs[valueNdx];
1775 cmpTypeFound[(size_t)val.type.getBasicType()] = true;
1776 }
1777
1778 if (useFloatTypes)
1779 {
1780 if (cmpTypeFound[TYPE_BOOL]) stream << "bool isOk (float a, bool b) { return ((a > 0.5) == b); }\n";
1781 if (cmpTypeFound[TYPE_BOOL_VEC2]) stream << "bool isOk (vec2 a, bvec2 b) { return (greaterThan(a, vec2(0.5)) == b); }\n";
1782 if (cmpTypeFound[TYPE_BOOL_VEC3]) stream << "bool isOk (vec3 a, bvec3 b) { return (greaterThan(a, vec3(0.5)) == b); }\n";
1783 if (cmpTypeFound[TYPE_BOOL_VEC4]) stream << "bool isOk (vec4 a, bvec4 b) { return (greaterThan(a, vec4(0.5)) == b); }\n";
1784 if (cmpTypeFound[TYPE_INT]) stream << "bool isOk (float a, int b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1)); }\n";
1785 if (cmpTypeFound[TYPE_INT_VEC2]) stream << "bool isOk (vec2 a, ivec2 b) { return (ivec2(floor(a + 0.5)) == b); }\n";
1786 if (cmpTypeFound[TYPE_INT_VEC3]) stream << "bool isOk (vec3 a, ivec3 b) { return (ivec3(floor(a + 0.5)) == b); }\n";
1787 if (cmpTypeFound[TYPE_INT_VEC4]) stream << "bool isOk (vec4 a, ivec4 b) { return (ivec4(floor(a + 0.5)) == b); }\n";
1788 if (cmpTypeFound[TYPE_UINT]) stream << "bool isOk (float a, uint b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1u)); }\n";
1789 if (cmpTypeFound[TYPE_UINT_VEC2]) stream << "bool isOk (vec2 a, uvec2 b) { return (uvec2(floor(a + 0.5)) == b); }\n";
1790 if (cmpTypeFound[TYPE_UINT_VEC3]) stream << "bool isOk (vec3 a, uvec3 b) { return (uvec3(floor(a + 0.5)) == b); }\n";
1791 if (cmpTypeFound[TYPE_UINT_VEC4]) stream << "bool isOk (vec4 a, uvec4 b) { return (uvec4(floor(a + 0.5)) == b); }\n";
1792 }
1793 else
1794 {
1795 if (cmpTypeFound[TYPE_BOOL]) stream << "bool isOk (bool a, bool b) { return (a == b); }\n";
1796 if (cmpTypeFound[TYPE_BOOL_VEC2]) stream << "bool isOk (bvec2 a, bvec2 b) { return (a == b); }\n";
1797 if (cmpTypeFound[TYPE_BOOL_VEC3]) stream << "bool isOk (bvec3 a, bvec3 b) { return (a == b); }\n";
1798 if (cmpTypeFound[TYPE_BOOL_VEC4]) stream << "bool isOk (bvec4 a, bvec4 b) { return (a == b); }\n";
1799 if (cmpTypeFound[TYPE_INT]) stream << "bool isOk (int a, int b) { return (a == b); }\n";
1800 if (cmpTypeFound[TYPE_INT_VEC2]) stream << "bool isOk (ivec2 a, ivec2 b) { return (a == b); }\n";
1801 if (cmpTypeFound[TYPE_INT_VEC3]) stream << "bool isOk (ivec3 a, ivec3 b) { return (a == b); }\n";
1802 if (cmpTypeFound[TYPE_INT_VEC4]) stream << "bool isOk (ivec4 a, ivec4 b) { return (a == b); }\n";
1803 if (cmpTypeFound[TYPE_UINT]) stream << "bool isOk (uint a, uint b) { return (a == b); }\n";
1804 if (cmpTypeFound[TYPE_UINT_VEC2]) stream << "bool isOk (uvec2 a, uvec2 b) { return (a == b); }\n";
1805 if (cmpTypeFound[TYPE_UINT_VEC3]) stream << "bool isOk (uvec3 a, uvec3 b) { return (a == b); }\n";
1806 if (cmpTypeFound[TYPE_UINT_VEC4]) stream << "bool isOk (uvec4 a, uvec4 b) { return (a == b); }\n";
1807 }
1808
1809 if (cmpTypeFound[TYPE_FLOAT]) stream << "bool isOk (float a, float b, float eps) { return (abs(a-b) <= (eps*abs(b) + eps)); }\n";
1810 if (cmpTypeFound[TYPE_FLOAT_VEC2]) stream << "bool isOk (vec2 a, vec2 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
1811 if (cmpTypeFound[TYPE_FLOAT_VEC3]) stream << "bool isOk (vec3 a, vec3 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
1812 if (cmpTypeFound[TYPE_FLOAT_VEC4]) stream << "bool isOk (vec4 a, vec4 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
1813
1814 if (cmpTypeFound[TYPE_FLOAT_MAT2]) stream << "bool isOk (mat2 a, mat2 b, float eps) { vec2 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec2(eps))); }\n";
1815 if (cmpTypeFound[TYPE_FLOAT_MAT2X3]) stream << "bool isOk (mat2x3 a, mat2x3 b, float eps) { vec3 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec3(eps))); }\n";
1816 if (cmpTypeFound[TYPE_FLOAT_MAT2X4]) stream << "bool isOk (mat2x4 a, mat2x4 b, float eps) { vec4 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec4(eps))); }\n";
1817 if (cmpTypeFound[TYPE_FLOAT_MAT3X2]) stream << "bool isOk (mat3x2 a, mat3x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec2(eps))); }\n";
1818 if (cmpTypeFound[TYPE_FLOAT_MAT3]) stream << "bool isOk (mat3 a, mat3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec3(eps))); }\n";
1819 if (cmpTypeFound[TYPE_FLOAT_MAT3X4]) stream << "bool isOk (mat3x4 a, mat3x4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec4(eps))); }\n";
1820 if (cmpTypeFound[TYPE_FLOAT_MAT4X2]) stream << "bool isOk (mat4x2 a, mat4x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec2(eps))); }\n";
1821 if (cmpTypeFound[TYPE_FLOAT_MAT4X3]) stream << "bool isOk (mat4x3 a, mat4x3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec3(eps))); }\n";
1822 if (cmpTypeFound[TYPE_FLOAT_MAT4]) stream << "bool isOk (mat4 a, mat4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec4(eps))); }\n";
1823 }
1824
1825 } // sl
1826 } // glu
1827