| /*------------------------------------------------------------------------- |
| * OpenGL Conformance Test Suite |
| * ----------------------------- |
| * |
| * Copyright (c) 2016 Google Inc. |
| * Copyright (c) 2016 The Khronos Group Inc. |
| * |
| * 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. |
| * |
| */ /*! |
| * \file |
| * \brief Compiler test case. |
| */ /*-------------------------------------------------------------------*/ |
| |
| #include "glcShaderLibrary.hpp" |
| #include "glcShaderLibraryCase.hpp" |
| #include "gluShaderUtil.hpp" |
| #include "tcuResource.hpp" |
| |
| #include "deInt32.h" |
| |
| #include <fstream> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| using std::string; |
| using std::vector; |
| using std::ostringstream; |
| |
| using namespace glu; |
| |
| #if 0 |
| #define PARSE_DBG(X) printf X |
| #else |
| #define PARSE_DBG(X) DE_NULL_STATEMENT |
| #endif |
| |
| namespace deqp |
| { |
| namespace sl |
| { |
| |
| static const glu::GLSLVersion DEFAULT_GLSL_VERSION = glu::GLSL_VERSION_100_ES; |
| |
| DE_INLINE deBool isWhitespace(char c) |
| { |
| return (c == ' ') || (c == '\t') || (c == '\r') || (c == '\n'); |
| } |
| |
| DE_INLINE deBool isEOL(char c) |
| { |
| return (c == '\r') || (c == '\n'); |
| } |
| |
| DE_INLINE deBool isNumeric(char c) |
| { |
| return deInRange32(c, '0', '9'); |
| } |
| |
| DE_INLINE deBool isAlpha(char c) |
| { |
| return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z'); |
| } |
| |
| DE_INLINE deBool isCaseNameChar(char c) |
| { |
| return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z') || deInRange32(c, '0', '9') || (c == '_') || |
| (c == '-') || (c == '.'); |
| } |
| |
| // \todo [2011-02-11 pyry] Should not depend on Context or TestContext! |
| class ShaderParser |
| { |
| public: |
| ShaderParser(tcu::TestContext& testCtx, RenderContext& renderCtx); |
| ~ShaderParser(void); |
| |
| vector<tcu::TestNode*> parse(const char* input); |
| |
| private: |
| enum Token |
| { |
| TOKEN_INVALID = 0, |
| TOKEN_EOF, |
| TOKEN_STRING, |
| TOKEN_SHADER_SOURCE, |
| |
| TOKEN_INT_LITERAL, |
| TOKEN_FLOAT_LITERAL, |
| |
| // identifiers |
| TOKEN_IDENTIFIER, |
| TOKEN_TRUE, |
| TOKEN_FALSE, |
| TOKEN_DESC, |
| TOKEN_EXPECT, |
| TOKEN_GROUP, |
| TOKEN_CASE, |
| TOKEN_END, |
| TOKEN_VALUES, |
| TOKEN_BOTH, |
| TOKEN_VERTEX, |
| TOKEN_FRAGMENT, |
| TOKEN_UNIFORM, |
| TOKEN_INPUT, |
| TOKEN_OUTPUT, |
| TOKEN_FLOAT, |
| TOKEN_FLOAT_VEC2, |
| TOKEN_FLOAT_VEC3, |
| TOKEN_FLOAT_VEC4, |
| TOKEN_FLOAT_MAT2, |
| TOKEN_FLOAT_MAT2X3, |
| TOKEN_FLOAT_MAT2X4, |
| TOKEN_FLOAT_MAT3X2, |
| TOKEN_FLOAT_MAT3, |
| TOKEN_FLOAT_MAT3X4, |
| TOKEN_FLOAT_MAT4X2, |
| TOKEN_FLOAT_MAT4X3, |
| TOKEN_FLOAT_MAT4, |
| TOKEN_INT, |
| TOKEN_INT_VEC2, |
| TOKEN_INT_VEC3, |
| TOKEN_INT_VEC4, |
| TOKEN_UINT, |
| TOKEN_UINT_VEC2, |
| TOKEN_UINT_VEC3, |
| TOKEN_UINT_VEC4, |
| TOKEN_BOOL, |
| TOKEN_BOOL_VEC2, |
| TOKEN_BOOL_VEC3, |
| TOKEN_BOOL_VEC4, |
| TOKEN_VERSION, |
| |
| // symbols |
| TOKEN_ASSIGN, |
| TOKEN_PLUS, |
| TOKEN_MINUS, |
| TOKEN_COMMA, |
| TOKEN_VERTICAL_BAR, |
| TOKEN_SEMI_COLON, |
| TOKEN_LEFT_PAREN, |
| TOKEN_RIGHT_PAREN, |
| TOKEN_LEFT_BRACKET, |
| TOKEN_RIGHT_BRACKET, |
| TOKEN_LEFT_BRACE, |
| TOKEN_RIGHT_BRACE, |
| |
| TOKEN_LAST |
| }; |
| |
| void parseError(const std::string& errorStr); |
| float parseFloatLiteral(const char* str); |
| long long int parseIntLiteral(const char* str); |
| string parseStringLiteral(const char* str); |
| string parseShaderSource(const char* str); |
| void advanceToken(void); |
| void advanceToken(Token assumed); |
| void assumeToken(Token token); |
| DataType mapDataTypeToken(Token token); |
| const char* getTokenName(Token token); |
| |
| void parseValueElement(DataType dataType, ShaderCase::Value& result); |
| void parseValue(ShaderCase::ValueBlock& valueBlock); |
| void parseValueBlock(ShaderCase::ValueBlock& valueBlock); |
| void parseShaderCase(vector<tcu::TestNode*>& shaderNodeList); |
| void parseShaderGroup(vector<tcu::TestNode*>& shaderNodeList); |
| |
| // Member variables. |
| tcu::TestContext& m_testCtx; |
| RenderContext& m_renderCtx; |
| std::string m_input; |
| const char* m_curPtr; |
| Token m_curToken; |
| std::string m_curTokenStr; |
| }; |
| |
| ShaderParser::ShaderParser(tcu::TestContext& testCtx, RenderContext& renderCtx) |
| : m_testCtx(testCtx), m_renderCtx(renderCtx), m_curPtr(DE_NULL), m_curToken(TOKEN_LAST) |
| { |
| } |
| |
| ShaderParser::~ShaderParser(void) |
| { |
| // nada |
| } |
| |
| void ShaderParser::parseError(const std::string& errorStr) |
| { |
| string atStr = string(m_curPtr, 80); |
| throw tcu::InternalError((string("Parser error: ") + errorStr + " near '" + atStr + " ...'").c_str(), "", __FILE__, |
| __LINE__); |
| } |
| |
| float ShaderParser::parseFloatLiteral(const char* str) |
| { |
| return (float)atof(str); |
| } |
| |
| long long int ShaderParser::parseIntLiteral(const char* str) |
| { |
| return strtoll(str, NULL, 0); |
| } |
| |
| string ShaderParser::parseStringLiteral(const char* str) |
| { |
| const char* p = str; |
| char endChar = *p++; |
| ostringstream o; |
| |
| while (*p != endChar && *p) |
| { |
| if (*p == '\\') |
| { |
| switch (p[1]) |
| { |
| case 0: |
| DE_ASSERT(DE_FALSE); |
| break; |
| case 'n': |
| o << '\n'; |
| break; |
| case 't': |
| o << '\t'; |
| break; |
| default: |
| o << p[1]; |
| break; |
| } |
| |
| p += 2; |
| } |
| else |
| o << *p++; |
| } |
| |
| return o.str(); |
| } |
| |
| static string removeExtraIndentation(const string& source) |
| { |
| // Detect indentation from first line. |
| int numIndentChars = 0; |
| for (int ndx = 0; ndx < (int)source.length() && isWhitespace(source[ndx]); ndx++) |
| numIndentChars += source[ndx] == '\t' ? 4 : 1; |
| |
| // Process all lines and remove preceding indentation. |
| ostringstream processed; |
| { |
| bool atLineStart = true; |
| int indentCharsOmitted = 0; |
| |
| for (int pos = 0; pos < (int)source.length(); pos++) |
| { |
| char c = source[pos]; |
| |
| if (atLineStart && indentCharsOmitted < numIndentChars && (c == ' ' || c == '\t')) |
| { |
| indentCharsOmitted += c == '\t' ? 4 : 1; |
| } |
| else if (isEOL(c)) |
| { |
| if (source[pos] == '\r' && source[pos + 1] == '\n') |
| { |
| pos += 1; |
| processed << '\n'; |
| } |
| else |
| processed << c; |
| |
| atLineStart = true; |
| indentCharsOmitted = 0; |
| } |
| else |
| { |
| processed << c; |
| atLineStart = false; |
| } |
| } |
| } |
| |
| return processed.str(); |
| } |
| |
| string ShaderParser::parseShaderSource(const char* str) |
| { |
| const char* p = str + 2; |
| ostringstream o; |
| |
| // Eat first empty line from beginning. |
| while (*p == ' ') |
| p++; |
| while (isEOL(*p)) |
| p++; |
| |
| while ((p[0] != '"') || (p[1] != '"')) |
| { |
| if (*p == '\\') |
| { |
| switch (p[1]) |
| { |
| case 0: |
| DE_ASSERT(DE_FALSE); |
| break; |
| case 'n': |
| o << '\n'; |
| break; |
| case 't': |
| o << '\t'; |
| break; |
| default: |
| o << p[1]; |
| break; |
| } |
| |
| p += 2; |
| } |
| else |
| o << *p++; |
| } |
| |
| return removeExtraIndentation(o.str()); |
| } |
| |
| void ShaderParser::advanceToken(void) |
| { |
| // Skip old token. |
| m_curPtr += m_curTokenStr.length(); |
| |
| // Reset token (for safety). |
| m_curToken = TOKEN_INVALID; |
| m_curTokenStr = ""; |
| |
| // Eat whitespace & comments while they last. |
| for (;;) |
| { |
| while (isWhitespace(*m_curPtr)) |
| m_curPtr++; |
| |
| // Check for EOL comment. |
| if (*m_curPtr == '#') |
| { |
| while (*m_curPtr && !isEOL(*m_curPtr)) |
| m_curPtr++; |
| } |
| else |
| break; |
| } |
| |
| if (!*m_curPtr) |
| { |
| m_curToken = TOKEN_EOF; |
| m_curTokenStr = "<EOF>"; |
| } |
| else if (isAlpha(*m_curPtr)) |
| { |
| struct Named |
| { |
| const char* str; |
| Token token; |
| }; |
| |
| static const Named s_named[] = { { "true", TOKEN_TRUE }, |
| { "false", TOKEN_FALSE }, |
| { "desc", TOKEN_DESC }, |
| { "expect", TOKEN_EXPECT }, |
| { "group", TOKEN_GROUP }, |
| { "case", TOKEN_CASE }, |
| { "end", TOKEN_END }, |
| { "values", TOKEN_VALUES }, |
| { "both", TOKEN_BOTH }, |
| { "vertex", TOKEN_VERTEX }, |
| { "fragment", TOKEN_FRAGMENT }, |
| { "uniform", TOKEN_UNIFORM }, |
| { "input", TOKEN_INPUT }, |
| { "output", TOKEN_OUTPUT }, |
| { "float", TOKEN_FLOAT }, |
| { "vec2", TOKEN_FLOAT_VEC2 }, |
| { "vec3", TOKEN_FLOAT_VEC3 }, |
| { "vec4", TOKEN_FLOAT_VEC4 }, |
| { "mat2", TOKEN_FLOAT_MAT2 }, |
| { "mat2x3", TOKEN_FLOAT_MAT2X3 }, |
| { "mat2x4", TOKEN_FLOAT_MAT2X4 }, |
| { "mat3x2", TOKEN_FLOAT_MAT3X2 }, |
| { "mat3", TOKEN_FLOAT_MAT3 }, |
| { "mat3x4", TOKEN_FLOAT_MAT3X4 }, |
| { "mat4x2", TOKEN_FLOAT_MAT4X2 }, |
| { "mat4x3", TOKEN_FLOAT_MAT4X3 }, |
| { "mat4", TOKEN_FLOAT_MAT4 }, |
| { "int", TOKEN_INT }, |
| { "ivec2", TOKEN_INT_VEC2 }, |
| { "ivec3", TOKEN_INT_VEC3 }, |
| { "ivec4", TOKEN_INT_VEC4 }, |
| { "uint", TOKEN_UINT }, |
| { "uvec2", TOKEN_UINT_VEC2 }, |
| { "uvec3", TOKEN_UINT_VEC3 }, |
| { "uvec4", TOKEN_UINT_VEC4 }, |
| { "bool", TOKEN_BOOL }, |
| { "bvec2", TOKEN_BOOL_VEC2 }, |
| { "bvec3", TOKEN_BOOL_VEC3 }, |
| { "bvec4", TOKEN_BOOL_VEC4 }, |
| { "version", TOKEN_VERSION } }; |
| |
| const char* end = m_curPtr + 1; |
| while (isCaseNameChar(*end)) |
| end++; |
| m_curTokenStr = string(m_curPtr, end - m_curPtr); |
| |
| m_curToken = TOKEN_IDENTIFIER; |
| |
| for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_named); ndx++) |
| { |
| if (m_curTokenStr == s_named[ndx].str) |
| { |
| m_curToken = s_named[ndx].token; |
| break; |
| } |
| } |
| } |
| else if (isNumeric(*m_curPtr)) |
| { |
| /* \todo [2010-03-31 petri] Hex? */ |
| const char* p = m_curPtr; |
| while (isNumeric(*p)) |
| p++; |
| if (*p == '.') |
| { |
| p++; |
| while (isNumeric(*p)) |
| p++; |
| |
| if (*p == 'e' || *p == 'E') |
| { |
| p++; |
| if (*p == '+' || *p == '-') |
| p++; |
| DE_ASSERT(isNumeric(*p)); |
| while (isNumeric(*p)) |
| p++; |
| } |
| |
| m_curToken = TOKEN_FLOAT_LITERAL; |
| m_curTokenStr = string(m_curPtr, p - m_curPtr); |
| } |
| else |
| { |
| m_curToken = TOKEN_INT_LITERAL; |
| m_curTokenStr = string(m_curPtr, p - m_curPtr); |
| } |
| } |
| else if (*m_curPtr == '"' && m_curPtr[1] == '"') |
| { |
| const char* p = m_curPtr + 2; |
| |
| while ((p[0] != '"') || (p[1] != '"')) |
| { |
| DE_ASSERT(*p); |
| if (*p == '\\') |
| { |
| DE_ASSERT(p[1] != 0); |
| p += 2; |
| } |
| else |
| p++; |
| } |
| p += 2; |
| |
| m_curToken = TOKEN_SHADER_SOURCE; |
| m_curTokenStr = string(m_curPtr, (int)(p - m_curPtr)); |
| } |
| else if (*m_curPtr == '"' || *m_curPtr == '\'') |
| { |
| char endChar = *m_curPtr; |
| const char* p = m_curPtr + 1; |
| |
| while (*p != endChar) |
| { |
| DE_ASSERT(*p); |
| if (*p == '\\') |
| { |
| DE_ASSERT(p[1] != 0); |
| p += 2; |
| } |
| else |
| p++; |
| } |
| p++; |
| |
| m_curToken = TOKEN_STRING; |
| m_curTokenStr = string(m_curPtr, (int)(p - m_curPtr)); |
| } |
| else |
| { |
| struct SimpleToken |
| { |
| const char* str; |
| Token token; |
| }; |
| |
| static const SimpleToken s_simple[] = { { "=", TOKEN_ASSIGN }, { "+", TOKEN_PLUS }, |
| { "-", TOKEN_MINUS }, { ",", TOKEN_COMMA }, |
| { "|", TOKEN_VERTICAL_BAR }, { ";", TOKEN_SEMI_COLON }, |
| { "(", TOKEN_LEFT_PAREN }, { ")", TOKEN_RIGHT_PAREN }, |
| { "[", TOKEN_LEFT_BRACKET }, { "]", TOKEN_RIGHT_BRACKET }, |
| { "{", TOKEN_LEFT_BRACE }, { "}", TOKEN_RIGHT_BRACE } }; |
| |
| for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_simple); ndx++) |
| { |
| if (strncmp(s_simple[ndx].str, m_curPtr, strlen(s_simple[ndx].str)) == 0) |
| { |
| m_curToken = s_simple[ndx].token; |
| m_curTokenStr = s_simple[ndx].str; |
| return; |
| } |
| } |
| |
| // Otherwise invalid token. |
| m_curToken = TOKEN_INVALID; |
| m_curTokenStr = *m_curPtr; |
| } |
| } |
| |
| void ShaderParser::advanceToken(Token assumed) |
| { |
| assumeToken(assumed); |
| advanceToken(); |
| } |
| |
| void ShaderParser::assumeToken(Token token) |
| { |
| if (m_curToken != token) |
| parseError( |
| (string("unexpected token '") + m_curTokenStr + "', expecting '" + getTokenName(token) + "'").c_str()); |
| DE_TEST_ASSERT(m_curToken == token); |
| } |
| |
| DataType ShaderParser::mapDataTypeToken(Token token) |
| { |
| switch (token) |
| { |
| case TOKEN_FLOAT: |
| return TYPE_FLOAT; |
| case TOKEN_FLOAT_VEC2: |
| return TYPE_FLOAT_VEC2; |
| case TOKEN_FLOAT_VEC3: |
| return TYPE_FLOAT_VEC3; |
| case TOKEN_FLOAT_VEC4: |
| return TYPE_FLOAT_VEC4; |
| case TOKEN_FLOAT_MAT2: |
| return TYPE_FLOAT_MAT2; |
| case TOKEN_FLOAT_MAT2X3: |
| return TYPE_FLOAT_MAT2X3; |
| case TOKEN_FLOAT_MAT2X4: |
| return TYPE_FLOAT_MAT2X4; |
| case TOKEN_FLOAT_MAT3X2: |
| return TYPE_FLOAT_MAT3X2; |
| case TOKEN_FLOAT_MAT3: |
| return TYPE_FLOAT_MAT3; |
| case TOKEN_FLOAT_MAT3X4: |
| return TYPE_FLOAT_MAT3X4; |
| case TOKEN_FLOAT_MAT4X2: |
| return TYPE_FLOAT_MAT4X2; |
| case TOKEN_FLOAT_MAT4X3: |
| return TYPE_FLOAT_MAT4X3; |
| case TOKEN_FLOAT_MAT4: |
| return TYPE_FLOAT_MAT4; |
| case TOKEN_INT: |
| return TYPE_INT; |
| case TOKEN_INT_VEC2: |
| return TYPE_INT_VEC2; |
| case TOKEN_INT_VEC3: |
| return TYPE_INT_VEC3; |
| case TOKEN_INT_VEC4: |
| return TYPE_INT_VEC4; |
| case TOKEN_UINT: |
| return TYPE_UINT; |
| case TOKEN_UINT_VEC2: |
| return TYPE_UINT_VEC2; |
| case TOKEN_UINT_VEC3: |
| return TYPE_UINT_VEC3; |
| case TOKEN_UINT_VEC4: |
| return TYPE_UINT_VEC4; |
| case TOKEN_BOOL: |
| return TYPE_BOOL; |
| case TOKEN_BOOL_VEC2: |
| return TYPE_BOOL_VEC2; |
| case TOKEN_BOOL_VEC3: |
| return TYPE_BOOL_VEC3; |
| case TOKEN_BOOL_VEC4: |
| return TYPE_BOOL_VEC4; |
| default: |
| return TYPE_INVALID; |
| } |
| } |
| |
| const char* ShaderParser::getTokenName(Token token) |
| { |
| switch (token) |
| { |
| case TOKEN_INVALID: |
| return "<invalid>"; |
| case TOKEN_EOF: |
| return "<eof>"; |
| case TOKEN_STRING: |
| return "<string>"; |
| case TOKEN_SHADER_SOURCE: |
| return "source"; |
| |
| case TOKEN_INT_LITERAL: |
| return "<int>"; |
| case TOKEN_FLOAT_LITERAL: |
| return "<float>"; |
| |
| // identifiers |
| case TOKEN_IDENTIFIER: |
| return "<identifier>"; |
| case TOKEN_TRUE: |
| return "true"; |
| case TOKEN_FALSE: |
| return "false"; |
| case TOKEN_DESC: |
| return "desc"; |
| case TOKEN_EXPECT: |
| return "expect"; |
| case TOKEN_GROUP: |
| return "group"; |
| case TOKEN_CASE: |
| return "case"; |
| case TOKEN_END: |
| return "end"; |
| case TOKEN_VALUES: |
| return "values"; |
| case TOKEN_BOTH: |
| return "both"; |
| case TOKEN_VERTEX: |
| return "vertex"; |
| case TOKEN_FRAGMENT: |
| return "fragment"; |
| case TOKEN_UNIFORM: |
| return "uniform"; |
| case TOKEN_INPUT: |
| return "input"; |
| case TOKEN_OUTPUT: |
| return "output"; |
| case TOKEN_FLOAT: |
| return "float"; |
| case TOKEN_FLOAT_VEC2: |
| return "vec2"; |
| case TOKEN_FLOAT_VEC3: |
| return "vec3"; |
| case TOKEN_FLOAT_VEC4: |
| return "vec4"; |
| case TOKEN_FLOAT_MAT2: |
| return "mat2"; |
| case TOKEN_FLOAT_MAT2X3: |
| return "mat2x3"; |
| case TOKEN_FLOAT_MAT2X4: |
| return "mat2x4"; |
| case TOKEN_FLOAT_MAT3X2: |
| return "mat3x2"; |
| case TOKEN_FLOAT_MAT3: |
| return "mat3"; |
| case TOKEN_FLOAT_MAT3X4: |
| return "mat3x4"; |
| case TOKEN_FLOAT_MAT4X2: |
| return "mat4x2"; |
| case TOKEN_FLOAT_MAT4X3: |
| return "mat4x3"; |
| case TOKEN_FLOAT_MAT4: |
| return "mat4"; |
| case TOKEN_INT: |
| return "int"; |
| case TOKEN_INT_VEC2: |
| return "ivec2"; |
| case TOKEN_INT_VEC3: |
| return "ivec3"; |
| case TOKEN_INT_VEC4: |
| return "ivec4"; |
| case TOKEN_UINT: |
| return "uint"; |
| case TOKEN_UINT_VEC2: |
| return "uvec2"; |
| case TOKEN_UINT_VEC3: |
| return "uvec3"; |
| case TOKEN_UINT_VEC4: |
| return "uvec4"; |
| case TOKEN_BOOL: |
| return "bool"; |
| case TOKEN_BOOL_VEC2: |
| return "bvec2"; |
| case TOKEN_BOOL_VEC3: |
| return "bvec3"; |
| case TOKEN_BOOL_VEC4: |
| return "bvec4"; |
| |
| case TOKEN_ASSIGN: |
| return "="; |
| case TOKEN_PLUS: |
| return "+"; |
| case TOKEN_MINUS: |
| return "-"; |
| case TOKEN_COMMA: |
| return ","; |
| case TOKEN_VERTICAL_BAR: |
| return "|"; |
| case TOKEN_SEMI_COLON: |
| return ";"; |
| case TOKEN_LEFT_PAREN: |
| return "("; |
| case TOKEN_RIGHT_PAREN: |
| return ")"; |
| case TOKEN_LEFT_BRACKET: |
| return "["; |
| case TOKEN_RIGHT_BRACKET: |
| return "]"; |
| case TOKEN_LEFT_BRACE: |
| return "{"; |
| case TOKEN_RIGHT_BRACE: |
| return "}"; |
| |
| default: |
| return "<unknown>"; |
| } |
| } |
| |
| void ShaderParser::parseValueElement(DataType expectedDataType, ShaderCase::Value& result) |
| { |
| DataType scalarType = getDataTypeScalarType(expectedDataType); |
| int scalarSize = getDataTypeScalarSize(expectedDataType); |
| |
| /* \todo [2010-04-19 petri] Support arrays. */ |
| ShaderCase::Value::Element elems[16]; |
| |
| if (scalarSize > 1) |
| { |
| DE_ASSERT(mapDataTypeToken(m_curToken) == expectedDataType); |
| advanceToken(); // data type (float, vec2, etc.) |
| advanceToken(TOKEN_LEFT_PAREN); |
| } |
| |
| for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++) |
| { |
| if (scalarType == TYPE_FLOAT) |
| { |
| float signMult = 1.0f; |
| if (m_curToken == TOKEN_MINUS) |
| { |
| signMult = -1.0f; |
| advanceToken(); |
| } |
| |
| assumeToken(TOKEN_FLOAT_LITERAL); |
| elems[scalarNdx].float32 = signMult * parseFloatLiteral(m_curTokenStr.c_str()); |
| advanceToken(TOKEN_FLOAT_LITERAL); |
| } |
| else if (scalarType == TYPE_INT || scalarType == TYPE_UINT) |
| { |
| int signMult = 1; |
| if (m_curToken == TOKEN_MINUS) |
| { |
| signMult = -1; |
| advanceToken(); |
| } |
| |
| assumeToken(TOKEN_INT_LITERAL); |
| elems[scalarNdx].int32 = (deInt32)(signMult * parseIntLiteral(m_curTokenStr.c_str())); |
| advanceToken(TOKEN_INT_LITERAL); |
| } |
| else |
| { |
| DE_ASSERT(scalarType == TYPE_BOOL); |
| elems[scalarNdx].bool32 = (m_curToken == TOKEN_TRUE); |
| if (m_curToken != TOKEN_TRUE && m_curToken != TOKEN_FALSE) |
| parseError(string("unexpected token, expecting bool: " + m_curTokenStr)); |
| advanceToken(); // true/false |
| } |
| |
| if (scalarNdx != (scalarSize - 1)) |
| advanceToken(TOKEN_COMMA); |
| } |
| |
| if (scalarSize > 1) |
| advanceToken(TOKEN_RIGHT_PAREN); |
| |
| // Store results. |
| for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++) |
| result.elements.push_back(elems[scalarNdx]); |
| } |
| |
| void ShaderParser::parseValue(ShaderCase::ValueBlock& valueBlock) |
| { |
| PARSE_DBG((" parseValue()\n")); |
| |
| // Parsed results. |
| ShaderCase::Value result; |
| |
| // Parse storage. |
| if (m_curToken == TOKEN_UNIFORM) |
| result.storageType = ShaderCase::Value::STORAGE_UNIFORM; |
| else if (m_curToken == TOKEN_INPUT) |
| result.storageType = ShaderCase::Value::STORAGE_INPUT; |
| else if (m_curToken == TOKEN_OUTPUT) |
| result.storageType = ShaderCase::Value::STORAGE_OUTPUT; |
| else |
| parseError(string("unexpected token encountered when parsing value classifier")); |
| advanceToken(); |
| |
| // Parse data type. |
| result.dataType = mapDataTypeToken(m_curToken); |
| if (result.dataType == TYPE_INVALID) |
| parseError(string("unexpected token when parsing value data type: " + m_curTokenStr)); |
| advanceToken(); |
| |
| // Parse value name. |
| if (m_curToken == TOKEN_IDENTIFIER || m_curToken == TOKEN_STRING) |
| { |
| if (m_curToken == TOKEN_IDENTIFIER) |
| result.valueName = m_curTokenStr; |
| else |
| result.valueName = parseStringLiteral(m_curTokenStr.c_str()); |
| } |
| else |
| parseError(string("unexpected token when parsing value name: " + m_curTokenStr)); |
| advanceToken(); |
| |
| // Parse assignment operator. |
| advanceToken(TOKEN_ASSIGN); |
| |
| // Parse actual value. |
| if (m_curToken == TOKEN_LEFT_BRACKET) // value list |
| { |
| advanceToken(TOKEN_LEFT_BRACKET); |
| result.arrayLength = 0; |
| |
| for (;;) |
| { |
| parseValueElement(result.dataType, result); |
| result.arrayLength++; |
| |
| if (m_curToken == TOKEN_RIGHT_BRACKET) |
| break; |
| else if (m_curToken == TOKEN_VERTICAL_BAR) |
| { |
| advanceToken(); |
| continue; |
| } |
| else |
| parseError(string("unexpected token in value element array: " + m_curTokenStr)); |
| } |
| |
| advanceToken(TOKEN_RIGHT_BRACKET); |
| } |
| else // arrays, single elements |
| { |
| parseValueElement(result.dataType, result); |
| result.arrayLength = 1; |
| } |
| |
| advanceToken(TOKEN_SEMI_COLON); // end of declaration |
| |
| valueBlock.values.push_back(result); |
| } |
| |
| void ShaderParser::parseValueBlock(ShaderCase::ValueBlock& valueBlock) |
| { |
| PARSE_DBG((" parseValueBlock()\n")); |
| advanceToken(TOKEN_VALUES); |
| advanceToken(TOKEN_LEFT_BRACE); |
| |
| for (;;) |
| { |
| if (m_curToken == TOKEN_UNIFORM || m_curToken == TOKEN_INPUT || m_curToken == TOKEN_OUTPUT) |
| parseValue(valueBlock); |
| else if (m_curToken == TOKEN_RIGHT_BRACE) |
| break; |
| else |
| parseError(string("unexpected token when parsing a value block: " + m_curTokenStr)); |
| } |
| |
| advanceToken(TOKEN_RIGHT_BRACE); |
| |
| // Compute combined array length of value block. |
| int arrayLength = 1; |
| for (int valueNdx = 0; valueNdx < (int)valueBlock.values.size(); valueNdx++) |
| { |
| const ShaderCase::Value& val = valueBlock.values[valueNdx]; |
| if (val.arrayLength > 1) |
| { |
| DE_ASSERT(arrayLength == 1 || arrayLength == val.arrayLength); |
| arrayLength = val.arrayLength; |
| } |
| } |
| valueBlock.arrayLength = arrayLength; |
| } |
| |
| void ShaderParser::parseShaderCase(vector<tcu::TestNode*>& shaderNodeList) |
| { |
| // Parse 'case'. |
| PARSE_DBG((" parseShaderCase()\n")); |
| advanceToken(TOKEN_CASE); |
| |
| // Parse case name. |
| string caseName = m_curTokenStr; |
| advanceToken(); // \note [pyry] All token types are allowed here. |
| |
| // Setup case. |
| vector<ShaderCase::ValueBlock> valueBlockList; |
| |
| GLSLVersion version = DEFAULT_GLSL_VERSION; |
| ShaderCase::ExpectResult expectResult = ShaderCase::EXPECT_PASS; |
| string description; |
| string bothSource; |
| string vertexSource; |
| string fragmentSource; |
| |
| for (;;) |
| { |
| if (m_curToken == TOKEN_END) |
| break; |
| else if (m_curToken == TOKEN_DESC) |
| { |
| advanceToken(); |
| assumeToken(TOKEN_STRING); |
| |
| description = parseStringLiteral(m_curTokenStr.c_str()); |
| advanceToken(); |
| } |
| else if (m_curToken == TOKEN_EXPECT) |
| { |
| advanceToken(); |
| assumeToken(TOKEN_IDENTIFIER); |
| |
| if (m_curTokenStr == "pass") |
| expectResult = ShaderCase::EXPECT_PASS; |
| else if (m_curTokenStr == "compile_fail") |
| expectResult = ShaderCase::EXPECT_COMPILE_FAIL; |
| else if (m_curTokenStr == "link_fail") |
| expectResult = ShaderCase::EXPECT_LINK_FAIL; |
| else |
| parseError(string("invalid expected result value: " + m_curTokenStr)); |
| |
| advanceToken(); |
| } |
| else if (m_curToken == TOKEN_VALUES) |
| { |
| ShaderCase::ValueBlock block; |
| parseValueBlock(block); |
| valueBlockList.push_back(block); |
| } |
| else if (m_curToken == TOKEN_BOTH || m_curToken == TOKEN_VERTEX || m_curToken == TOKEN_FRAGMENT) |
| { |
| Token token = m_curToken; |
| advanceToken(); |
| assumeToken(TOKEN_SHADER_SOURCE); |
| string source = parseShaderSource(m_curTokenStr.c_str()); |
| advanceToken(); |
| switch (token) |
| { |
| case TOKEN_BOTH: |
| bothSource = source; |
| break; |
| case TOKEN_VERTEX: |
| vertexSource = source; |
| break; |
| case TOKEN_FRAGMENT: |
| fragmentSource = source; |
| break; |
| default: |
| DE_ASSERT(DE_FALSE); |
| } |
| } |
| else if (m_curToken == TOKEN_VERSION) |
| { |
| advanceToken(); |
| |
| int versionNum = 0; |
| std::string postfix = ""; |
| |
| assumeToken(TOKEN_INT_LITERAL); |
| versionNum = (int)parseIntLiteral(m_curTokenStr.c_str()); |
| advanceToken(); |
| |
| if (m_curToken == TOKEN_IDENTIFIER) |
| { |
| postfix = m_curTokenStr; |
| advanceToken(); |
| } |
| |
| if (versionNum == 100 && postfix == "es") |
| version = glu::GLSL_VERSION_100_ES; |
| else if (versionNum == 300 && postfix == "es") |
| version = glu::GLSL_VERSION_300_ES; |
| else if (versionNum == 310 && postfix == "es") |
| version = glu::GLSL_VERSION_310_ES; |
| else if (versionNum == 130) |
| version = glu::GLSL_VERSION_130; |
| else if (versionNum == 140) |
| version = glu::GLSL_VERSION_140; |
| else if (versionNum == 150) |
| version = glu::GLSL_VERSION_150; |
| else if (versionNum == 330) |
| version = glu::GLSL_VERSION_330; |
| else if (versionNum == 400) |
| version = glu::GLSL_VERSION_400; |
| else if (versionNum == 410) |
| version = glu::GLSL_VERSION_410; |
| else if (versionNum == 420) |
| version = glu::GLSL_VERSION_420; |
| else if (versionNum == 430) |
| version = glu::GLSL_VERSION_430; |
| else if (versionNum == 440) |
| version = glu::GLSL_VERSION_440; |
| else if (versionNum == 450) |
| version = glu::GLSL_VERSION_450; |
| else |
| parseError("Unknown GLSL version"); |
| } |
| else |
| parseError(string("unexpected token while parsing shader case: " + m_curTokenStr)); |
| } |
| |
| advanceToken(TOKEN_END); // case end |
| |
| if (bothSource.length() > 0) |
| { |
| DE_ASSERT(vertexSource.length() == 0); |
| DE_ASSERT(fragmentSource.length() == 0); |
| |
| string vertName = caseName + "_vertex"; |
| string fragName = caseName + "_fragment"; |
| shaderNodeList.push_back(new ShaderCase(m_testCtx, m_renderCtx, vertName.c_str(), description.c_str(), |
| expectResult, valueBlockList, version, bothSource.c_str(), DE_NULL)); |
| shaderNodeList.push_back(new ShaderCase(m_testCtx, m_renderCtx, fragName.c_str(), description.c_str(), |
| expectResult, valueBlockList, version, DE_NULL, bothSource.c_str())); |
| } |
| else |
| { |
| shaderNodeList.push_back(new ShaderCase(m_testCtx, m_renderCtx, caseName.c_str(), description.c_str(), |
| expectResult, valueBlockList, version, vertexSource.c_str(), |
| fragmentSource.c_str())); |
| } |
| } |
| |
| void ShaderParser::parseShaderGroup(vector<tcu::TestNode*>& shaderNodeList) |
| { |
| // Parse 'case'. |
| PARSE_DBG((" parseShaderGroup()\n")); |
| advanceToken(TOKEN_GROUP); |
| |
| // Parse case name. |
| string 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_STRING); |
| string description = parseStringLiteral(m_curTokenStr.c_str()); |
| advanceToken(TOKEN_STRING); |
| |
| std::vector<tcu::TestNode*> children; |
| |
| // Parse group children. |
| for (;;) |
| { |
| if (m_curToken == TOKEN_END) |
| break; |
| else if (m_curToken == TOKEN_GROUP) |
| parseShaderGroup(children); |
| else if (m_curToken == TOKEN_CASE) |
| parseShaderCase(children); |
| else |
| parseError(string("unexpected token while parsing shader group: " + m_curTokenStr)); |
| } |
| |
| advanceToken(TOKEN_END); // group end |
| |
| // Create group node. |
| tcu::TestCaseGroup* groupNode = new tcu::TestCaseGroup(m_testCtx, name.c_str(), description.c_str(), children); |
| shaderNodeList.push_back(groupNode); |
| } |
| |
| vector<tcu::TestNode*> ShaderParser::parse(const char* input) |
| { |
| // Initialize parser. |
| m_input = input; |
| m_curPtr = m_input.c_str(); |
| m_curToken = TOKEN_INVALID; |
| m_curTokenStr = ""; |
| advanceToken(); |
| |
| vector<tcu::TestNode*> nodeList; |
| |
| // Parse all cases. |
| PARSE_DBG(("parse()\n")); |
| for (;;) |
| { |
| if (m_curToken == TOKEN_CASE) |
| parseShaderCase(nodeList); |
| else if (m_curToken == TOKEN_GROUP) |
| parseShaderGroup(nodeList); |
| else if (m_curToken == TOKEN_EOF) |
| break; |
| else |
| parseError(string("invalid token encountered at main level: '") + m_curTokenStr + "'"); |
| } |
| |
| assumeToken(TOKEN_EOF); |
| // printf(" parsed %d test cases.\n", caseList.size()); |
| return nodeList; |
| } |
| |
| } // sl |
| |
| ShaderLibrary::ShaderLibrary(tcu::TestContext& testCtx, RenderContext& renderCtx) |
| : m_testCtx(testCtx), m_renderCtx(renderCtx) |
| { |
| } |
| |
| ShaderLibrary::~ShaderLibrary(void) |
| { |
| } |
| |
| vector<tcu::TestNode*> ShaderLibrary::loadShaderFile(const char* fileName) |
| { |
| tcu::Resource* resource = m_testCtx.getArchive().getResource(fileName); |
| std::vector<char> buf; |
| |
| /* printf(" loading '%s'\n", fileName);*/ |
| |
| try |
| { |
| int size = resource->getSize(); |
| buf.resize(size + 1); |
| resource->read((deUint8*)&buf[0], size); |
| buf[size] = '\0'; |
| } |
| catch (const std::exception&) |
| { |
| delete resource; |
| throw; |
| } |
| |
| delete resource; |
| |
| sl::ShaderParser parser(m_testCtx, m_renderCtx); |
| vector<tcu::TestNode*> nodes = parser.parse(&buf[0]); |
| |
| return nodes; |
| } |
| |
| // ShaderLibraryGroup |
| |
| ShaderLibraryGroup::ShaderLibraryGroup(Context& context, const char* name, const char* description, |
| const char* filename) |
| : TestCaseGroup(context, name, description), m_filename(filename) |
| { |
| } |
| |
| ShaderLibraryGroup::~ShaderLibraryGroup(void) |
| { |
| } |
| |
| void ShaderLibraryGroup::init(void) |
| { |
| deqp::ShaderLibrary shaderLibrary(m_testCtx, m_context.getRenderContext()); |
| std::vector<tcu::TestNode*> children = shaderLibrary.loadShaderFile(m_filename.c_str()); |
| |
| for (int i = 0; i < (int)children.size(); i++) |
| addChild(children[i]); |
| } |
| |
| } // deqp |