| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES Utilities |
| * ------------------------------------------------ |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| 'use strict'; |
| goog.provide('modules.shared.glsShaderLibrary'); |
| goog.require('framework.common.tcuTestCase'); |
| goog.require('framework.opengl.gluShaderUtil'); |
| goog.require('modules.shared.glsShaderLibraryCase'); |
| |
| goog.scope(function() { |
| |
| var glsShaderLibrary = modules.shared.glsShaderLibrary; |
| var tcuTestCase = framework.common.tcuTestCase; |
| var glsShaderLibraryCase = modules.shared.glsShaderLibraryCase; |
| var gluShaderUtil = framework.opengl.gluShaderUtil; |
| |
| glsShaderLibrary.generateTestCases = function() { |
| /** @type {glsShaderLibrary.Parser} */ var parser = new glsShaderLibrary.Parser(); |
| try { |
| /** @type {Object} */ var state = tcuTestCase.runner; |
| var tree = parser.parse(state.testFile); |
| var rootTest = tcuTestCase.newTest(state.testName, 'Top level'); |
| rootTest.setChildren(tree); |
| state.setRoot(rootTest); |
| } |
| catch (err) { |
| bufferedLogToConsole(err); |
| testFailed('Failed to parse shader test case file'); |
| return false; |
| } |
| return true; |
| }; |
| |
| glsShaderLibrary.processTestFile = function() { |
| if (glsShaderLibrary.generateTestCases()) { |
| tcuTestCase.runner.runCallback(glsShaderLibraryCase.runTestCases); |
| } else { |
| tcuTestCase.runner.terminate(); |
| } |
| }; |
| |
| glsShaderLibrary.isWhitespace = function(value) { |
| return /^[ \t\r\n]+$/.test(value); |
| }; |
| glsShaderLibrary.isEOL = function(value) { |
| return /^[\r\n]+$/.test(value); |
| }; |
| glsShaderLibrary.isAlpha = function(value) { |
| return /^[a-zA-Z]$/.test(value); |
| }; |
| glsShaderLibrary.isNumeric = function(value) { |
| return /^[0-9]$/.test(value); |
| }; |
| glsShaderLibrary.isCaseNameChar = function(value) { |
| return /^[a-zA-Z0-9_\-\.]$/.test(value); |
| }; |
| |
| /** |
| * Removes however many indents there are on the first line from all lines. |
| * @param {string} str |
| * @return {string} output |
| */ |
| glsShaderLibrary.removeExtraIndentation = function(str) { |
| return glsShaderLibrary.removeExtraIndentationArray( |
| str.split(/\r\n|\r|\n/) |
| ).join('\n'); |
| }; |
| |
| /** |
| * Returns an array of strings without indentation. |
| * @param {Array<string>} arr |
| * @return {Array<string>} output |
| */ |
| glsShaderLibrary.removeExtraIndentationArray = function(arr) { |
| /** @type {Array<string>} */ var output = []; |
| |
| if (arr.length) { |
| |
| /** @type {number} */ var numIndentChars = 0; |
| for (var i = 0; i < arr[0].length && glsShaderLibrary.isWhitespace(arr[0].charAt(i)); ++i) { |
| numIndentChars += arr[0].charAt(i) === '\t' ? 4 : 1; |
| } |
| |
| for (var i = 0; i < arr.length; ++i) { |
| /** @type {number} */ var removed = 0; |
| /** @type {number} */ var j; |
| // Some tests are indented inconsistently, so we have to check for non-whitespace characters here. |
| for (j = 0; removed < numIndentChars && j < arr[i].length && glsShaderLibrary.isWhitespace(arr[i].charAt(j)); ++j) { |
| removed += (arr[i].charAt(j) === '\t' ? 4 : 1); |
| } |
| |
| output.push(arr[i].substr(j, arr[i].length - j)); |
| } |
| |
| } |
| |
| return output; |
| }; |
| |
| glsShaderLibrary.de_assert = function(condition) { |
| if (!condition) { |
| throw Error(); |
| } |
| }; |
| |
| /** |
| * @param {string} str |
| * @param {string} endstr end of string character |
| * @param {boolean=} trimFront trim leading whitespace |
| * @return {string} str |
| * @private |
| */ |
| glsShaderLibrary.parseStringLiteralHelper = function(str, endstr, trimFront) { |
| trimFront = trimFront || false; |
| |
| /** @type {number} */ var index_end = 0; |
| // isolate the string |
| do { |
| index_end = str.indexOf(endstr, index_end + 1); |
| } while (index_end >= 0 && str.charAt(index_end - 1) === '\\'); |
| |
| if (index_end <= 0) { |
| index_end = str.length; |
| } |
| |
| // strip quotes, replace \n and \t with nl and tabs respectively |
| str = str.substr(endstr.length, index_end - endstr.length); |
| if (trimFront) |
| str = str.replace(/^\s*\n/, ''); |
| var result = ''; |
| var i = 0; |
| while (str[i] != undefined) { |
| if (str[i] == '\\') { |
| switch (str[i + 1]) { |
| case undefined: |
| break; |
| case 'n': |
| result += '\n'; |
| break; |
| case 't': |
| result += '\t'; |
| break; |
| default: |
| result += str[i + 1]; |
| break; |
| } |
| i += 2; |
| } else { |
| result += str[i]; |
| i++; |
| } |
| } |
| return result; |
| |
| }; |
| |
| /** |
| * glsShaderLibrary.Parser class |
| * @constructor |
| */ |
| glsShaderLibrary.Parser = function() { |
| |
| /* data members */ |
| |
| /** |
| * The Token constants |
| * @enum {number} |
| */ |
| var Token = { |
| TOKEN_INVALID: 0, |
| TOKEN_EOF: 1, |
| TOKEN_STRING: 2, |
| TOKEN_SHADER_SOURCE: 3, |
| |
| TOKEN_INT_LITERAL: 4, |
| TOKEN_FLOAT_LITERAL: 5, |
| |
| // identifiers |
| TOKEN_IDENTIFIER: 6, |
| TOKEN_TRUE: 7, |
| TOKEN_FALSE: 8, |
| TOKEN_DESC: 9, |
| TOKEN_EXPECT: 10, |
| TOKEN_GROUP: 11, |
| TOKEN_CASE: 12, |
| TOKEN_END: 13, |
| TOKEN_VALUES: 14, |
| TOKEN_BOTH: 15, |
| TOKEN_VERTEX: 26, |
| TOKEN_FRAGMENT: 17, |
| TOKEN_UNIFORM: 18, |
| TOKEN_INPUT: 19, |
| TOKEN_OUTPUT: 20, |
| TOKEN_FLOAT: 21, |
| TOKEN_FLOAT_VEC2: 22, |
| TOKEN_FLOAT_VEC3: 23, |
| TOKEN_FLOAT_VEC4: 24, |
| TOKEN_FLOAT_MAT2: 25, |
| TOKEN_FLOAT_MAT2X3: 26, |
| TOKEN_FLOAT_MAT2X4: 27, |
| TOKEN_FLOAT_MAT3X2: 28, |
| TOKEN_FLOAT_MAT3: 29, |
| TOKEN_FLOAT_MAT3X4: 30, |
| TOKEN_FLOAT_MAT4X2: 31, |
| TOKEN_FLOAT_MAT4X3: 32, |
| TOKEN_FLOAT_MAT4: 33, |
| TOKEN_INT: 34, |
| TOKEN_INT_VEC2: 35, |
| TOKEN_INT_VEC3: 36, |
| TOKEN_INT_VEC4: 37, |
| TOKEN_UINT: 38, |
| TOKEN_UINT_VEC2: 39, |
| TOKEN_UINT_VEC3: 40, |
| TOKEN_UINT_VEC4: 41, |
| TOKEN_BOOL: 42, |
| TOKEN_BOOL_VEC2: 43, |
| TOKEN_BOOL_VEC3: 44, |
| TOKEN_BOOL_VEC4: 45, |
| TOKEN_VERSION: 46, |
| |
| // symbols |
| TOKEN_ASSIGN: 47, |
| TOKEN_PLUS: 48, |
| TOKEN_MINUS: 49, |
| TOKEN_COMMA: 50, |
| TOKEN_VERTICAL_BAR: 51, |
| TOKEN_SEMI_COLON: 52, |
| TOKEN_LEFT_PAREN: 53, |
| TOKEN_RIGHT_PAREN: 54, |
| TOKEN_LEFT_BRACKET: 55, |
| TOKEN_RIGHT_BRACKET: 56, |
| TOKEN_LEFT_BRACE: 57, |
| TOKEN_RIGHT_BRACE: 58, |
| |
| TOKEN_LAST: 59 |
| }; |
| |
| /** @type {string} */ var m_input = ''; |
| /** @type {number} */ var m_curPtr = 0; |
| /** @type {number} */ var m_curToken;// = Token.TOKEN_INVALID; |
| /** @type {string} */ var m_curTokenStr = ''; |
| |
| /* function members */ |
| this.parse = function(input) { |
| |
| // initialise parser |
| m_input = input; |
| m_curPtr = 0; |
| m_curToken = Token.TOKEN_INVALID; |
| m_curTokenStr = ''; |
| advanceToken(); |
| |
| /** @type {Array<tcuTestCase.DeqpTest>} */ var nodeList = []; |
| |
| for (;;) { |
| |
| if (m_curToken === Token.TOKEN_CASE) { |
| parseShaderCase(nodeList); |
| } else if (m_curToken === Token.TOKEN_GROUP) { |
| parseShaderGroup(nodeList); |
| } else if (m_curToken === Token.TOKEN_EOF) { |
| break; |
| } else { |
| // throw Error("invalid token encountered at main level: '" + m_curTokenStr + "'"); |
| testFailed("invalid token encountered at main level: '" + m_curTokenStr + "'"); |
| tcuTestCase.runner.terminate(); |
| } |
| |
| } |
| |
| return nodeList; |
| |
| }; |
| |
| /** |
| * ensures that the token exists |
| * otherwise it returns the corresponding token's name depending on enum number value |
| * @param {number} id |
| * @return {string} name |
| */ |
| var resolveTokenName = function(id) { |
| for (var name in Token) { |
| if (Token[name] === id) return name; |
| } |
| return 'TOKEN_UNKNOWN'; |
| }; |
| |
| /** |
| * Throws an error which contains the passed string |
| * @param {string} errorStr that contains an error to notify |
| * @return {string} error |
| */ |
| var parseError = function(errorStr) { |
| // abort |
| throw 'glsShaderLibrary.Parser error: ' + errorStr + ' near ' + m_input.substr(m_curPtr, m_curPtr + 80); |
| }; |
| |
| /** |
| * Converts string into float |
| * @param {string} str |
| * @return {number} |
| */ |
| var parseFloatLiteral = function(str) { |
| return parseFloat(str); |
| }; |
| |
| /** |
| * Converts string into integer |
| * @param {string} str |
| * @return {number} |
| */ |
| var parseIntLiteral = function(str) { |
| return parseInt(str, 10); |
| }; |
| var parseStringLiteral = function(str) { |
| /** |
| * @type {string} |
| * find delimitor |
| */ var endchar = str.substr(0, 1); |
| return glsShaderLibrary.parseStringLiteralHelper(str, endchar); |
| }; |
| var parseShaderSource = function(str) { |
| // similar to parse literal, delimitors are two double quotes ("") |
| return glsShaderLibrary.removeExtraIndentation( |
| glsShaderLibrary.parseStringLiteralHelper(str, '""', true) |
| ); |
| }; |
| |
| var advanceTokenWorker = function() { |
| |
| // Skip old token |
| m_curPtr += m_curTokenStr.length; |
| |
| // Reset token (for safety). |
| m_curToken = Token.TOKEN_INVALID; |
| m_curTokenStr = ''; |
| |
| // Eat whitespace & comments while they last. |
| for (;;) { |
| |
| while (glsShaderLibrary.isWhitespace(m_input.charAt(m_curPtr))) ++m_curPtr; |
| |
| // check for EOL comment |
| if (m_input.charAt(m_curPtr) === '#') { |
| // if m_input is to be an array of lines then this probably wont work very well |
| while ( |
| m_curPtr < m_input.length && |
| !glsShaderLibrary.isEOL(m_input.charAt(m_curPtr)) |
| ) ++m_curPtr; |
| } else { |
| break; |
| } |
| |
| } |
| |
| if (m_curPtr >= m_input.length) { |
| |
| m_curToken = Token.TOKEN_EOF; |
| m_curTokenStr = '<EOF>'; |
| |
| } else if (glsShaderLibrary.isAlpha(m_input.charAt(m_curPtr))) { |
| |
| /** @type {number} */ var end = m_curPtr + 1; |
| while (glsShaderLibrary.isCaseNameChar(m_input.charAt(end))) ++end; |
| |
| m_curTokenStr = m_input.substr(m_curPtr, end - m_curPtr); |
| |
| m_curToken = (function() { |
| // consider reimplementing with a binary search |
| switch (m_curTokenStr) { |
| case 'true': return Token.TOKEN_TRUE; |
| case 'false': return Token.TOKEN_FALSE; |
| case 'desc': return Token.TOKEN_DESC; |
| case 'expect': return Token.TOKEN_EXPECT; |
| case 'group': return Token.TOKEN_GROUP; |
| case 'case': return Token.TOKEN_CASE; |
| case 'end': return Token.TOKEN_END; |
| case 'values': return Token.TOKEN_VALUES; |
| case 'both': return Token.TOKEN_BOTH; |
| case 'vertex': return Token.TOKEN_VERTEX; |
| case 'fragment': return Token.TOKEN_FRAGMENT; |
| case 'uniform': return Token.TOKEN_UNIFORM; |
| case 'input': return Token.TOKEN_INPUT; |
| case 'output': return Token.TOKEN_OUTPUT; |
| case 'float': return Token.TOKEN_FLOAT; |
| case 'vec2': return Token.TOKEN_FLOAT_VEC2; |
| case 'vec3': return Token.TOKEN_FLOAT_VEC3; |
| case 'vec4': return Token.TOKEN_FLOAT_VEC4; |
| case 'mat2': return Token.TOKEN_FLOAT_MAT2; |
| case 'mat2x3': return Token.TOKEN_FLOAT_MAT2X3; |
| case 'mat2x4': return Token.TOKEN_FLOAT_MAT2X4; |
| case 'mat3x2': return Token.TOKEN_FLOAT_MAT3X2; |
| case 'mat3': return Token.TOKEN_FLOAT_MAT3; |
| case 'mat3x4': return Token.TOKEN_FLOAT_MAT3X4; |
| case 'mat4x2': return Token.TOKEN_FLOAT_MAT4X2; |
| case 'mat4x3': return Token.TOKEN_FLOAT_MAT4X3; |
| case 'mat4': return Token.TOKEN_FLOAT_MAT4; |
| case 'int': return Token.TOKEN_INT; |
| case 'ivec2': return Token.TOKEN_INT_VEC2; |
| case 'ivec3': return Token.TOKEN_INT_VEC3; |
| case 'ivec4': return Token.TOKEN_INT_VEC4; |
| case 'uint': return Token.TOKEN_UINT; |
| case 'uvec2': return Token.TOKEN_UINT_VEC2; |
| case 'uvec3': return Token.TOKEN_UINT_VEC3; |
| case 'uvec4': return Token.TOKEN_UINT_VEC4; |
| case 'bool': return Token.TOKEN_BOOL; |
| case 'bvec2': return Token.TOKEN_BOOL_VEC2; |
| case 'bvec3': return Token.TOKEN_BOOL_VEC3; |
| case 'bvec4': return Token.TOKEN_BOOL_VEC4; |
| case 'version': return Token.TOKEN_VERSION; |
| default: return Token.TOKEN_IDENTIFIER; |
| } |
| }()); |
| |
| } else if (glsShaderLibrary.isNumeric(m_input.charAt(m_curPtr))) { |
| |
| /** @type {number} */ var p = m_curPtr; |
| while (glsShaderLibrary.isNumeric(m_input.charAt(p))) ++p; |
| |
| if (m_input.charAt(p) === '.') { // float |
| |
| ++p; |
| while (glsShaderLibrary.isNumeric(m_input.charAt(p))) ++p; |
| |
| if (m_input.charAt(p) === 'e' || m_input.charAt(p) === 'E') { |
| |
| ++p; |
| if (m_input.charAt(p) === '+' || m_input.charAt(p) === '-') ++p; |
| |
| glsShaderLibrary.de_assert(p < m_input.length && glsShaderLibrary.isNumeric(m_input.charAt(p))); |
| while (glsShaderLibrary.isNumeric(m_input.charAt(p))) ++p; |
| |
| } |
| |
| m_curToken = Token.TOKEN_FLOAT_LITERAL; |
| m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr); |
| |
| } else { |
| |
| m_curToken = Token.TOKEN_INT_LITERAL; |
| m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr); |
| |
| } |
| |
| } else if (m_input.charAt(m_curPtr) === '"' && m_input.charAt(m_curPtr + 1) === '"') { // shader source |
| |
| var p = m_curPtr + 2; |
| |
| while (m_input.charAt(p) != '"' || m_input.charAt(p + 1) != '"') { |
| glsShaderLibrary.de_assert(p < m_input.length); |
| if (m_input.charAt(p) === '\\') { |
| glsShaderLibrary.de_assert(p + 1 < m_input.length); |
| p += 2; |
| } else { |
| ++p; |
| } |
| } |
| p += 2; |
| |
| m_curToken = Token.TOKEN_SHADER_SOURCE; |
| m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr); |
| |
| } else if (m_input.charAt(m_curPtr) === '"' || m_input.charAt(m_curPtr) === "'") { |
| |
| /** @type {string} */ var delimitor = m_input.charAt(m_curPtr); |
| var p = m_curPtr + 1; |
| |
| while (m_input.charAt(p) != delimitor) { |
| |
| glsShaderLibrary.de_assert(p < m_input.length); |
| if (m_input.charAt(p) === '\\') { |
| glsShaderLibrary.de_assert(p + 1 < m_input.length); |
| p += 2; |
| } else { |
| ++p; |
| } |
| |
| } |
| ++p; |
| |
| m_curToken = Token.TOKEN_STRING; |
| m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr); |
| |
| } else { |
| |
| m_curTokenStr = m_input.charAt(m_curPtr); |
| m_curToken = (function() { |
| // consider reimplementing with a binary search |
| switch (m_curTokenStr) { |
| case '=': return Token.TOKEN_ASSIGN; |
| case '+': return Token.TOKEN_PLUS; |
| case '-': return Token.TOKEN_MINUS; |
| case ',': return Token.TOKEN_COMMA; |
| case '|': return Token.TOKEN_VERTICAL_BAR; |
| case ';': return Token.TOKEN_SEMI_COLON; |
| case '(': return Token.TOKEN_LEFT_PAREN; |
| case ')': return Token.TOKEN_RIGHT_PAREN; |
| case '[': return Token.TOKEN_LEFT_BRACKET; |
| case ']': return Token.TOKEN_RIGHT_BRACKET; |
| case '{': return Token.TOKEN_LEFT_BRACE; |
| case '}': return Token.TOKEN_RIGHT_BRACE; |
| |
| default: return Token.TOKEN_INVALID; |
| } |
| }()); |
| |
| } |
| |
| }; |
| |
| /** |
| * @return {Object.<number, string, string>} |
| */ |
| var advanceTokenTester = function(input, current_index) { |
| m_input = input; |
| m_curPtr = current_index; |
| m_curTokenStr = ''; |
| advanceTokenWorker(); |
| return { |
| /** @type {number} */ idType: m_curToken, |
| /** @type {string} */ name: resolveTokenName(m_curToken), |
| /** @type {string} */ value: m_curTokenStr |
| }; |
| }; |
| |
| /** |
| * @param {Token=} tokenAssumed |
| */ |
| var advanceToken = function(tokenAssumed) { |
| if (typeof(tokenAssumed) !== 'undefined') { |
| assumeToken(tokenAssumed); |
| } |
| advanceTokenWorker(); |
| }; |
| var assumeToken = function(token) { |
| |
| if (m_curToken != token) { |
| // parse error |
| /** @type {string} */ var msg = "unexpected token '" + m_curTokenStr + "', expecting '" + getTokenName(token) + "'"; |
| throw Error('Parse Error. ' + msg + ' near ' + m_curPtr + ' ...'); |
| } |
| }; |
| var mapDataTypeToken = function(token) { |
| switch (token) { |
| case Token.TOKEN_FLOAT: return gluShaderUtil.DataType.FLOAT; |
| case Token.TOKEN_FLOAT_VEC2: return gluShaderUtil.DataType.FLOAT_VEC2; |
| case Token.TOKEN_FLOAT_VEC3: return gluShaderUtil.DataType.FLOAT_VEC3; |
| case Token.TOKEN_FLOAT_VEC4: return gluShaderUtil.DataType.FLOAT_VEC4; |
| case Token.TOKEN_FLOAT_MAT2: return gluShaderUtil.DataType.FLOAT_MAT2; |
| case Token.TOKEN_FLOAT_MAT2X3: return gluShaderUtil.DataType.FLOAT_MAT2X3; |
| case Token.TOKEN_FLOAT_MAT2X4: return gluShaderUtil.DataType.FLOAT_MAT2X4; |
| case Token.TOKEN_FLOAT_MAT3X2: return gluShaderUtil.DataType.FLOAT_MAT3X2; |
| case Token.TOKEN_FLOAT_MAT3: return gluShaderUtil.DataType.FLOAT_MAT3; |
| case Token.TOKEN_FLOAT_MAT3X4: return gluShaderUtil.DataType.FLOAT_MAT3X4; |
| case Token.TOKEN_FLOAT_MAT4X2: return gluShaderUtil.DataType.FLOAT_MAT4X2; |
| case Token.TOKEN_FLOAT_MAT4X3: return gluShaderUtil.DataType.FLOAT_MAT4X3; |
| case Token.TOKEN_FLOAT_MAT4: return gluShaderUtil.DataType.FLOAT_MAT4; |
| case Token.TOKEN_INT: return gluShaderUtil.DataType.INT; |
| case Token.TOKEN_INT_VEC2: return gluShaderUtil.DataType.INT_VEC2; |
| case Token.TOKEN_INT_VEC3: return gluShaderUtil.DataType.INT_VEC3; |
| case Token.TOKEN_INT_VEC4: return gluShaderUtil.DataType.INT_VEC4; |
| case Token.TOKEN_UINT: return gluShaderUtil.DataType.UINT; |
| case Token.TOKEN_UINT_VEC2: return gluShaderUtil.DataType.UINT_VEC2; |
| case Token.TOKEN_UINT_VEC3: return gluShaderUtil.DataType.UINT_VEC3; |
| case Token.TOKEN_UINT_VEC4: return gluShaderUtil.DataType.UINT_VEC4; |
| case Token.TOKEN_BOOL: return gluShaderUtil.DataType.BOOL; |
| case Token.TOKEN_BOOL_VEC2: return gluShaderUtil.DataType.BOOL_VEC2; |
| case Token.TOKEN_BOOL_VEC3: return gluShaderUtil.DataType.BOOL_VEC3; |
| case Token.TOKEN_BOOL_VEC4: return gluShaderUtil.DataType.BOOL_VEC4; |
| default: return gluShaderUtil.DataType.INVALID; |
| } |
| }; |
| |
| /** |
| * Returns the corresponding token's name depending on enum number value |
| * @param {number} token |
| * @return {string} |
| */ |
| var getTokenName = function(token) { |
| switch (token) { |
| case Token.TOKEN_INVALID: return '<invalid>'; |
| case Token.TOKEN_EOF: return '<eof>'; |
| case Token.TOKEN_STRING: return '<string>'; |
| case Token.TOKEN_SHADER_SOURCE: return 'source'; |
| |
| case Token.TOKEN_INT_LITERAL: return '<int>'; |
| case Token.TOKEN_FLOAT_LITERAL: return '<float>'; |
| |
| // identifiers |
| case Token.TOKEN_IDENTIFIER: return '<identifier>'; |
| case Token.TOKEN_TRUE: return 'true'; |
| case Token.TOKEN_FALSE: return 'false'; |
| case Token.TOKEN_DESC: return 'desc'; |
| case Token.TOKEN_EXPECT: return 'expect'; |
| case Token.TOKEN_GROUP: return 'group'; |
| case Token.TOKEN_CASE: return 'case'; |
| case Token.TOKEN_END: return 'end'; |
| case Token.TOKEN_VALUES: return 'values'; |
| case Token.TOKEN_BOTH: return 'both'; |
| case Token.TOKEN_VERTEX: return 'vertex'; |
| case Token.TOKEN_FRAGMENT: return 'fragment'; |
| case Token.TOKEN_UNIFORM: return 'uniform'; |
| case Token.TOKEN_INPUT: return 'input'; |
| case Token.TOKEN_OUTPUT: return 'output'; |
| case Token.TOKEN_FLOAT: return 'float'; |
| case Token.TOKEN_FLOAT_VEC2: return 'vec2'; |
| case Token.TOKEN_FLOAT_VEC3: return 'vec3'; |
| case Token.TOKEN_FLOAT_VEC4: return 'vec4'; |
| case Token.TOKEN_FLOAT_MAT2: return 'mat2'; |
| case Token.TOKEN_FLOAT_MAT2X3: return 'mat2x3'; |
| case Token.TOKEN_FLOAT_MAT2X4: return 'mat2x4'; |
| case Token.TOKEN_FLOAT_MAT3X2: return 'mat3x2'; |
| case Token.TOKEN_FLOAT_MAT3: return 'mat3'; |
| case Token.TOKEN_FLOAT_MAT3X4: return 'mat3x4'; |
| case Token.TOKEN_FLOAT_MAT4X2: return 'mat4x2'; |
| case Token.TOKEN_FLOAT_MAT4X3: return 'mat4x3'; |
| case Token.TOKEN_FLOAT_MAT4: return 'mat4'; |
| case Token.TOKEN_INT: return 'int'; |
| case Token.TOKEN_INT_VEC2: return 'ivec2'; |
| case Token.TOKEN_INT_VEC3: return 'ivec3'; |
| case Token.TOKEN_INT_VEC4: return 'ivec4'; |
| case Token.TOKEN_UINT: return 'uint'; |
| case Token.TOKEN_UINT_VEC2: return 'uvec2'; |
| case Token.TOKEN_UINT_VEC3: return 'uvec3'; |
| case Token.TOKEN_UINT_VEC4: return 'uvec4'; |
| case Token.TOKEN_BOOL: return 'bool'; |
| case Token.TOKEN_BOOL_VEC2: return 'bvec2'; |
| case Token.TOKEN_BOOL_VEC3: return 'bvec3'; |
| case Token.TOKEN_BOOL_VEC4: return 'bvec4'; |
| |
| case Token.TOKEN_ASSIGN: return '='; |
| case Token.TOKEN_PLUS: return '+'; |
| case Token.TOKEN_MINUS: return '-'; |
| case Token.TOKEN_COMMA: return ','; |
| case Token.TOKEN_VERTICAL_BAR: return '|'; |
| case Token.TOKEN_SEMI_COLON: return ';'; |
| case Token.TOKEN_LEFT_PAREN: return '('; |
| case Token.TOKEN_RIGHT_PAREN: return ')'; |
| case Token.TOKEN_LEFT_BRACKET: return '['; |
| case Token.TOKEN_RIGHT_BRACKET: return ']'; |
| case Token.TOKEN_LEFT_BRACE: return ' {'; |
| case Token.TOKEN_RIGHT_BRACE: return '}'; |
| |
| default: return '<unknown>'; |
| } |
| }; |
| |
| /** |
| * @param {?gluShaderUtil.DataType} expectedDataType |
| * @param {Object} result |
| */ |
| var parseValueElement = function(expectedDataType, result) { |
| /** @type {?string} */ var scalarType = null; |
| /** @type {number} */ var scalarSize = 0; |
| if (expectedDataType) { |
| scalarType = gluShaderUtil.getDataTypeScalarType(expectedDataType); |
| scalarSize = gluShaderUtil.getDataTypeScalarSize(expectedDataType); |
| } |
| |
| /** @type {Array<number>} */ var elems = []; |
| |
| if (scalarSize > 1) { |
| glsShaderLibrary.de_assert(mapDataTypeToken(m_curToken) === expectedDataType); |
| advanceToken(); // data type(float, vec2, etc.) |
| advanceToken(Token.TOKEN_LEFT_PAREN); |
| } |
| |
| for (var i = 0; i < scalarSize; ++i) { |
| if (scalarType === 'float') { |
| |
| /** @type {number} */ var signMult = 1.0; |
| if (m_curToken === Token.TOKEN_MINUS) { |
| signMult = -1.0; |
| advanceToken(); |
| } |
| |
| assumeToken(Token.TOKEN_FLOAT_LITERAL); |
| elems.push(signMult * parseFloatLiteral(m_curTokenStr)); |
| advanceToken(Token.TOKEN_FLOAT_LITERAL); |
| |
| } else if (scalarType === 'int' || scalarType === 'uint') { |
| |
| var signMult = 1; |
| if (m_curToken === Token.TOKEN_MINUS) { |
| signMult = -1; |
| advanceToken(); |
| } |
| |
| assumeToken(Token.TOKEN_INT_LITERAL); |
| elems.push(signMult * parseIntLiteral(m_curTokenStr)); |
| advanceToken(Token.TOKEN_INT_LITERAL); |
| |
| } else { |
| |
| glsShaderLibrary.de_assert(scalarType === 'bool'); |
| elems.push(m_curToken === Token.TOKEN_TRUE); |
| if (m_curToken != Token.TOKEN_TRUE && m_curToken != Token.TOKEN_FALSE) { |
| throw Error('unexpected token, expecting bool: ' + m_curTokenStr); |
| } |
| advanceToken(); // true/false |
| |
| } |
| |
| if (i != (scalarSize - 1)) { |
| advanceToken(Token.TOKEN_COMMA); |
| } |
| } |
| |
| if (scalarSize > 1) { |
| advanceToken(Token.TOKEN_RIGHT_PAREN); |
| } |
| |
| for (var i = 0; i < elems.length; i++) |
| result.elements.push(elems[i]); |
| |
| }; |
| |
| /** |
| * @param {Object.<Array, number>} valueBlock |
| */ |
| var parseValue = function(valueBlock) { |
| |
| /** |
| * @type {Object} |
| */ |
| var result = { |
| /** @type {?gluShaderUtil.DataType} */ dataType: null, |
| /** @type {?glsShaderLibraryCase.shaderCase} */ storageType: null, |
| /** @type {?string} */ valueName: null, |
| /** @type {Array} */ elements: [] |
| }; |
| |
| // parse storage |
| switch (m_curToken) { |
| case Token.TOKEN_UNIFORM: |
| result.storageType = glsShaderLibraryCase.shaderCase.STORAGE_UNIFORM; |
| break; |
| case Token.TOKEN_INPUT: |
| result.storageType = glsShaderLibraryCase.shaderCase.STORAGE_INPUT; |
| break; |
| case Token.TOKEN_OUTPUT: |
| result.storageType = glsShaderLibraryCase.shaderCase.STORAGE_OUTPUT; |
| break; |
| default: |
| throw Error('unexpected token encountered when parsing value classifier'); |
| break; |
| } |
| advanceToken(); |
| |
| // parse data type |
| result.dataType = mapDataTypeToken(m_curToken); |
| if (result.dataType === gluShaderUtil.DataType.INVALID) { |
| throw Error('unexpected token when parsing value data type: ' + m_curTokenStr); |
| } |
| advanceToken(); |
| |
| // parse value name |
| if (m_curToken === Token.TOKEN_IDENTIFIER) { |
| result.valueName = m_curTokenStr; |
| } else if (m_curToken === Token.TOKEN_STRING) { |
| result.valueName = parseStringLiteral(m_curTokenStr); |
| } else { |
| throw Error('unexpected token when parsing value name: ' + m_curTokenStr); |
| } |
| advanceToken(); |
| |
| // parse assignment operator. |
| advanceToken(Token.TOKEN_ASSIGN); |
| |
| // parse actual value |
| if (m_curToken === Token.TOKEN_LEFT_BRACKET) { // value list |
| advanceToken(Token.TOKEN_LEFT_BRACKET); |
| result.arrayLength = 0; |
| |
| for (;;) { |
| parseValueElement(result.dataType, result); |
| result.arrayLength += 1; |
| |
| if (m_curToken === Token.TOKEN_RIGHT_BRACKET) { |
| break; |
| } else if (m_curToken === Token.TOKEN_VERTICAL_BAR) { // pipe? |
| advanceToken(); |
| continue; |
| } else { |
| throw Error('unexpected token in value element array: ' + m_curTokenStr); |
| } |
| } |
| |
| advanceToken(Token.TOKEN_RIGHT_BRACKET); |
| |
| } else { // arrays, single elements |
| parseValueElement(result.dataType, result); |
| result.arrayLength = 1; |
| } |
| |
| advanceToken(Token.TOKEN_SEMI_COLON); |
| |
| valueBlock.values.push(result); |
| |
| }; |
| |
| /** |
| * @param {Object.<Array, number>} valueBlock |
| */ |
| var parseValueBlock = function(valueBlock) { |
| |
| advanceToken(Token.TOKEN_VALUES); |
| advanceToken(Token.TOKEN_LEFT_BRACE); |
| |
| for (;;) { |
| if ( |
| m_curToken === Token.TOKEN_UNIFORM || |
| m_curToken === Token.TOKEN_INPUT || |
| m_curToken === Token.TOKEN_OUTPUT |
| ) { |
| parseValue(valueBlock); |
| } else if (m_curToken === Token.TOKEN_RIGHT_BRACE) { |
| break; |
| } else { |
| throw Error('unexpected( token when parsing a value block: ' + m_curTokenStr); |
| } |
| } |
| |
| advanceToken(Token.TOKEN_RIGHT_BRACE); |
| |
| /** @type {number} */ var arrayLength = 1; |
| // compute combined array length of value block. |
| for (var i = 0; i < valueBlock.values.length; ++i) { |
| if (valueBlock.values[i].arrayLength > 1) { |
| glsShaderLibrary.de_assert(arrayLength === 1 || arrayLength === valueBlock.values[i].arrayLength); |
| arrayLength = valueBlock.values[i].arrayLength; |
| } |
| } |
| |
| valueBlock.arrayLength = arrayLength; |
| |
| }; |
| |
| /** |
| * @param {Array<tcuTestCase.DeqpTest>} shaderNodeList |
| */ |
| var parseShaderCase = function(shaderNodeList) { |
| |
| // parse case |
| advanceToken(Token.TOKEN_CASE); |
| |
| /** |
| * @type {string} |
| * parse case name |
| */ |
| var caseName = m_curTokenStr; |
| advanceToken(); // \note [pyry] All token types are allowed here. |
| |
| /** |
| * @type {Array<Object>} |
| * setup case |
| */ |
| var valueBlockList = []; |
| |
| /** TODO: Should the default version be defined elsewhere? */ |
| /** @type {string} */ var version = '100'; |
| /** @type {number} */ var expectResult = glsShaderLibraryCase.expectResult.EXPECT_PASS; |
| /** @type {string} */ var description; |
| /** @type {string} */ var bothSource = ''; |
| /** @type {string} */ var vertexSource = ''; |
| /** @type {string} */ var fragmentSource = ''; |
| |
| for (;;) { |
| |
| if (m_curToken === Token.TOKEN_END) { |
| |
| break; |
| |
| } else if (m_curToken === Token.TOKEN_DESC) { |
| |
| advanceToken(); |
| assumeToken(Token.TOKEN_STRING); |
| |
| description = parseStringLiteral(m_curTokenStr); |
| advanceToken(); |
| |
| } else if (m_curToken === Token.TOKEN_EXPECT) { |
| |
| advanceToken(); |
| assumeToken(Token.TOKEN_IDENTIFIER); |
| |
| expectResult = (function(token) { |
| switch (token) { |
| case 'pass': return glsShaderLibraryCase.expectResult.EXPECT_PASS; |
| case 'compile_fail': return glsShaderLibraryCase.expectResult.EXPECT_COMPILE_FAIL; |
| case 'link_fail': return glsShaderLibraryCase.expectResult.EXPECT_LINK_FAIL; |
| case 'compile_or_link_fail': return glsShaderLibraryCase.expectResult.EXPECT_COMPILE_LINK_FAIL; |
| case 'build_successful': return glsShaderLibraryCase.expectResult.EXPECT_BUILD_SUCCESSFUL; |
| default: |
| throw Error('invalid expected result value: ' + m_curTokenStr); |
| } |
| }(m_curTokenStr)); |
| |
| advanceToken(); |
| |
| } else if (m_curToken === Token.TOKEN_VALUES) { |
| |
| /** @type {Object.<Array, number>} */ var block = glsShaderLibraryCase.genValueBlock(); |
| parseValueBlock(block); |
| valueBlockList.push(block); |
| |
| } else if ( |
| m_curToken === Token.TOKEN_BOTH || |
| m_curToken === Token.TOKEN_VERTEX || |
| m_curToken === Token.TOKEN_FRAGMENT |
| ) { |
| |
| /** @type {number} */ var token = m_curToken; |
| advanceToken(); |
| assumeToken(Token.TOKEN_SHADER_SOURCE); |
| /** @type {string} */ var source = parseShaderSource(m_curTokenStr); |
| |
| advanceToken(); |
| switch (token) { |
| case Token.TOKEN_BOTH: bothSource = source; break; |
| case Token.TOKEN_VERTEX: vertexSource = source; break; |
| case Token.TOKEN_FRAGMENT: fragmentSource = source; break; |
| default: glsShaderLibrary.de_assert(false); break; |
| } |
| |
| } else if (m_curToken === Token.TOKEN_VERSION) { |
| |
| advanceToken(); |
| |
| /** @type {number} */ var versionNum = 0; |
| /** @type {string} */ var postfix = ''; |
| |
| assumeToken(Token.TOKEN_INT_LITERAL); |
| versionNum = parseIntLiteral(m_curTokenStr); |
| advanceToken(); |
| |
| if (m_curToken === Token.TOKEN_IDENTIFIER) { |
| postfix = m_curTokenStr; |
| advanceToken(); |
| } |
| |
| // TODO: need to fix these constants, we dont have glu |
| if (versionNum === 100 && postfix === 'es') version = '100'; |
| else if (versionNum === 300 && postfix === 'es') version = '300 es'; |
| else if (versionNum === 310 && postfix === 'es') version = '310 es'; |
| else if (versionNum === 130) version = '130'; |
| else if (versionNum === 140) version = '140'; |
| else if (versionNum === 150) version = '150'; |
| else if (versionNum === 330) version = '330'; |
| else if (versionNum === 400) version = '400'; |
| else if (versionNum === 410) version = '410'; |
| else if (versionNum === 420) version = '420'; |
| else if (versionNum === 430) version = '430'; |
| else if (versionNum === 440) version = '440'; |
| else if (versionNum === 450) version = '450'; |
| else { |
| throw Error('Unknown GLSL version'); |
| } |
| |
| } else { |
| throw Error('unexpected token while parsing shader case: ' + m_curTokenStr); |
| } |
| |
| } |
| |
| advanceToken(Token.TOKEN_END); // case end |
| |
| /** |
| * no ShaderCase yet? |
| * @param {?string} vert |
| * @param {?string} frag |
| * @param {glsShaderLibraryCase.caseType} type |
| * @return {Object} |
| */ |
| var getShaderSpec = function(vert, frag, type) { |
| return { |
| /** @type {glsShaderLibraryCase.expectResult} */ expectResult: expectResult, |
| /** @type {glsShaderLibraryCase.caseType} */ caseType: type, |
| /** @type {Array<Object>} */ valueBlockList: valueBlockList, |
| /** @type {string} */ targetVersion: version, |
| /** @type {?string} */ vertexSource: vert, |
| /** @type {?string} */ fragmentSource: frag |
| }; |
| }; |
| getShaderSpec.bind(this); |
| |
| if (bothSource.length) { |
| |
| glsShaderLibrary.de_assert(!vertexSource); |
| glsShaderLibrary.de_assert(!fragmentSource); |
| |
| shaderNodeList.push(tcuTestCase.newTest(caseName + '_vertex', description, getShaderSpec(bothSource, null, |
| glsShaderLibraryCase.caseType.CASETYPE_VERTEX_ONLY))); |
| shaderNodeList.push(tcuTestCase.newTest(caseName + '_fragment', description, getShaderSpec(null, bothSource, |
| glsShaderLibraryCase.caseType.CASETYPE_FRAGMENT_ONLY))); |
| |
| } else { |
| glsShaderLibrary.de_assert(vertexSource); |
| glsShaderLibrary.de_assert(fragmentSource); |
| |
| shaderNodeList.push(tcuTestCase.newTest(caseName, description, getShaderSpec(vertexSource, fragmentSource, |
| glsShaderLibraryCase.caseType.CASETYPE_COMPLETE))); |
| } |
| }; |
| |
| /** |
| * @param {Array<tcuTestCase.DeqpTest>} shaderNodeList |
| */ |
| var parseShaderGroup = function(shaderNodeList) { |
| |
| // parse 'case' |
| advanceToken(Token.TOKEN_GROUP); |
| |
| /** @type {string} |
| * parse case name |
| */ var name = m_curTokenStr; |
| advanceToken(); // \note [pyry] We don't want to check token type here (for instance to allow "uniform") group. |
| |
| // Parse description. |
| assumeToken(Token.TOKEN_STRING); |
| /** @type {string} */ var description = parseStringLiteral(m_curTokenStr); |
| advanceToken(Token.TOKEN_STRING); |
| |
| /** @type {Array<tcuTestCase.DeqpTest>} */ var children = []; |
| |
| for (;;) { |
| |
| if (m_curToken === Token.TOKEN_END) { |
| break; |
| } else if (m_curToken === Token.TOKEN_GROUP) { |
| parseShaderGroup(children); |
| } else if (m_curToken === Token.TOKEN_CASE) { |
| parseShaderCase(children); |
| } else { |
| testFailed('unexpected token while parsing shader group: ' + m_curTokenStr); |
| tcuTestCase.runner.terminate(); |
| } |
| |
| } |
| |
| advanceToken(Token.TOKEN_END); // group end |
| |
| /** @type {tcuTestCase.DeqpTest} */ var groupNode = tcuTestCase.newTest(name, description, null); |
| groupNode.setChildren(children); |
| |
| shaderNodeList.push(groupNode); |
| |
| }; |
| |
| // uncomment to expose private functions |
| (function(obj) { |
| obj.priv = { |
| m_curPtr: m_curPtr, |
| |
| parseError: parseError, |
| parseFloatLiteral: parseFloatLiteral, |
| parseIntLiteral: parseIntLiteral, |
| parseStringLiteral: parseStringLiteral, |
| parseShaderSource: parseShaderSource, |
| advanceTokenTester: advanceTokenTester, |
| assumeToken: assumeToken, |
| mapDataTypeToken: mapDataTypeToken, |
| getTokenName: getTokenName, |
| |
| Token: Token, |
| |
| parseValueElement: parseValueElement, |
| parseValue: parseValue, |
| parseValueBlock: parseValueBlock, |
| parseShaderCase: parseShaderCase, |
| parseShaderGroup: parseShaderGroup, |
| |
| none: false |
| }; |
| }(this)); |
| //*/ |
| }; |
| |
| /** |
| * Parse the test file and execute the test cases |
| * @param {string} testName Name of the test file (without extension) |
| * @param {string} filter Optional filter. Common substring of the names of the tests that should be glsShaderLibrary.run. |
| */ |
| glsShaderLibrary.run = function(testName, filter) { |
| WebGLTestUtils.loadTextFileAsync(testName + '.test', function(success, content) { |
| if (success) { |
| tcuTestCase.runner.testFile = content; |
| tcuTestCase.runner.testName = testName; |
| tcuTestCase.runner.runCallback(glsShaderLibrary.processTestFile); |
| } else { |
| testFailed('Failed to load test file: ' + testName); |
| tcuTestCase.runner.terminate(); |
| } |
| }); |
| }; |
| |
| }); |