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 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 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 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 253 DE_INLINE deBool isWhitespace (char c) 254 { 255 return (c == ' ') || (c == '\t') || (c == '\r') || (c == '\n'); 256 } 257 258 DE_INLINE deBool isEOL (char c) 259 { 260 return (c == '\r') || (c == '\n'); 261 } 262 263 DE_INLINE deBool isNumeric (char c) 264 { 265 return deInRange32(c, '0', '9'); 266 } 267 268 DE_INLINE deBool isAlpha (char c) 269 { 270 return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z'); 271 } 272 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 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 416 ShaderParser::~ShaderParser (void) 417 { 418 } 419 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 426 float ShaderParser::parseFloatLiteral (const char* str) 427 { 428 return (float)atof(str); 429 } 430 431 int ShaderParser::parseIntLiteral (const char* str) 432 { 433 return atoi(str); 434 } 435 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 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 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 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 760 void ShaderParser::advanceToken (Token assumed) 761 { 762 assumeToken(assumed); 763 advanceToken(); 764 } 765 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 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 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 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 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 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 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 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 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 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 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 1233 void ShaderParser::parseFormat (DataType& format) 1234 { 1235 format = mapDataTypeToken(m_curToken); 1236 advanceToken(); 1237 } 1238 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 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 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 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 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 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 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 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 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 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 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 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 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