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 46namespace glu 47{ 48namespace sl 49{ 50 51using namespace tcu; 52 53using std::vector; 54using std::string; 55using std::map; 56using std::ostringstream; 57using std::pair; 58using de::UniquePtr; 59 60// Specification 61 62bool 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 92bool 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 236bool 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 251static const glu::GLSLVersion DEFAULT_GLSL_VERSION = glu::GLSL_VERSION_100_ES; 252 253DE_INLINE deBool isWhitespace (char c) 254{ 255 return (c == ' ') || (c == '\t') || (c == '\r') || (c == '\n'); 256} 257 258DE_INLINE deBool isEOL (char c) 259{ 260 return (c == '\r') || (c == '\n'); 261} 262 263DE_INLINE deBool isNumeric (char c) 264{ 265 return deInRange32(c, '0', '9'); 266} 267 268DE_INLINE deBool isAlpha (char c) 269{ 270 return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z'); 271} 272 273DE_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 278class ShaderParser 279{ 280public: 281 ShaderParser (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory); 282 ~ShaderParser (void); 283 284 vector<tcu::TestNode*> parse (void); 285 286private: 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 406ShaderParser::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 416ShaderParser::~ShaderParser (void) 417{ 418} 419 420void 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 426float ShaderParser::parseFloatLiteral (const char* str) 427{ 428 return (float)atof(str); 429} 430 431int ShaderParser::parseIntLiteral (const char* str) 432{ 433 return atoi(str); 434} 435 436string 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 463static 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 508string 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 539void 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 760void ShaderParser::advanceToken (Token assumed) 761{ 762 assumeToken(assumed); 763 advanceToken(); 764} 765 766void 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 773DataType 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 806const 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 886deUint32 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 902deUint32 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 924void 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 988void 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 advanceToken(TOKEN_LEFT_BRACKET); 1040 1041 for (;;) 1042 { 1043 parseValueElement(basicType, dstBlock->back()); 1044 1045 if (m_curToken == TOKEN_RIGHT_BRACKET) 1046 break; 1047 else if (m_curToken == TOKEN_VERTICAL_BAR) 1048 { 1049 advanceToken(); 1050 continue; 1051 } 1052 else 1053 parseError(string("unexpected token in value element array: " + m_curTokenStr)); 1054 } 1055 1056 advanceToken(TOKEN_RIGHT_BRACKET); 1057 } 1058 else // single elements 1059 { 1060 parseValueElement(basicType, dstBlock->back()); 1061 } 1062 1063 advanceToken(TOKEN_SEMI_COLON); // end of declaration 1064} 1065 1066void ShaderParser::parseValueBlock (ValueBlock& valueBlock) 1067{ 1068 PARSE_DBG((" parseValueBlock()\n")); 1069 advanceToken(TOKEN_VALUES); 1070 advanceToken(TOKEN_LEFT_BRACE); 1071 1072 for (;;) 1073 { 1074 if (m_curToken == TOKEN_UNIFORM || m_curToken == TOKEN_INPUT || m_curToken == TOKEN_OUTPUT) 1075 parseValue(valueBlock); 1076 else if (m_curToken == TOKEN_RIGHT_BRACE) 1077 break; 1078 else 1079 parseError(string("unexpected token when parsing a value block: " + m_curTokenStr)); 1080 } 1081 1082 advanceToken(TOKEN_RIGHT_BRACE); 1083} 1084 1085deUint32 ShaderParser::parseShaderStageList (void) 1086{ 1087 deUint32 mask = 0; 1088 1089 assumeToken(TOKEN_LEFT_BRACE); 1090 1091 // don't allow 0-sized lists 1092 advanceToken(); 1093 mask |= getShaderStageLiteralFlag(); 1094 advanceToken(); 1095 1096 for (;;) 1097 { 1098 if (m_curToken == TOKEN_RIGHT_BRACE) 1099 break; 1100 else if (m_curToken == TOKEN_COMMA) 1101 { 1102 deUint32 stageFlag; 1103 advanceToken(); 1104 1105 stageFlag = getShaderStageLiteralFlag(); 1106 if (stageFlag & mask) 1107 parseError(string("stage already set in the shader stage set: " + m_curTokenStr)); 1108 1109 mask |= stageFlag; 1110 advanceToken(); 1111 } 1112 else 1113 parseError(string("invalid shader stage set token: " + m_curTokenStr)); 1114 } 1115 advanceToken(TOKEN_RIGHT_BRACE); 1116 1117 return mask; 1118} 1119 1120void ShaderParser::parseRequirement (vector<RequiredCapability>& requiredCaps, vector<RequiredExtension>& requiredExts) 1121{ 1122 PARSE_DBG((" parseRequirement()\n")); 1123 1124 advanceToken(); 1125 assumeToken(TOKEN_IDENTIFIER); 1126 1127 if (m_curTokenStr == "extension") 1128 { 1129 std::vector<std::string> anyExtensionStringList; 1130 deUint32 affectedCasesFlags = -1; // by default all stages 1131 1132 advanceToken(); 1133 assumeToken(TOKEN_LEFT_BRACE); 1134 1135 advanceToken(); 1136 assumeToken(TOKEN_STRING); 1137 1138 anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str())); 1139 advanceToken(); 1140 1141 for (;;) 1142 { 1143 if (m_curToken == TOKEN_RIGHT_BRACE) 1144 break; 1145 else if (m_curToken == TOKEN_VERTICAL_BAR) 1146 { 1147 advanceToken(); 1148 assumeToken(TOKEN_STRING); 1149 1150 anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str())); 1151 advanceToken(); 1152 } 1153 else 1154 parseError(string("invalid extension list token: " + m_curTokenStr)); 1155 } 1156 advanceToken(TOKEN_RIGHT_BRACE); 1157 1158 if (m_curToken == TOKEN_IN) 1159 { 1160 advanceToken(); 1161 affectedCasesFlags = parseShaderStageList(); 1162 } 1163 1164 requiredExts.push_back(RequiredExtension(anyExtensionStringList, affectedCasesFlags)); 1165 } 1166 else if (m_curTokenStr == "limit") 1167 { 1168 deUint32 limitEnum; 1169 int limitValue; 1170 1171 advanceToken(); 1172 1173 assumeToken(TOKEN_STRING); 1174 limitEnum = getGLEnumFromName(parseStringLiteral(m_curTokenStr.c_str())); 1175 advanceToken(); 1176 1177 assumeToken(TOKEN_GREATER); 1178 advanceToken(); 1179 1180 assumeToken(TOKEN_INT_LITERAL); 1181 limitValue = parseIntLiteral(m_curTokenStr.c_str()); 1182 advanceToken(); 1183 1184 requiredCaps.push_back(RequiredCapability(limitEnum, limitValue)); 1185 } 1186 else if (m_curTokenStr == "full_glsl_es_100_support") 1187 { 1188 advanceToken(); 1189 1190 requiredCaps.push_back(RequiredCapability(CAPABILITY_FULL_GLSL_ES_100_SUPPORT)); 1191 } 1192 else if (m_curTokenStr == "only_glsl_es_100_support") 1193 { 1194 advanceToken(); 1195 1196 requiredCaps.push_back(RequiredCapability(CAPABILITY_ONLY_GLSL_ES_100_SUPPORT)); 1197 } 1198 else if (m_curTokenStr == "exactly_one_draw_buffer") 1199 { 1200 advanceToken(); 1201 1202 requiredCaps.push_back(RequiredCapability(CAPABILITY_EXACTLY_ONE_DRAW_BUFFER)); 1203 } 1204 else 1205 parseError(string("invalid requirement value: " + m_curTokenStr)); 1206} 1207 1208void ShaderParser::parseExpectResult (ExpectResult& expectResult) 1209{ 1210 assumeToken(TOKEN_IDENTIFIER); 1211 1212 if (m_curTokenStr == "pass") 1213 expectResult = EXPECT_PASS; 1214 else if (m_curTokenStr == "compile_fail") 1215 expectResult = EXPECT_COMPILE_FAIL; 1216 else if (m_curTokenStr == "link_fail") 1217 expectResult = EXPECT_LINK_FAIL; 1218 else if (m_curTokenStr == "compile_or_link_fail") 1219 expectResult = EXPECT_COMPILE_LINK_FAIL; 1220 else if (m_curTokenStr == "validation_fail") 1221 expectResult = EXPECT_VALIDATION_FAIL; 1222 else if (m_curTokenStr == "build_successful") 1223 expectResult = EXPECT_BUILD_SUCCESSFUL; 1224 else 1225 parseError(string("invalid expected result value: " + m_curTokenStr)); 1226 1227 advanceToken(); 1228} 1229 1230void ShaderParser::parseFormat (DataType& format) 1231{ 1232 format = mapDataTypeToken(m_curToken); 1233 advanceToken(); 1234} 1235 1236void ShaderParser::parseGLSLVersion (glu::GLSLVersion& version) 1237{ 1238 int versionNum = 0; 1239 std::string postfix = ""; 1240 1241 assumeToken(TOKEN_INT_LITERAL); 1242 versionNum = parseIntLiteral(m_curTokenStr.c_str()); 1243 advanceToken(); 1244 1245 if (m_curToken == TOKEN_IDENTIFIER) 1246 { 1247 postfix = m_curTokenStr; 1248 advanceToken(); 1249 } 1250 1251 DE_STATIC_ASSERT(glu::GLSL_VERSION_LAST == 15); 1252 1253 if (versionNum == 100 && postfix == "es") version = glu::GLSL_VERSION_100_ES; 1254 else if (versionNum == 300 && postfix == "es") version = glu::GLSL_VERSION_300_ES; 1255 else if (versionNum == 310 && postfix == "es") version = glu::GLSL_VERSION_310_ES; 1256 else if (versionNum == 320 && postfix == "es") version = glu::GLSL_VERSION_320_ES; 1257 else if (versionNum == 130) version = glu::GLSL_VERSION_130; 1258 else if (versionNum == 140) version = glu::GLSL_VERSION_140; 1259 else if (versionNum == 150) version = glu::GLSL_VERSION_150; 1260 else if (versionNum == 330) version = glu::GLSL_VERSION_330; 1261 else if (versionNum == 400) version = glu::GLSL_VERSION_400; 1262 else if (versionNum == 410) version = glu::GLSL_VERSION_410; 1263 else if (versionNum == 420) version = glu::GLSL_VERSION_420; 1264 else if (versionNum == 430) version = glu::GLSL_VERSION_430; 1265 else if (versionNum == 440) version = glu::GLSL_VERSION_440; 1266 else if (versionNum == 450) version = glu::GLSL_VERSION_450; 1267 else if (versionNum == 460) version = glu::GLSL_VERSION_460; 1268 else 1269 parseError("Unknown GLSL version"); 1270} 1271 1272void ShaderParser::parsePipelineProgram (ProgramSpecification& program) 1273{ 1274 advanceToken(TOKEN_PIPELINE_PROGRAM); 1275 1276 for (;;) 1277 { 1278 if (m_curToken == TOKEN_END) 1279 break; 1280 else if (m_curToken == TOKEN_ACTIVE_STAGES) 1281 { 1282 advanceToken(); 1283 program.activeStages = parseShaderStageList(); 1284 } 1285 else if (m_curToken == TOKEN_REQUIRE) 1286 { 1287 vector<RequiredCapability> unusedCaps; 1288 size_t size = program.requiredExtensions.size(); 1289 parseRequirement(unusedCaps, program.requiredExtensions); 1290 1291 if (size == program.requiredExtensions.size()) 1292 parseError("only extension requirements are allowed inside pipeline program"); 1293 } 1294 else if (m_curToken == TOKEN_VERTEX || 1295 m_curToken == TOKEN_FRAGMENT || 1296 m_curToken == TOKEN_TESSELLATION_CONTROL || 1297 m_curToken == TOKEN_TESSELLATION_EVALUATION || 1298 m_curToken == TOKEN_GEOMETRY) 1299 { 1300 const Token token = m_curToken; 1301 string source; 1302 1303 advanceToken(); 1304 assumeToken(TOKEN_SHADER_SOURCE); 1305 source = parseShaderSource(m_curTokenStr.c_str()); 1306 advanceToken(); 1307 1308 switch (token) 1309 { 1310 case TOKEN_VERTEX: program.sources.sources[SHADERTYPE_VERTEX].push_back(source); break; 1311 case TOKEN_FRAGMENT: program.sources.sources[SHADERTYPE_FRAGMENT].push_back(source); break; 1312 case TOKEN_TESSELLATION_CONTROL: program.sources.sources[SHADERTYPE_TESSELLATION_CONTROL].push_back(source); break; 1313 case TOKEN_TESSELLATION_EVALUATION: program.sources.sources[SHADERTYPE_TESSELLATION_EVALUATION].push_back(source); break; 1314 case TOKEN_GEOMETRY: program.sources.sources[SHADERTYPE_GEOMETRY].push_back(source); break; 1315 default: 1316 DE_FATAL("Unreachable"); 1317 } 1318 } 1319 else 1320 parseError(string("invalid pipeline program value: " + m_curTokenStr)); 1321 } 1322 advanceToken(TOKEN_END); 1323 1324 if (program.activeStages == 0) 1325 parseError("program pipeline object must have active stages"); 1326} 1327 1328void ShaderParser::parseShaderCase (vector<tcu::TestNode*>& shaderNodeList) 1329{ 1330 // Parse 'case'. 1331 PARSE_DBG((" parseShaderCase()\n")); 1332 advanceToken(TOKEN_CASE); 1333 1334 // Parse case name. 1335 string caseName = m_curTokenStr; 1336 advanceToken(); // \note [pyry] All token types are allowed here. 1337 1338 // \todo [pyry] Optimize by parsing most stuff directly to ShaderCaseSpecification 1339 1340 // Setup case. 1341 GLSLVersion version = DEFAULT_GLSL_VERSION; 1342 ExpectResult expectResult = EXPECT_PASS; 1343 OutputType outputType = OUTPUT_RESULT; 1344 DataType format = TYPE_LAST; 1345 string description; 1346 string bothSource; 1347 vector<string> vertexSources; 1348 vector<string> fragmentSources; 1349 vector<string> tessellationCtrlSources; 1350 vector<string> tessellationEvalSources; 1351 vector<string> geometrySources; 1352 ValueBlock valueBlock; 1353 bool valueBlockSeen = false; 1354 vector<RequiredCapability> requiredCaps; 1355 vector<RequiredExtension> requiredExts; 1356 vector<ProgramSpecification> pipelinePrograms; 1357 1358 for (;;) 1359 { 1360 if (m_curToken == TOKEN_END) 1361 break; 1362 else if (m_curToken == TOKEN_DESC) 1363 { 1364 advanceToken(); 1365 assumeToken(TOKEN_STRING); 1366 description = parseStringLiteral(m_curTokenStr.c_str()); 1367 advanceToken(); 1368 } 1369 else if (m_curToken == TOKEN_EXPECT) 1370 { 1371 advanceToken(); 1372 parseExpectResult(expectResult); 1373 } 1374 else if (m_curToken == TOKEN_OUTPUT_COLOR) 1375 { 1376 outputType = OUTPUT_COLOR; 1377 advanceToken(); 1378 parseFormat(format); 1379 } 1380 else if (m_curToken == TOKEN_VALUES) 1381 { 1382 if (valueBlockSeen) 1383 parseError("multiple value blocks"); 1384 parseValueBlock(valueBlock); 1385 valueBlockSeen = true; 1386 } 1387 else if (m_curToken == TOKEN_BOTH || 1388 m_curToken == TOKEN_VERTEX || 1389 m_curToken == TOKEN_FRAGMENT || 1390 m_curToken == TOKEN_TESSELLATION_CONTROL || 1391 m_curToken == TOKEN_TESSELLATION_EVALUATION || 1392 m_curToken == TOKEN_GEOMETRY) 1393 { 1394 const Token token = m_curToken; 1395 string source; 1396 1397 advanceToken(); 1398 assumeToken(TOKEN_SHADER_SOURCE); 1399 source = parseShaderSource(m_curTokenStr.c_str()); 1400 advanceToken(); 1401 1402 switch (token) 1403 { 1404 case TOKEN_VERTEX: vertexSources.push_back(source); break; 1405 case TOKEN_FRAGMENT: fragmentSources.push_back(source); break; 1406 case TOKEN_TESSELLATION_CONTROL: tessellationCtrlSources.push_back(source); break; 1407 case TOKEN_TESSELLATION_EVALUATION: tessellationEvalSources.push_back(source); break; 1408 case TOKEN_GEOMETRY: geometrySources.push_back(source); break; 1409 case TOKEN_BOTH: 1410 { 1411 if (!bothSource.empty()) 1412 parseError("multiple 'both' blocks"); 1413 bothSource = source; 1414 break; 1415 } 1416 1417 default: 1418 DE_FATAL("Unreachable"); 1419 } 1420 } 1421 else if (m_curToken == TOKEN_VERSION) 1422 { 1423 advanceToken(); 1424 parseGLSLVersion(version); 1425 } 1426 else if (m_curToken == TOKEN_REQUIRE) 1427 { 1428 parseRequirement(requiredCaps, requiredExts); 1429 } 1430 else if (m_curToken == TOKEN_PIPELINE_PROGRAM) 1431 { 1432 ProgramSpecification pipelineProgram; 1433 parsePipelineProgram(pipelineProgram); 1434 pipelineProgram.sources.separable = true; 1435 pipelinePrograms.push_back(pipelineProgram); 1436 } 1437 else 1438 parseError(string("unexpected token while parsing shader case: " + m_curTokenStr)); 1439 } 1440 1441 advanceToken(TOKEN_END); // case end 1442 1443 if (!bothSource.empty()) 1444 { 1445 if (!vertexSources.empty() || 1446 !fragmentSources.empty() || 1447 !tessellationCtrlSources.empty() || 1448 !tessellationEvalSources.empty() || 1449 !geometrySources.empty() || 1450 !pipelinePrograms.empty()) 1451 { 1452 parseError("'both' cannot be mixed with other shader stages"); 1453 } 1454 1455 // vertex 1456 { 1457 ShaderCaseSpecification spec; 1458 spec.caseType = CASETYPE_VERTEX_ONLY; 1459 spec.expectResult = expectResult; 1460 spec.targetVersion = version; 1461 spec.requiredCaps = requiredCaps; 1462 spec.values = valueBlock; 1463 1464 spec.programs.resize(1); 1465 spec.programs[0].sources << VertexSource(bothSource); 1466 spec.programs[0].requiredExtensions = requiredExts; 1467 1468 shaderNodeList.push_back(m_caseFactory->createCase(caseName + "_vertex", description, ShaderCaseSpecification(spec))); 1469 } 1470 1471 // fragment 1472 { 1473 ShaderCaseSpecification spec; 1474 spec.caseType = CASETYPE_FRAGMENT_ONLY; 1475 spec.expectResult = expectResult; 1476 spec.targetVersion = version; 1477 spec.requiredCaps = requiredCaps; 1478 spec.values = valueBlock; 1479 1480 spec.programs.resize(1); 1481 spec.programs[0].sources << FragmentSource(bothSource); 1482 spec.programs[0].requiredExtensions = requiredExts; 1483 1484 shaderNodeList.push_back(m_caseFactory->createCase(caseName + "_fragment", description, ShaderCaseSpecification(spec))); 1485 } 1486 } 1487 else if (pipelinePrograms.empty()) 1488 { 1489 ShaderCaseSpecification spec; 1490 spec.caseType = CASETYPE_COMPLETE; 1491 spec.expectResult = expectResult; 1492 spec.outputType = outputType; 1493 spec.outputFormat = format; 1494 spec.targetVersion = version; 1495 spec.requiredCaps = requiredCaps; 1496 spec.values = valueBlock; 1497 1498 spec.programs.resize(1); 1499 spec.programs[0].sources.sources[SHADERTYPE_VERTEX].swap(vertexSources); 1500 spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].swap(fragmentSources); 1501 spec.programs[0].sources.sources[SHADERTYPE_TESSELLATION_CONTROL].swap(tessellationCtrlSources); 1502 spec.programs[0].sources.sources[SHADERTYPE_TESSELLATION_EVALUATION].swap(tessellationEvalSources); 1503 spec.programs[0].sources.sources[SHADERTYPE_GEOMETRY].swap(geometrySources); 1504 spec.programs[0].requiredExtensions.swap(requiredExts); 1505 1506 shaderNodeList.push_back(m_caseFactory->createCase(caseName, description, ShaderCaseSpecification(spec))); 1507 } 1508 else 1509 { 1510 if (!vertexSources.empty() || 1511 !fragmentSources.empty() || 1512 !tessellationCtrlSources.empty() || 1513 !tessellationEvalSources.empty() || 1514 !geometrySources.empty()) 1515 { 1516 parseError("pipeline programs cannot be mixed with complete programs"); 1517 } 1518 1519 if (!requiredExts.empty()) 1520 parseError("global extension requirements cannot be mixed with pipeline programs"); 1521 1522 // Pipeline case, multiple programs 1523 { 1524 ShaderCaseSpecification spec; 1525 spec.caseType = CASETYPE_COMPLETE; 1526 spec.expectResult = expectResult; 1527 spec.targetVersion = version; 1528 spec.requiredCaps = requiredCaps; 1529 spec.values = valueBlock; 1530 1531 spec.programs.swap(pipelinePrograms); 1532 1533 shaderNodeList.push_back(m_caseFactory->createCase(caseName, description, ShaderCaseSpecification(spec))); 1534 } 1535 } 1536} 1537 1538void ShaderParser::parseShaderGroup (vector<tcu::TestNode*>& shaderNodeList) 1539{ 1540 // Parse 'case'. 1541 PARSE_DBG((" parseShaderGroup()\n")); 1542 advanceToken(TOKEN_GROUP); 1543 1544 // Parse case name. 1545 string name = m_curTokenStr; 1546 advanceToken(); // \note [pyry] We don't want to check token type here (for instance to allow "uniform") group. 1547 1548 // Parse description. 1549 assumeToken(TOKEN_STRING); 1550 string description = parseStringLiteral(m_curTokenStr.c_str()); 1551 advanceToken(TOKEN_STRING); 1552 1553 std::vector<tcu::TestNode*> children; 1554 1555 // Parse group children. 1556 for (;;) 1557 { 1558 if (m_curToken == TOKEN_END) 1559 break; 1560 else if (m_curToken == TOKEN_GROUP) 1561 parseShaderGroup(children); 1562 else if (m_curToken == TOKEN_CASE) 1563 parseShaderCase(children); 1564 else if (m_curToken == TOKEN_IMPORT) 1565 parseImport(children); 1566 else 1567 parseError(string("unexpected token while parsing shader group: " + m_curTokenStr)); 1568 } 1569 1570 advanceToken(TOKEN_END); // group end 1571 1572 // Create group node. 1573 tcu::TestCaseGroup* groupNode = m_caseFactory->createGroup(name, description, children); 1574 shaderNodeList.push_back(groupNode); 1575} 1576 1577void ShaderParser::parseImport (vector<tcu::TestNode*>& shaderNodeList) 1578{ 1579 std::string importFileName; 1580 1581 advanceToken(TOKEN_IMPORT); 1582 1583 assumeToken(TOKEN_STRING); 1584 importFileName = parseStringLiteral(m_curTokenStr.c_str()); 1585 advanceToken(TOKEN_STRING); 1586 1587 { 1588 ShaderParser subParser (m_archive, de::FilePath::join(de::FilePath(m_filename).getDirName(), importFileName).getPath(), m_caseFactory); 1589 const vector<tcu::TestNode*> importedCases = subParser.parse(); 1590 1591 // \todo [2015-08-03 pyry] Not exception safe 1592 shaderNodeList.insert(shaderNodeList.end(), importedCases.begin(), importedCases.end()); 1593 } 1594} 1595 1596vector<tcu::TestNode*> ShaderParser::parse (void) 1597{ 1598 const int dataLen = m_resource->getSize(); 1599 1600 m_input.resize(dataLen+1); 1601 m_resource->setPosition(0); 1602 m_resource->read((deUint8*)&m_input[0], dataLen); 1603 m_input[dataLen] = '\0'; 1604 1605 // Initialize parser. 1606 m_curPtr = &m_input[0]; 1607 m_curToken = TOKEN_INVALID; 1608 m_curTokenStr = ""; 1609 advanceToken(); 1610 1611 vector<tcu::TestNode*> nodeList; 1612 1613 // Parse all cases. 1614 PARSE_DBG(("parse()\n")); 1615 for (;;) 1616 { 1617 if (m_curToken == TOKEN_CASE) 1618 parseShaderCase(nodeList); 1619 else if (m_curToken == TOKEN_GROUP) 1620 parseShaderGroup(nodeList); 1621 else if (m_curToken == TOKEN_IMPORT) 1622 parseImport(nodeList); 1623 else if (m_curToken == TOKEN_EOF) 1624 break; 1625 else 1626 parseError(string("invalid token encountered at main level: '") + m_curTokenStr + "'"); 1627 } 1628 1629 assumeToken(TOKEN_EOF); 1630// printf(" parsed %d test cases.\n", caseList.size()); 1631 return nodeList; 1632} 1633 1634std::vector<tcu::TestNode*> parseFile (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory) 1635{ 1636 sl::ShaderParser parser (archive, filename, caseFactory); 1637 1638 return parser.parse(); 1639} 1640 1641// Execution utilities 1642 1643static void dumpValue (tcu::TestLog& log, const Value& val, const char* storageName, int arrayNdx) 1644{ 1645 const char* const valueName = val.name.c_str(); 1646 const DataType dataType = val.type.getBasicType(); 1647 int scalarSize = getDataTypeScalarSize(dataType); 1648 ostringstream result; 1649 1650 result << " " << storageName << " "; 1651 1652 result << getDataTypeName(dataType) << " " << valueName << ":"; 1653 1654 if (isDataTypeScalar(dataType)) 1655 result << " "; 1656 if (isDataTypeVector(dataType)) 1657 result << " [ "; 1658 else if (isDataTypeMatrix(dataType)) 1659 result << "\n"; 1660 1661 if (isDataTypeScalarOrVector(dataType)) 1662 { 1663 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++) 1664 { 1665 int elemNdx = arrayNdx; 1666 const Value::Element& e = val.elements[elemNdx*scalarSize + scalarNdx]; 1667 result << ((scalarNdx != 0) ? ", " : ""); 1668 1669 if (isDataTypeFloatOrVec(dataType)) 1670 result << e.float32; 1671 else if (isDataTypeIntOrIVec(dataType)) 1672 result << e.int32; 1673 else if (isDataTypeUintOrUVec(dataType)) 1674 result << (deUint32)e.int32; 1675 else if (isDataTypeBoolOrBVec(dataType)) 1676 result << (e.bool32 ? "true" : "false"); 1677 } 1678 } 1679 else if (isDataTypeMatrix(dataType)) 1680 { 1681 int numRows = getDataTypeMatrixNumRows(dataType); 1682 int numCols = getDataTypeMatrixNumColumns(dataType); 1683 for (int rowNdx = 0; rowNdx < numRows; rowNdx++) 1684 { 1685 result << " [ "; 1686 for (int colNdx = 0; colNdx < numCols; colNdx++) 1687 { 1688 int elemNdx = arrayNdx; 1689 float v = val.elements[elemNdx*scalarSize + rowNdx*numCols + colNdx].float32; 1690 result << ((colNdx==0) ? "" : ", ") << v; 1691 } 1692 result << " ]\n"; 1693 } 1694 } 1695 1696 if (isDataTypeScalar(dataType)) 1697 result << "\n"; 1698 else if (isDataTypeVector(dataType)) 1699 result << " ]\n"; 1700 1701 log << TestLog::Message << result.str() << TestLog::EndMessage; 1702} 1703 1704static void dumpValues (tcu::TestLog& log, const vector<Value>& values, const char* storageName, int arrayNdx) 1705{ 1706 for (size_t valNdx = 0; valNdx < values.size(); valNdx++) 1707 dumpValue(log, values[valNdx], storageName, arrayNdx); 1708} 1709 1710void dumpValues (tcu::TestLog& log, const ValueBlock& values, int arrayNdx) 1711{ 1712 dumpValues(log, values.inputs, "input", arrayNdx); 1713 dumpValues(log, values.outputs, "expected", arrayNdx); 1714 dumpValues(log, values.uniforms, "uniform", arrayNdx); 1715} 1716 1717static void generateExtensionStatements (std::ostringstream& buf, const std::vector<RequiredExtension>& extensions, glu::ShaderType type) 1718{ 1719 for (size_t ndx = 0; ndx < extensions.size(); ++ndx) 1720 { 1721 DE_ASSERT(extensions[ndx].effectiveStages != 0u && 1722 extensions[ndx].alternatives.size() == 1); 1723 1724 if ((extensions[ndx].effectiveStages & (1u << (deUint32)type)) != 0) 1725 buf << "#extension " << extensions[ndx].alternatives[0] << " : require\n"; 1726 } 1727} 1728 1729// Injects #extension XXX : require lines after the last preprocessor directive in the shader code. Does not support line continuations 1730std::string injectExtensionRequirements (const std::string& baseCode, const std::vector<RequiredExtension>& extensions, glu::ShaderType shaderType) 1731{ 1732 std::istringstream baseCodeBuf (baseCode); 1733 std::ostringstream resultBuf; 1734 std::string line; 1735 bool firstNonPreprocessorLine = true; 1736 std::ostringstream extStr; 1737 1738 generateExtensionStatements(extStr, extensions, shaderType); 1739 1740 // skip if no requirements 1741 if (extStr.str().empty()) 1742 return baseCode; 1743 1744 while (std::getline(baseCodeBuf, line)) 1745 { 1746 // begins with '#'? 1747 const std::string::size_type firstNonWhitespace = line.find_first_not_of("\t "); 1748 const bool isPreprocessorDirective = (firstNonWhitespace != std::string::npos && line.at(firstNonWhitespace) == '#'); 1749 1750 // Inject #extensions 1751 if (!isPreprocessorDirective && firstNonPreprocessorLine) 1752 { 1753 firstNonPreprocessorLine = false; 1754 resultBuf << extStr.str(); 1755 } 1756 1757 resultBuf << line << "\n"; 1758 } 1759 1760 return resultBuf.str(); 1761} 1762 1763void genCompareFunctions (ostringstream& stream, const ValueBlock& valueBlock, bool useFloatTypes) 1764{ 1765 bool cmpTypeFound[TYPE_LAST]; 1766 for (int i = 0; i < TYPE_LAST; i++) 1767 cmpTypeFound[i] = false; 1768 1769 for (size_t valueNdx = 0; valueNdx < valueBlock.outputs.size(); valueNdx++) 1770 { 1771 const Value& val = valueBlock.outputs[valueNdx]; 1772 cmpTypeFound[(size_t)val.type.getBasicType()] = true; 1773 } 1774 1775 if (useFloatTypes) 1776 { 1777 if (cmpTypeFound[TYPE_BOOL]) stream << "bool isOk (float a, bool b) { return ((a > 0.5) == b); }\n"; 1778 if (cmpTypeFound[TYPE_BOOL_VEC2]) stream << "bool isOk (vec2 a, bvec2 b) { return (greaterThan(a, vec2(0.5)) == b); }\n"; 1779 if (cmpTypeFound[TYPE_BOOL_VEC3]) stream << "bool isOk (vec3 a, bvec3 b) { return (greaterThan(a, vec3(0.5)) == b); }\n"; 1780 if (cmpTypeFound[TYPE_BOOL_VEC4]) stream << "bool isOk (vec4 a, bvec4 b) { return (greaterThan(a, vec4(0.5)) == b); }\n"; 1781 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"; 1782 if (cmpTypeFound[TYPE_INT_VEC2]) stream << "bool isOk (vec2 a, ivec2 b) { return (ivec2(floor(a + 0.5)) == b); }\n"; 1783 if (cmpTypeFound[TYPE_INT_VEC3]) stream << "bool isOk (vec3 a, ivec3 b) { return (ivec3(floor(a + 0.5)) == b); }\n"; 1784 if (cmpTypeFound[TYPE_INT_VEC4]) stream << "bool isOk (vec4 a, ivec4 b) { return (ivec4(floor(a + 0.5)) == b); }\n"; 1785 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"; 1786 if (cmpTypeFound[TYPE_UINT_VEC2]) stream << "bool isOk (vec2 a, uvec2 b) { return (uvec2(floor(a + 0.5)) == b); }\n"; 1787 if (cmpTypeFound[TYPE_UINT_VEC3]) stream << "bool isOk (vec3 a, uvec3 b) { return (uvec3(floor(a + 0.5)) == b); }\n"; 1788 if (cmpTypeFound[TYPE_UINT_VEC4]) stream << "bool isOk (vec4 a, uvec4 b) { return (uvec4(floor(a + 0.5)) == b); }\n"; 1789 } 1790 else 1791 { 1792 if (cmpTypeFound[TYPE_BOOL]) stream << "bool isOk (bool a, bool b) { return (a == b); }\n"; 1793 if (cmpTypeFound[TYPE_BOOL_VEC2]) stream << "bool isOk (bvec2 a, bvec2 b) { return (a == b); }\n"; 1794 if (cmpTypeFound[TYPE_BOOL_VEC3]) stream << "bool isOk (bvec3 a, bvec3 b) { return (a == b); }\n"; 1795 if (cmpTypeFound[TYPE_BOOL_VEC4]) stream << "bool isOk (bvec4 a, bvec4 b) { return (a == b); }\n"; 1796 if (cmpTypeFound[TYPE_INT]) stream << "bool isOk (int a, int b) { return (a == b); }\n"; 1797 if (cmpTypeFound[TYPE_INT_VEC2]) stream << "bool isOk (ivec2 a, ivec2 b) { return (a == b); }\n"; 1798 if (cmpTypeFound[TYPE_INT_VEC3]) stream << "bool isOk (ivec3 a, ivec3 b) { return (a == b); }\n"; 1799 if (cmpTypeFound[TYPE_INT_VEC4]) stream << "bool isOk (ivec4 a, ivec4 b) { return (a == b); }\n"; 1800 if (cmpTypeFound[TYPE_UINT]) stream << "bool isOk (uint a, uint b) { return (a == b); }\n"; 1801 if (cmpTypeFound[TYPE_UINT_VEC2]) stream << "bool isOk (uvec2 a, uvec2 b) { return (a == b); }\n"; 1802 if (cmpTypeFound[TYPE_UINT_VEC3]) stream << "bool isOk (uvec3 a, uvec3 b) { return (a == b); }\n"; 1803 if (cmpTypeFound[TYPE_UINT_VEC4]) stream << "bool isOk (uvec4 a, uvec4 b) { return (a == b); }\n"; 1804 } 1805 1806 if (cmpTypeFound[TYPE_FLOAT]) stream << "bool isOk (float a, float b, float eps) { return (abs(a-b) <= (eps*abs(b) + eps)); }\n"; 1807 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"; 1808 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"; 1809 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"; 1810 1811 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"; 1812 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"; 1813 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"; 1814 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"; 1815 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"; 1816 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"; 1817 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"; 1818 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"; 1819 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"; 1820} 1821 1822} // sl 1823} // glu 1824