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