glsShaderLibrary.js (43573B)
1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES Utilities 3 * ------------------------------------------------ 4 * 5 * Copyright 2014 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 21 'use strict'; 22 goog.provide('modules.shared.glsShaderLibrary'); 23 goog.require('framework.common.tcuTestCase'); 24 goog.require('framework.opengl.gluShaderUtil'); 25 goog.require('modules.shared.glsShaderLibraryCase'); 26 27 goog.scope(function() { 28 29 var glsShaderLibrary = modules.shared.glsShaderLibrary; 30 var tcuTestCase = framework.common.tcuTestCase; 31 var glsShaderLibraryCase = modules.shared.glsShaderLibraryCase; 32 var gluShaderUtil = framework.opengl.gluShaderUtil; 33 34 glsShaderLibrary.generateTestCases = function() { 35 /** @type {glsShaderLibrary.Parser} */ var parser = new glsShaderLibrary.Parser(); 36 try { 37 /** @type {Object} */ var state = tcuTestCase.runner; 38 var tree = parser.parse(state.testFile); 39 var rootTest = tcuTestCase.newTest(state.testName, 'Top level'); 40 rootTest.setChildren(tree); 41 state.setRoot(rootTest); 42 } 43 catch (err) { 44 bufferedLogToConsole(err); 45 testFailed('Failed to parse shader test case file'); 46 return false; 47 } 48 return true; 49 }; 50 51 glsShaderLibrary.processTestFile = function() { 52 if (glsShaderLibrary.generateTestCases()) { 53 tcuTestCase.runner.runCallback(glsShaderLibraryCase.runTestCases); 54 } else { 55 tcuTestCase.runner.terminate(); 56 } 57 }; 58 59 glsShaderLibrary.isWhitespace = function(value) { 60 return /^[ \t\r\n]+$/.test(value); 61 }; 62 glsShaderLibrary.isEOL = function(value) { 63 return /^[\r\n]+$/.test(value); 64 }; 65 glsShaderLibrary.isAlpha = function(value) { 66 return /^[a-zA-Z]$/.test(value); 67 }; 68 glsShaderLibrary.isNumeric = function(value) { 69 return /^[0-9]$/.test(value); 70 }; 71 glsShaderLibrary.isCaseNameChar = function(value) { 72 return /^[a-zA-Z0-9_\-\.]$/.test(value); 73 }; 74 75 /** 76 * Removes however many indents there are on the first line from all lines. 77 * @param {string} str 78 * @return {string} output 79 */ 80 glsShaderLibrary.removeExtraIndentation = function(str) { 81 return glsShaderLibrary.removeExtraIndentationArray( 82 str.split(/\r\n|\r|\n/) 83 ).join('\n'); 84 }; 85 86 /** 87 * Returns an array of strings without indentation. 88 * @param {Array<string>} arr 89 * @return {Array<string>} output 90 */ 91 glsShaderLibrary.removeExtraIndentationArray = function(arr) { 92 /** @type {Array<string>} */ var output = []; 93 94 if (arr.length) { 95 96 /** @type {number} */ var numIndentChars = 0; 97 for (var i = 0; i < arr[0].length && glsShaderLibrary.isWhitespace(arr[0].charAt(i)); ++i) { 98 numIndentChars += arr[0].charAt(i) === '\t' ? 4 : 1; 99 } 100 101 for (var i = 0; i < arr.length; ++i) { 102 /** @type {number} */ var removed = 0; 103 /** @type {number} */ var j; 104 // Some tests are indented inconsistently, so we have to check for non-whitespace characters here. 105 for (j = 0; removed < numIndentChars && j < arr[i].length && glsShaderLibrary.isWhitespace(arr[i].charAt(j)); ++j) { 106 removed += (arr[i].charAt(j) === '\t' ? 4 : 1); 107 } 108 109 output.push(arr[i].substr(j, arr[i].length - j)); 110 } 111 112 } 113 114 return output; 115 }; 116 117 glsShaderLibrary.de_assert = function(condition) { 118 if (!condition) { 119 throw Error(); 120 } 121 }; 122 123 /** 124 * @param {string} str 125 * @param {string} endstr end of string character 126 * @param {boolean=} trimFront trim leading whitespace 127 * @return {string} str 128 * @private 129 */ 130 glsShaderLibrary.parseStringLiteralHelper = function(str, endstr, trimFront) { 131 trimFront = trimFront || false; 132 133 /** @type {number} */ var index_end = 0; 134 // isolate the string 135 do { 136 index_end = str.indexOf(endstr, index_end + 1); 137 } while (index_end >= 0 && str.charAt(index_end - 1) === '\\'); 138 139 if (index_end <= 0) { 140 index_end = str.length; 141 } 142 143 // strip quotes, replace \n and \t with nl and tabs respectively 144 str = str.substr(endstr.length, index_end - endstr.length); 145 if (trimFront) 146 str = str.replace(/^\s*\n/, ''); 147 var result = ''; 148 var i = 0; 149 while (str[i] != undefined) { 150 if (str[i] == '\\') { 151 switch (str[i + 1]) { 152 case undefined: 153 break; 154 case 'n': 155 result += '\n'; 156 break; 157 case 't': 158 result += '\t'; 159 break; 160 default: 161 result += str[i + 1]; 162 break; 163 } 164 i += 2; 165 } else { 166 result += str[i]; 167 i++; 168 } 169 } 170 return result; 171 172 }; 173 174 /** 175 * glsShaderLibrary.Parser class 176 * @constructor 177 */ 178 glsShaderLibrary.Parser = function() { 179 180 /* data members */ 181 182 /** 183 * The Token constants 184 * @enum {number} 185 */ 186 var Token = { 187 TOKEN_INVALID: 0, 188 TOKEN_EOF: 1, 189 TOKEN_STRING: 2, 190 TOKEN_SHADER_SOURCE: 3, 191 192 TOKEN_INT_LITERAL: 4, 193 TOKEN_FLOAT_LITERAL: 5, 194 195 // identifiers 196 TOKEN_IDENTIFIER: 6, 197 TOKEN_TRUE: 7, 198 TOKEN_FALSE: 8, 199 TOKEN_DESC: 9, 200 TOKEN_EXPECT: 10, 201 TOKEN_GROUP: 11, 202 TOKEN_CASE: 12, 203 TOKEN_END: 13, 204 TOKEN_VALUES: 14, 205 TOKEN_BOTH: 15, 206 TOKEN_VERTEX: 26, 207 TOKEN_FRAGMENT: 17, 208 TOKEN_UNIFORM: 18, 209 TOKEN_INPUT: 19, 210 TOKEN_OUTPUT: 20, 211 TOKEN_FLOAT: 21, 212 TOKEN_FLOAT_VEC2: 22, 213 TOKEN_FLOAT_VEC3: 23, 214 TOKEN_FLOAT_VEC4: 24, 215 TOKEN_FLOAT_MAT2: 25, 216 TOKEN_FLOAT_MAT2X3: 26, 217 TOKEN_FLOAT_MAT2X4: 27, 218 TOKEN_FLOAT_MAT3X2: 28, 219 TOKEN_FLOAT_MAT3: 29, 220 TOKEN_FLOAT_MAT3X4: 30, 221 TOKEN_FLOAT_MAT4X2: 31, 222 TOKEN_FLOAT_MAT4X3: 32, 223 TOKEN_FLOAT_MAT4: 33, 224 TOKEN_INT: 34, 225 TOKEN_INT_VEC2: 35, 226 TOKEN_INT_VEC3: 36, 227 TOKEN_INT_VEC4: 37, 228 TOKEN_UINT: 38, 229 TOKEN_UINT_VEC2: 39, 230 TOKEN_UINT_VEC3: 40, 231 TOKEN_UINT_VEC4: 41, 232 TOKEN_BOOL: 42, 233 TOKEN_BOOL_VEC2: 43, 234 TOKEN_BOOL_VEC3: 44, 235 TOKEN_BOOL_VEC4: 45, 236 TOKEN_VERSION: 46, 237 238 // symbols 239 TOKEN_ASSIGN: 47, 240 TOKEN_PLUS: 48, 241 TOKEN_MINUS: 49, 242 TOKEN_COMMA: 50, 243 TOKEN_VERTICAL_BAR: 51, 244 TOKEN_SEMI_COLON: 52, 245 TOKEN_LEFT_PAREN: 53, 246 TOKEN_RIGHT_PAREN: 54, 247 TOKEN_LEFT_BRACKET: 55, 248 TOKEN_RIGHT_BRACKET: 56, 249 TOKEN_LEFT_BRACE: 57, 250 TOKEN_RIGHT_BRACE: 58, 251 252 TOKEN_LAST: 59 253 }; 254 255 /** @type {string} */ var m_input = ''; 256 /** @type {number} */ var m_curPtr = 0; 257 /** @type {number} */ var m_curToken;// = Token.TOKEN_INVALID; 258 /** @type {string} */ var m_curTokenStr = ''; 259 260 /* function members */ 261 this.parse = function(input) { 262 263 // initialise parser 264 m_input = input; 265 m_curPtr = 0; 266 m_curToken = Token.TOKEN_INVALID; 267 m_curTokenStr = ''; 268 advanceToken(); 269 270 /** @type {Array<tcuTestCase.DeqpTest>} */ var nodeList = []; 271 272 for (;;) { 273 274 if (m_curToken === Token.TOKEN_CASE) { 275 parseShaderCase(nodeList); 276 } else if (m_curToken === Token.TOKEN_GROUP) { 277 parseShaderGroup(nodeList); 278 } else if (m_curToken === Token.TOKEN_EOF) { 279 break; 280 } else { 281 // throw Error("invalid token encountered at main level: '" + m_curTokenStr + "'"); 282 testFailed("invalid token encountered at main level: '" + m_curTokenStr + "'"); 283 tcuTestCase.runner.terminate(); 284 } 285 286 } 287 288 return nodeList; 289 290 }; 291 292 /** 293 * ensures that the token exists 294 * otherwise it returns the corresponding token's name depending on enum number value 295 * @param {number} id 296 * @return {string} name 297 */ 298 var resolveTokenName = function(id) { 299 for (var name in Token) { 300 if (Token[name] === id) return name; 301 } 302 return 'TOKEN_UNKNOWN'; 303 }; 304 305 /** 306 * Throws an error which contains the passed string 307 * @param {string} errorStr that contains an error to notify 308 * @return {string} error 309 */ 310 var parseError = function(errorStr) { 311 // abort 312 throw 'glsShaderLibrary.Parser error: ' + errorStr + ' near ' + m_input.substr(m_curPtr, m_curPtr + 80); 313 }; 314 315 /** 316 * Converts string into float 317 * @param {string} str 318 * @return {number} 319 */ 320 var parseFloatLiteral = function(str) { 321 return parseFloat(str); 322 }; 323 324 /** 325 * Converts string into integer 326 * @param {string} str 327 * @return {number} 328 */ 329 var parseIntLiteral = function(str) { 330 return parseInt(str, 10); 331 }; 332 var parseStringLiteral = function(str) { 333 /** 334 * @type {string} 335 * find delimitor 336 */ var endchar = str.substr(0, 1); 337 return glsShaderLibrary.parseStringLiteralHelper(str, endchar); 338 }; 339 var parseShaderSource = function(str) { 340 // similar to parse literal, delimitors are two double quotes ("") 341 return glsShaderLibrary.removeExtraIndentation( 342 glsShaderLibrary.parseStringLiteralHelper(str, '""', true) 343 ); 344 }; 345 346 var advanceTokenWorker = function() { 347 348 // Skip old token 349 m_curPtr += m_curTokenStr.length; 350 351 // Reset token (for safety). 352 m_curToken = Token.TOKEN_INVALID; 353 m_curTokenStr = ''; 354 355 // Eat whitespace & comments while they last. 356 for (;;) { 357 358 while (glsShaderLibrary.isWhitespace(m_input.charAt(m_curPtr))) ++m_curPtr; 359 360 // check for EOL comment 361 if (m_input.charAt(m_curPtr) === '#') { 362 // if m_input is to be an array of lines then this probably wont work very well 363 while ( 364 m_curPtr < m_input.length && 365 !glsShaderLibrary.isEOL(m_input.charAt(m_curPtr)) 366 ) ++m_curPtr; 367 } else { 368 break; 369 } 370 371 } 372 373 if (m_curPtr >= m_input.length) { 374 375 m_curToken = Token.TOKEN_EOF; 376 m_curTokenStr = '<EOF>'; 377 378 } else if (glsShaderLibrary.isAlpha(m_input.charAt(m_curPtr))) { 379 380 /** @type {number} */ var end = m_curPtr + 1; 381 while (glsShaderLibrary.isCaseNameChar(m_input.charAt(end))) ++end; 382 383 m_curTokenStr = m_input.substr(m_curPtr, end - m_curPtr); 384 385 m_curToken = (function() { 386 // consider reimplementing with a binary search 387 switch (m_curTokenStr) { 388 case 'true': return Token.TOKEN_TRUE; 389 case 'false': return Token.TOKEN_FALSE; 390 case 'desc': return Token.TOKEN_DESC; 391 case 'expect': return Token.TOKEN_EXPECT; 392 case 'group': return Token.TOKEN_GROUP; 393 case 'case': return Token.TOKEN_CASE; 394 case 'end': return Token.TOKEN_END; 395 case 'values': return Token.TOKEN_VALUES; 396 case 'both': return Token.TOKEN_BOTH; 397 case 'vertex': return Token.TOKEN_VERTEX; 398 case 'fragment': return Token.TOKEN_FRAGMENT; 399 case 'uniform': return Token.TOKEN_UNIFORM; 400 case 'input': return Token.TOKEN_INPUT; 401 case 'output': return Token.TOKEN_OUTPUT; 402 case 'float': return Token.TOKEN_FLOAT; 403 case 'vec2': return Token.TOKEN_FLOAT_VEC2; 404 case 'vec3': return Token.TOKEN_FLOAT_VEC3; 405 case 'vec4': return Token.TOKEN_FLOAT_VEC4; 406 case 'mat2': return Token.TOKEN_FLOAT_MAT2; 407 case 'mat2x3': return Token.TOKEN_FLOAT_MAT2X3; 408 case 'mat2x4': return Token.TOKEN_FLOAT_MAT2X4; 409 case 'mat3x2': return Token.TOKEN_FLOAT_MAT3X2; 410 case 'mat3': return Token.TOKEN_FLOAT_MAT3; 411 case 'mat3x4': return Token.TOKEN_FLOAT_MAT3X4; 412 case 'mat4x2': return Token.TOKEN_FLOAT_MAT4X2; 413 case 'mat4x3': return Token.TOKEN_FLOAT_MAT4X3; 414 case 'mat4': return Token.TOKEN_FLOAT_MAT4; 415 case 'int': return Token.TOKEN_INT; 416 case 'ivec2': return Token.TOKEN_INT_VEC2; 417 case 'ivec3': return Token.TOKEN_INT_VEC3; 418 case 'ivec4': return Token.TOKEN_INT_VEC4; 419 case 'uint': return Token.TOKEN_UINT; 420 case 'uvec2': return Token.TOKEN_UINT_VEC2; 421 case 'uvec3': return Token.TOKEN_UINT_VEC3; 422 case 'uvec4': return Token.TOKEN_UINT_VEC4; 423 case 'bool': return Token.TOKEN_BOOL; 424 case 'bvec2': return Token.TOKEN_BOOL_VEC2; 425 case 'bvec3': return Token.TOKEN_BOOL_VEC3; 426 case 'bvec4': return Token.TOKEN_BOOL_VEC4; 427 case 'version': return Token.TOKEN_VERSION; 428 default: return Token.TOKEN_IDENTIFIER; 429 } 430 }()); 431 432 } else if (glsShaderLibrary.isNumeric(m_input.charAt(m_curPtr))) { 433 434 /** @type {number} */ var p = m_curPtr; 435 while (glsShaderLibrary.isNumeric(m_input.charAt(p))) ++p; 436 437 if (m_input.charAt(p) === '.') { // float 438 439 ++p; 440 while (glsShaderLibrary.isNumeric(m_input.charAt(p))) ++p; 441 442 if (m_input.charAt(p) === 'e' || m_input.charAt(p) === 'E') { 443 444 ++p; 445 if (m_input.charAt(p) === '+' || m_input.charAt(p) === '-') ++p; 446 447 glsShaderLibrary.de_assert(p < m_input.length && glsShaderLibrary.isNumeric(m_input.charAt(p))); 448 while (glsShaderLibrary.isNumeric(m_input.charAt(p))) ++p; 449 450 } 451 452 m_curToken = Token.TOKEN_FLOAT_LITERAL; 453 m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr); 454 455 } else { 456 457 m_curToken = Token.TOKEN_INT_LITERAL; 458 m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr); 459 460 } 461 462 } else if (m_input.charAt(m_curPtr) === '"' && m_input.charAt(m_curPtr + 1) === '"') { // shader source 463 464 var p = m_curPtr + 2; 465 466 while (m_input.charAt(p) != '"' || m_input.charAt(p + 1) != '"') { 467 glsShaderLibrary.de_assert(p < m_input.length); 468 if (m_input.charAt(p) === '\\') { 469 glsShaderLibrary.de_assert(p + 1 < m_input.length); 470 p += 2; 471 } else { 472 ++p; 473 } 474 } 475 p += 2; 476 477 m_curToken = Token.TOKEN_SHADER_SOURCE; 478 m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr); 479 480 } else if (m_input.charAt(m_curPtr) === '"' || m_input.charAt(m_curPtr) === "'") { 481 482 /** @type {string} */ var delimitor = m_input.charAt(m_curPtr); 483 var p = m_curPtr + 1; 484 485 while (m_input.charAt(p) != delimitor) { 486 487 glsShaderLibrary.de_assert(p < m_input.length); 488 if (m_input.charAt(p) === '\\') { 489 glsShaderLibrary.de_assert(p + 1 < m_input.length); 490 p += 2; 491 } else { 492 ++p; 493 } 494 495 } 496 ++p; 497 498 m_curToken = Token.TOKEN_STRING; 499 m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr); 500 501 } else { 502 503 m_curTokenStr = m_input.charAt(m_curPtr); 504 m_curToken = (function() { 505 // consider reimplementing with a binary search 506 switch (m_curTokenStr) { 507 case '=': return Token.TOKEN_ASSIGN; 508 case '+': return Token.TOKEN_PLUS; 509 case '-': return Token.TOKEN_MINUS; 510 case ',': return Token.TOKEN_COMMA; 511 case '|': return Token.TOKEN_VERTICAL_BAR; 512 case ';': return Token.TOKEN_SEMI_COLON; 513 case '(': return Token.TOKEN_LEFT_PAREN; 514 case ')': return Token.TOKEN_RIGHT_PAREN; 515 case '[': return Token.TOKEN_LEFT_BRACKET; 516 case ']': return Token.TOKEN_RIGHT_BRACKET; 517 case '{': return Token.TOKEN_LEFT_BRACE; 518 case '}': return Token.TOKEN_RIGHT_BRACE; 519 520 default: return Token.TOKEN_INVALID; 521 } 522 }()); 523 524 } 525 526 }; 527 528 /** 529 * @return {Object.<number, string, string>} 530 */ 531 var advanceTokenTester = function(input, current_index) { 532 m_input = input; 533 m_curPtr = current_index; 534 m_curTokenStr = ''; 535 advanceTokenWorker(); 536 return { 537 /** @type {number} */ idType: m_curToken, 538 /** @type {string} */ name: resolveTokenName(m_curToken), 539 /** @type {string} */ value: m_curTokenStr 540 }; 541 }; 542 543 /** 544 * @param {Token=} tokenAssumed 545 */ 546 var advanceToken = function(tokenAssumed) { 547 if (typeof(tokenAssumed) !== 'undefined') { 548 assumeToken(tokenAssumed); 549 } 550 advanceTokenWorker(); 551 }; 552 var assumeToken = function(token) { 553 554 if (m_curToken != token) { 555 // parse error 556 /** @type {string} */ var msg = "unexpected token '" + m_curTokenStr + "', expecting '" + getTokenName(token) + "'"; 557 throw Error('Parse Error. ' + msg + ' near ' + m_curPtr + ' ...'); 558 } 559 }; 560 var mapDataTypeToken = function(token) { 561 switch (token) { 562 case Token.TOKEN_FLOAT: return gluShaderUtil.DataType.FLOAT; 563 case Token.TOKEN_FLOAT_VEC2: return gluShaderUtil.DataType.FLOAT_VEC2; 564 case Token.TOKEN_FLOAT_VEC3: return gluShaderUtil.DataType.FLOAT_VEC3; 565 case Token.TOKEN_FLOAT_VEC4: return gluShaderUtil.DataType.FLOAT_VEC4; 566 case Token.TOKEN_FLOAT_MAT2: return gluShaderUtil.DataType.FLOAT_MAT2; 567 case Token.TOKEN_FLOAT_MAT2X3: return gluShaderUtil.DataType.FLOAT_MAT2X3; 568 case Token.TOKEN_FLOAT_MAT2X4: return gluShaderUtil.DataType.FLOAT_MAT2X4; 569 case Token.TOKEN_FLOAT_MAT3X2: return gluShaderUtil.DataType.FLOAT_MAT3X2; 570 case Token.TOKEN_FLOAT_MAT3: return gluShaderUtil.DataType.FLOAT_MAT3; 571 case Token.TOKEN_FLOAT_MAT3X4: return gluShaderUtil.DataType.FLOAT_MAT3X4; 572 case Token.TOKEN_FLOAT_MAT4X2: return gluShaderUtil.DataType.FLOAT_MAT4X2; 573 case Token.TOKEN_FLOAT_MAT4X3: return gluShaderUtil.DataType.FLOAT_MAT4X3; 574 case Token.TOKEN_FLOAT_MAT4: return gluShaderUtil.DataType.FLOAT_MAT4; 575 case Token.TOKEN_INT: return gluShaderUtil.DataType.INT; 576 case Token.TOKEN_INT_VEC2: return gluShaderUtil.DataType.INT_VEC2; 577 case Token.TOKEN_INT_VEC3: return gluShaderUtil.DataType.INT_VEC3; 578 case Token.TOKEN_INT_VEC4: return gluShaderUtil.DataType.INT_VEC4; 579 case Token.TOKEN_UINT: return gluShaderUtil.DataType.UINT; 580 case Token.TOKEN_UINT_VEC2: return gluShaderUtil.DataType.UINT_VEC2; 581 case Token.TOKEN_UINT_VEC3: return gluShaderUtil.DataType.UINT_VEC3; 582 case Token.TOKEN_UINT_VEC4: return gluShaderUtil.DataType.UINT_VEC4; 583 case Token.TOKEN_BOOL: return gluShaderUtil.DataType.BOOL; 584 case Token.TOKEN_BOOL_VEC2: return gluShaderUtil.DataType.BOOL_VEC2; 585 case Token.TOKEN_BOOL_VEC3: return gluShaderUtil.DataType.BOOL_VEC3; 586 case Token.TOKEN_BOOL_VEC4: return gluShaderUtil.DataType.BOOL_VEC4; 587 default: return gluShaderUtil.DataType.INVALID; 588 } 589 }; 590 591 /** 592 * Returns the corresponding token's name depending on enum number value 593 * @param {number} token 594 * @return {string} 595 */ 596 var getTokenName = function(token) { 597 switch (token) { 598 case Token.TOKEN_INVALID: return '<invalid>'; 599 case Token.TOKEN_EOF: return '<eof>'; 600 case Token.TOKEN_STRING: return '<string>'; 601 case Token.TOKEN_SHADER_SOURCE: return 'source'; 602 603 case Token.TOKEN_INT_LITERAL: return '<int>'; 604 case Token.TOKEN_FLOAT_LITERAL: return '<float>'; 605 606 // identifiers 607 case Token.TOKEN_IDENTIFIER: return '<identifier>'; 608 case Token.TOKEN_TRUE: return 'true'; 609 case Token.TOKEN_FALSE: return 'false'; 610 case Token.TOKEN_DESC: return 'desc'; 611 case Token.TOKEN_EXPECT: return 'expect'; 612 case Token.TOKEN_GROUP: return 'group'; 613 case Token.TOKEN_CASE: return 'case'; 614 case Token.TOKEN_END: return 'end'; 615 case Token.TOKEN_VALUES: return 'values'; 616 case Token.TOKEN_BOTH: return 'both'; 617 case Token.TOKEN_VERTEX: return 'vertex'; 618 case Token.TOKEN_FRAGMENT: return 'fragment'; 619 case Token.TOKEN_UNIFORM: return 'uniform'; 620 case Token.TOKEN_INPUT: return 'input'; 621 case Token.TOKEN_OUTPUT: return 'output'; 622 case Token.TOKEN_FLOAT: return 'float'; 623 case Token.TOKEN_FLOAT_VEC2: return 'vec2'; 624 case Token.TOKEN_FLOAT_VEC3: return 'vec3'; 625 case Token.TOKEN_FLOAT_VEC4: return 'vec4'; 626 case Token.TOKEN_FLOAT_MAT2: return 'mat2'; 627 case Token.TOKEN_FLOAT_MAT2X3: return 'mat2x3'; 628 case Token.TOKEN_FLOAT_MAT2X4: return 'mat2x4'; 629 case Token.TOKEN_FLOAT_MAT3X2: return 'mat3x2'; 630 case Token.TOKEN_FLOAT_MAT3: return 'mat3'; 631 case Token.TOKEN_FLOAT_MAT3X4: return 'mat3x4'; 632 case Token.TOKEN_FLOAT_MAT4X2: return 'mat4x2'; 633 case Token.TOKEN_FLOAT_MAT4X3: return 'mat4x3'; 634 case Token.TOKEN_FLOAT_MAT4: return 'mat4'; 635 case Token.TOKEN_INT: return 'int'; 636 case Token.TOKEN_INT_VEC2: return 'ivec2'; 637 case Token.TOKEN_INT_VEC3: return 'ivec3'; 638 case Token.TOKEN_INT_VEC4: return 'ivec4'; 639 case Token.TOKEN_UINT: return 'uint'; 640 case Token.TOKEN_UINT_VEC2: return 'uvec2'; 641 case Token.TOKEN_UINT_VEC3: return 'uvec3'; 642 case Token.TOKEN_UINT_VEC4: return 'uvec4'; 643 case Token.TOKEN_BOOL: return 'bool'; 644 case Token.TOKEN_BOOL_VEC2: return 'bvec2'; 645 case Token.TOKEN_BOOL_VEC3: return 'bvec3'; 646 case Token.TOKEN_BOOL_VEC4: return 'bvec4'; 647 648 case Token.TOKEN_ASSIGN: return '='; 649 case Token.TOKEN_PLUS: return '+'; 650 case Token.TOKEN_MINUS: return '-'; 651 case Token.TOKEN_COMMA: return ','; 652 case Token.TOKEN_VERTICAL_BAR: return '|'; 653 case Token.TOKEN_SEMI_COLON: return ';'; 654 case Token.TOKEN_LEFT_PAREN: return '('; 655 case Token.TOKEN_RIGHT_PAREN: return ')'; 656 case Token.TOKEN_LEFT_BRACKET: return '['; 657 case Token.TOKEN_RIGHT_BRACKET: return ']'; 658 case Token.TOKEN_LEFT_BRACE: return ' {'; 659 case Token.TOKEN_RIGHT_BRACE: return '}'; 660 661 default: return '<unknown>'; 662 } 663 }; 664 665 /** 666 * @param {?gluShaderUtil.DataType} expectedDataType 667 * @param {Object} result 668 */ 669 var parseValueElement = function(expectedDataType, result) { 670 /** @type {?string} */ var scalarType = null; 671 /** @type {number} */ var scalarSize = 0; 672 if (expectedDataType) { 673 scalarType = gluShaderUtil.getDataTypeScalarType(expectedDataType); 674 scalarSize = gluShaderUtil.getDataTypeScalarSize(expectedDataType); 675 } 676 677 /** @type {Array<number>} */ var elems = []; 678 679 if (scalarSize > 1) { 680 glsShaderLibrary.de_assert(mapDataTypeToken(m_curToken) === expectedDataType); 681 advanceToken(); // data type(float, vec2, etc.) 682 advanceToken(Token.TOKEN_LEFT_PAREN); 683 } 684 685 for (var i = 0; i < scalarSize; ++i) { 686 if (scalarType === 'float') { 687 688 /** @type {number} */ var signMult = 1.0; 689 if (m_curToken === Token.TOKEN_MINUS) { 690 signMult = -1.0; 691 advanceToken(); 692 } 693 694 assumeToken(Token.TOKEN_FLOAT_LITERAL); 695 elems.push(signMult * parseFloatLiteral(m_curTokenStr)); 696 advanceToken(Token.TOKEN_FLOAT_LITERAL); 697 698 } else if (scalarType === 'int' || scalarType === 'uint') { 699 700 var signMult = 1; 701 if (m_curToken === Token.TOKEN_MINUS) { 702 signMult = -1; 703 advanceToken(); 704 } 705 706 assumeToken(Token.TOKEN_INT_LITERAL); 707 elems.push(signMult * parseIntLiteral(m_curTokenStr)); 708 advanceToken(Token.TOKEN_INT_LITERAL); 709 710 } else { 711 712 glsShaderLibrary.de_assert(scalarType === 'bool'); 713 elems.push(m_curToken === Token.TOKEN_TRUE); 714 if (m_curToken != Token.TOKEN_TRUE && m_curToken != Token.TOKEN_FALSE) { 715 throw Error('unexpected token, expecting bool: ' + m_curTokenStr); 716 } 717 advanceToken(); // true/false 718 719 } 720 721 if (i != (scalarSize - 1)) { 722 advanceToken(Token.TOKEN_COMMA); 723 } 724 } 725 726 if (scalarSize > 1) { 727 advanceToken(Token.TOKEN_RIGHT_PAREN); 728 } 729 730 for (var i = 0; i < elems.length; i++) 731 result.elements.push(elems[i]); 732 733 }; 734 735 /** 736 * @param {Object.<Array, number>} valueBlock 737 */ 738 var parseValue = function(valueBlock) { 739 740 /** 741 * @type {Object} 742 */ 743 var result = { 744 /** @type {?gluShaderUtil.DataType} */ dataType: null, 745 /** @type {?glsShaderLibraryCase.shaderCase} */ storageType: null, 746 /** @type {?string} */ valueName: null, 747 /** @type {Array} */ elements: [] 748 }; 749 750 // parse storage 751 switch (m_curToken) { 752 case Token.TOKEN_UNIFORM: 753 result.storageType = glsShaderLibraryCase.shaderCase.STORAGE_UNIFORM; 754 break; 755 case Token.TOKEN_INPUT: 756 result.storageType = glsShaderLibraryCase.shaderCase.STORAGE_INPUT; 757 break; 758 case Token.TOKEN_OUTPUT: 759 result.storageType = glsShaderLibraryCase.shaderCase.STORAGE_OUTPUT; 760 break; 761 default: 762 throw Error('unexpected token encountered when parsing value classifier'); 763 break; 764 } 765 advanceToken(); 766 767 // parse data type 768 result.dataType = mapDataTypeToken(m_curToken); 769 if (result.dataType === gluShaderUtil.DataType.INVALID) { 770 throw Error('unexpected token when parsing value data type: ' + m_curTokenStr); 771 } 772 advanceToken(); 773 774 // parse value name 775 if (m_curToken === Token.TOKEN_IDENTIFIER) { 776 result.valueName = m_curTokenStr; 777 } else if (m_curToken === Token.TOKEN_STRING) { 778 result.valueName = parseStringLiteral(m_curTokenStr); 779 } else { 780 throw Error('unexpected token when parsing value name: ' + m_curTokenStr); 781 } 782 advanceToken(); 783 784 // parse assignment operator. 785 advanceToken(Token.TOKEN_ASSIGN); 786 787 // parse actual value 788 if (m_curToken === Token.TOKEN_LEFT_BRACKET) { // value list 789 advanceToken(Token.TOKEN_LEFT_BRACKET); 790 result.arrayLength = 0; 791 792 for (;;) { 793 parseValueElement(result.dataType, result); 794 result.arrayLength += 1; 795 796 if (m_curToken === Token.TOKEN_RIGHT_BRACKET) { 797 break; 798 } else if (m_curToken === Token.TOKEN_VERTICAL_BAR) { // pipe? 799 advanceToken(); 800 continue; 801 } else { 802 throw Error('unexpected token in value element array: ' + m_curTokenStr); 803 } 804 } 805 806 advanceToken(Token.TOKEN_RIGHT_BRACKET); 807 808 } else { // arrays, single elements 809 parseValueElement(result.dataType, result); 810 result.arrayLength = 1; 811 } 812 813 advanceToken(Token.TOKEN_SEMI_COLON); 814 815 valueBlock.values.push(result); 816 817 }; 818 819 /** 820 * @param {Object.<Array, number>} valueBlock 821 */ 822 var parseValueBlock = function(valueBlock) { 823 824 advanceToken(Token.TOKEN_VALUES); 825 advanceToken(Token.TOKEN_LEFT_BRACE); 826 827 for (;;) { 828 if ( 829 m_curToken === Token.TOKEN_UNIFORM || 830 m_curToken === Token.TOKEN_INPUT || 831 m_curToken === Token.TOKEN_OUTPUT 832 ) { 833 parseValue(valueBlock); 834 } else if (m_curToken === Token.TOKEN_RIGHT_BRACE) { 835 break; 836 } else { 837 throw Error('unexpected( token when parsing a value block: ' + m_curTokenStr); 838 } 839 } 840 841 advanceToken(Token.TOKEN_RIGHT_BRACE); 842 843 /** @type {number} */ var arrayLength = 1; 844 // compute combined array length of value block. 845 for (var i = 0; i < valueBlock.values.length; ++i) { 846 if (valueBlock.values[i].arrayLength > 1) { 847 glsShaderLibrary.de_assert(arrayLength === 1 || arrayLength === valueBlock.values[i].arrayLength); 848 arrayLength = valueBlock.values[i].arrayLength; 849 } 850 } 851 852 valueBlock.arrayLength = arrayLength; 853 854 }; 855 856 /** 857 * @param {Array<tcuTestCase.DeqpTest>} shaderNodeList 858 */ 859 var parseShaderCase = function(shaderNodeList) { 860 861 // parse case 862 advanceToken(Token.TOKEN_CASE); 863 864 /** 865 * @type {string} 866 * parse case name 867 */ 868 var caseName = m_curTokenStr; 869 advanceToken(); // \note [pyry] All token types are allowed here. 870 871 /** 872 * @type {Array<Object>} 873 * setup case 874 */ 875 var valueBlockList = []; 876 877 /** TODO: Should the default version be defined elsewhere? */ 878 /** @type {string} */ var version = '100'; 879 /** @type {number} */ var expectResult = glsShaderLibraryCase.expectResult.EXPECT_PASS; 880 /** @type {string} */ var description; 881 /** @type {string} */ var bothSource = ''; 882 /** @type {string} */ var vertexSource = ''; 883 /** @type {string} */ var fragmentSource = ''; 884 885 for (;;) { 886 887 if (m_curToken === Token.TOKEN_END) { 888 889 break; 890 891 } else if (m_curToken === Token.TOKEN_DESC) { 892 893 advanceToken(); 894 assumeToken(Token.TOKEN_STRING); 895 896 description = parseStringLiteral(m_curTokenStr); 897 advanceToken(); 898 899 } else if (m_curToken === Token.TOKEN_EXPECT) { 900 901 advanceToken(); 902 assumeToken(Token.TOKEN_IDENTIFIER); 903 904 expectResult = (function(token) { 905 switch (token) { 906 case 'pass': return glsShaderLibraryCase.expectResult.EXPECT_PASS; 907 case 'compile_fail': return glsShaderLibraryCase.expectResult.EXPECT_COMPILE_FAIL; 908 case 'link_fail': return glsShaderLibraryCase.expectResult.EXPECT_LINK_FAIL; 909 case 'compile_or_link_fail': return glsShaderLibraryCase.expectResult.EXPECT_COMPILE_LINK_FAIL; 910 case 'build_successful': return glsShaderLibraryCase.expectResult.EXPECT_BUILD_SUCCESSFUL; 911 default: 912 throw Error('invalid expected result value: ' + m_curTokenStr); 913 } 914 }(m_curTokenStr)); 915 916 advanceToken(); 917 918 } else if (m_curToken === Token.TOKEN_VALUES) { 919 920 /** @type {Object.<Array, number>} */ var block = glsShaderLibraryCase.genValueBlock(); 921 parseValueBlock(block); 922 valueBlockList.push(block); 923 924 } else if ( 925 m_curToken === Token.TOKEN_BOTH || 926 m_curToken === Token.TOKEN_VERTEX || 927 m_curToken === Token.TOKEN_FRAGMENT 928 ) { 929 930 /** @type {number} */ var token = m_curToken; 931 advanceToken(); 932 assumeToken(Token.TOKEN_SHADER_SOURCE); 933 /** @type {string} */ var source = parseShaderSource(m_curTokenStr); 934 935 advanceToken(); 936 switch (token) { 937 case Token.TOKEN_BOTH: bothSource = source; break; 938 case Token.TOKEN_VERTEX: vertexSource = source; break; 939 case Token.TOKEN_FRAGMENT: fragmentSource = source; break; 940 default: glsShaderLibrary.de_assert(false); break; 941 } 942 943 } else if (m_curToken === Token.TOKEN_VERSION) { 944 945 advanceToken(); 946 947 /** @type {number} */ var versionNum = 0; 948 /** @type {string} */ var postfix = ''; 949 950 assumeToken(Token.TOKEN_INT_LITERAL); 951 versionNum = parseIntLiteral(m_curTokenStr); 952 advanceToken(); 953 954 if (m_curToken === Token.TOKEN_IDENTIFIER) { 955 postfix = m_curTokenStr; 956 advanceToken(); 957 } 958 959 // TODO: need to fix these constants, we dont have glu 960 if (versionNum === 100 && postfix === 'es') version = '100'; 961 else if (versionNum === 300 && postfix === 'es') version = '300 es'; 962 else if (versionNum === 310 && postfix === 'es') version = '310 es'; 963 else if (versionNum === 130) version = '130'; 964 else if (versionNum === 140) version = '140'; 965 else if (versionNum === 150) version = '150'; 966 else if (versionNum === 330) version = '330'; 967 else if (versionNum === 400) version = '400'; 968 else if (versionNum === 410) version = '410'; 969 else if (versionNum === 420) version = '420'; 970 else if (versionNum === 430) version = '430'; 971 else if (versionNum === 440) version = '440'; 972 else if (versionNum === 450) version = '450'; 973 else { 974 throw Error('Unknown GLSL version'); 975 } 976 977 } else { 978 throw Error('unexpected token while parsing shader case: ' + m_curTokenStr); 979 } 980 981 } 982 983 advanceToken(Token.TOKEN_END); // case end 984 985 /** 986 * no ShaderCase yet? 987 * @param {?string} vert 988 * @param {?string} frag 989 * @param {glsShaderLibraryCase.caseType} type 990 * @return {Object} 991 */ 992 var getShaderSpec = function(vert, frag, type) { 993 return { 994 /** @type {glsShaderLibraryCase.expectResult} */ expectResult: expectResult, 995 /** @type {glsShaderLibraryCase.caseType} */ caseType: type, 996 /** @type {Array<Object>} */ valueBlockList: valueBlockList, 997 /** @type {string} */ targetVersion: version, 998 /** @type {?string} */ vertexSource: vert, 999 /** @type {?string} */ fragmentSource: frag 1000 }; 1001 }; 1002 getShaderSpec.bind(this); 1003 1004 if (bothSource.length) { 1005 1006 glsShaderLibrary.de_assert(!vertexSource); 1007 glsShaderLibrary.de_assert(!fragmentSource); 1008 1009 shaderNodeList.push(tcuTestCase.newTest(caseName + '_vertex', description, getShaderSpec(bothSource, null, 1010 glsShaderLibraryCase.caseType.CASETYPE_VERTEX_ONLY))); 1011 shaderNodeList.push(tcuTestCase.newTest(caseName + '_fragment', description, getShaderSpec(null, bothSource, 1012 glsShaderLibraryCase.caseType.CASETYPE_FRAGMENT_ONLY))); 1013 1014 } else { 1015 glsShaderLibrary.de_assert(vertexSource); 1016 glsShaderLibrary.de_assert(fragmentSource); 1017 1018 shaderNodeList.push(tcuTestCase.newTest(caseName, description, getShaderSpec(vertexSource, fragmentSource, 1019 glsShaderLibraryCase.caseType.CASETYPE_COMPLETE))); 1020 } 1021 }; 1022 1023 /** 1024 * @param {Array<tcuTestCase.DeqpTest>} shaderNodeList 1025 */ 1026 var parseShaderGroup = function(shaderNodeList) { 1027 1028 // parse 'case' 1029 advanceToken(Token.TOKEN_GROUP); 1030 1031 /** @type {string} 1032 * parse case name 1033 */ var name = m_curTokenStr; 1034 advanceToken(); // \note [pyry] We don't want to check token type here (for instance to allow "uniform") group. 1035 1036 // Parse description. 1037 assumeToken(Token.TOKEN_STRING); 1038 /** @type {string} */ var description = parseStringLiteral(m_curTokenStr); 1039 advanceToken(Token.TOKEN_STRING); 1040 1041 /** @type {Array<tcuTestCase.DeqpTest>} */ var children = []; 1042 1043 for (;;) { 1044 1045 if (m_curToken === Token.TOKEN_END) { 1046 break; 1047 } else if (m_curToken === Token.TOKEN_GROUP) { 1048 parseShaderGroup(children); 1049 } else if (m_curToken === Token.TOKEN_CASE) { 1050 parseShaderCase(children); 1051 } else { 1052 testFailed('unexpected token while parsing shader group: ' + m_curTokenStr); 1053 tcuTestCase.runner.terminate(); 1054 } 1055 1056 } 1057 1058 advanceToken(Token.TOKEN_END); // group end 1059 1060 /** @type {tcuTestCase.DeqpTest} */ var groupNode = tcuTestCase.newTest(name, description, null); 1061 groupNode.setChildren(children); 1062 1063 shaderNodeList.push(groupNode); 1064 1065 }; 1066 1067 // uncomment to expose private functions 1068 (function(obj) { 1069 obj.priv = { 1070 m_curPtr: m_curPtr, 1071 1072 parseError: parseError, 1073 parseFloatLiteral: parseFloatLiteral, 1074 parseIntLiteral: parseIntLiteral, 1075 parseStringLiteral: parseStringLiteral, 1076 parseShaderSource: parseShaderSource, 1077 advanceTokenTester: advanceTokenTester, 1078 assumeToken: assumeToken, 1079 mapDataTypeToken: mapDataTypeToken, 1080 getTokenName: getTokenName, 1081 1082 Token: Token, 1083 1084 parseValueElement: parseValueElement, 1085 parseValue: parseValue, 1086 parseValueBlock: parseValueBlock, 1087 parseShaderCase: parseShaderCase, 1088 parseShaderGroup: parseShaderGroup, 1089 1090 none: false 1091 }; 1092 }(this)); 1093 //*/ 1094 }; 1095 1096 /** 1097 * Parse the test file and execute the test cases 1098 * @param {string} testName Name of the test file (without extension) 1099 * @param {string} filter Optional filter. Common substring of the names of the tests that should be glsShaderLibrary.run. 1100 */ 1101 glsShaderLibrary.run = function(testName, filter) { 1102 WebGLTestUtils.loadTextFileAsync(testName + '.test', function(success, content) { 1103 if (success) { 1104 tcuTestCase.runner.testFile = content; 1105 tcuTestCase.runner.testName = testName; 1106 tcuTestCase.runner.runCallback(glsShaderLibrary.processTestFile); 1107 } else { 1108 testFailed('Failed to load test file: ' + testName); 1109 tcuTestCase.runner.terminate(); 1110 } 1111 }); 1112 }; 1113 1114 });