From: Pyry Haulos Date: Wed, 2 Sep 2015 17:47:12 +0000 (+0000) Subject: Revert "Refactor ShaderLibrary" X-Git-Tag: upstream/0.1.0~1391 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0024f5512d4e3796b42bb2afe5449e3adac6a137;p=platform%2Fupstream%2FVK-GL-CTS.git Revert "Refactor ShaderLibrary" This reverts commit 877323dd00656b1045e89b4cc27d2e3ab9ecac2c. Change-Id: I65ba2756b218d76f1189d82287dafbdb6316d725 --- diff --git a/data/gles2/shaders/constants.test b/data/gles2/shaders/constants.test index b2cb1b7..7564a5e 100644 --- a/data/gles2/shaders/constants.test +++ b/data/gles2/shaders/constants.test @@ -1,8 +1,13 @@ case float_input values { - input float in0 = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 | -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ]; - output float out0 = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 | -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ]; + input float in0 = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 ]; + output float out0 = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 ]; + } + values + { + input float in0 = [ -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ]; + output float out0 = [ -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ]; } both "" diff --git a/data/gles3/shaders/constants.test b/data/gles3/shaders/constants.test index 43a035c..d8752a1 100644 --- a/data/gles3/shaders/constants.test +++ b/data/gles3/shaders/constants.test @@ -2,8 +2,13 @@ case float_input version 300 es values { - input float in0 = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 | -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ]; - output float out0 = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 | -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ]; + input float in0 = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 ]; + output float out0 = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 ]; + } + values + { + input float in0 = [ -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ]; + output float out0 = [ -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ]; } both "" diff --git a/framework/opengl/CMakeLists.txt b/framework/opengl/CMakeLists.txt index 810fb38..13b7db2 100644 --- a/framework/opengl/CMakeLists.txt +++ b/framework/opengl/CMakeLists.txt @@ -52,8 +52,6 @@ set(GLUTIL_SRCS gluDummyRenderContext.hpp gluPlatform.cpp gluPlatform.hpp - gluShaderLibrary.cpp - gluShaderLibrary.hpp ) set(GLUTIL_LIBS diff --git a/framework/opengl/gluShaderLibrary.cpp b/framework/opengl/gluShaderLibrary.cpp deleted file mode 100644 index 0e8530b..0000000 --- a/framework/opengl/gluShaderLibrary.cpp +++ /dev/null @@ -1,1847 +0,0 @@ -/*------------------------------------------------------------------------- - * drawElements Quality Program OpenGL ES Utilities - * ------------------------------------------------ - * - * Copyright 2015 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. - * - *//*! - * \file - * \brief Shader .test file utilities. - *//*--------------------------------------------------------------------*/ - -#include "gluShaderLibrary.hpp" - -#include "tcuStringTemplate.hpp" -#include "tcuResource.hpp" -#include "tcuTestLog.hpp" - -#include "deStringUtil.hpp" -#include "deUniquePtr.hpp" -#include "deFilePath.hpp" - -#include "glwEnums.hpp" - -#include -#include -#include - -#if 0 -# define PARSE_DBG(X) printf X -#else -# define PARSE_DBG(X) DE_NULL_STATEMENT -#endif - -namespace glu -{ -namespace sl -{ - -using namespace tcu; - -using std::vector; -using std::string; -using std::map; -using std::ostringstream; -using std::pair; -using de::UniquePtr; - -// Specification - -bool isValid (const ValueBlock& block) -{ - for (size_t storageNdx = 0; storageNdx < 3; ++storageNdx) - { - const vector& values = storageNdx == 0 ? block.inputs : - storageNdx == 1 ? block.outputs : - block.uniforms; - const size_t refArrayLen = values.empty() ? 0 : (values[0].elements.size() / (size_t)values[0].type.getScalarSize()); - - for (size_t valNdx = 0; valNdx < values.size(); ++valNdx) - { - const Value& value = values[valNdx]; - - if (!value.type.isBasicType()) - { - print("ERROR: Value '%s' is of unsupported type!\n", value.name.c_str()); - return false; - } - - if (value.elements.size() != refArrayLen*(size_t)value.type.getScalarSize()) - { - print("ERROR: Value '%s' has invalid number of scalars!\n", value.name.c_str()); - return false; - } - } - } - - return true; -} - -bool isValid (const ShaderCaseSpecification& spec) -{ - const deUint32 vtxFragMask = (1u << SHADERTYPE_VERTEX) - | (1u << SHADERTYPE_FRAGMENT); - const deUint32 tessCtrlEvalMask = (1u << SHADERTYPE_TESSELLATION_CONTROL) - | (1u << SHADERTYPE_TESSELLATION_EVALUATION); - const deUint32 supportedStageMask = vtxFragMask | tessCtrlEvalMask - | (1u << SHADERTYPE_GEOMETRY); - const bool isSeparable = !spec.programs.empty() && spec.programs[0].sources.separable; - - if (spec.programs.empty()) - { - print("ERROR: No programs specified!\n"); - return false; - } - - if (spec.fullGLSLES100Required) - { - if (spec.targetVersion != GLSL_VERSION_100_ES) - { - print("ERROR: Full GLSL ES 1.00 support requested for other GLSL version!\n"); - return false; - } - - if (spec.expectResult != EXPECT_PASS && - spec.expectResult != EXPECT_VALIDATION_FAIL && - spec.expectResult != EXPECT_BUILD_SUCCESSFUL) - { - print("ERROR: Full GLSL ES 1.00 support doesn't make sense when expecting compile/link failure!\n"); - return false; - } - } - - if (!de::inBounds(spec.caseType, (CaseType)0, CASETYPE_LAST)) - { - print("ERROR: Invalid case type!\n"); - return false; - } - - if (!de::inBounds(spec.expectResult, (ExpectResult)0, EXPECT_LAST)) - { - print("ERROR: Invalid expected result!\n"); - return false; - } - - if (!isValid(spec.values)) - return false; - - if (!spec.values.inputs.empty() && !spec.values.outputs.empty() && - spec.values.inputs[0].elements.size() / spec.values.inputs[0].type.getScalarSize() != spec.values.outputs[0].elements.size() / spec.values.outputs[0].type.getScalarSize()) - { - print("ERROR: Number of input and output elements don't match!\n"); - return false; - } - - if (isSeparable) - { - deUint32 usedStageMask = 0u; - - if (spec.caseType != CASETYPE_COMPLETE) - { - print("ERROR: Separable shaders supported only for complete cases!\n"); - return false; - } - - for (size_t progNdx = 0; progNdx < spec.programs.size(); ++progNdx) - { - for (int shaderStageNdx = 0; shaderStageNdx < SHADERTYPE_LAST; ++shaderStageNdx) - { - const deUint32 curStageMask = (1u << shaderStageNdx); - - if (supportedStageMask & curStageMask) - { - const bool hasShader = !spec.programs[progNdx].sources.sources[shaderStageNdx].empty(); - const bool isEnabled = (spec.programs[progNdx].activeStages & curStageMask) != 0; - - if (hasShader != isEnabled) - { - print("ERROR: Inconsistent source/enable for shader stage %s!\n", getShaderTypeName((ShaderType)shaderStageNdx)); - return false; - } - - if (hasShader && (usedStageMask & curStageMask) != 0) - { - print("ERROR: Stage %s enabled on multiple programs!\n", getShaderTypeName((ShaderType)shaderStageNdx)); - return false; - } - - usedStageMask |= curStageMask; - } - else if (!spec.programs[progNdx].sources.sources[shaderStageNdx].empty()) - { - print("ERROR: Source specified for unsupported shader stage %s!\n", getShaderTypeName((ShaderType)shaderStageNdx)); - return false; - } - } - } - - if ((usedStageMask & vtxFragMask) != vtxFragMask) - { - print("ERROR: Vertex and fragment shaders are mandatory!\n"); - return false; - } - - if ((usedStageMask & tessCtrlEvalMask) != 0 && (usedStageMask & tessCtrlEvalMask) != tessCtrlEvalMask) - { - print("ERROR: Both tessellation control and eval shaders must be either enabled or disabled!\n"); - return false; - } - } - else - { - const bool hasVertex = !spec.programs[0].sources.sources[SHADERTYPE_VERTEX].empty(); - const bool hasFragment = !spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].empty(); - - if (spec.programs.size() != 1) - { - print("ERROR: Only cases using separable programs can have multiple programs!\n"); - return false; - } - - if (spec.caseType == CASETYPE_VERTEX_ONLY && (!hasVertex || hasFragment)) - { - print("ERROR: Vertex-only case must have only vertex shader!\n"); - return false; - } - - if (spec.caseType == CASETYPE_FRAGMENT_ONLY && (hasVertex || !hasFragment)) - { - print("ERROR: Fragment-only case must have only fragment shader!\n"); - return false; - } - - if (spec.caseType == CASETYPE_COMPLETE && (!hasVertex || !hasFragment)) - { - print("ERROR: Complete case must have at least vertex and fragment shaders\n"); - return false; - } - } - - return true; -} - -// Parser - -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 == '.'); -} - -struct CaseRequirement -{ - enum Type - { - TYPE_EXTENSION = 0, - TYPE_FULL_GLSL_ES_100_SUPPORT, - TYPE_IMPLEMENTATION_LIMIT, - - TYPE_LAST - }; - - Type type; - - // TYPE_EXTENSION: - RequiredExtension extension; - - // TYPE_IMPLEMENTATION_LIMIT - RequiredCapability requiredCap; - - CaseRequirement (void) : type(TYPE_LAST) {} - - static CaseRequirement createFullGLSLES100SpecificationRequirement (void) - { - CaseRequirement req; - req.type = TYPE_FULL_GLSL_ES_100_SUPPORT; - return req; - } - - static CaseRequirement createAnyExtensionRequirement (const vector& alternatives, deUint32 effectiveStages) - { - CaseRequirement req; - req.type = TYPE_EXTENSION; - req.extension = RequiredExtension(alternatives, effectiveStages); - return req; - } - - static CaseRequirement createLimitRequirement (deUint32 enumName, int referenceValue) - { - CaseRequirement req; - req.type = TYPE_IMPLEMENTATION_LIMIT; - req.requiredCap = RequiredCapability(enumName, referenceValue); - return req; - } -}; - -class ShaderParser -{ -public: - ShaderParser (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory); - ~ShaderParser (void); - - vector parse (void); - -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, - TOKEN_TESSELLATION_CONTROL, - TOKEN_TESSELLATION_EVALUATION, - TOKEN_GEOMETRY, - TOKEN_REQUIRE, - TOKEN_IN, - TOKEN_IMPORT, - TOKEN_PIPELINE_PROGRAM, - TOKEN_ACTIVE_STAGES, - - // 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_GREATER, - - TOKEN_LAST - }; - - void parseError (const std::string& errorStr); - float parseFloatLiteral (const char* str); - 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); - deUint32 getShaderStageLiteralFlag (void); - deUint32 getGLEnumFromName (const std::string& enumName); - - void parseValueElement (DataType dataType, Value& result); - void parseValue (ValueBlock& valueBlock); - void parseValueBlock (ValueBlock& valueBlock); - deUint32 parseShaderStageList (void); - void parseRequirement (CaseRequirement& valueBlock); - void parseExpectResult (ExpectResult& expectResult); - void parseGLSLVersion (glu::GLSLVersion& version); - void parsePipelineProgram (ProgramSpecification& program); - void parseShaderCase (vector& shaderNodeList); - void parseShaderGroup (vector& shaderNodeList); - void parseImport (vector& shaderNodeList); - - const tcu::Archive& m_archive; - const string m_filename; - ShaderCaseFactory* const m_caseFactory; - - UniquePtr m_resource; - vector m_input; - - const char* m_curPtr; - Token m_curToken; - std::string m_curTokenStr; -}; - -ShaderParser::ShaderParser (const tcu::Archive& archive, const string& filename, ShaderCaseFactory* caseFactroy) - : m_archive (archive) - , m_filename (filename) - , m_caseFactory (caseFactroy) - , m_resource (archive.getResource(m_filename.c_str())) - , m_curPtr (DE_NULL) - , m_curToken (TOKEN_LAST) -{ -} - -ShaderParser::~ShaderParser (void) -{ -} - -void ShaderParser::parseError (const std::string& errorStr) -{ - string atStr = string(m_curPtr, 80); - throw tcu::InternalError((string("Parser error: ") + errorStr + " near '" + atStr + " ...'").c_str(), DE_NULL, __FILE__, __LINE__); -} - -float ShaderParser::parseFloatLiteral (const char* str) -{ - return (float)atof(str); -} - -int ShaderParser::parseIntLiteral (const char* str) -{ - return atoi(str); -} - -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++; - if (*p == '\r') p++; - if (*p == '\n') 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 = ""; - } - 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 }, - { "tessellation_control", TOKEN_TESSELLATION_CONTROL }, - { "tessellation_evaluation", TOKEN_TESSELLATION_EVALUATION }, - { "geometry", TOKEN_GEOMETRY }, - { "require", TOKEN_REQUIRE }, - { "in", TOKEN_IN }, - { "import", TOKEN_IMPORT }, - { "pipeline_program", TOKEN_PIPELINE_PROGRAM }, - { "active_stages", TOKEN_ACTIVE_STAGES }, - }; - - 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 }, - { ">", TOKEN_GREATER }, - }; - - 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 ""; - case TOKEN_EOF: return ""; - case TOKEN_STRING: return ""; - case TOKEN_SHADER_SOURCE: return "source"; - - case TOKEN_INT_LITERAL: return ""; - case TOKEN_FLOAT_LITERAL: return ""; - - // identifiers - case TOKEN_IDENTIFIER: return ""; - 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_TESSELLATION_CONTROL: return "tessellation_control"; - case TOKEN_TESSELLATION_EVALUATION: return "tessellation_evaluation"; - case TOKEN_GEOMETRY: return "geometry"; - case TOKEN_REQUIRE: return "require"; - 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_IN: return "in"; - case TOKEN_IMPORT: return "import"; - case TOKEN_PIPELINE_PROGRAM: return "pipeline_program"; - case TOKEN_ACTIVE_STAGES: return "active_stages"; - - 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 "}"; - case TOKEN_GREATER: return ">"; - - default: return ""; - } -} - -deUint32 ShaderParser::getShaderStageLiteralFlag (void) -{ - switch (m_curToken) - { - case TOKEN_VERTEX: return (1 << glu::SHADERTYPE_VERTEX); - case TOKEN_FRAGMENT: return (1 << glu::SHADERTYPE_FRAGMENT); - case TOKEN_GEOMETRY: return (1 << glu::SHADERTYPE_GEOMETRY); - case TOKEN_TESSELLATION_CONTROL: return (1 << glu::SHADERTYPE_TESSELLATION_CONTROL); - case TOKEN_TESSELLATION_EVALUATION: return (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION); - - default: - parseError(std::string() + "invalid shader stage name, got " + m_curTokenStr); - return 0; - } -} - -deUint32 ShaderParser::getGLEnumFromName (const std::string& enumName) -{ - static const struct - { - const char* name; - deUint32 value; - } names[] = - { - { "GL_MAX_VERTEX_IMAGE_UNIFORMS", GL_MAX_VERTEX_IMAGE_UNIFORMS }, - { "GL_MAX_VERTEX_ATOMIC_COUNTERS", GL_MAX_VERTEX_ATOMIC_COUNTERS }, - { "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS", GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS }, - { "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS", GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS }, - }; - - for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(names); ++ndx) - if (names[ndx].name == enumName) - return names[ndx].value; - - parseError(std::string() + "unknown enum name, got " + enumName); - return 0; -} - -void ShaderParser::parseValueElement (DataType expectedDataType, Value& result) -{ - DataType scalarType = getDataTypeScalarType(expectedDataType); - int scalarSize = getDataTypeScalarSize(expectedDataType); - - /* \todo [2010-04-19 petri] Support arrays. */ - 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 = 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 (ValueBlock& valueBlock) -{ - PARSE_DBG((" parseValue()\n")); - - // Parsed results. - vector* dstBlock = DE_NULL; - DataType basicType = TYPE_LAST; - std::string valueName; - - // Parse storage. - if (m_curToken == TOKEN_UNIFORM) - dstBlock = &valueBlock.uniforms; - else if (m_curToken == TOKEN_INPUT) - dstBlock = &valueBlock.inputs; - else if (m_curToken == TOKEN_OUTPUT) - dstBlock = &valueBlock.outputs; - else - parseError(string("unexpected token encountered when parsing value classifier")); - advanceToken(); - - // Parse data type. - basicType = mapDataTypeToken(m_curToken); - if (basicType == 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) - valueName = m_curTokenStr; - else - valueName = parseStringLiteral(m_curTokenStr.c_str()); - } - else - parseError(string("unexpected token when parsing value name: " + m_curTokenStr)); - advanceToken(); - - // Parse assignment operator. - advanceToken(TOKEN_ASSIGN); - - { - Value value; - value.name = valueName; - value.type = VarType(basicType, PRECISION_LAST); - dstBlock->push_back(value); - } - - // Parse actual value. - if (m_curToken == TOKEN_LEFT_BRACKET) // value list - { - int arrayLength = 0; // \todo [2015-08-03 pyry] Currently unused - - advanceToken(TOKEN_LEFT_BRACKET); - - for (;;) - { - parseValueElement(basicType, dstBlock->back()); - 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 // single elements - { - parseValueElement(basicType, dstBlock->back()); - } - - advanceToken(TOKEN_SEMI_COLON); // end of declaration -} - -void ShaderParser::parseValueBlock (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); -} - -deUint32 ShaderParser::parseShaderStageList (void) -{ - deUint32 mask = 0; - - assumeToken(TOKEN_LEFT_BRACE); - - // don't allow 0-sized lists - advanceToken(); - mask |= getShaderStageLiteralFlag(); - advanceToken(); - - for (;;) - { - if (m_curToken == TOKEN_RIGHT_BRACE) - break; - else if (m_curToken == TOKEN_COMMA) - { - deUint32 stageFlag; - advanceToken(); - - stageFlag = getShaderStageLiteralFlag(); - if (stageFlag & mask) - parseError(string("stage already set in the shader stage set: " + m_curTokenStr)); - - mask |= stageFlag; - advanceToken(); - } - else - parseError(string("invalid shader stage set token: " + m_curTokenStr)); - } - advanceToken(TOKEN_RIGHT_BRACE); - - return mask; -} - -void ShaderParser::parseRequirement (CaseRequirement& valueBlock) -{ - PARSE_DBG((" parseRequirement()\n")); - - advanceToken(); - assumeToken(TOKEN_IDENTIFIER); - - if (m_curTokenStr == "extension") - { - std::vector anyExtensionStringList; - deUint32 affectedCasesFlags = -1; // by default all stages - - advanceToken(); - assumeToken(TOKEN_LEFT_BRACE); - - advanceToken(); - assumeToken(TOKEN_STRING); - - anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str())); - advanceToken(); - - for (;;) - { - if (m_curToken == TOKEN_RIGHT_BRACE) - break; - else if (m_curToken == TOKEN_VERTICAL_BAR) - { - advanceToken(); - assumeToken(TOKEN_STRING); - - anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str())); - advanceToken(); - } - else - parseError(string("invalid extension list token: " + m_curTokenStr)); - } - advanceToken(TOKEN_RIGHT_BRACE); - - if (m_curToken == TOKEN_IN) - { - advanceToken(); - affectedCasesFlags = parseShaderStageList(); - } - - valueBlock = CaseRequirement::createAnyExtensionRequirement(anyExtensionStringList, affectedCasesFlags); - } - else if (m_curTokenStr == "limit") - { - deUint32 limitEnum; - int limitValue; - - advanceToken(); - - assumeToken(TOKEN_STRING); - limitEnum = getGLEnumFromName(parseStringLiteral(m_curTokenStr.c_str())); - advanceToken(); - - assumeToken(TOKEN_GREATER); - advanceToken(); - - assumeToken(TOKEN_INT_LITERAL); - limitValue = parseIntLiteral(m_curTokenStr.c_str()); - advanceToken(); - - valueBlock = CaseRequirement::createLimitRequirement(limitEnum, limitValue); - } - else if (m_curTokenStr == "full_glsl_es_100_support") - { - advanceToken(); - - valueBlock = CaseRequirement::createFullGLSLES100SpecificationRequirement(); - } - else - parseError(string("invalid requirement value: " + m_curTokenStr)); -} - -void ShaderParser::parseExpectResult (ExpectResult& expectResult) -{ - assumeToken(TOKEN_IDENTIFIER); - - if (m_curTokenStr == "pass") - expectResult = EXPECT_PASS; - else if (m_curTokenStr == "compile_fail") - expectResult = EXPECT_COMPILE_FAIL; - else if (m_curTokenStr == "link_fail") - expectResult = EXPECT_LINK_FAIL; - else if (m_curTokenStr == "compile_or_link_fail") - expectResult = EXPECT_COMPILE_LINK_FAIL; - else if (m_curTokenStr == "validation_fail") - expectResult = EXPECT_VALIDATION_FAIL; - else if (m_curTokenStr == "build_successful") - expectResult = EXPECT_BUILD_SUCCESSFUL; - else - parseError(string("invalid expected result value: " + m_curTokenStr)); - - advanceToken(); -} - -void ShaderParser::parseGLSLVersion (glu::GLSLVersion& version) -{ - int versionNum = 0; - std::string postfix = ""; - - assumeToken(TOKEN_INT_LITERAL); - versionNum = parseIntLiteral(m_curTokenStr.c_str()); - advanceToken(); - - if (m_curToken == TOKEN_IDENTIFIER) - { - postfix = m_curTokenStr; - advanceToken(); - } - - DE_STATIC_ASSERT(glu::GLSL_VERSION_LAST == 14); - - 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 == 320 && postfix == "es") version = glu::GLSL_VERSION_320_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"); -} - -void ShaderParser::parsePipelineProgram (ProgramSpecification& program) -{ - advanceToken(TOKEN_PIPELINE_PROGRAM); - - for (;;) - { - if (m_curToken == TOKEN_END) - break; - else if (m_curToken == TOKEN_ACTIVE_STAGES) - { - advanceToken(); - program.activeStages = parseShaderStageList(); - } - else if (m_curToken == TOKEN_REQUIRE) - { - CaseRequirement requirement; - parseRequirement(requirement); - - if (requirement.type == CaseRequirement::TYPE_EXTENSION) - program.requiredExtensions.push_back(requirement.extension); - else - parseError("only extension requirements are allowed inside pipeline program"); - } - else if (m_curToken == TOKEN_VERTEX || - m_curToken == TOKEN_FRAGMENT || - m_curToken == TOKEN_TESSELLATION_CONTROL || - m_curToken == TOKEN_TESSELLATION_EVALUATION || - m_curToken == TOKEN_GEOMETRY) - { - const Token token = m_curToken; - string source; - - advanceToken(); - assumeToken(TOKEN_SHADER_SOURCE); - source = parseShaderSource(m_curTokenStr.c_str()); - advanceToken(); - - switch (token) - { - case TOKEN_VERTEX: program.sources.sources[SHADERTYPE_VERTEX].push_back(source); break; - case TOKEN_FRAGMENT: program.sources.sources[SHADERTYPE_FRAGMENT].push_back(source); break; - case TOKEN_TESSELLATION_CONTROL: program.sources.sources[SHADERTYPE_TESSELLATION_CONTROL].push_back(source); break; - case TOKEN_TESSELLATION_EVALUATION: program.sources.sources[SHADERTYPE_TESSELLATION_EVALUATION].push_back(source); break; - case TOKEN_GEOMETRY: program.sources.sources[SHADERTYPE_GEOMETRY].push_back(source); break; - default: - parseError(DE_FALSE); - } - } - else - parseError(string("invalid pipeline program value: " + m_curTokenStr)); - } - advanceToken(TOKEN_END); - - if (program.activeStages == 0) - parseError("program pipeline object must have active stages"); -} - -void ShaderParser::parseShaderCase (vector& 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. - - // \todo [pyry] Optimize by parsing most stuff directly to ShaderCaseSpecification - - // Setup case. - GLSLVersion version = DEFAULT_GLSL_VERSION; - ExpectResult expectResult = EXPECT_PASS; - string description; - string bothSource; - vector vertexSources; - vector fragmentSources; - vector tessellationCtrlSources; - vector tessellationEvalSources; - vector geometrySources; - ValueBlock valueBlock; - bool valueBlockSeen = false; - vector requirements; - vector pipelinePrograms; - - 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(); - parseExpectResult(expectResult); - } - else if (m_curToken == TOKEN_VALUES) - { - if (valueBlockSeen) - parseError("multiple value blocks"); - parseValueBlock(valueBlock); - valueBlockSeen = true; - } - else if (m_curToken == TOKEN_BOTH || - m_curToken == TOKEN_VERTEX || - m_curToken == TOKEN_FRAGMENT || - m_curToken == TOKEN_TESSELLATION_CONTROL || - m_curToken == TOKEN_TESSELLATION_EVALUATION || - m_curToken == TOKEN_GEOMETRY) - { - const Token token = m_curToken; - string source; - - advanceToken(); - assumeToken(TOKEN_SHADER_SOURCE); - source = parseShaderSource(m_curTokenStr.c_str()); - advanceToken(); - - switch (token) - { - case TOKEN_VERTEX: vertexSources.push_back(source); break; - case TOKEN_FRAGMENT: fragmentSources.push_back(source); break; - case TOKEN_TESSELLATION_CONTROL: tessellationCtrlSources.push_back(source); break; - case TOKEN_TESSELLATION_EVALUATION: tessellationEvalSources.push_back(source); break; - case TOKEN_GEOMETRY: geometrySources.push_back(source); break; - case TOKEN_BOTH: - { - if (!bothSource.empty()) - parseError("multiple 'both' blocks"); - bothSource = source; - break; - } - - default: - parseError(DE_FALSE); - } - } - else if (m_curToken == TOKEN_VERSION) - { - advanceToken(); - parseGLSLVersion(version); - } - else if (m_curToken == TOKEN_REQUIRE) - { - CaseRequirement requirement; - parseRequirement(requirement); - requirements.push_back(requirement); - } - else if (m_curToken == TOKEN_PIPELINE_PROGRAM) - { - ProgramSpecification pipelineProgram; - parsePipelineProgram(pipelineProgram); - pipelinePrograms.push_back(pipelineProgram); - } - else - parseError(string("unexpected token while parsing shader case: " + m_curTokenStr)); - } - - advanceToken(TOKEN_END); // case end - - // \todo [pyry] Clean up - vector requiredCaps; - vector requiredExts; - bool fullGLSLES100Required = false; - - for (size_t reqNdx = 0; reqNdx < requirements.size(); ++reqNdx) - { - const CaseRequirement& requirement = requirements[reqNdx]; - - if (requirement.type == CaseRequirement::TYPE_EXTENSION) - requiredExts.push_back(requirement.extension); - else if (requirement.type == CaseRequirement::TYPE_IMPLEMENTATION_LIMIT) - requiredCaps.push_back(requirement.requiredCap); - else if (requirement.type == CaseRequirement::TYPE_FULL_GLSL_ES_100_SUPPORT) - fullGLSLES100Required = true; - else - DE_ASSERT(false); - } - - if (!bothSource.empty()) - { - if (!vertexSources.empty() || - !fragmentSources.empty() || - !tessellationCtrlSources.empty() || - !tessellationEvalSources.empty() || - !geometrySources.empty() || - !pipelinePrograms.empty()) - { - parseError("'both' cannot be mixed with other shader stages"); - } - - // vertex - { - ShaderCaseSpecification spec; - spec.caseType = CASETYPE_VERTEX_ONLY; - spec.expectResult = expectResult; - spec.targetVersion = version; - spec.fullGLSLES100Required = fullGLSLES100Required; - spec.requiredCaps = requiredCaps; - spec.values = valueBlock; - - spec.programs.resize(1); - spec.programs[0].sources << VertexSource(bothSource); - spec.programs[0].requiredExtensions = requiredExts; - - shaderNodeList.push_back(m_caseFactory->createCase(caseName + "_vertex", description, ShaderCaseSpecification(spec))); - } - - // fragment - { - ShaderCaseSpecification spec; - spec.caseType = CASETYPE_FRAGMENT_ONLY; - spec.expectResult = expectResult; - spec.targetVersion = version; - spec.fullGLSLES100Required = fullGLSLES100Required; - spec.requiredCaps = requiredCaps; - spec.values = valueBlock; - - spec.programs.resize(1); - spec.programs[0].sources << FragmentSource(bothSource); - spec.programs[0].requiredExtensions = requiredExts; - - shaderNodeList.push_back(m_caseFactory->createCase(caseName + "_fragment", description, ShaderCaseSpecification(spec))); - } - } - else if (pipelinePrograms.empty()) - { - ShaderCaseSpecification spec; - spec.caseType = CASETYPE_COMPLETE; - spec.expectResult = expectResult; - spec.targetVersion = version; - spec.fullGLSLES100Required = fullGLSLES100Required; - spec.requiredCaps = requiredCaps; - spec.values = valueBlock; - - spec.programs.resize(1); - spec.programs[0].sources.sources[SHADERTYPE_VERTEX].swap(vertexSources); - spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].swap(fragmentSources); - spec.programs[0].sources.sources[SHADERTYPE_TESSELLATION_CONTROL].swap(tessellationCtrlSources); - spec.programs[0].sources.sources[SHADERTYPE_TESSELLATION_EVALUATION].swap(tessellationEvalSources); - spec.programs[0].sources.sources[SHADERTYPE_GEOMETRY].swap(geometrySources); - spec.programs[0].requiredExtensions.swap(requiredExts); - - shaderNodeList.push_back(m_caseFactory->createCase(caseName, description, ShaderCaseSpecification(spec))); - } - else - { - if (!vertexSources.empty() || - !fragmentSources.empty() || - !tessellationCtrlSources.empty() || - !tessellationEvalSources.empty() || - !geometrySources.empty()) - { - parseError("pipeline programs cannot be mixed with complete programs"); - } - - if (!requiredExts.empty()) - parseError("global extension requirements cannot be mixed with pipeline programs"); - - // Pipeline case, multiple programs - { - ShaderCaseSpecification spec; - spec.caseType = CASETYPE_COMPLETE; - spec.expectResult = expectResult; - spec.targetVersion = version; - spec.fullGLSLES100Required = fullGLSLES100Required; - spec.requiredCaps = requiredCaps; - spec.values = valueBlock; - - spec.programs.swap(pipelinePrograms); - - shaderNodeList.push_back(m_caseFactory->createCase(caseName, description, ShaderCaseSpecification(spec))); - } - } -} - -void ShaderParser::parseShaderGroup (vector& 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 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 if (m_curToken == TOKEN_IMPORT) - parseImport(children); - else - parseError(string("unexpected token while parsing shader group: " + m_curTokenStr)); - } - - advanceToken(TOKEN_END); // group end - - // Create group node. - tcu::TestCaseGroup* groupNode = m_caseFactory->createGroup(name, description, children); - shaderNodeList.push_back(groupNode); -} - -void ShaderParser::parseImport (vector& shaderNodeList) -{ - std::string importFileName; - - advanceToken(TOKEN_IMPORT); - - assumeToken(TOKEN_STRING); - importFileName = parseStringLiteral(m_curTokenStr.c_str()); - advanceToken(TOKEN_STRING); - - { - ShaderParser subParser (m_archive, de::FilePath::join(de::FilePath(m_filename).getDirName(), importFileName).getPath(), m_caseFactory); - const vector importedCases = subParser.parse(); - - // \todo [2015-08-03 pyry] Not exception safe - shaderNodeList.insert(shaderNodeList.end(), importedCases.begin(), importedCases.end()); - } -} - -vector ShaderParser::parse (void) -{ - const int dataLen = m_resource->getSize(); - - m_input.resize(dataLen+1); - m_resource->setPosition(0); - m_resource->read((deUint8*)&m_input[0], dataLen); - m_input[dataLen] = '\0'; - - // Initialize parser. - m_curPtr = &m_input[0]; - m_curToken = TOKEN_INVALID; - m_curTokenStr = ""; - advanceToken(); - - vector 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_IMPORT) - parseImport(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; -} - -std::vector parseFile (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory) -{ - sl::ShaderParser parser (archive, filename, caseFactory); - - return parser.parse(); -} - -// Execution utilities - -static void dumpValue (tcu::TestLog& log, const Value& val, const char* storageName, int arrayNdx) -{ - const char* const valueName = val.name.c_str(); - const DataType dataType = val.type.getBasicType(); - int scalarSize = getDataTypeScalarSize(dataType); - ostringstream result; - - result << " " << storageName << " "; - - result << getDataTypeName(dataType) << " " << valueName << ":"; - - if (isDataTypeScalar(dataType)) - result << " "; - if (isDataTypeVector(dataType)) - result << " [ "; - else if (isDataTypeMatrix(dataType)) - result << "\n"; - - if (isDataTypeScalarOrVector(dataType)) - { - for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++) - { - int elemNdx = arrayNdx; - const Value::Element& e = val.elements[elemNdx*scalarSize + scalarNdx]; - result << ((scalarNdx != 0) ? ", " : ""); - - if (isDataTypeFloatOrVec(dataType)) - result << e.float32; - else if (isDataTypeIntOrIVec(dataType)) - result << e.int32; - else if (isDataTypeUintOrUVec(dataType)) - result << (deUint32)e.int32; - else if (isDataTypeBoolOrBVec(dataType)) - result << (e.bool32 ? "true" : "false"); - } - } - else if (isDataTypeMatrix(dataType)) - { - int numRows = getDataTypeMatrixNumRows(dataType); - int numCols = getDataTypeMatrixNumColumns(dataType); - for (int rowNdx = 0; rowNdx < numRows; rowNdx++) - { - result << " [ "; - for (int colNdx = 0; colNdx < numCols; colNdx++) - { - int elemNdx = arrayNdx; - float v = val.elements[elemNdx*scalarSize + rowNdx*numCols + colNdx].float32; - result << ((colNdx==0) ? "" : ", ") << v; - } - result << " ]\n"; - } - } - - if (isDataTypeScalar(dataType)) - result << "\n"; - else if (isDataTypeVector(dataType)) - result << " ]\n"; - - log << TestLog::Message << result.str() << TestLog::EndMessage; -} - -static void dumpValues (tcu::TestLog& log, const vector& values, const char* storageName, int arrayNdx) -{ - for (size_t valNdx = 0; valNdx < values.size(); valNdx++) - dumpValue(log, values[valNdx], storageName, arrayNdx); -} - -void dumpValues (tcu::TestLog& log, const ValueBlock& values, int arrayNdx) -{ - dumpValues(log, values.inputs, "input", arrayNdx); - dumpValues(log, values.outputs, "expected", arrayNdx); - dumpValues(log, values.uniforms, "uniform", arrayNdx); -} - -static void generateExtensionStatements (std::ostringstream& buf, const std::vector& extensions, glu::ShaderType type) -{ - for (size_t ndx = 0; ndx < extensions.size(); ++ndx) - { - DE_ASSERT(extensions[ndx].effectiveStages != 0u && - extensions[ndx].alternatives.size() == 1); - - if ((extensions[ndx].effectiveStages & (1u << (deUint32)type)) != 0) - buf << "#extension " << extensions[ndx].alternatives[0] << " : require\n"; - } -} - -// Injects #extension XXX : require lines after the last preprocessor directive in the shader code. Does not support line continuations -std::string injectExtensionRequirements (const std::string& baseCode, const std::vector& extensions, glu::ShaderType shaderType) -{ - std::istringstream baseCodeBuf (baseCode); - std::ostringstream resultBuf; - std::string line; - bool firstNonPreprocessorLine = true; - std::ostringstream extStr; - - generateExtensionStatements(extStr, extensions, shaderType); - - // skip if no requirements - if (extStr.str().empty()) - return baseCode; - - while (std::getline(baseCodeBuf, line)) - { - // begins with '#'? - const std::string::size_type firstNonWhitespace = line.find_first_not_of("\t "); - const bool isPreprocessorDirective = (firstNonWhitespace != std::string::npos && line.at(firstNonWhitespace) == '#'); - - // Inject #extensions - if (!isPreprocessorDirective && firstNonPreprocessorLine) - { - firstNonPreprocessorLine = false; - resultBuf << extStr.str(); - } - - resultBuf << line << "\n"; - } - - return resultBuf.str(); -} - -void genCompareFunctions (ostringstream& stream, const ValueBlock& valueBlock, bool useFloatTypes) -{ - bool cmpTypeFound[TYPE_LAST]; - for (int i = 0; i < TYPE_LAST; i++) - cmpTypeFound[i] = false; - - for (size_t valueNdx = 0; valueNdx < valueBlock.outputs.size(); valueNdx++) - { - const Value& val = valueBlock.outputs[valueNdx]; - cmpTypeFound[(size_t)val.type.getBasicType()] = true; - } - - if (useFloatTypes) - { - if (cmpTypeFound[TYPE_BOOL]) stream << "bool isOk (float a, bool b) { return ((a > 0.5) == b); }\n"; - if (cmpTypeFound[TYPE_BOOL_VEC2]) stream << "bool isOk (vec2 a, bvec2 b) { return (greaterThan(a, vec2(0.5)) == b); }\n"; - if (cmpTypeFound[TYPE_BOOL_VEC3]) stream << "bool isOk (vec3 a, bvec3 b) { return (greaterThan(a, vec3(0.5)) == b); }\n"; - if (cmpTypeFound[TYPE_BOOL_VEC4]) stream << "bool isOk (vec4 a, bvec4 b) { return (greaterThan(a, vec4(0.5)) == b); }\n"; - if (cmpTypeFound[TYPE_INT]) stream << "bool isOk (float a, int b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1)); }\n"; - if (cmpTypeFound[TYPE_INT_VEC2]) stream << "bool isOk (vec2 a, ivec2 b) { return (ivec2(floor(a + 0.5)) == b); }\n"; - if (cmpTypeFound[TYPE_INT_VEC3]) stream << "bool isOk (vec3 a, ivec3 b) { return (ivec3(floor(a + 0.5)) == b); }\n"; - if (cmpTypeFound[TYPE_INT_VEC4]) stream << "bool isOk (vec4 a, ivec4 b) { return (ivec4(floor(a + 0.5)) == b); }\n"; - if (cmpTypeFound[TYPE_UINT]) stream << "bool isOk (float a, uint b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1u)); }\n"; - if (cmpTypeFound[TYPE_UINT_VEC2]) stream << "bool isOk (vec2 a, uvec2 b) { return (uvec2(floor(a + 0.5)) == b); }\n"; - if (cmpTypeFound[TYPE_UINT_VEC3]) stream << "bool isOk (vec3 a, uvec3 b) { return (uvec3(floor(a + 0.5)) == b); }\n"; - if (cmpTypeFound[TYPE_UINT_VEC4]) stream << "bool isOk (vec4 a, uvec4 b) { return (uvec4(floor(a + 0.5)) == b); }\n"; - } - else - { - if (cmpTypeFound[TYPE_BOOL]) stream << "bool isOk (bool a, bool b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_BOOL_VEC2]) stream << "bool isOk (bvec2 a, bvec2 b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_BOOL_VEC3]) stream << "bool isOk (bvec3 a, bvec3 b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_BOOL_VEC4]) stream << "bool isOk (bvec4 a, bvec4 b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_INT]) stream << "bool isOk (int a, int b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_INT_VEC2]) stream << "bool isOk (ivec2 a, ivec2 b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_INT_VEC3]) stream << "bool isOk (ivec3 a, ivec3 b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_INT_VEC4]) stream << "bool isOk (ivec4 a, ivec4 b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_UINT]) stream << "bool isOk (uint a, uint b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_UINT_VEC2]) stream << "bool isOk (uvec2 a, uvec2 b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_UINT_VEC3]) stream << "bool isOk (uvec3 a, uvec3 b) { return (a == b); }\n"; - if (cmpTypeFound[TYPE_UINT_VEC4]) stream << "bool isOk (uvec4 a, uvec4 b) { return (a == b); }\n"; - } - - if (cmpTypeFound[TYPE_FLOAT]) stream << "bool isOk (float a, float b, float eps) { return (abs(a-b) <= (eps*abs(b) + eps)); }\n"; - if (cmpTypeFound[TYPE_FLOAT_VEC2]) stream << "bool isOk (vec2 a, vec2 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n"; - if (cmpTypeFound[TYPE_FLOAT_VEC3]) stream << "bool isOk (vec3 a, vec3 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n"; - if (cmpTypeFound[TYPE_FLOAT_VEC4]) stream << "bool isOk (vec4 a, vec4 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n"; - - if (cmpTypeFound[TYPE_FLOAT_MAT2]) stream << "bool isOk (mat2 a, mat2 b, float eps) { vec2 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec2(eps))); }\n"; - if (cmpTypeFound[TYPE_FLOAT_MAT2X3]) stream << "bool isOk (mat2x3 a, mat2x3 b, float eps) { vec3 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec3(eps))); }\n"; - if (cmpTypeFound[TYPE_FLOAT_MAT2X4]) stream << "bool isOk (mat2x4 a, mat2x4 b, float eps) { vec4 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec4(eps))); }\n"; - if (cmpTypeFound[TYPE_FLOAT_MAT3X2]) stream << "bool isOk (mat3x2 a, mat3x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec2(eps))); }\n"; - if (cmpTypeFound[TYPE_FLOAT_MAT3]) stream << "bool isOk (mat3 a, mat3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec3(eps))); }\n"; - if (cmpTypeFound[TYPE_FLOAT_MAT3X4]) stream << "bool isOk (mat3x4 a, mat3x4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec4(eps))); }\n"; - if (cmpTypeFound[TYPE_FLOAT_MAT4X2]) stream << "bool isOk (mat4x2 a, mat4x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec2(eps))); }\n"; - if (cmpTypeFound[TYPE_FLOAT_MAT4X3]) stream << "bool isOk (mat4x3 a, mat4x3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec3(eps))); }\n"; - if (cmpTypeFound[TYPE_FLOAT_MAT4]) stream << "bool isOk (mat4 a, mat4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec4(eps))); }\n"; -} - -} // sl -} // glu diff --git a/framework/opengl/gluShaderLibrary.hpp b/framework/opengl/gluShaderLibrary.hpp deleted file mode 100644 index ce05193..0000000 --- a/framework/opengl/gluShaderLibrary.hpp +++ /dev/null @@ -1,198 +0,0 @@ -#ifndef _GLUSHADERLIBRARY_HPP -#define _GLUSHADERLIBRARY_HPP -/*------------------------------------------------------------------------- - * drawElements Quality Program OpenGL ES Utilities - * ------------------------------------------------ - * - * Copyright 2015 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. - * - *//*! - * \file - * \brief Shader .test file utilities. - *//*--------------------------------------------------------------------*/ - -#include "gluDefs.hpp" -#include "gluVarType.hpp" -#include "gluShaderProgram.hpp" -#include "tcuTestCase.hpp" - -#include -#include - -namespace glu -{ -namespace sl -{ - -enum CaseType -{ - CASETYPE_COMPLETE = 0, //!< Has all shaders specified separately. - CASETYPE_VERTEX_ONLY, //!< "Both" case, vertex shader sub case. - CASETYPE_FRAGMENT_ONLY, //!< "Both" case, fragment shader sub case. - - CASETYPE_LAST -}; - -enum ExpectResult -{ - EXPECT_PASS = 0, - EXPECT_COMPILE_FAIL, - EXPECT_LINK_FAIL, - EXPECT_COMPILE_LINK_FAIL, - EXPECT_VALIDATION_FAIL, - EXPECT_BUILD_SUCCESSFUL, - - EXPECT_LAST -}; - -struct Value -{ - union Element - { - float float32; - deInt32 int32; - deInt32 bool32; - }; - - VarType type; - std::string name; - std::vector elements; // Scalar values (variable.varType.getScalarSize() * #values). -}; - -struct ValueBlock -{ - std::vector inputs; - std::vector outputs; - std::vector uniforms; -}; - -struct RequiredCapability -{ - deUint32 enumName; - int referenceValue; - - RequiredCapability (void) - : enumName (0u) - , referenceValue (0) - { - } - - RequiredCapability (deUint32 enumName_, int referenceValue_) - : enumName (enumName_) - , referenceValue (referenceValue_) - { - } -}; - -struct RequiredExtension -{ - std::vector alternatives; // One or more extensions, at least one (but not all) must be supported - deUint32 effectiveStages; // Bitfield of shader stages requiring this extension - - RequiredExtension (const std::vector& alternatives_, - deUint32 effectiveStages_) - : alternatives (alternatives_) - , effectiveStages (effectiveStages_) - { - } - - RequiredExtension (const std::string& extension, - deUint32 effectiveStages_) - : effectiveStages (effectiveStages_) - { - alternatives.push_back(extension); - } - - RequiredExtension (void) - : effectiveStages (0u) - { - } -}; - -struct ProgramSpecification -{ - glu::ProgramSources sources; - std::vector requiredExtensions; - deUint32 activeStages; // Has an effect only if sources.separable == true, must be 0 otherwise - - ProgramSpecification (void) - : activeStages(0u) - { - } -}; - -struct ShaderCaseSpecification -{ - CaseType caseType; - ExpectResult expectResult; - glu::GLSLVersion targetVersion; - - // \todo [pyry] Clean this up - std::vector requiredCaps; - bool fullGLSLES100Required; - - ValueBlock values; - std::vector programs; - - ShaderCaseSpecification (void) - : caseType (CASETYPE_LAST) - , expectResult (EXPECT_LAST) - , targetVersion (glu::GLSL_VERSION_LAST) - , fullGLSLES100Required (false) - { - } -}; - -bool isValid (const ValueBlock& block); -bool isValid (const ShaderCaseSpecification& spec); - -class ShaderCaseFactory -{ -public: - virtual tcu::TestCaseGroup* createGroup (const std::string& name, const std::string& description, const std::vector& children) = 0; - virtual tcu::TestCase* createCase (const std::string& name, const std::string& description, const ShaderCaseSpecification& spec) = 0; -}; - -std::vector parseFile (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory); - -// Specialization utilties - -struct ProgramSpecializationParams -{ - const ShaderCaseSpecification& caseSpec; - const std::vector requiredExtensions; // Extensions, must be resolved to single ext per entry - const int maxPatchVertices; // Used by tess shaders only - - ProgramSpecializationParams (const ShaderCaseSpecification& caseSpec_, - const std::vector& requiredExtensions_, - int maxPatchVertices_) - : caseSpec (caseSpec_) - , requiredExtensions (requiredExtensions_) - , maxPatchVertices (maxPatchVertices_) - { - } -}; - -void genCompareFunctions (std::ostringstream& stream, const ValueBlock& valueBlock, bool useFloatTypes); -std::string injectExtensionRequirements (const std::string& baseCode, const std::vector& extensions, ShaderType shaderType); - -// Other utilities - -void dumpValues (tcu::TestLog& log, const ValueBlock& values, int arrayNdx); - -} // sl -} // glu - -#endif // _GLUSHADERLIBRARY_HPP diff --git a/modules/glshared/glsShaderConstExprTests.cpp b/modules/glshared/glsShaderConstExprTests.cpp index 3495baf..f15fa74 100644 --- a/modules/glshared/glsShaderConstExprTests.cpp +++ b/modules/glshared/glsShaderConstExprTests.cpp @@ -37,41 +37,6 @@ namespace gls namespace ShaderConstExpr { -static void addOutputVar (glu::sl::ValueBlock* dst, glu::DataType type, float output) -{ - dst->outputs.push_back(glu::sl::Value()); - - { - glu::sl::Value& value = dst->outputs.back(); - - value.name = "out0"; - value.type = glu::VarType(type, glu::PRECISION_LAST); - value.elements.resize(1); - - switch (type) - { - case glu::TYPE_INT: - value.elements[0].int32 = (int)output; - break; - - case glu::TYPE_UINT: - value.elements[0].int32 = (unsigned int)output; - break; - - case glu::TYPE_BOOL: - value.elements[0].bool32 = output!=0.0f; - break; - - case glu::TYPE_FLOAT: - value.elements[0].float32 = output; - break; - - default: - DE_ASSERT(false); - } - } -} - std::vector createTests (tcu::TestContext& testContext, glu::RenderContext& renderContext, const glu::ContextInfo& contextInfo, @@ -82,7 +47,7 @@ std::vector createTests (tcu::TestContext& testContext, { using std::string; using std::vector; - using gls::ShaderLibraryCase; + using gls::sl::ShaderCase; // Needed for autogenerating shader code for increased component counts DE_STATIC_ASSERT(glu::TYPE_FLOAT+1 == glu::TYPE_FLOAT_VEC2); @@ -115,8 +80,17 @@ std::vector createTests (tcu::TestContext& testContext, " ${OUTPUT}\n" "}\n"; - const tcu::StringTemplate shaderTemplate (shaderTemplateSrc); - vector ret; + const tcu::StringTemplate shaderTemplate (shaderTemplateSrc); + vector ret; + vector shaderOutput (1); + + shaderOutput[0].arrayLength = 1; + shaderOutput[0].values.push_back(ShaderCase::Value()); + shaderOutput[0].values[0].storageType = ShaderCase::Value::STORAGE_OUTPUT; + shaderOutput[0].values[0].valueName = "out0"; + shaderOutput[0].values[0].dataType = glu::TYPE_FLOAT; + shaderOutput[0].values[0].arrayLength = 1; + shaderOutput[0].values[0].elements.push_back(ShaderCase::Value::Element()); for (int caseNdx = 0; caseNdx < numCases; caseNdx++) { @@ -128,6 +102,31 @@ std::vector createTests (tcu::TestContext& testContext, const string expression = cases[caseNdx].expression; // Check for presence of func(vec, scalar) style specialization, use as gatekeeper for applying said specialization const bool alwaysScalar = expression.find("${MT}")!=string::npos; + ShaderCase::Value& expected = shaderOutput[0].values[0]; + + switch (outType) + { + case glu::TYPE_INT: + expected.elements[0].int32 = (int)cases[caseNdx].output; + break; + + case glu::TYPE_UINT: + expected.elements[0].int32 = (unsigned int)cases[caseNdx].output; + break; + + case glu::TYPE_BOOL: + expected.elements[0].bool32 = cases[caseNdx].output!=0.0f; + break; + + case glu::TYPE_FLOAT: + expected.elements[0].float32 = cases[caseNdx].output; + break; + + default: + DE_ASSERT(false); + } + + expected.dataType = outType; shaderTemplateParams["GLES_VERSION"] = version == glu::GLSL_VERSION_300_ES ? "300 es" : "100"; shaderTemplateParams["CASE_BASE_TYPE"] = glu::getDataTypeName(outType); @@ -154,46 +153,26 @@ std::vector createTests (tcu::TestContext& testContext, const string mapped = shaderTemplate.specialize(shaderTemplateParams); if (testStage & SHADER_VERTEX) - { - glu::sl::ShaderCaseSpecification spec; - - spec.targetVersion = version; - spec.expectResult = glu::sl::EXPECT_PASS; - spec.caseType = glu::sl::CASETYPE_VERTEX_ONLY; - spec.programs.resize(1); - - spec.programs[0].sources << glu::VertexSource(mapped); - - addOutputVar(&spec.values, outType, cases[caseNdx].output); - - ret.push_back(new ShaderLibraryCase(testContext, - renderContext, - contextInfo, - (caseName + "_vertex").c_str(), - "", - spec)); - } + ret.push_back(new ShaderCase(testContext, + renderContext, + contextInfo, + (caseName + "_vertex").c_str(), + "", + ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase(ShaderCase::EXPECT_PASS, + version, + shaderOutput, + mapped))); if (testStage & SHADER_FRAGMENT) - { - glu::sl::ShaderCaseSpecification spec; - - spec.targetVersion = version; - spec.expectResult = glu::sl::EXPECT_PASS; - spec.caseType = glu::sl::CASETYPE_FRAGMENT_ONLY; - spec.programs.resize(1); - - spec.programs[0].sources << glu::FragmentSource(mapped); - - addOutputVar(&spec.values, outType, cases[caseNdx].output); - - ret.push_back(new ShaderLibraryCase(testContext, - renderContext, - contextInfo, - (caseName + "_fragment").c_str(), - "", - spec)); - } + ret.push_back(new ShaderCase(testContext, + renderContext, + contextInfo, + (caseName + "_fragment").c_str(), + "", + ShaderCase::ShaderCaseSpecification::generateSharedSourceFragmentCase(ShaderCase::EXPECT_PASS, + version, + shaderOutput, + mapped))); } // Deal with functions that allways accept one ore more scalar parameters even when others are vectors @@ -208,46 +187,26 @@ std::vector createTests (tcu::TestContext& testContext, const string mapped = shaderTemplate.specialize(shaderTemplateParams); if (testStage & SHADER_VERTEX) - { - glu::sl::ShaderCaseSpecification spec; - - spec.targetVersion = version; - spec.expectResult = glu::sl::EXPECT_PASS; - spec.caseType = glu::sl::CASETYPE_VERTEX_ONLY; - spec.programs.resize(1); - - spec.programs[0].sources << glu::VertexSource(mapped); - - addOutputVar(&spec.values, outType, cases[caseNdx].output); - - ret.push_back(new ShaderLibraryCase(testContext, - renderContext, - contextInfo, - (scalarCaseName + "_vertex").c_str(), - "", - spec)); - } + ret.push_back(new ShaderCase(testContext, + renderContext, + contextInfo, + (scalarCaseName + "_vertex").c_str(), + "", + ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase(ShaderCase::EXPECT_PASS, + version, + shaderOutput, + mapped))); if (testStage & SHADER_FRAGMENT) - { - glu::sl::ShaderCaseSpecification spec; - - spec.targetVersion = version; - spec.expectResult = glu::sl::EXPECT_PASS; - spec.caseType = glu::sl::CASETYPE_FRAGMENT_ONLY; - spec.programs.resize(1); - - spec.programs[0].sources << glu::FragmentSource(mapped); - - addOutputVar(&spec.values, outType, cases[caseNdx].output); - - ret.push_back(new ShaderLibraryCase(testContext, - renderContext, - contextInfo, - (scalarCaseName + "_fragment").c_str(), - "", - spec)); - } + ret.push_back(new ShaderCase(testContext, + renderContext, + contextInfo, + (scalarCaseName + "_fragment").c_str(), + "", + ShaderCase::ShaderCaseSpecification::generateSharedSourceFragmentCase(ShaderCase::EXPECT_PASS, + version, + shaderOutput, + mapped))); } } } diff --git a/modules/glshared/glsShaderLibrary.cpp b/modules/glshared/glsShaderLibrary.cpp index 9877180..3956628 100644 --- a/modules/glshared/glsShaderLibrary.cpp +++ b/modules/glshared/glsShaderLibrary.cpp @@ -23,44 +23,1394 @@ #include "glsShaderLibrary.hpp" #include "glsShaderLibraryCase.hpp" +#include "gluShaderUtil.hpp" +#include "tcuResource.hpp" +#include "glwEnums.hpp" + +#include "deInt32.h" + +#include +#include +#include +#include + +#include +#include +#include + +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 gls { +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'); +} -namespace +DE_INLINE deBool isCaseNameChar (char c) { + return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z') || deInRange32(c, '0', '9') || (c == '_') || (c == '-') || (c == '.'); +} -class CaseFactory : public glu::sl::ShaderCaseFactory +// \todo [2011-02-11 pyry] Should not depend on Context or TestContext! +class ShaderParser { public: - CaseFactory (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo) - : m_testCtx (testCtx) - , m_renderCtx (renderCtx) - , m_contextInfo (contextInfo) + ShaderParser (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* currentDir = DE_NULL); + ~ShaderParser (void); + + vector 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, + TOKEN_TESSELLATION_CONTROL, + TOKEN_TESSELLATION_EVALUATION, + TOKEN_GEOMETRY, + TOKEN_REQUIRE, + TOKEN_IN, + TOKEN_IMPORT, + TOKEN_PIPELINE_PROGRAM, + TOKEN_ACTIVE_STAGES, + + // 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_GREATER, + + TOKEN_LAST + }; + + void parseError (const std::string& errorStr); + float parseFloatLiteral (const char* str); + 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); + deUint32 getShaderStageLiteralFlag (void); + deUint32 getGLEnumFromName (const std::string& enumName); + + void parseValueElement (DataType dataType, ShaderCase::Value& result); + void parseValue (ShaderCase::ValueBlock& valueBlock); + void parseValueBlock (ShaderCase::ValueBlock& valueBlock); + deUint32 parseShaderStageList (void); + void parseRequirement (ShaderCase::CaseRequirement& valueBlock); + void parseExpectResult (ShaderCase::ExpectResult& expectResult); + void parseGLSLVersion (glu::GLSLVersion& version); + void parsePipelineProgram (ShaderCase::PipelineProgram& program); + void parseShaderCase (vector& shaderNodeList); + void parseShaderGroup (vector& shaderNodeList); + void parseImport (vector& shaderNodeList); + + // Member variables. + tcu::TestContext& m_testCtx; + RenderContext& m_renderCtx; + const glu::ContextInfo& m_contextInfo; + std::string m_input; + const char* m_curPtr; + Token m_curToken; + std::string m_curTokenStr; + const char* const m_currentDir; +}; + +ShaderParser::ShaderParser (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* currentDir) + : m_testCtx (testCtx) + , m_renderCtx (renderCtx) + , m_contextInfo (contextInfo) + , m_curPtr (DE_NULL) + , m_curToken (TOKEN_LAST) + , m_currentDir (currentDir) +{ +} + +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); +} + +int ShaderParser::parseIntLiteral (const char* str) +{ + return atoi(str); +} + +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++; } - tcu::TestCaseGroup* createGroup (const std::string& name, const std::string& description, const std::vector& children) + 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; { - return new tcu::TestCaseGroup(m_testCtx, name.c_str(), description.c_str(), children); + 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; + } + } } - tcu::TestCase* createCase (const std::string& name, const std::string& description, const glu::sl::ShaderCaseSpecification& spec) + 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++; + if (*p == '\r') p++; + if (*p == '\n') p++; + + while ((p[0] != '"') || (p[1] != '"')) { - return new ShaderLibraryCase(m_testCtx, m_renderCtx, m_contextInfo, name.c_str(), description.c_str(), spec); + 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++; } -private: - tcu::TestContext& m_testCtx; - glu::RenderContext& m_renderCtx; - const glu::ContextInfo& m_contextInfo; -}; + 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 = ""; + } + 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 }, + { "tessellation_control", TOKEN_TESSELLATION_CONTROL }, + { "tessellation_evaluation", TOKEN_TESSELLATION_EVALUATION }, + { "geometry", TOKEN_GEOMETRY }, + { "require", TOKEN_REQUIRE }, + { "in", TOKEN_IN }, + { "import", TOKEN_IMPORT }, + { "pipeline_program", TOKEN_PIPELINE_PROGRAM }, + { "active_stages", TOKEN_ACTIVE_STAGES }, + }; + + 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 }, + { ">", TOKEN_GREATER }, + }; + + 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 ""; + case TOKEN_EOF: return ""; + case TOKEN_STRING: return ""; + case TOKEN_SHADER_SOURCE: return "source"; + + case TOKEN_INT_LITERAL: return ""; + case TOKEN_FLOAT_LITERAL: return ""; + + // identifiers + case TOKEN_IDENTIFIER: return ""; + 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_TESSELLATION_CONTROL: return "tessellation_control"; + case TOKEN_TESSELLATION_EVALUATION: return "tessellation_evaluation"; + case TOKEN_GEOMETRY: return "geometry"; + case TOKEN_REQUIRE: return "require"; + 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_IN: return "in"; + case TOKEN_IMPORT: return "import"; + case TOKEN_PIPELINE_PROGRAM: return "pipeline_program"; + case TOKEN_ACTIVE_STAGES: return "active_stages"; + + 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 "}"; + case TOKEN_GREATER: return ">"; + + default: return ""; + } +} + +deUint32 ShaderParser::getShaderStageLiteralFlag (void) +{ + switch (m_curToken) + { + case TOKEN_VERTEX: return (1 << glu::SHADERTYPE_VERTEX); + case TOKEN_FRAGMENT: return (1 << glu::SHADERTYPE_FRAGMENT); + case TOKEN_GEOMETRY: return (1 << glu::SHADERTYPE_GEOMETRY); + case TOKEN_TESSELLATION_CONTROL: return (1 << glu::SHADERTYPE_TESSELLATION_CONTROL); + case TOKEN_TESSELLATION_EVALUATION: return (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION); + + default: + parseError(std::string() + "invalid shader stage name, got " + m_curTokenStr); + return 0; + } +} + +deUint32 ShaderParser::getGLEnumFromName (const std::string& enumName) +{ + static const struct + { + const char* name; + deUint32 value; + } names[] = + { + { "GL_MAX_VERTEX_IMAGE_UNIFORMS", GL_MAX_VERTEX_IMAGE_UNIFORMS }, + { "GL_MAX_VERTEX_ATOMIC_COUNTERS", GL_MAX_VERTEX_ATOMIC_COUNTERS }, + { "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS", GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS }, + { "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS", GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS }, + }; + + for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(names); ++ndx) + if (names[ndx].name == enumName) + return names[ndx].value; + + parseError(std::string() + "unknown enum name, got " + enumName); + return 0; +} + +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 = 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")); -} // anonymous + // Parsed results. + ShaderCase::Value result; -ShaderLibrary::ShaderLibrary (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo) + // 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; +} + +deUint32 ShaderParser::parseShaderStageList (void) +{ + deUint32 mask = 0; + + assumeToken(TOKEN_LEFT_BRACE); + + // don't allow 0-sized lists + advanceToken(); + mask |= getShaderStageLiteralFlag(); + advanceToken(); + + for (;;) + { + if (m_curToken == TOKEN_RIGHT_BRACE) + break; + else if (m_curToken == TOKEN_COMMA) + { + deUint32 stageFlag; + advanceToken(); + + stageFlag = getShaderStageLiteralFlag(); + if (stageFlag & mask) + parseError(string("stage already set in the shader stage set: " + m_curTokenStr)); + + mask |= stageFlag; + advanceToken(); + } + else + parseError(string("invalid shader stage set token: " + m_curTokenStr)); + } + advanceToken(TOKEN_RIGHT_BRACE); + + return mask; +} + +void ShaderParser::parseRequirement (ShaderCase::CaseRequirement& valueBlock) +{ + PARSE_DBG((" parseRequirement()\n")); + + advanceToken(); + assumeToken(TOKEN_IDENTIFIER); + + if (m_curTokenStr == "extension") + { + std::vector anyExtensionStringList; + deUint32 affectedCasesFlags = -1; // by default all stages + + advanceToken(); + assumeToken(TOKEN_LEFT_BRACE); + + advanceToken(); + assumeToken(TOKEN_STRING); + + anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str())); + advanceToken(); + + for (;;) + { + if (m_curToken == TOKEN_RIGHT_BRACE) + break; + else if (m_curToken == TOKEN_VERTICAL_BAR) + { + advanceToken(); + assumeToken(TOKEN_STRING); + + anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str())); + advanceToken(); + } + else + parseError(string("invalid extension list token: " + m_curTokenStr)); + } + advanceToken(TOKEN_RIGHT_BRACE); + + if (m_curToken == TOKEN_IN) + { + advanceToken(); + affectedCasesFlags = parseShaderStageList(); + } + + valueBlock = ShaderCase::CaseRequirement::createAnyExtensionRequirement(anyExtensionStringList, affectedCasesFlags); + } + else if (m_curTokenStr == "limit") + { + deUint32 limitEnum; + int limitValue; + + advanceToken(); + + assumeToken(TOKEN_STRING); + limitEnum = getGLEnumFromName(parseStringLiteral(m_curTokenStr.c_str())); + advanceToken(); + + assumeToken(TOKEN_GREATER); + advanceToken(); + + assumeToken(TOKEN_INT_LITERAL); + limitValue = parseIntLiteral(m_curTokenStr.c_str()); + advanceToken(); + + valueBlock = ShaderCase::CaseRequirement::createLimitRequirement(limitEnum, limitValue); + } + else if (m_curTokenStr == "full_glsl_es_100_support") + { + advanceToken(); + + valueBlock = ShaderCase::CaseRequirement::createFullGLSLES100SpecificationRequirement(); + } + else + parseError(string("invalid requirement value: " + m_curTokenStr)); +} + +void ShaderParser::parseExpectResult (ShaderCase::ExpectResult& expectResult) +{ + 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 if (m_curTokenStr == "compile_or_link_fail") + expectResult = ShaderCase::EXPECT_COMPILE_LINK_FAIL; + else if (m_curTokenStr == "validation_fail") + expectResult = ShaderCase::EXPECT_VALIDATION_FAIL; + else if (m_curTokenStr == "build_successful") + expectResult = ShaderCase::EXPECT_BUILD_SUCCESSFUL; + else + parseError(string("invalid expected result value: " + m_curTokenStr)); + + advanceToken(); +} + +void ShaderParser::parseGLSLVersion (glu::GLSLVersion& version) +{ + int versionNum = 0; + std::string postfix = ""; + + assumeToken(TOKEN_INT_LITERAL); + versionNum = parseIntLiteral(m_curTokenStr.c_str()); + advanceToken(); + + if (m_curToken == TOKEN_IDENTIFIER) + { + postfix = m_curTokenStr; + advanceToken(); + } + + DE_STATIC_ASSERT(glu::GLSL_VERSION_LAST == 14); + + 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 == 320 && postfix == "es") version = glu::GLSL_VERSION_320_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"); +} + +void ShaderParser::parsePipelineProgram (ShaderCase::PipelineProgram& program) +{ + deUint32 activeStages = 0; + vector vertexSources; + vector fragmentSources; + vector tessellationCtrlSources; + vector tessellationEvalSources; + vector geometrySources; + vector requirements; + + advanceToken(TOKEN_PIPELINE_PROGRAM); + + for (;;) + { + if (m_curToken == TOKEN_END) + break; + else if (m_curToken == TOKEN_ACTIVE_STAGES) + { + advanceToken(); + activeStages = parseShaderStageList(); + } + else if (m_curToken == TOKEN_REQUIRE) + { + ShaderCase::CaseRequirement requirement; + parseRequirement(requirement); + requirements.push_back(requirement); + } + else if (m_curToken == TOKEN_VERTEX || + m_curToken == TOKEN_FRAGMENT || + m_curToken == TOKEN_TESSELLATION_CONTROL || + m_curToken == TOKEN_TESSELLATION_EVALUATION || + m_curToken == TOKEN_GEOMETRY) + { + const Token token = m_curToken; + string source; + + advanceToken(); + assumeToken(TOKEN_SHADER_SOURCE); + source = parseShaderSource(m_curTokenStr.c_str()); + advanceToken(); + + switch (token) + { + case TOKEN_VERTEX: vertexSources.push_back(source); break; + case TOKEN_FRAGMENT: fragmentSources.push_back(source); break; + case TOKEN_TESSELLATION_CONTROL: tessellationCtrlSources.push_back(source); break; + case TOKEN_TESSELLATION_EVALUATION: tessellationEvalSources.push_back(source); break; + case TOKEN_GEOMETRY: geometrySources.push_back(source); break; + default: + parseError(DE_FALSE); + } + } + else + parseError(string("invalid pipeline program value: " + m_curTokenStr)); + } + advanceToken(TOKEN_END); + + if (activeStages == 0) + parseError("program pipeline object must have active stages"); + + // return pipeline part + program.activeStageBits = activeStages; + program.requirements.swap(requirements); + program.vertexSources.swap(vertexSources); + program.fragmentSources.swap(fragmentSources); + program.tessCtrlSources.swap(tessellationCtrlSources); + program.tessEvalSources.swap(tessellationEvalSources); + program.geometrySources.swap(geometrySources); +} + +void ShaderParser::parseShaderCase (vector& 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. + GLSLVersion version = DEFAULT_GLSL_VERSION; + ShaderCase::ExpectResult expectResult = ShaderCase::EXPECT_PASS; + string description; + string bothSource; + vector vertexSources; + vector fragmentSources; + vector tessellationCtrlSources; + vector tessellationEvalSources; + vector geometrySources; + vector valueBlockList; + vector requirements; + vector pipelinePrograms; + + 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(); + parseExpectResult(expectResult); + } + 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 || + m_curToken == TOKEN_TESSELLATION_CONTROL || + m_curToken == TOKEN_TESSELLATION_EVALUATION || + m_curToken == TOKEN_GEOMETRY) + { + const Token token = m_curToken; + string source; + + advanceToken(); + assumeToken(TOKEN_SHADER_SOURCE); + source = parseShaderSource(m_curTokenStr.c_str()); + advanceToken(); + + switch (token) + { + case TOKEN_VERTEX: vertexSources.push_back(source); break; + case TOKEN_FRAGMENT: fragmentSources.push_back(source); break; + case TOKEN_TESSELLATION_CONTROL: tessellationCtrlSources.push_back(source); break; + case TOKEN_TESSELLATION_EVALUATION: tessellationEvalSources.push_back(source); break; + case TOKEN_GEOMETRY: geometrySources.push_back(source); break; + case TOKEN_BOTH: + { + if (!bothSource.empty()) + parseError("multiple 'both' blocks"); + bothSource = source; + break; + } + + default: + parseError(DE_FALSE); + } + } + else if (m_curToken == TOKEN_VERSION) + { + advanceToken(); + parseGLSLVersion(version); + } + else if (m_curToken == TOKEN_REQUIRE) + { + ShaderCase::CaseRequirement requirement; + parseRequirement(requirement); + requirements.push_back(requirement); + } + else if (m_curToken == TOKEN_PIPELINE_PROGRAM) + { + ShaderCase::PipelineProgram pipelineProgram; + parsePipelineProgram(pipelineProgram); + pipelinePrograms.push_back(pipelineProgram); + } + else + parseError(string("unexpected token while parsing shader case: " + m_curTokenStr)); + } + + advanceToken(TOKEN_END); // case end + + if (!bothSource.empty()) + { + if (!vertexSources.empty() || + !fragmentSources.empty() || + !tessellationCtrlSources.empty() || + !tessellationEvalSources.empty() || + !geometrySources.empty() || + !pipelinePrograms.empty()) + { + parseError("'both' cannot be mixed with other shader stages"); + } + + // vertex + { + ShaderCase::ShaderCaseSpecification spec = ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase(expectResult, version, valueBlockList, bothSource); + spec.requirements = requirements; + + shaderNodeList.push_back(new ShaderCase(m_testCtx, m_renderCtx, m_contextInfo, (caseName + "_vertex").c_str(), description.c_str(), spec)); + } + + // fragment + { + ShaderCase::ShaderCaseSpecification spec = ShaderCase::ShaderCaseSpecification::generateSharedSourceFragmentCase(expectResult, version, valueBlockList, bothSource); + spec.requirements = requirements; + + shaderNodeList.push_back(new ShaderCase(m_testCtx, m_renderCtx, m_contextInfo, (caseName + "_fragment").c_str(), description.c_str(), spec)); + } + } + else if (pipelinePrograms.empty()) + { + ShaderCase::ShaderCaseSpecification spec; + + spec.expectResult = expectResult; + spec.caseType = ShaderCase::CASETYPE_COMPLETE; + spec.targetVersion = version; + spec.requirements.swap(requirements); + spec.valueBlocks.swap(valueBlockList); + spec.vertexSources.swap(vertexSources); + spec.fragmentSources.swap(fragmentSources); + spec.tessCtrlSources.swap(tessellationCtrlSources); + spec.tessEvalSources.swap(tessellationEvalSources); + spec.geometrySources.swap(geometrySources); + + shaderNodeList.push_back(new ShaderCase(m_testCtx, m_renderCtx, m_contextInfo, caseName.c_str(), description.c_str(), spec)); + } + else + { + if (!vertexSources.empty() || + !fragmentSources.empty() || + !tessellationCtrlSources.empty() || + !tessellationEvalSources.empty() || + !geometrySources.empty()) + { + parseError("pipeline programs cannot be mixed with complete programs"); + } + + // Pipeline case, multiple programs + { + ShaderCase::PipelineCaseSpecification spec; + + spec.expectResult = expectResult; + spec.caseType = ShaderCase::CASETYPE_COMPLETE; + spec.targetVersion = version; + spec.valueBlocks.swap(valueBlockList); + spec.programs.swap(pipelinePrograms); + + shaderNodeList.push_back(new ShaderCase(m_testCtx, m_renderCtx, m_contextInfo, caseName.c_str(), description.c_str(), spec)); + } + } +} + +void ShaderParser::parseShaderGroup (vector& 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 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 if (m_curToken == TOKEN_IMPORT) + parseImport(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); +} + +void ShaderParser::parseImport (vector& shaderNodeList) +{ + ShaderLibrary subLibrary (m_testCtx, m_renderCtx, m_contextInfo); + vector importedCases; + std::string filename; + + if (!m_currentDir) + parseError(string("cannot use import in inline shader source")); + + advanceToken(TOKEN_IMPORT); + + assumeToken(TOKEN_STRING); + filename = m_currentDir + parseStringLiteral(m_curTokenStr.c_str()); + advanceToken(TOKEN_STRING); + + importedCases = subLibrary.loadShaderFile(filename.c_str()); + shaderNodeList.insert(shaderNodeList.end(), importedCases.begin(), importedCases.end()); +} + +vector ShaderParser::parse (const char* input) +{ + // Initialize parser. + m_input = input; + m_curPtr = m_input.c_str(); + m_curToken = TOKEN_INVALID; + m_curTokenStr = ""; + advanceToken(); + + vector 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_IMPORT) + parseImport(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 + +static std::string getFileDirectory (const std::string& filePath) +{ + const std::string::size_type lastDelim = filePath.find_last_of('/'); + + if (lastDelim == std::string::npos) + return ""; + else + return filePath.substr(0, lastDelim+1); +} + +ShaderLibrary::ShaderLibrary (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo) : m_testCtx (testCtx) , m_renderCtx (renderCtx) , m_contextInfo (contextInfo) @@ -71,11 +1421,41 @@ ShaderLibrary::~ShaderLibrary (void) { } -std::vector ShaderLibrary::loadShaderFile (const char* fileName) +vector ShaderLibrary::loadShaderFile (const char* fileName) +{ + tcu::Resource* resource = m_testCtx.getArchive().getResource(fileName); + std::string fileDirectory = getFileDirectory(fileName); + std::vector 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, m_contextInfo, fileDirectory.c_str()); + vector nodes = parser.parse(&buf[0]); + + return nodes; +} + +vector ShaderLibrary::parseShader (const char* shaderSource) { - CaseFactory caseFactory (m_testCtx, m_renderCtx, m_contextInfo); + sl::ShaderParser parser(m_testCtx, m_renderCtx, m_contextInfo); + vector nodes = parser.parse(shaderSource); - return glu::sl::parseFile(m_testCtx.getArchive(), fileName, &caseFactory); + return nodes; } } // gls diff --git a/modules/glshared/glsShaderLibrary.hpp b/modules/glshared/glsShaderLibrary.hpp index 2040cd3..3639394 100644 --- a/modules/glshared/glsShaderLibrary.hpp +++ b/modules/glshared/glsShaderLibrary.hpp @@ -42,11 +42,13 @@ public: ~ShaderLibrary (void); std::vector loadShaderFile (const char* fileName); + std::vector parseShader (const char* shaderSource); private: ShaderLibrary (const ShaderLibrary&); // not allowed! ShaderLibrary& operator= (const ShaderLibrary&); // not allowed! + // Member variables. tcu::TestContext& m_testCtx; glu::RenderContext& m_renderCtx; const glu::ContextInfo& m_contextInfo; diff --git a/modules/glshared/glsShaderLibraryCase.cpp b/modules/glshared/glsShaderLibraryCase.cpp index db8e498..a37bb47 100644 --- a/modules/glshared/glsShaderLibraryCase.cpp +++ b/modules/glshared/glsShaderLibraryCase.cpp @@ -25,8 +25,6 @@ #include "tcuTestLog.hpp" #include "tcuRenderTarget.hpp" -#include "tcuTextureUtil.hpp" -#include "tcuSurface.hpp" #include "tcuStringTemplate.hpp" #include "gluShaderProgram.hpp" @@ -50,688 +48,450 @@ #include #include +using namespace std; +using namespace tcu; +using namespace glu; + namespace deqp { namespace gls { +namespace sl +{ -using namespace tcu; -using namespace glu; -using namespace glu::sl; +enum +{ + VIEWPORT_WIDTH = 128, + VIEWPORT_HEIGHT = 128 +}; -using std::vector; -using std::string; -using std::ostringstream; -using std::map; -using std::pair; +static inline bool usesShaderInoutQualifiers (glu::GLSLVersion version) +{ + switch (version) + { + case glu::GLSL_VERSION_100_ES: + case glu::GLSL_VERSION_130: + case glu::GLSL_VERSION_140: + case glu::GLSL_VERSION_150: + return false; -using de::SharedPtr; + default: + return true; + } +} -// OpenGL-specific specialization utils +static inline bool supportsFragmentHighp (glu::GLSLVersion version) +{ + return version != glu::GLSL_VERSION_100_ES; +} -static vector checkAndSpecializeExtensions (const vector& src, - const ContextInfo& ctxInfo) +static int queryGLInt (const glu::RenderContext& renderCtx, deUint32 pname) { - vector specialized; + const glw::Functions& gl = renderCtx.getFunctions(); + glw::GLint value = 0; - for (size_t extNdx = 0; extNdx < src.size(); ++extNdx) - { - const RequiredExtension& extension = src[extNdx]; - int supportedAltNdx = -1; + gl.getIntegerv(pname, &value); + GLU_EXPECT_NO_ERROR(gl.getError(), ("query " + de::toString(glu::getGettableStateStr(pname))).c_str()); - for (size_t alternativeNdx = 0; alternativeNdx < extension.alternatives.size(); ++alternativeNdx) - { - if (ctxInfo.isExtensionSupported(extension.alternatives[alternativeNdx].c_str())) - { - supportedAltNdx = (int)alternativeNdx; - break; - } - } + return value; +} - if (supportedAltNdx >= 0) - { - specialized.push_back(RequiredExtension(extension.alternatives[supportedAltNdx], extension.effectiveStages)); - } - else - { - // no extension(s). Make a nice output - std::ostringstream extensionList; +ShaderCase::ValueBlock::ValueBlock (void) + : arrayLength(0) +{ +} - for (size_t ndx = 0; ndx < extension.alternatives.size(); ++ndx) - { - if (!extensionList.str().empty()) - extensionList << ", "; - extensionList << extension.alternatives[ndx]; - } +ShaderCase::CaseRequirement::CaseRequirement (void) + : m_type (REQUIREMENTTYPE_LAST) + , m_supportedExtensionNdx (-1) + , m_effectiveShaderStageFlags (-1) + , m_enumName (-1) + , m_referenceValue (-1) +{ +} - if (extension.alternatives.size() == 1) - throw tcu::NotSupportedError("Test requires extension " + extensionList.str()); - else - throw tcu::NotSupportedError("Test requires any extension of " + extensionList.str()); - } - } +ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createAnyExtensionRequirement (const std::vector& requirements, deUint32 effectiveShaderStageFlags) +{ + CaseRequirement retVal; + + retVal.m_type = REQUIREMENTTYPE_EXTENSION; + retVal.m_extensions = requirements; + retVal.m_effectiveShaderStageFlags = effectiveShaderStageFlags; - return specialized; + return retVal; } -static void checkImplementationLimits (const vector& requiredCaps, - const ContextInfo& ctxInfo) +ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createLimitRequirement (deUint32 enumName, int ref) { - for (size_t capNdx = 0; capNdx < requiredCaps.size(); ++capNdx) - { - const deUint32 pname = requiredCaps[capNdx].enumName; - const int requiredValue = requiredCaps[capNdx].referenceValue; - const int supportedValue = ctxInfo.getInt((int)pname); + CaseRequirement retVal; - if (supportedValue <= requiredValue) - throw tcu::NotSupportedError("Test requires " + de::toString(glu::getGettableStateStr(pname)) + " (" + de::toString(supportedValue) + ") >= " + de::toString(requiredValue)); - } -} + retVal.m_type = REQUIREMENTTYPE_IMPLEMENTATION_LIMIT; + retVal.m_enumName = enumName; + retVal.m_referenceValue = ref; -// Shader source specialization + return retVal; +} -// This functions builds a matching vertex shader for a 'both' case, when -// the fragment shader is being tested. -// We need to build attributes and varyings for each 'input'. -static string genVertexShader (const ShaderCaseSpecification& spec) +ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createFullGLSLES100SpecificationRequirement (void) { - ostringstream res; - const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); - const char* const vtxIn = usesInout ? "in" : "attribute"; - const char* const vtxOut = usesInout ? "out" : "varying"; + CaseRequirement retVal; - res << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n"; + retVal.m_type = REQUIREMENTTYPE_FULL_GLSL_ES_100_SPEC; - // Declarations (position + attribute/varying for each input). - res << "precision highp float;\n"; - res << "precision highp int;\n"; - res << "\n"; - res << vtxIn << " highp vec4 dEQP_Position;\n"; + return retVal; +} + +void ShaderCase::CaseRequirement::checkRequirements (glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo) +{ + DE_UNREF(renderCtx); - for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) + switch (m_type) { - const Value& val = spec.values.inputs[ndx]; - const DataType basicType = val.type.getBasicType(); - const DataType floatType = getDataTypeFloatScalars(basicType); - const char* const typeStr = getDataTypeName(floatType); + case REQUIREMENTTYPE_EXTENSION: + { + for (int ndx = 0; ndx < (int)m_extensions.size(); ++ndx) + { + if (contextInfo.isExtensionSupported(m_extensions[ndx].c_str())) + { + m_supportedExtensionNdx = ndx; + return; + } + } - res << vtxIn << " " << typeStr << " a_" << val.name << ";\n"; + // no extension(s). Make a nice output + { + std::ostringstream extensionList; - if (getDataTypeScalarType(basicType) == TYPE_FLOAT) - res << vtxOut << " " << typeStr << " " << val.name << ";\n"; - else - res << vtxOut << " " << typeStr << " v_" << val.name << ";\n"; - } - res << "\n"; + for (int ndx = 0; ndx < (int)m_extensions.size(); ++ndx) + { + if (!extensionList.str().empty()) + extensionList << ", "; + extensionList << m_extensions[ndx]; + } - // Main function. - // - gl_Position = dEQP_Position; - // - for each input: write attribute directly to varying - res << "void main()\n"; - res << "{\n"; - res << " gl_Position = dEQP_Position;\n"; - for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) - { - const Value& val = spec.values.inputs[ndx]; - const string& name = val.name; + if (m_extensions.size() == 1) + throw tcu::NotSupportedError("Test requires extension " + extensionList.str()); + else + throw tcu::NotSupportedError("Test requires any extension of " + extensionList.str()); + } - if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT) - res << " " << name << " = a_" << name << ";\n"; - else - res << " v_" << name << " = a_" << name << ";\n"; - } + // cannot be reached + } - res << "}\n"; - return res.str(); -} + case REQUIREMENTTYPE_IMPLEMENTATION_LIMIT: + { + const glw::Functions& gl = renderCtx.getFunctions(); + glw::GLint value = 0; + glw::GLenum error; -static void genCompareOp (ostringstream& output, const char* dstVec4Var, const ValueBlock& valueBlock, const char* nonFloatNamePrefix, const char* checkVarName) -{ - bool isFirstOutput = true; + gl.getIntegerv(m_enumName, &value); + error = gl.getError(); - for (size_t ndx = 0; ndx < valueBlock.outputs.size(); ndx++) - { - const Value& val = valueBlock.outputs[ndx]; + if (error != GL_NO_ERROR) + throw tcu::TestError("Query for " + de::toString(glu::getGettableStateStr(m_enumName)) + " generated " + de::toString(glu::getErrorStr(error))); - // Check if we're only interested in one variable (then skip if not the right one). - if (checkVarName && val.name != checkVarName) - continue; + if (!(value > m_referenceValue)) + throw tcu::NotSupportedError("Test requires " + de::toString(glu::getGettableStateStr(m_enumName)) + " (" + de::toString(value) + ") > " + de::toString(m_referenceValue)); + + return; + } - // Prefix. - if (isFirstOutput) + case REQUIREMENTTYPE_FULL_GLSL_ES_100_SPEC: { - output << "bool RES = "; - isFirstOutput = false; + // cannot be queried + return; } - else - output << "RES = RES && "; - // Generate actual comparison. - if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT) - output << "isOk(" << val.name << ", ref_" << val.name << ", 0.05);\n"; - else - output << "isOk(" << nonFloatNamePrefix << val.name << ", ref_" << val.name << ");\n"; + default: + DE_ASSERT(false); } - - if (isFirstOutput) - output << dstVec4Var << " = vec4(1.0);\n"; // \todo [petri] Should we give warning if not expect-failure case? - else - output << dstVec4Var << " = vec4(RES, RES, RES, 1.0);\n"; } -static inline bool supportsFragmentHighp (glu::GLSLVersion version) +ShaderCase::ShaderCaseSpecification::ShaderCaseSpecification (void) + : expectResult (EXPECT_LAST) + , targetVersion (glu::GLSL_VERSION_LAST) + , caseType (CASETYPE_COMPLETE) { - return version != glu::GLSL_VERSION_100_ES; } -static string genFragmentShader (const ShaderCaseSpecification& spec) +ShaderCase::ShaderCaseSpecification ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase (ExpectResult expectResult_, glu::GLSLVersion targetVersion_, const std::vector& values, const std::string& sharedSource) { - ostringstream shader; - const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); - const bool customColorOut = usesInout; - const char* const fragIn = usesInout ? "in" : "varying"; - const char* const prec = supportsFragmentHighp(spec.targetVersion) ? "highp" : "mediump"; - - shader << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n"; + ShaderCaseSpecification retVal; + retVal.expectResult = expectResult_; + retVal.targetVersion = targetVersion_; + retVal.caseType = CASETYPE_VERTEX_ONLY; + retVal.valueBlocks = values; + retVal.vertexSources.push_back(sharedSource); + return retVal; +} - shader << "precision " << prec << " float;\n"; - shader << "precision " << prec << " int;\n"; - shader << "\n"; +ShaderCase::ShaderCaseSpecification ShaderCase::ShaderCaseSpecification::generateSharedSourceFragmentCase (ExpectResult expectResult_, glu::GLSLVersion targetVersion_, const std::vector& values, const std::string& sharedSource) +{ + ShaderCaseSpecification retVal; + retVal.expectResult = expectResult_; + retVal.targetVersion = targetVersion_; + retVal.caseType = CASETYPE_FRAGMENT_ONLY; + retVal.valueBlocks = values; + retVal.fragmentSources.push_back(sharedSource); + return retVal; +} - if (customColorOut) +class BeforeDrawValidator : public glu::DrawUtilCallback +{ +public: + enum TargetType { - shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; - shader << "\n"; - } + TARGETTYPE_PROGRAM = 0, + TARGETTYPE_PIPELINE, - genCompareFunctions(shader, spec.values, true); - shader << "\n"; + TARGETTYPE_LAST + }; - // Declarations (varying, reference for each output). - for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++) - { - const Value& val = spec.values.outputs[ndx]; - const DataType basicType = val.type.getBasicType(); - const DataType floatType = getDataTypeFloatScalars(basicType); - const char* const floatTypeStr = getDataTypeName(floatType); - const char* const refTypeStr = getDataTypeName(basicType); - - if (getDataTypeScalarType(basicType) == TYPE_FLOAT) - shader << fragIn << " " << floatTypeStr << " " << val.name << ";\n"; - else - shader << fragIn << " " << floatTypeStr << " v_" << val.name << ";\n"; + BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType); - shader << "uniform " << refTypeStr << " ref_" << val.name << ";\n"; - } + void beforeDrawCall (void); - shader << "\n"; - shader << "void main()\n"; - shader << "{\n"; + const std::string& getInfoLog (void) const; + glw::GLint getValidateStatus (void) const; - shader << " "; - genCompareOp(shader, customColorOut ? "dEQP_FragColor" : "gl_FragColor", spec.values, "v_", DE_NULL); +private: + const glw::Functions& m_gl; + const glw::GLuint m_target; + const TargetType m_targetType; - shader << "}\n"; - return shader.str(); + glw::GLint m_validateStatus; + std::string m_logMessage; +}; + +BeforeDrawValidator::BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType) + : m_gl (gl) + , m_target (target) + , m_targetType (targetType) + , m_validateStatus (-1) +{ + DE_ASSERT(targetType < TARGETTYPE_LAST); } -// Specialize a shader for the vertex shader test case. -static string specializeVertexShader (const ShaderCaseSpecification& spec, const std::string& src) +void BeforeDrawValidator::beforeDrawCall (void) { - ostringstream decl; - ostringstream setup; - ostringstream output; - const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); - const char* const vtxIn = usesInout ? "in" : "attribute"; - const char* const vtxOut = usesInout ? "out" : "varying"; + glw::GLint bytesWritten = 0; + glw::GLint infoLogLength; + std::vector logBuffer; + int stringLength; - // generated from "both" case - DE_ASSERT(spec.caseType == CASETYPE_VERTEX_ONLY); + // validate + if (m_targetType == TARGETTYPE_PROGRAM) + m_gl.validateProgram(m_target); + else if (m_targetType == TARGETTYPE_PIPELINE) + m_gl.validateProgramPipeline(m_target); + else + DE_ASSERT(false); - // Output (write out position). - output << "gl_Position = dEQP_Position;\n"; + GLU_EXPECT_NO_ERROR(m_gl.getError(), "validate"); - // Declarations (position + attribute for each input, varying for each output). - decl << vtxIn << " highp vec4 dEQP_Position;\n"; + // check status + m_validateStatus = -1; - for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) - { - const Value& val = spec.values.inputs[ndx]; - const DataType basicType = val.type.getBasicType(); - const DataType floatType = getDataTypeFloatScalars(basicType); - const char* const floatTypeStr = getDataTypeName(floatType); - const char* const refTypeStr = getDataTypeName(basicType); + if (m_targetType == TARGETTYPE_PROGRAM) + m_gl.getProgramiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus); + else if (m_targetType == TARGETTYPE_PIPELINE) + m_gl.getProgramPipelineiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus); + else + DE_ASSERT(false); - if (getDataTypeScalarType(basicType) == TYPE_FLOAT) - { - decl << vtxIn << " " << floatTypeStr << " " << val.name << ";\n"; - } - else - { - decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n"; - setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(a_" << val.name << ");\n"; - } - } + GLU_EXPECT_NO_ERROR(m_gl.getError(), "get validate status"); + TCU_CHECK(m_validateStatus == GL_TRUE || m_validateStatus == GL_FALSE); - // \todo [2015-07-24 pyry] Why are uniforms missing? + // read log - for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++) - { - const Value& val = spec.values.outputs[ndx]; - const DataType basicType = val.type.getBasicType(); - const DataType floatType = getDataTypeFloatScalars(basicType); - const char* const floatTypeStr = getDataTypeName(floatType); - const char* const refTypeStr = getDataTypeName(basicType); - - if (getDataTypeScalarType(basicType) == TYPE_FLOAT) - decl << vtxOut << " " << floatTypeStr << " " << val.name << ";\n"; - else - { - decl << vtxOut << " " << floatTypeStr << " v_" << val.name << ";\n"; - decl << refTypeStr << " " << val.name << ";\n"; + infoLogLength = 0; - output << "v_" << val.name << " = " << floatTypeStr << "(" << val.name << ");\n"; - } + if (m_targetType == TARGETTYPE_PROGRAM) + m_gl.getProgramiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength); + else if (m_targetType == TARGETTYPE_PIPELINE) + m_gl.getProgramPipelineiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength); + else + DE_ASSERT(false); + + GLU_EXPECT_NO_ERROR(m_gl.getError(), "get info log length"); + + if (infoLogLength <= 0) + { + m_logMessage.clear(); + return; } - // Shader specialization. - map params; - params.insert(pair("DECLARATIONS", decl.str())); - params.insert(pair("SETUP", setup.str())); - params.insert(pair("OUTPUT", output.str())); - params.insert(pair("POSITION_FRAG_COLOR", "gl_Position")); + logBuffer.resize(infoLogLength + 2, '0'); // +1 for zero terminator (infoLogLength should include it, but better play it safe), +1 to make sure buffer is always larger - StringTemplate tmpl (src); - const string baseSrc = tmpl.specialize(params); - const string withExt = injectExtensionRequirements(baseSrc, spec.programs[0].requiredExtensions, SHADERTYPE_VERTEX); + if (m_targetType == TARGETTYPE_PROGRAM) + m_gl.getProgramInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]); + else if (m_targetType == TARGETTYPE_PIPELINE) + m_gl.getProgramPipelineInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]); + else + DE_ASSERT(false); - return withExt; + // just ignore bytesWritten to be safe, find the null terminator + stringLength = (int)(std::find(logBuffer.begin(), logBuffer.end(), '0') - logBuffer.begin()); + m_logMessage.assign(&logBuffer[0], stringLength); } -// Specialize a shader for the fragment shader test case. -static string specializeFragmentShader (const ShaderCaseSpecification& spec, const std::string& src) +const std::string& BeforeDrawValidator::getInfoLog (void) const { - ostringstream decl; - ostringstream setup; - ostringstream output; - - const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); - const bool customColorOut = usesInout; - const char* const fragIn = usesInout ? "in" : "varying"; - const char* const fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor"; - - // generated from "both" case - DE_ASSERT(spec.caseType == CASETYPE_FRAGMENT_ONLY); + return m_logMessage; +} - genCompareFunctions(decl, spec.values, false); - genCompareOp(output, fragColor, spec.values, "", DE_NULL); +glw::GLint BeforeDrawValidator::getValidateStatus (void) const +{ + return m_validateStatus; +} - if (customColorOut) - decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; +// ShaderCase. - for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) +ShaderCase::ShaderCase (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* name, const char* description, const ShaderCaseSpecification& specification) + : tcu::TestCase (testCtx, name, description) + , m_renderCtx (renderCtx) + , m_contextInfo (contextInfo) + , m_caseType (specification.caseType) + , m_expectResult (specification.expectResult) + , m_targetVersion (specification.targetVersion) + , m_separatePrograms (false) + , m_valueBlocks (specification.valueBlocks) +{ + if (m_caseType == CASETYPE_VERTEX_ONLY) { - const Value& val = spec.values.inputs[ndx]; - const DataType basicType = val.type.getBasicType(); - const DataType floatType = getDataTypeFloatScalars(basicType); - const char* const floatTypeStr = getDataTypeName(floatType); - const char* const refTypeStr = getDataTypeName(basicType); - - if (getDataTypeScalarType(basicType) == TYPE_FLOAT) - decl << fragIn << " " << floatTypeStr << " " << val.name << ";\n"; - else - { - decl << fragIn << " " << floatTypeStr << " v_" << val.name << ";\n"; - std::string offset = isDataTypeIntOrIVec(basicType) ? " * 1.0025" : ""; // \todo [petri] bit of a hack to avoid errors in chop() due to varying interpolation - setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(v_" << val.name << offset << ");\n"; - } + // case generated from "both" target, vertex case + DE_ASSERT(specification.vertexSources.size() == 1); + DE_ASSERT(specification.fragmentSources.empty()); + DE_ASSERT(specification.tessCtrlSources.empty()); + DE_ASSERT(specification.tessEvalSources.empty()); + DE_ASSERT(specification.geometrySources.empty()); } - - // \todo [2015-07-24 pyry] Why are uniforms missing? - - for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++) + else if (m_caseType == CASETYPE_FRAGMENT_ONLY) { - const Value& val = spec.values.outputs[ndx]; - const DataType basicType = val.type.getBasicType(); - const char* const refTypeStr = getDataTypeName(basicType); + // case generated from "both" target, fragment case + DE_ASSERT(specification.vertexSources.empty()); + DE_ASSERT(specification.fragmentSources.size() == 1); + DE_ASSERT(specification.tessCtrlSources.empty()); + DE_ASSERT(specification.tessEvalSources.empty()); + DE_ASSERT(specification.geometrySources.empty()); + } - decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n"; - decl << refTypeStr << " " << val.name << ";\n"; + if (m_expectResult == EXPECT_BUILD_SUCCESSFUL) + { + // Shader is never executed. Presense of input/output values is likely an error + DE_ASSERT(m_valueBlocks.empty()); } - /* \todo [2010-04-01 petri] Check all outputs. */ - - // Shader specialization. - map params; - params.insert(pair("DECLARATIONS", decl.str())); - params.insert(pair("SETUP", setup.str())); - params.insert(pair("OUTPUT", output.str())); - params.insert(pair("POSITION_FRAG_COLOR", fragColor)); - - StringTemplate tmpl (src); - const string baseSrc = tmpl.specialize(params); - const string withExt = injectExtensionRequirements(baseSrc, spec.programs[0].requiredExtensions, SHADERTYPE_FRAGMENT); - - return withExt; -} - -static void generateUniformDeclarations (std::ostream& dst, const ValueBlock& valueBlock) -{ - for (size_t ndx = 0; ndx < valueBlock.uniforms.size(); ndx++) + // single program object { - const Value& val = valueBlock.uniforms[ndx]; - const char* const typeStr = getDataTypeName(val.type.getBasicType()); - - if (val.name.find('.') == string::npos) - dst << "uniform " << typeStr << " " << val.name << ";\n"; + ProgramObject program; + program.spec.requirements = specification.requirements; + program.spec.vertexSources = specification.vertexSources; + program.spec.fragmentSources = specification.fragmentSources; + program.spec.tessCtrlSources = specification.tessCtrlSources; + program.spec.tessEvalSources = specification.tessEvalSources; + program.spec.geometrySources = specification.geometrySources; + + m_programs.push_back(program); } } -static map generateVertexSpecialization (const ProgramSpecializationParams& specParams) +ShaderCase::ShaderCase (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* name, const char* description, const PipelineCaseSpecification& specification) + : tcu::TestCase (testCtx, name, description) + , m_renderCtx (renderCtx) + , m_contextInfo (contextInfo) + , m_caseType (specification.caseType) + , m_expectResult (specification.expectResult) + , m_targetVersion (specification.targetVersion) + , m_separatePrograms (true) + , m_valueBlocks (specification.valueBlocks) { - const bool usesInout = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion); - const char* vtxIn = usesInout ? "in" : "attribute"; - ostringstream decl; - ostringstream setup; - map params; + deUint32 totalActiveMask = 0; - decl << vtxIn << " highp vec4 dEQP_Position;\n"; - - for (size_t ndx = 0; ndx < specParams.caseSpec.values.inputs.size(); ndx++) - { - const Value& val = specParams.caseSpec.values.inputs[ndx]; - const DataType basicType = val.type.getBasicType(); - const char* const typeStr = getDataTypeName(val.type.getBasicType()); + DE_ASSERT(m_caseType == CASETYPE_COMPLETE); - if (getDataTypeScalarType(basicType) == TYPE_FLOAT) - { - decl << vtxIn << " " << typeStr << " " << val.name << ";\n"; - } - else - { - const DataType floatType = getDataTypeFloatScalars(basicType); - const char* const floatTypeStr = getDataTypeName(floatType); + // validate - decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n"; - setup << typeStr << " " << val.name << " = " << typeStr << "(a_" << val.name << ");\n"; - } + for (int pipelineProgramNdx = 0; pipelineProgramNdx < (int)specification.programs.size(); ++pipelineProgramNdx) + { + // program with an active stage must contain executable code for that stage + DE_ASSERT(((specification.programs[pipelineProgramNdx].activeStageBits & (1 << glu::SHADERTYPE_VERTEX)) == 0) || !specification.programs[pipelineProgramNdx].vertexSources.empty()); + DE_ASSERT(((specification.programs[pipelineProgramNdx].activeStageBits & (1 << glu::SHADERTYPE_FRAGMENT)) == 0) || !specification.programs[pipelineProgramNdx].fragmentSources.empty()); + DE_ASSERT(((specification.programs[pipelineProgramNdx].activeStageBits & (1 << glu::SHADERTYPE_TESSELLATION_CONTROL)) == 0) || !specification.programs[pipelineProgramNdx].tessCtrlSources.empty()); + DE_ASSERT(((specification.programs[pipelineProgramNdx].activeStageBits & (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION)) == 0) || !specification.programs[pipelineProgramNdx].tessEvalSources.empty()); + DE_ASSERT(((specification.programs[pipelineProgramNdx].activeStageBits & (1 << glu::SHADERTYPE_GEOMETRY)) == 0) || !specification.programs[pipelineProgramNdx].geometrySources.empty()); + + // no two programs with with the same stage active + DE_ASSERT((totalActiveMask & specification.programs[pipelineProgramNdx].activeStageBits) == 0); + totalActiveMask |= specification.programs[pipelineProgramNdx].activeStageBits; } - generateUniformDeclarations(decl, specParams.caseSpec.values); - - params.insert(pair("VERTEX_DECLARATIONS", decl.str())); - params.insert(pair("VERTEX_SETUP", setup.str())); - params.insert(pair("VERTEX_OUTPUT", string("gl_Position = dEQP_Position;\n"))); - - return params; -} - -static map generateFragmentSpecialization (const ProgramSpecializationParams& specParams) -{ - const bool usesInout = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion); - const bool customColorOut = usesInout; - const char* const fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor"; - ostringstream decl; - ostringstream output; - map params; + // create ProgramObjects - genCompareFunctions(decl, specParams.caseSpec.values, false); - genCompareOp(output, fragColor, specParams.caseSpec.values, "", DE_NULL); - - if (customColorOut) - decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; - - for (size_t ndx = 0; ndx < specParams.caseSpec.values.outputs.size(); ndx++) + for (int pipelineProgramNdx = 0; pipelineProgramNdx < (int)specification.programs.size(); ++pipelineProgramNdx) { - const Value& val = specParams.caseSpec.values.outputs[ndx]; - const char* const refTypeStr = getDataTypeName(val.type.getBasicType()); - - decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n"; - decl << refTypeStr << " " << val.name << ";\n"; + ProgramObject program; + program.spec = specification.programs[pipelineProgramNdx]; + m_programs.push_back(program); } - - generateUniformDeclarations(decl, specParams.caseSpec.values); - - params.insert(pair("FRAGMENT_DECLARATIONS", decl.str())); - params.insert(pair("FRAGMENT_OUTPUT", output.str())); - params.insert(pair("FRAG_COLOR", fragColor)); - - return params; -} - -static map generateGeometrySpecialization (const ProgramSpecializationParams& specParams) -{ - ostringstream decl; - map params; - - decl << "layout (triangles) in;\n"; - decl << "layout (triangle_strip, max_vertices=3) out;\n"; - decl << "\n"; - - generateUniformDeclarations(decl, specParams.caseSpec.values); - - params.insert(pair("GEOMETRY_DECLARATIONS", decl.str())); - - return params; } -static map generateTessControlSpecialization (const ProgramSpecializationParams& specParams) +ShaderCase::~ShaderCase (void) { - ostringstream decl; - ostringstream output; - map params; - - decl << "layout (vertices=3) out;\n"; - decl << "\n"; - - generateUniformDeclarations(decl, specParams.caseSpec.values); - - output << "gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" - "gl_TessLevelInner[0] = 2.0;\n" - "gl_TessLevelInner[1] = 2.0;\n" - "gl_TessLevelOuter[0] = 2.0;\n" - "gl_TessLevelOuter[1] = 2.0;\n" - "gl_TessLevelOuter[2] = 2.0;\n" - "gl_TessLevelOuter[3] = 2.0;"; - - params.insert(pair("TESSELLATION_CONTROL_DECLARATIONS", decl.str())); - params.insert(pair("TESSELLATION_CONTROL_OUTPUT", output.str())); - params.insert(pair("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices))); - - return params; } -static map generateTessEvalSpecialization (const ProgramSpecializationParams& specParams) +void ShaderCase::init (void) { - ostringstream decl; - ostringstream output; - map params; - - decl << "layout (triangles) in;\n"; - decl << "\n"; - - generateUniformDeclarations(decl, specParams.caseSpec.values); - - output << "gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + gl_TessCoord[2] * gl_in[2].gl_Position;\n"; + // If no value blocks given, use an empty one. + if (m_valueBlocks.empty()) + m_valueBlocks.push_back(ValueBlock()); - params.insert(pair("TESSELLATION_EVALUATION_DECLARATIONS", decl.str())); - params.insert(pair("TESSELLATION_EVALUATION_OUTPUT", output.str())); - params.insert(pair("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices))); + // Use first value block to specialize shaders. + const ValueBlock& valueBlock = m_valueBlocks[0]; - return params; -} + // \todo [2010-04-01 petri] Check that all value blocks have matching values. -static void specializeShaderSources (ProgramSources& dst, - const ProgramSources& src, - const ProgramSpecializationParams& specParams, - glu::ShaderType shaderType, - map (*specializationGenerator) (const ProgramSpecializationParams& specParams)) -{ - if (!src.sources[shaderType].empty()) + // prepare programs + for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx) { - const map tmplParams = specializationGenerator(specParams); + // Check requirements + for (int ndx = 0; ndx < (int)m_programs[programNdx].spec.requirements.size(); ++ndx) + m_programs[programNdx].spec.requirements[ndx].checkRequirements(m_renderCtx, m_contextInfo); - for (size_t ndx = 0; ndx < src.sources[shaderType].size(); ++ndx) + // Generate specialized shader sources. + if (m_caseType == CASETYPE_COMPLETE) { - const StringTemplate tmpl (src.sources[shaderType][ndx]); - const std::string baseGLSLCode = tmpl.specialize(tmplParams); - const std::string sourceWithExts = injectExtensionRequirements(baseGLSLCode, specParams.requiredExtensions, shaderType); - - dst << glu::ShaderSource(shaderType, sourceWithExts); + // all shaders specified separately + specializeVertexShaders (m_programs[programNdx].programSources, m_programs[programNdx].spec.vertexSources, valueBlock, m_programs[programNdx].spec.requirements); + specializeFragmentShaders (m_programs[programNdx].programSources, m_programs[programNdx].spec.fragmentSources, valueBlock, m_programs[programNdx].spec.requirements); + specializeGeometryShaders (m_programs[programNdx].programSources, m_programs[programNdx].spec.geometrySources, valueBlock, m_programs[programNdx].spec.requirements); + specializeTessControlShaders(m_programs[programNdx].programSources, m_programs[programNdx].spec.tessCtrlSources, valueBlock, m_programs[programNdx].spec.requirements); + specializeTessEvalShaders (m_programs[programNdx].programSources, m_programs[programNdx].spec.tessEvalSources, valueBlock, m_programs[programNdx].spec.requirements); } - } -} - -static void specializeProgramSources (glu::ProgramSources& dst, - const glu::ProgramSources& src, - const ProgramSpecializationParams& specParams) -{ - specializeShaderSources(dst, src, specParams, SHADERTYPE_VERTEX, generateVertexSpecialization); - specializeShaderSources(dst, src, specParams, SHADERTYPE_FRAGMENT, generateFragmentSpecialization); - specializeShaderSources(dst, src, specParams, SHADERTYPE_GEOMETRY, generateGeometrySpecialization); - specializeShaderSources(dst, src, specParams, SHADERTYPE_TESSELLATION_CONTROL, generateTessControlSpecialization); - specializeShaderSources(dst, src, specParams, SHADERTYPE_TESSELLATION_EVALUATION, generateTessEvalSpecialization); - - dst << ProgramSeparable(src.separable); -} - -enum -{ - VIEWPORT_WIDTH = 128, - VIEWPORT_HEIGHT = 128 -}; - -class BeforeDrawValidator : public glu::DrawUtilCallback -{ -public: - enum TargetType - { - TARGETTYPE_PROGRAM = 0, - TARGETTYPE_PIPELINE, - - TARGETTYPE_LAST - }; - - BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType); - - void beforeDrawCall (void); - - const std::string& getInfoLog (void) const; - glw::GLint getValidateStatus (void) const; - -private: - const glw::Functions& m_gl; - const glw::GLuint m_target; - const TargetType m_targetType; - - glw::GLint m_validateStatus; - std::string m_logMessage; -}; - -BeforeDrawValidator::BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType) - : m_gl (gl) - , m_target (target) - , m_targetType (targetType) - , m_validateStatus (-1) -{ - DE_ASSERT(targetType < TARGETTYPE_LAST); -} - -void BeforeDrawValidator::beforeDrawCall (void) -{ - glw::GLint bytesWritten = 0; - glw::GLint infoLogLength; - std::vector logBuffer; - int stringLength; - - // validate - if (m_targetType == TARGETTYPE_PROGRAM) - m_gl.validateProgram(m_target); - else if (m_targetType == TARGETTYPE_PIPELINE) - m_gl.validateProgramPipeline(m_target); - else - DE_ASSERT(false); - - GLU_EXPECT_NO_ERROR(m_gl.getError(), "validate"); - - // check status - m_validateStatus = -1; - - if (m_targetType == TARGETTYPE_PROGRAM) - m_gl.getProgramiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus); - else if (m_targetType == TARGETTYPE_PIPELINE) - m_gl.getProgramPipelineiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus); - else - DE_ASSERT(false); - - GLU_EXPECT_NO_ERROR(m_gl.getError(), "get validate status"); - TCU_CHECK(m_validateStatus == GL_TRUE || m_validateStatus == GL_FALSE); - - // read log - - infoLogLength = 0; + else if (m_caseType == CASETYPE_VERTEX_ONLY) + { + DE_ASSERT(m_programs.size() == 1); + DE_ASSERT(!m_separatePrograms); - if (m_targetType == TARGETTYPE_PROGRAM) - m_gl.getProgramiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength); - else if (m_targetType == TARGETTYPE_PIPELINE) - m_gl.getProgramPipelineiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength); - else - DE_ASSERT(false); + // case generated from "both" target, vertex case + m_programs[0].programSources << glu::VertexSource(specializeVertexShader(m_programs[0].spec.vertexSources[0].c_str(), valueBlock)); + m_programs[0].programSources << glu::FragmentSource(genFragmentShader(valueBlock)); + } + else if (m_caseType == CASETYPE_FRAGMENT_ONLY) + { + DE_ASSERT(m_programs.size() == 1); + DE_ASSERT(!m_separatePrograms); - GLU_EXPECT_NO_ERROR(m_gl.getError(), "get info log length"); + // case generated from "both" target, fragment case + m_programs[0].programSources << glu::VertexSource(genVertexShader(valueBlock)); + m_programs[0].programSources << glu::FragmentSource(specializeFragmentShader(m_programs[0].spec.fragmentSources[0].c_str(), valueBlock)); + } - if (infoLogLength <= 0) - { - m_logMessage.clear(); - return; + m_programs[programNdx].programSources << glu::ProgramSeparable(m_separatePrograms); } - logBuffer.resize(infoLogLength + 2, '0'); // +1 for zero terminator (infoLogLength should include it, but better play it safe), +1 to make sure buffer is always larger - - if (m_targetType == TARGETTYPE_PROGRAM) - m_gl.getProgramInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]); - else if (m_targetType == TARGETTYPE_PIPELINE) - m_gl.getProgramPipelineInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]); - else - DE_ASSERT(false); - - // just ignore bytesWritten to be safe, find the null terminator - stringLength = (int)(std::find(logBuffer.begin(), logBuffer.end(), '0') - logBuffer.begin()); - m_logMessage.assign(&logBuffer[0], stringLength); -} - -const std::string& BeforeDrawValidator::getInfoLog (void) const -{ - return m_logMessage; -} - -glw::GLint BeforeDrawValidator::getValidateStatus (void) const -{ - return m_validateStatus; -} - -// ShaderCase. - -ShaderLibraryCase::ShaderLibraryCase (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* name, const char* description, const ShaderCaseSpecification& specification) - : tcu::TestCase (testCtx, name, description) - , m_renderCtx (renderCtx) - , m_contextInfo (contextInfo) - , m_spec (specification) -{ -} - -ShaderLibraryCase::~ShaderLibraryCase (void) -{ -} - -void ShaderLibraryCase::init (void) -{ - DE_ASSERT(isValid(m_spec)); - - checkImplementationLimits(m_spec.requiredCaps, m_contextInfo); - // log the expected result - switch (m_spec.expectResult) + switch (m_expectResult) { case EXPECT_PASS: // Don't write anything @@ -761,32 +521,42 @@ void ShaderLibraryCase::init (void) DE_ASSERT(false); break; } + + // sanity of arguments + + if (anyProgramRequiresFullGLSLES100Specification()) + { + // makes only sense in tests where shader compiles + DE_ASSERT(m_expectResult == EXPECT_PASS || + m_expectResult == EXPECT_VALIDATION_FAIL || + m_expectResult == EXPECT_BUILD_SUCCESSFUL); + + // only makes sense for ES 100 programs + DE_ASSERT(m_targetVersion == glu::GLSL_VERSION_100_ES); + } } -static void setUniformValue (const glw::Functions& gl, const std::vector& pipelinePrograms, const std::string& name, const Value& val, int arrayNdx, tcu::TestLog& log) +static void setUniformValue (const glw::Functions& gl, const std::vector& pipelinePrograms, const std::string& name, const ShaderCase::Value& val, int arrayNdx, tcu::TestLog& log) { bool foundAnyMatch = false; for (int programNdx = 0; programNdx < (int)pipelinePrograms.size(); ++programNdx) { - const DataType dataType = val.type.getBasicType(); - const int scalarSize = getDataTypeScalarSize(dataType); - const int loc = gl.getUniformLocation(pipelinePrograms[programNdx], name.c_str()); - const int elemNdx = arrayNdx * scalarSize; - - DE_ASSERT(elemNdx+scalarSize <= (int)val.elements.size()); + const int scalarSize = getDataTypeScalarSize(val.dataType); + const int loc = gl.getUniformLocation(pipelinePrograms[programNdx], name.c_str()); + const int elemNdx = (val.arrayLength == 1) ? (0) : (arrayNdx * scalarSize); if (loc == -1) continue; foundAnyMatch = true; - DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLfloat)); - DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLint)); + DE_STATIC_ASSERT(sizeof(ShaderCase::Value::Element) == sizeof(glw::GLfloat)); + DE_STATIC_ASSERT(sizeof(ShaderCase::Value::Element) == sizeof(glw::GLint)); gl.useProgram(pipelinePrograms[programNdx]); - switch (dataType) + switch (val.dataType) { case TYPE_FLOAT: gl.uniform1fv(loc, 1, &val.elements[elemNdx].float32); break; case TYPE_FLOAT_VEC2: gl.uniform2fv(loc, 1, &val.elements[elemNdx].float32); break; @@ -828,38 +598,52 @@ static void setUniformValue (const glw::Functions& gl, const std::vector minX) && (maxY > minY)); - for (int y = 0; y < surface.getHeight(); y++) + for (int y = minY; y <= maxY; y++) { - for (int x = 0; x < surface.getWidth(); x++) + for (int x = minX; x <= maxX; x++) { - const tcu::IVec4 pixel = surface.getPixelInt(x, y); + RGBA pixel = surface.getPixel(x, y); // Note: we really do not want to involve alpha in the check comparison // \todo [2010-09-22 kalle] Do we know that alpha would be one? If yes, could use color constants white and black. - const bool isWhite = (pixel[0] == 255) && (pixel[1] == 255) && (pixel[2] == 255); - const bool isBlack = (pixel[0] == 0) && (pixel[1] == 0) && (pixel[2] == 0); + bool isWhite = (pixel.getRed() == 255) && (pixel.getGreen() == 255) && (pixel.getBlue() == 255); + bool isBlack = (pixel.getRed() == 0) && (pixel.getGreen() == 0) && (pixel.getBlue() == 0); allWhite = allWhite && isWhite; allBlack = allBlack && isBlack; @@ -876,14 +660,13 @@ static bool checkPixels (tcu::TestLog& log, const tcu::ConstPixelBufferAccess& s return false; } - return true; } -bool ShaderLibraryCase::execute (void) +bool ShaderCase::execute (void) { - const float quadSize = 1.0f; - static const float s_positions[4*4] = + const float quadSize = 1.0f; + static const float s_positions[4*4] = { -quadSize, -quadSize, 0.0f, 1.0f, -quadSize, +quadSize, 0.0f, 1.0f, @@ -891,69 +674,40 @@ bool ShaderLibraryCase::execute (void) +quadSize, +quadSize, 0.0f, 1.0f }; - static const deUint16 s_indices[2*3] = + static const deUint16 s_indices[2*3] = { 0, 1, 2, 1, 3, 2 }; - TestLog& log = m_testCtx.getLog(); - const glw::Functions& gl = m_renderCtx.getFunctions(); + TestLog& log = m_testCtx.getLog(); + const glw::Functions& gl = m_renderCtx.getFunctions(); // Compute viewport. - const tcu::RenderTarget& renderTarget = m_renderCtx.getRenderTarget(); - de::Random rnd (deStringHash(getName())); - const int width = deMin32(renderTarget.getWidth(), VIEWPORT_WIDTH); - const int height = deMin32(renderTarget.getHeight(), VIEWPORT_HEIGHT); - const int viewportX = rnd.getInt(0, renderTarget.getWidth() - width); - const int viewportY = rnd.getInt(0, renderTarget.getHeight() - height); - const int numVerticesPerDraw = 4; - const bool tessellationPresent = isTessellationPresent(m_spec); - const bool separablePrograms = m_spec.programs[0].sources.separable; - - bool allCompilesOk = true; - bool allLinksOk = true; - const char* failReason = DE_NULL; - - vector specializedSources (m_spec.programs.size()); - - deUint32 vertexProgramID = -1; - vector pipelineProgramIDs; - vector > programs; - SharedPtr programPipeline; + const tcu::RenderTarget& renderTarget = m_renderCtx.getRenderTarget(); + de::Random rnd (deStringHash(getName())); + const int width = deMin32(renderTarget.getWidth(), VIEWPORT_WIDTH); + const int height = deMin32(renderTarget.getHeight(), VIEWPORT_HEIGHT); + const int viewportX = rnd.getInt(0, renderTarget.getWidth() - width); + const int viewportY = rnd.getInt(0, renderTarget.getHeight() - height); + const int numVerticesPerDraw = 4; + const bool tessellationPresent = isTessellationPresent(); + const bool requiresFullGLSLES100 = anyProgramRequiresFullGLSLES100Specification(); + + bool allCompilesOk = true; + bool allLinksOk = true; + const char* failReason = DE_NULL; + + deUint32 vertexProgramID = -1; + std::vector pipelineProgramIDs; + std::vector > programs; + de::SharedPtr programPipeline; GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): start"); - // Specialize shaders - if (m_spec.caseType == CASETYPE_VERTEX_ONLY) - { - DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[SHADERTYPE_VERTEX].size() == 1); - specializedSources[0] << glu::VertexSource(specializeVertexShader(m_spec, m_spec.programs[0].sources.sources[SHADERTYPE_VERTEX][0])) - << glu::FragmentSource(genFragmentShader(m_spec)); - } - else if (m_spec.caseType == CASETYPE_FRAGMENT_ONLY) - { - DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].size() == 1); - specializedSources[0] << glu::VertexSource(genVertexShader(m_spec)) - << glu::FragmentSource(specializeFragmentShader(m_spec, m_spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT][0])); - } - else - { - DE_ASSERT(m_spec.caseType == CASETYPE_COMPLETE); - - const int maxPatchVertices = isTessellationPresent(m_spec) ? m_contextInfo.getInt(GL_MAX_PATCH_VERTICES) : 0; - - for (size_t progNdx = 0; progNdx < m_spec.programs.size(); progNdx++) - { - const ProgramSpecializationParams progSpecParams (m_spec, checkAndSpecializeExtensions(m_spec.programs[progNdx].requiredExtensions, m_contextInfo), maxPatchVertices); - - specializeProgramSources(specializedSources[progNdx], m_spec.programs[progNdx].sources, progSpecParams); - } - } - - if (!separablePrograms) + if (!m_separatePrograms) { - de::SharedPtr program (new glu::ShaderProgram(m_renderCtx, specializedSources[0])); + de::SharedPtr program (new glu::ShaderProgram(m_renderCtx, m_programs[0].programSources)); vertexProgramID = program->getProgram(); pipelineProgramIDs.push_back(program->getProgram()); @@ -974,11 +728,11 @@ bool ShaderLibraryCase::execute (void) else { // Separate programs - for (size_t programNdx = 0; programNdx < m_spec.programs.size(); ++programNdx) + for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx) { - de::SharedPtr program(new glu::ShaderProgram(m_renderCtx, specializedSources[programNdx])); + de::SharedPtr program(new glu::ShaderProgram(m_renderCtx, m_programs[programNdx].programSources)); - if (m_spec.programs[programNdx].activeStages & (1u << glu::SHADERTYPE_VERTEX)) + if (m_programs[programNdx].spec.activeStageBits & (1 << glu::SHADERTYPE_VERTEX)) vertexProgramID = program->getProgram(); pipelineProgramIDs.push_back(program->getProgram()); @@ -1003,7 +757,7 @@ bool ShaderLibraryCase::execute (void) builder << "Pipeline uses stages: "; for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage) { - if (m_spec.programs[programNdx].activeStages & (1u << stage)) + if (m_programs[programNdx].spec.activeStageBits & (1 << stage)) { if (!firstStage) builder << ", "; @@ -1018,7 +772,7 @@ bool ShaderLibraryCase::execute (void) } } - switch (m_spec.expectResult) + switch (m_expectResult) { case EXPECT_PASS: case EXPECT_VALIDATION_FAIL: @@ -1058,7 +812,7 @@ bool ShaderLibraryCase::execute (void) // \todo [2010-06-07 petri] These should be handled in the test case? log << TestLog::Message << "ERROR: " << failReason << TestLog::EndMessage; - if (m_spec.fullGLSLES100Required) + if (requiresFullGLSLES100) { log << TestLog::Message << "Assuming build failure is caused by implementation not supporting full GLSL ES 100 specification, which is not required." @@ -1073,7 +827,7 @@ bool ShaderLibraryCase::execute (void) else m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Full GLSL ES 100 is not supported"); } - else if (m_spec.expectResult == EXPECT_COMPILE_FAIL && allCompilesOk && !allLinksOk) + else if (m_expectResult == EXPECT_COMPILE_FAIL && allCompilesOk && !allLinksOk) { // If implementation parses shader at link time, report it as quality warning. m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason); @@ -1084,26 +838,26 @@ bool ShaderLibraryCase::execute (void) } // Return if shader is not intended to be run - if (m_spec.expectResult == EXPECT_COMPILE_FAIL || - m_spec.expectResult == EXPECT_COMPILE_LINK_FAIL || - m_spec.expectResult == EXPECT_LINK_FAIL || - m_spec.expectResult == EXPECT_BUILD_SUCCESSFUL) + if (m_expectResult == EXPECT_COMPILE_FAIL || + m_expectResult == EXPECT_COMPILE_LINK_FAIL || + m_expectResult == EXPECT_LINK_FAIL || + m_expectResult == EXPECT_BUILD_SUCCESSFUL) return true; // Setup viewport. gl.viewport(viewportX, viewportY, width, height); - if (separablePrograms) + if (m_separatePrograms) { programPipeline = de::SharedPtr(new glu::ProgramPipeline(m_renderCtx)); // Setup pipeline gl.bindProgramPipeline(programPipeline->getPipeline()); - for (int programNdx = 0; programNdx < (int)m_spec.programs.size(); ++programNdx) + for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx) { deUint32 shaderFlags = 0; for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage) - if (m_spec.programs[programNdx].activeStages & (1u << stage)) + if (m_programs[programNdx].spec.activeStageBits & (1 << stage)) shaderFlags |= glu::getGLShaderTypeBit((glu::ShaderType)stage); programPipeline->useProgramStages(shaderFlags, pipelineProgramIDs[programNdx]); @@ -1128,111 +882,114 @@ bool ShaderLibraryCase::execute (void) } // Iterate all value blocks. + for (int blockNdx = 0; blockNdx < (int)m_valueBlocks.size(); blockNdx++) { - const ValueBlock& valueBlock = m_spec.values; + const ValueBlock& valueBlock = m_valueBlocks[blockNdx]; // always render at least one pass even if there is no input/output data - const int numRenderPasses = valueBlock.outputs.empty() ? 1 : (int)valueBlock.outputs[0].elements.size() / valueBlock.outputs[0].type.getScalarSize(); + const int numRenderPasses = (valueBlock.arrayLength == 0) ? (1) : (valueBlock.arrayLength); // Iterate all array sub-cases. for (int arrayNdx = 0; arrayNdx < numRenderPasses; arrayNdx++) { + int numValues = (int)valueBlock.values.size(); vector vertexArrays; int attribValueNdx = 0; - vector > attribValues (valueBlock.inputs.size()); + vector > attribValues (numValues); glw::GLenum postDrawError; BeforeDrawValidator beforeDrawValidator (gl, - (separablePrograms) ? (programPipeline->getPipeline()) : (vertexProgramID), - (separablePrograms) ? (BeforeDrawValidator::TARGETTYPE_PIPELINE) : (BeforeDrawValidator::TARGETTYPE_PROGRAM)); + (m_separatePrograms) ? (programPipeline->getPipeline()) : (vertexProgramID), + (m_separatePrograms) ? (BeforeDrawValidator::TARGETTYPE_PIPELINE) : (BeforeDrawValidator::TARGETTYPE_PROGRAM)); vertexArrays.push_back(va::Float(positionLoc, 4, numVerticesPerDraw, 0, &s_positions[0])); // Collect VA pointer for inputs - for (size_t valNdx = 0; valNdx < valueBlock.inputs.size(); valNdx++) + for (int valNdx = 0; valNdx < numValues; valNdx++) { - const Value& val = valueBlock.inputs[valNdx]; - const char* const valueName = val.name.c_str(); - const DataType dataType = val.type.getBasicType(); - const int scalarSize = getDataTypeScalarSize(dataType); - - // Replicate values four times. - std::vector& scalars = attribValues[attribValueNdx++]; - scalars.resize(numVerticesPerDraw * scalarSize); - if (isDataTypeFloatOrVec(dataType) || isDataTypeMatrix(dataType)) - { - for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++) - for (int ndx = 0; ndx < scalarSize; ndx++) - scalars[repNdx*scalarSize + ndx] = val.elements[arrayNdx*scalarSize + ndx].float32; - } - else + const ShaderCase::Value& val = valueBlock.values[valNdx]; + const char* const valueName = val.valueName.c_str(); + const DataType dataType = val.dataType; + const int scalarSize = getDataTypeScalarSize(val.dataType); + + if (val.storageType == ShaderCase::Value::STORAGE_INPUT) { - // convert to floats. - for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++) + // Replicate values four times. + std::vector& scalars = attribValues[attribValueNdx++]; + scalars.resize(numVerticesPerDraw * scalarSize); + if (isDataTypeFloatOrVec(dataType) || isDataTypeMatrix(dataType)) + { + for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++) + for (int ndx = 0; ndx < scalarSize; ndx++) + scalars[repNdx*scalarSize + ndx] = val.elements[arrayNdx*scalarSize + ndx].float32; + } + else { - for (int ndx = 0; ndx < scalarSize; ndx++) + // convert to floats. + for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++) { - float v = (float)val.elements[arrayNdx*scalarSize + ndx].int32; - DE_ASSERT(val.elements[arrayNdx*scalarSize + ndx].int32 == (int)v); - scalars[repNdx*scalarSize + ndx] = v; + for (int ndx = 0; ndx < scalarSize; ndx++) + { + float v = (float)val.elements[arrayNdx*scalarSize + ndx].int32; + DE_ASSERT(val.elements[arrayNdx*scalarSize + ndx].int32 == (int)v); + scalars[repNdx*scalarSize + ndx] = v; + } } } - } - // Attribute name prefix. - string attribPrefix = ""; - // \todo [2010-05-27 petri] Should latter condition only apply for vertex cases (or actually non-fragment cases)? - if ((m_spec.caseType == CASETYPE_FRAGMENT_ONLY) || (getDataTypeScalarType(dataType) != TYPE_FLOAT)) - attribPrefix = "a_"; + // Attribute name prefix. + string attribPrefix = ""; + // \todo [2010-05-27 petri] Should latter condition only apply for vertex cases (or actually non-fragment cases)? + if ((m_caseType == CASETYPE_FRAGMENT_ONLY) || (getDataTypeScalarType(dataType) != TYPE_FLOAT)) + attribPrefix = "a_"; - // Input always given as attribute. - string attribName = attribPrefix + valueName; - int attribLoc = gl.getAttribLocation(vertexProgramID, attribName.c_str()); - if (attribLoc == -1) - { - log << TestLog::Message << "Warning: no location found for attribute '" << attribName << "'" << TestLog::EndMessage; - continue; - } + // Input always given as attribute. + string attribName = attribPrefix + valueName; + int attribLoc = gl.getAttribLocation(vertexProgramID, attribName.c_str()); + if (attribLoc == -1) + { + log << TestLog::Message << "Warning: no location found for attribute '" << attribName << "'" << TestLog::EndMessage; + continue; + } - if (isDataTypeMatrix(dataType)) - { - int numCols = getDataTypeMatrixNumColumns(dataType); - int numRows = getDataTypeMatrixNumRows(dataType); - DE_ASSERT(scalarSize == numCols*numRows); + if (isDataTypeMatrix(dataType)) + { + int numCols = getDataTypeMatrixNumColumns(dataType); + int numRows = getDataTypeMatrixNumRows(dataType); + DE_ASSERT(scalarSize == numCols*numRows); - for (int i = 0; i < numCols; i++) - vertexArrays.push_back(va::Float(attribLoc + i, numRows, numVerticesPerDraw, scalarSize*(int)sizeof(float), &scalars[i * numRows])); - } - else - { - DE_ASSERT(isDataTypeFloatOrVec(dataType) || isDataTypeIntOrIVec(dataType) || isDataTypeUintOrUVec(dataType) || isDataTypeBoolOrBVec(dataType)); - vertexArrays.push_back(va::Float(attribLoc, scalarSize, numVerticesPerDraw, 0, &scalars[0])); - } + for (int i = 0; i < numCols; i++) + vertexArrays.push_back(va::Float(attribLoc + i, numRows, numVerticesPerDraw, scalarSize*(int)sizeof(float), &scalars[i * numRows])); + } + else + { + DE_ASSERT(isDataTypeFloatOrVec(dataType) || isDataTypeIntOrIVec(dataType) || isDataTypeUintOrUVec(dataType) || isDataTypeBoolOrBVec(dataType)); + vertexArrays.push_back(va::Float(attribLoc, scalarSize, numVerticesPerDraw, 0, &scalars[0])); + } - GLU_EXPECT_NO_ERROR(gl.getError(), "set vertex attrib array"); + GLU_EXPECT_NO_ERROR(gl.getError(), "set vertex attrib array"); + } } GLU_EXPECT_NO_ERROR(gl.getError(), "before set uniforms"); - // set reference values for outputs. - for (size_t valNdx = 0; valNdx < valueBlock.outputs.size(); valNdx++) - { - const Value& val = valueBlock.outputs[valNdx]; - const char* const valueName = val.name.c_str(); - - // Set reference value. - string refName = string("ref_") + valueName; - setUniformValue(gl, pipelineProgramIDs, refName, val, arrayNdx, m_testCtx.getLog()); - GLU_EXPECT_NO_ERROR(gl.getError(), "set reference uniforms"); - } - - // set uniform values - for (size_t valNdx = 0; valNdx < valueBlock.uniforms.size(); valNdx++) + // set uniform values for outputs (refs). + for (int valNdx = 0; valNdx < numValues; valNdx++) { - const Value& val = valueBlock.uniforms[valNdx]; - const char* const valueName = val.name.c_str(); + const ShaderCase::Value& val = valueBlock.values[valNdx]; + const char* const valueName = val.valueName.c_str(); - setUniformValue(gl, pipelineProgramIDs, valueName, val, arrayNdx, m_testCtx.getLog()); - GLU_EXPECT_NO_ERROR(gl.getError(), "set uniforms"); + if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT) + { + // Set reference value. + string refName = string("ref_") + valueName; + setUniformValue(gl, pipelineProgramIDs, refName, val, arrayNdx, m_testCtx.getLog()); + GLU_EXPECT_NO_ERROR(gl.getError(), "set reference uniforms"); + } + else if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM) + { + setUniformValue(gl, pipelineProgramIDs, valueName, val, arrayNdx, m_testCtx.getLog()); + GLU_EXPECT_NO_ERROR(gl.getError(), "set uniforms"); + } } // Clear. @@ -1241,7 +998,7 @@ bool ShaderLibraryCase::execute (void) GLU_EXPECT_NO_ERROR(gl.getError(), "clear buffer"); // Use program or pipeline - if (separablePrograms) + if (m_separatePrograms) gl.useProgram(0); else gl.useProgram(vertexProgramID); @@ -1260,13 +1017,13 @@ bool ShaderLibraryCase::execute (void) (tessellationPresent) ? (pr::Patches(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])) : (pr::Triangles(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])), - (m_spec.expectResult == EXPECT_VALIDATION_FAIL) ? + (m_expectResult == EXPECT_VALIDATION_FAIL) ? (&beforeDrawValidator) : (DE_NULL)); postDrawError = gl.getError(); - if (m_spec.expectResult == EXPECT_PASS) + if (m_expectResult == EXPECT_PASS) { // Read back results. Surface surface (width, height); @@ -1281,13 +1038,14 @@ bool ShaderLibraryCase::execute (void) glu::readPixels(m_renderCtx, viewportX, viewportY, surface.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels"); - if (!checkPixels(log, tcu::getSubregion(surface.getAccess(), minX, minY, maxX-minX+1, maxY-minY+1))) + if (!checkPixels(surface, minX, maxX, minY, maxY)) { - log << TestLog::Message << "INCORRECT RESULT for sub-case " << arrayNdx+1 << " of " << numRenderPasses << "):" + log << TestLog::Message << "INCORRECT RESULT for (value block " << (blockNdx+1) << " of " << (int)m_valueBlocks.size() + << ", sub-case " << arrayNdx+1 << " of " << valueBlock.arrayLength << "):" << TestLog::EndMessage; log << TestLog::Message << "Failing shader input/output values:" << TestLog::EndMessage; - dumpValues(log, valueBlock, arrayNdx); + dumpValues(valueBlock, arrayNdx); // Dump image on failure. log << TestLog::Image("Result", "Rendered result image", surface); @@ -1297,7 +1055,7 @@ bool ShaderLibraryCase::execute (void) return false; } } - else if (m_spec.expectResult == EXPECT_VALIDATION_FAIL) + else if (m_expectResult == EXPECT_VALIDATION_FAIL) { log << TestLog::Message << "Draw call generated error: " @@ -1347,14 +1105,14 @@ bool ShaderLibraryCase::execute (void) } gl.useProgram(0); - if (separablePrograms) + if (m_separatePrograms) gl.bindProgramPipeline(0); GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): end"); return true; } -TestCase::IterateResult ShaderLibraryCase::iterate (void) +TestCase::IterateResult ShaderCase::iterate (void) { // Initialize state to pass. m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); @@ -1366,5 +1124,690 @@ TestCase::IterateResult ShaderLibraryCase::iterate (void) return TestCase::STOP; } +static void generateExtensionStatements (std::ostringstream& buf, const std::vector& requirements, glu::ShaderType type) +{ + for (int ndx = 0; ndx < (int)requirements.size(); ++ndx) + if (requirements[ndx].getType() == ShaderCase::CaseRequirement::REQUIREMENTTYPE_EXTENSION && + (requirements[ndx].getAffectedExtensionStageFlags() & (1 << (deUint32)type)) != 0) + buf << "#extension " << requirements[ndx].getSupportedExtension() << " : require\n"; +} + +// Injects #extension XXX : require lines after the last preprocessor directive in the shader code. Does not support line continuations +static std::string injectExtensionRequirements (const std::string& baseCode, glu::ShaderType shaderType, const std::vector& requirements) +{ + std::istringstream baseCodeBuf(baseCode); + std::ostringstream resultBuf; + std::string line; + bool firstNonPreprocessorLine = true; + std::ostringstream extensions; + + generateExtensionStatements(extensions, requirements, shaderType); + + // skip if no requirements + if (extensions.str().empty()) + return baseCode; + + while (std::getline(baseCodeBuf, line)) + { + // begins with '#'? + const std::string::size_type firstNonWhitespace = line.find_first_not_of("\t "); + const bool isPreprocessorDirective = (firstNonWhitespace != std::string::npos && line.at(firstNonWhitespace) == '#'); + + // Inject #extensions + if (!isPreprocessorDirective && firstNonPreprocessorLine) + { + firstNonPreprocessorLine = false; + resultBuf << extensions.str(); + } + + resultBuf << line << "\n"; + } + + return resultBuf.str(); +} + +// This functions builds a matching vertex shader for a 'both' case, when +// the fragment shader is being tested. +// We need to build attributes and varyings for each 'input'. +string ShaderCase::genVertexShader (const ValueBlock& valueBlock) const +{ + ostringstream res; + const bool usesInout = usesShaderInoutQualifiers(m_targetVersion); + const char* vtxIn = usesInout ? "in" : "attribute"; + const char* vtxOut = usesInout ? "out" : "varying"; + + res << glu::getGLSLVersionDeclaration(m_targetVersion) << "\n"; + + // Declarations (position + attribute/varying for each input). + res << "precision highp float;\n"; + res << "precision highp int;\n"; + res << "\n"; + res << vtxIn << " highp vec4 dEQP_Position;\n"; + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + if (val.storageType == ShaderCase::Value::STORAGE_INPUT) + { + DataType floatType = getDataTypeFloatScalars(val.dataType); + const char* typeStr = getDataTypeName(floatType); + res << vtxIn << " " << typeStr << " a_" << val.valueName << ";\n"; + + if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT) + res << vtxOut << " " << typeStr << " " << val.valueName << ";\n"; + else + res << vtxOut << " " << typeStr << " v_" << val.valueName << ";\n"; + } + } + res << "\n"; + + // Main function. + // - gl_Position = dEQP_Position; + // - for each input: write attribute directly to varying + res << "void main()\n"; + res << "{\n"; + res << " gl_Position = dEQP_Position;\n"; + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + if (val.storageType == ShaderCase::Value::STORAGE_INPUT) + { + const string& name = val.valueName; + if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT) + res << " " << name << " = a_" << name << ";\n"; + else + res << " v_" << name << " = a_" << name << ";\n"; + } + } + + res << "}\n"; + return res.str(); +} + +static void genCompareFunctions (ostringstream& stream, const ShaderCase::ValueBlock& valueBlock, bool useFloatTypes) +{ + bool cmpTypeFound[TYPE_LAST]; + for (int i = 0; i < TYPE_LAST; i++) + cmpTypeFound[i] = false; + + for (int valueNdx = 0; valueNdx < (int)valueBlock.values.size(); valueNdx++) + { + const ShaderCase::Value& val = valueBlock.values[valueNdx]; + if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT) + cmpTypeFound[(int)val.dataType] = true; + } + + if (useFloatTypes) + { + if (cmpTypeFound[TYPE_BOOL]) stream << "bool isOk (float a, bool b) { return ((a > 0.5) == b); }\n"; + if (cmpTypeFound[TYPE_BOOL_VEC2]) stream << "bool isOk (vec2 a, bvec2 b) { return (greaterThan(a, vec2(0.5)) == b); }\n"; + if (cmpTypeFound[TYPE_BOOL_VEC3]) stream << "bool isOk (vec3 a, bvec3 b) { return (greaterThan(a, vec3(0.5)) == b); }\n"; + if (cmpTypeFound[TYPE_BOOL_VEC4]) stream << "bool isOk (vec4 a, bvec4 b) { return (greaterThan(a, vec4(0.5)) == b); }\n"; + if (cmpTypeFound[TYPE_INT]) stream << "bool isOk (float a, int b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1)); }\n"; + if (cmpTypeFound[TYPE_INT_VEC2]) stream << "bool isOk (vec2 a, ivec2 b) { return (ivec2(floor(a + 0.5)) == b); }\n"; + if (cmpTypeFound[TYPE_INT_VEC3]) stream << "bool isOk (vec3 a, ivec3 b) { return (ivec3(floor(a + 0.5)) == b); }\n"; + if (cmpTypeFound[TYPE_INT_VEC4]) stream << "bool isOk (vec4 a, ivec4 b) { return (ivec4(floor(a + 0.5)) == b); }\n"; + if (cmpTypeFound[TYPE_UINT]) stream << "bool isOk (float a, uint b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1u)); }\n"; + if (cmpTypeFound[TYPE_UINT_VEC2]) stream << "bool isOk (vec2 a, uvec2 b) { return (uvec2(floor(a + 0.5)) == b); }\n"; + if (cmpTypeFound[TYPE_UINT_VEC3]) stream << "bool isOk (vec3 a, uvec3 b) { return (uvec3(floor(a + 0.5)) == b); }\n"; + if (cmpTypeFound[TYPE_UINT_VEC4]) stream << "bool isOk (vec4 a, uvec4 b) { return (uvec4(floor(a + 0.5)) == b); }\n"; + } + else + { + if (cmpTypeFound[TYPE_BOOL]) stream << "bool isOk (bool a, bool b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_BOOL_VEC2]) stream << "bool isOk (bvec2 a, bvec2 b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_BOOL_VEC3]) stream << "bool isOk (bvec3 a, bvec3 b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_BOOL_VEC4]) stream << "bool isOk (bvec4 a, bvec4 b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_INT]) stream << "bool isOk (int a, int b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_INT_VEC2]) stream << "bool isOk (ivec2 a, ivec2 b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_INT_VEC3]) stream << "bool isOk (ivec3 a, ivec3 b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_INT_VEC4]) stream << "bool isOk (ivec4 a, ivec4 b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_UINT]) stream << "bool isOk (uint a, uint b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_UINT_VEC2]) stream << "bool isOk (uvec2 a, uvec2 b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_UINT_VEC3]) stream << "bool isOk (uvec3 a, uvec3 b) { return (a == b); }\n"; + if (cmpTypeFound[TYPE_UINT_VEC4]) stream << "bool isOk (uvec4 a, uvec4 b) { return (a == b); }\n"; + } + + if (cmpTypeFound[TYPE_FLOAT]) stream << "bool isOk (float a, float b, float eps) { return (abs(a-b) <= (eps*abs(b) + eps)); }\n"; + if (cmpTypeFound[TYPE_FLOAT_VEC2]) stream << "bool isOk (vec2 a, vec2 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n"; + if (cmpTypeFound[TYPE_FLOAT_VEC3]) stream << "bool isOk (vec3 a, vec3 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n"; + if (cmpTypeFound[TYPE_FLOAT_VEC4]) stream << "bool isOk (vec4 a, vec4 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n"; + + if (cmpTypeFound[TYPE_FLOAT_MAT2]) stream << "bool isOk (mat2 a, mat2 b, float eps) { vec2 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec2(eps))); }\n"; + if (cmpTypeFound[TYPE_FLOAT_MAT2X3]) stream << "bool isOk (mat2x3 a, mat2x3 b, float eps) { vec3 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec3(eps))); }\n"; + if (cmpTypeFound[TYPE_FLOAT_MAT2X4]) stream << "bool isOk (mat2x4 a, mat2x4 b, float eps) { vec4 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec4(eps))); }\n"; + if (cmpTypeFound[TYPE_FLOAT_MAT3X2]) stream << "bool isOk (mat3x2 a, mat3x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec2(eps))); }\n"; + if (cmpTypeFound[TYPE_FLOAT_MAT3]) stream << "bool isOk (mat3 a, mat3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec3(eps))); }\n"; + if (cmpTypeFound[TYPE_FLOAT_MAT3X4]) stream << "bool isOk (mat3x4 a, mat3x4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec4(eps))); }\n"; + if (cmpTypeFound[TYPE_FLOAT_MAT4X2]) stream << "bool isOk (mat4x2 a, mat4x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec2(eps))); }\n"; + if (cmpTypeFound[TYPE_FLOAT_MAT4X3]) stream << "bool isOk (mat4x3 a, mat4x3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec3(eps))); }\n"; + if (cmpTypeFound[TYPE_FLOAT_MAT4]) stream << "bool isOk (mat4 a, mat4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec4(eps))); }\n"; +} + +static void genCompareOp (ostringstream& output, const char* dstVec4Var, const ShaderCase::ValueBlock& valueBlock, const char* nonFloatNamePrefix, const char* checkVarName) +{ + bool isFirstOutput = true; + + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + const char* valueName = val.valueName.c_str(); + + if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT) + { + // Check if we're only interested in one variable (then skip if not the right one). + if (checkVarName && !deStringEqual(valueName, checkVarName)) + continue; + + // Prefix. + if (isFirstOutput) + { + output << "bool RES = "; + isFirstOutput = false; + } + else + output << "RES = RES && "; + + // Generate actual comparison. + if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT) + output << "isOk(" << valueName << ", ref_" << valueName << ", 0.05);\n"; + else + output << "isOk(" << nonFloatNamePrefix << valueName << ", ref_" << valueName << ");\n"; + } + // \note Uniforms are already declared in shader. + } + + if (isFirstOutput) + output << dstVec4Var << " = vec4(1.0);\n"; // \todo [petri] Should we give warning if not expect-failure case? + else + output << dstVec4Var << " = vec4(RES, RES, RES, 1.0);\n"; +} + +string ShaderCase::genFragmentShader (const ValueBlock& valueBlock) const +{ + ostringstream shader; + const bool usesInout = usesShaderInoutQualifiers(m_targetVersion); + const bool customColorOut = usesInout; + const char* fragIn = usesInout ? "in" : "varying"; + const char* prec = supportsFragmentHighp(m_targetVersion) ? "highp" : "mediump"; + + shader << glu::getGLSLVersionDeclaration(m_targetVersion) << "\n"; + + shader << "precision " << prec << " float;\n"; + shader << "precision " << prec << " int;\n"; + shader << "\n"; + + if (customColorOut) + { + shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; + shader << "\n"; + } + + genCompareFunctions(shader, valueBlock, true); + shader << "\n"; + + // Declarations (varying, reference for each output). + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + DataType floatType = getDataTypeFloatScalars(val.dataType); + const char* floatTypeStr = getDataTypeName(floatType); + const char* refTypeStr = getDataTypeName(val.dataType); + + if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT) + { + if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT) + shader << fragIn << " " << floatTypeStr << " " << val.valueName << ";\n"; + else + shader << fragIn << " " << floatTypeStr << " v_" << val.valueName << ";\n"; + + shader << "uniform " << refTypeStr << " ref_" << val.valueName << ";\n"; + } + } + + shader << "\n"; + shader << "void main()\n"; + shader << "{\n"; + + shader << " "; + genCompareOp(shader, customColorOut ? "dEQP_FragColor" : "gl_FragColor", valueBlock, "v_", DE_NULL); + + shader << "}\n"; + return shader.str(); +} + +// Specialize a shader for the vertex shader test case. +string ShaderCase::specializeVertexShader (const char* src, const ValueBlock& valueBlock) const +{ + ostringstream decl; + ostringstream setup; + ostringstream output; + const bool usesInout = usesShaderInoutQualifiers(m_targetVersion); + const char* vtxIn = usesInout ? "in" : "attribute"; + const char* vtxOut = usesInout ? "out" : "varying"; + + // generated from "both" case + DE_ASSERT(m_caseType == CASETYPE_VERTEX_ONLY); + + // Output (write out position). + output << "gl_Position = dEQP_Position;\n"; + + // Declarations (position + attribute for each input, varying for each output). + decl << vtxIn << " highp vec4 dEQP_Position;\n"; + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + const char* valueName = val.valueName.c_str(); + DataType floatType = getDataTypeFloatScalars(val.dataType); + const char* floatTypeStr = getDataTypeName(floatType); + const char* refTypeStr = getDataTypeName(val.dataType); + + if (val.storageType == ShaderCase::Value::STORAGE_INPUT) + { + if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT) + { + decl << vtxIn << " " << floatTypeStr << " " << valueName << ";\n"; + } + else + { + decl << vtxIn << " " << floatTypeStr << " a_" << valueName << ";\n"; + setup << refTypeStr << " " << valueName << " = " << refTypeStr << "(a_" << valueName << ");\n"; + } + } + else if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT) + { + if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT) + decl << vtxOut << " " << floatTypeStr << " " << valueName << ";\n"; + else + { + decl << vtxOut << " " << floatTypeStr << " v_" << valueName << ";\n"; + decl << refTypeStr << " " << valueName << ";\n"; + + output << "v_" << valueName << " = " << floatTypeStr << "(" << valueName << ");\n"; + } + } + } + + // Shader specialization. + map params; + params.insert(pair("DECLARATIONS", decl.str())); + params.insert(pair("SETUP", setup.str())); + params.insert(pair("OUTPUT", output.str())); + params.insert(pair("POSITION_FRAG_COLOR", "gl_Position")); + + StringTemplate tmpl (src); + const string baseSrc = tmpl.specialize(params); + const string withExt = injectExtensionRequirements(baseSrc, SHADERTYPE_VERTEX, m_programs[0].spec.requirements); + + return withExt; +} + +// Specialize a shader for the fragment shader test case. +string ShaderCase::specializeFragmentShader (const char* src, const ValueBlock& valueBlock) const +{ + ostringstream decl; + ostringstream setup; + ostringstream output; + + const bool usesInout = usesShaderInoutQualifiers(m_targetVersion); + const bool customColorOut = usesInout; + const char* fragIn = usesInout ? "in" : "varying"; + const char* fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor"; + + // generated from "both" case + DE_ASSERT(m_caseType == CASETYPE_FRAGMENT_ONLY); + + genCompareFunctions(decl, valueBlock, false); + genCompareOp(output, fragColor, valueBlock, "", DE_NULL); + + if (customColorOut) + decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; + + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + const char* valueName = val.valueName.c_str(); + DataType floatType = getDataTypeFloatScalars(val.dataType); + const char* floatTypeStr = getDataTypeName(floatType); + const char* refTypeStr = getDataTypeName(val.dataType); + + if (val.storageType == ShaderCase::Value::STORAGE_INPUT) + { + if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT) + decl << fragIn << " " << floatTypeStr << " " << valueName << ";\n"; + else + { + decl << fragIn << " " << floatTypeStr << " v_" << valueName << ";\n"; + std::string offset = isDataTypeIntOrIVec(val.dataType) ? " * 1.0025" : ""; // \todo [petri] bit of a hack to avoid errors in chop() due to varying interpolation + setup << refTypeStr << " " << valueName << " = " << refTypeStr << "(v_" << valueName << offset << ");\n"; + } + } + else if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT) + { + decl << "uniform " << refTypeStr << " ref_" << valueName << ";\n"; + decl << refTypeStr << " " << valueName << ";\n"; + } + } + + /* \todo [2010-04-01 petri] Check all outputs. */ + + // Shader specialization. + map params; + params.insert(pair("DECLARATIONS", decl.str())); + params.insert(pair("SETUP", setup.str())); + params.insert(pair("OUTPUT", output.str())); + params.insert(pair("POSITION_FRAG_COLOR", fragColor)); + + StringTemplate tmpl (src); + const string baseSrc = tmpl.specialize(params); + const string withExt = injectExtensionRequirements(baseSrc, SHADERTYPE_FRAGMENT, m_programs[0].spec.requirements); + + return withExt; +} + +static map generateVertexSpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock) +{ + const bool usesInout = usesShaderInoutQualifiers(targetVersion); + const char* vtxIn = usesInout ? "in" : "attribute"; + ostringstream decl; + ostringstream setup; + map params; + + DE_UNREF(renderCtx); + + decl << vtxIn << " highp vec4 dEQP_Position;\n"; + + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + const char* typeStr = getDataTypeName(val.dataType); + + if (val.storageType == ShaderCase::Value::STORAGE_INPUT) + { + if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT) + { + decl << vtxIn << " " << typeStr << " " << val.valueName << ";\n"; + } + else + { + DataType floatType = getDataTypeFloatScalars(val.dataType); + const char* floatTypeStr = getDataTypeName(floatType); + + decl << vtxIn << " " << floatTypeStr << " a_" << val.valueName << ";\n"; + setup << typeStr << " " << val.valueName << " = " << typeStr << "(a_" << val.valueName << ");\n"; + } + } + else if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM && + val.valueName.find('.') == string::npos) + decl << "uniform " << typeStr << " " << val.valueName << ";\n"; + } + + params.insert(pair("VERTEX_DECLARATIONS", decl.str())); + params.insert(pair("VERTEX_SETUP", setup.str())); + params.insert(pair("VERTEX_OUTPUT", string("gl_Position = dEQP_Position;\n"))); + return params; +} + +static map generateFragmentSpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock) +{ + const bool usesInout = usesShaderInoutQualifiers(targetVersion); + const bool customColorOut = usesInout; + const char* fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor"; + ostringstream decl; + ostringstream output; + map params; + + DE_UNREF(renderCtx); + + genCompareFunctions(decl, valueBlock, false); + genCompareOp(output, fragColor, valueBlock, "", DE_NULL); + + if (customColorOut) + decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; + + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + const char* valueName = val.valueName.c_str(); + const char* refTypeStr = getDataTypeName(val.dataType); + + if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT) + { + decl << "uniform " << refTypeStr << " ref_" << valueName << ";\n"; + decl << refTypeStr << " " << valueName << ";\n"; + } + else if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM && + val.valueName.find('.') == string::npos) + { + decl << "uniform " << refTypeStr << " " << valueName << ";\n"; + } + } + + params.insert(pair("FRAGMENT_DECLARATIONS", decl.str())); + params.insert(pair("FRAGMENT_OUTPUT", output.str())); + params.insert(pair("FRAG_COLOR", fragColor)); + return params; +} + +static map generateGeometrySpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock) +{ + ostringstream decl; + map params; + + DE_UNREF(renderCtx); + DE_UNREF(targetVersion); + + decl << "layout (triangles) in;\n"; + decl << "layout (triangle_strip, max_vertices=3) out;\n"; + decl << "\n"; + + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + const char* valueName = val.valueName.c_str(); + const char* refTypeStr = getDataTypeName(val.dataType); + + if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM && + val.valueName.find('.') == string::npos) + { + decl << "uniform " << refTypeStr << " " << valueName << ";\n"; + } + } + + params.insert(pair("GEOMETRY_DECLARATIONS", decl.str())); + return params; +} + +static map generateTessControlSpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock) +{ + ostringstream decl; + ostringstream output; + map params; + + DE_UNREF(targetVersion); + + decl << "layout (vertices=3) out;\n"; + decl << "\n"; + + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + const char* valueName = val.valueName.c_str(); + const char* refTypeStr = getDataTypeName(val.dataType); + + if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM && + val.valueName.find('.') == string::npos) + { + decl << "uniform " << refTypeStr << " " << valueName << ";\n"; + } + } + + output << "gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" + "gl_TessLevelInner[0] = 2.0;\n" + "gl_TessLevelInner[1] = 2.0;\n" + "gl_TessLevelOuter[0] = 2.0;\n" + "gl_TessLevelOuter[1] = 2.0;\n" + "gl_TessLevelOuter[2] = 2.0;\n" + "gl_TessLevelOuter[3] = 2.0;"; + + params.insert(pair("TESSELLATION_CONTROL_DECLARATIONS", decl.str())); + params.insert(pair("TESSELLATION_CONTROL_OUTPUT", output.str())); + params.insert(pair("GL_MAX_PATCH_VERTICES", de::toString(queryGLInt(renderCtx, GL_MAX_PATCH_VERTICES)))); + return params; +} + +static map generateTessEvalSpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock) +{ + ostringstream decl; + ostringstream output; + map params; + + DE_UNREF(targetVersion); + + decl << "layout (triangles) in;\n"; + decl << "\n"; + + for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++) + { + const ShaderCase::Value& val = valueBlock.values[ndx]; + const char* valueName = val.valueName.c_str(); + const char* refTypeStr = getDataTypeName(val.dataType); + + if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM && + val.valueName.find('.') == string::npos) + { + decl << "uniform " << refTypeStr << " " << valueName << ";\n"; + } + } + + output << "gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + gl_TessCoord[2] * gl_in[2].gl_Position;\n"; + + params.insert(pair("TESSELLATION_EVALUATION_DECLARATIONS", decl.str())); + params.insert(pair("TESSELLATION_EVALUATION_OUTPUT", output.str())); + params.insert(pair("GL_MAX_PATCH_VERTICES", de::toString(queryGLInt(renderCtx, GL_MAX_PATCH_VERTICES)))); + return params; +} + +static void specializeShaders (const glu::RenderContext& renderCtx, + glu::ProgramSources& dst, + glu::ShaderType shaderType, + const std::vector& sources, + const ShaderCase::ValueBlock& valueBlock, + glu::GLSLVersion targetVersion, + const std::vector& requirements, + std::map (*specializationGenerator)(const glu::RenderContext&, glu::GLSLVersion, const ShaderCase::ValueBlock&)) +{ + if (!sources.empty()) + { + const std::map specializationParams = specializationGenerator(renderCtx, targetVersion, valueBlock); + + for (int ndx = 0; ndx < (int)sources.size(); ++ndx) + { + const StringTemplate tmpl (sources[ndx]); + const std::string baseGLSLCode = tmpl.specialize(specializationParams); + const std::string glslSource = injectExtensionRequirements(baseGLSLCode, shaderType, requirements); + + dst << glu::ShaderSource(shaderType, glslSource); + } + } +} + +void ShaderCase::specializeVertexShaders (glu::ProgramSources& dst, const std::vector& sources, const ValueBlock& valueBlock, const std::vector& requirements) const +{ + specializeShaders(m_renderCtx, dst, glu::SHADERTYPE_VERTEX, sources, valueBlock, m_targetVersion, requirements, generateVertexSpecialization); +} + +void ShaderCase::specializeFragmentShaders (glu::ProgramSources& dst, const std::vector& sources, const ValueBlock& valueBlock, const std::vector& requirements) const +{ + specializeShaders(m_renderCtx, dst, glu::SHADERTYPE_FRAGMENT, sources, valueBlock, m_targetVersion, requirements, generateFragmentSpecialization); +} + +void ShaderCase::specializeGeometryShaders (glu::ProgramSources& dst, const std::vector& sources, const ValueBlock& valueBlock, const std::vector& requirements) const +{ + specializeShaders(m_renderCtx, dst, glu::SHADERTYPE_GEOMETRY, sources, valueBlock, m_targetVersion, requirements, generateGeometrySpecialization); +} + +void ShaderCase::specializeTessControlShaders (glu::ProgramSources& dst, const std::vector& sources, const ValueBlock& valueBlock, const std::vector& requirements) const +{ + specializeShaders(m_renderCtx, dst, glu::SHADERTYPE_TESSELLATION_CONTROL, sources, valueBlock, m_targetVersion, requirements, generateTessControlSpecialization); +} + +void ShaderCase::specializeTessEvalShaders (glu::ProgramSources& dst, const std::vector& sources, const ValueBlock& valueBlock, const std::vector& requirements) const +{ + specializeShaders(m_renderCtx, dst, glu::SHADERTYPE_TESSELLATION_EVALUATION, sources, valueBlock, m_targetVersion, requirements, generateTessEvalSpecialization); +} + +void ShaderCase::dumpValues (const ValueBlock& valueBlock, int arrayNdx) +{ + int numValues = (int)valueBlock.values.size(); + for (int valNdx = 0; valNdx < numValues; valNdx++) + { + const ShaderCase::Value& val = valueBlock.values[valNdx]; + const char* valueName = val.valueName.c_str(); + DataType dataType = val.dataType; + int scalarSize = getDataTypeScalarSize(val.dataType); + ostringstream result; + + result << " "; + if (val.storageType == Value::STORAGE_INPUT) + result << "input "; + else if (val.storageType == Value::STORAGE_UNIFORM) + result << "uniform "; + else if (val.storageType == Value::STORAGE_OUTPUT) + result << "expected "; + + result << getDataTypeName(dataType) << " " << valueName << ":"; + + if (isDataTypeScalar(dataType)) + result << " "; + if (isDataTypeVector(dataType)) + result << " [ "; + else if (isDataTypeMatrix(dataType)) + result << "\n"; + + if (isDataTypeScalarOrVector(dataType)) + { + for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++) + { + int elemNdx = (val.arrayLength == 1) ? 0 : arrayNdx; + const Value::Element& e = val.elements[elemNdx*scalarSize + scalarNdx]; + result << ((scalarNdx != 0) ? ", " : ""); + + if (isDataTypeFloatOrVec(dataType)) + result << e.float32; + else if (isDataTypeIntOrIVec(dataType)) + result << e.int32; + else if (isDataTypeUintOrUVec(dataType)) + result << (deUint32)e.int32; + else if (isDataTypeBoolOrBVec(dataType)) + result << (e.bool32 ? "true" : "false"); + } + } + else if (isDataTypeMatrix(dataType)) + { + int numRows = getDataTypeMatrixNumRows(dataType); + int numCols = getDataTypeMatrixNumColumns(dataType); + for (int rowNdx = 0; rowNdx < numRows; rowNdx++) + { + result << " [ "; + for (int colNdx = 0; colNdx < numCols; colNdx++) + { + int elemNdx = (val.arrayLength == 1) ? 0 : arrayNdx; + float v = val.elements[elemNdx*scalarSize + rowNdx*numCols + colNdx].float32; + result << ((colNdx==0) ? "" : ", ") << v; + } + result << " ]\n"; + } + } + + if (isDataTypeScalar(dataType)) + result << "\n"; + else if (isDataTypeVector(dataType)) + result << " ]\n"; + + m_testCtx.getLog() << TestLog::Message << result.str() << TestLog::EndMessage; + } +} + +} // sl } // gls } // deqp diff --git a/modules/glshared/glsShaderLibraryCase.hpp b/modules/glshared/glsShaderLibraryCase.hpp index a1ae5a6..162ea0f 100644 --- a/modules/glshared/glsShaderLibraryCase.hpp +++ b/modules/glshared/glsShaderLibraryCase.hpp @@ -24,48 +24,214 @@ *//*--------------------------------------------------------------------*/ #include "gluDefs.hpp" -#include "gluShaderLibrary.hpp" +#include "gluShaderUtil.hpp" +#include "gluRenderContext.hpp" +#include "gluShaderProgram.hpp" #include "tcuTestCase.hpp" +#include "tcuSurface.hpp" -namespace glu -{ -class RenderContext; -class ContextInfo; -} +#include +#include namespace deqp { namespace gls { +namespace sl +{ // ShaderCase node. -class ShaderLibraryCase : public tcu::TestCase +class ShaderCase : public tcu::TestCase { public: + enum CaseType + { + CASETYPE_COMPLETE = 0, //!< Has all shaders specified separately. + CASETYPE_VERTEX_ONLY, //!< "Both" case, vertex shader sub case. + CASETYPE_FRAGMENT_ONLY, //!< "Both" case, fragment shader sub case. + + CASETYPE_LAST + }; + + enum ExpectResult + { + EXPECT_PASS = 0, + EXPECT_COMPILE_FAIL, + EXPECT_LINK_FAIL, + EXPECT_COMPILE_LINK_FAIL, + EXPECT_VALIDATION_FAIL, + EXPECT_BUILD_SUCCESSFUL, + + EXPECT_LAST + }; + + struct Value + { + enum StorageType + { + STORAGE_UNIFORM, + STORAGE_INPUT, + STORAGE_OUTPUT, + + STORAGE_LAST + }; + + /* \todo [2010-03-31 petri] Replace with another vector to allow a) arrays, b) compact representation */ + union Element + { + float float32; + deInt32 int32; + deInt32 bool32; + }; + + StorageType storageType; + std::string valueName; + glu::DataType dataType; + int arrayLength; // Number of elements in array (currently always 1). + std::vector elements; // Scalar values (length dataType.scalarSize * arrayLength). + }; + + struct ValueBlock + { + ValueBlock (void); + + int arrayLength; // Combined array length of each value (lengths must be same, or one). + std::vector values; + }; + + class CaseRequirement + { + public: + enum RequirementType + { + REQUIREMENTTYPE_EXTENSION = 0, + REQUIREMENTTYPE_IMPLEMENTATION_LIMIT, + REQUIREMENTTYPE_FULL_GLSL_ES_100_SPEC, //!< Full support (as opposed to limited as specified for GLES 2.0 (See GLSL Appendix A)) cannot be queried + + REQUIREMENTTYPE_LAST + }; + + CaseRequirement (void); + + static CaseRequirement createAnyExtensionRequirement (const std::vector& requirements, deUint32 effectiveShaderStageFlags); + static CaseRequirement createLimitRequirement (deUint32 enumName, int ref); + static CaseRequirement createFullGLSLES100SpecificationRequirement (void); + void checkRequirements (glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo); + + RequirementType getType (void) const { return m_type; }; + std::string getSupportedExtension (void) const { DE_ASSERT(m_type == REQUIREMENTTYPE_EXTENSION); DE_ASSERT(m_supportedExtensionNdx >= 0); return m_extensions[m_supportedExtensionNdx]; } + deUint32 getAffectedExtensionStageFlags (void) const { DE_ASSERT(m_type == REQUIREMENTTYPE_EXTENSION); return m_effectiveShaderStageFlags; } + + private: + RequirementType m_type; + + // REQUIREMENTTYPE_EXTENSION: + std::vector m_extensions; + int m_supportedExtensionNdx; + deUint32 m_effectiveShaderStageFlags; + + // REQUIREMENTTYPE_IMPLEMENTATION_LIMIT: + deUint32 m_enumName; + int m_referenceValue; + }; + + struct ShaderCaseSpecification + { + ShaderCaseSpecification (void); + + static ShaderCaseSpecification generateSharedSourceVertexCase (ExpectResult expectResult_, glu::GLSLVersion targetVersion_, const std::vector& values, const std::string& sharedSource); + static ShaderCaseSpecification generateSharedSourceFragmentCase (ExpectResult expectResult_, glu::GLSLVersion targetVersion_, const std::vector& values, const std::string& sharedSource); + + ExpectResult expectResult; + glu::GLSLVersion targetVersion; + CaseType caseType; + std::vector requirements; + std::vector valueBlocks; + std::vector vertexSources; + std::vector fragmentSources; + std::vector tessCtrlSources; + std::vector tessEvalSources; + std::vector geometrySources; + }; + + struct PipelineProgram + { + deUint32 activeStageBits; + std::vector requirements; + std::vector vertexSources; + std::vector fragmentSources; + std::vector tessCtrlSources; + std::vector tessEvalSources; + std::vector geometrySources; + }; + + struct PipelineCaseSpecification + { + ExpectResult expectResult; + glu::GLSLVersion targetVersion; + CaseType caseType; + std::vector valueBlocks; + std::vector programs; + }; + // Methods. - ShaderLibraryCase (tcu::TestContext& testCtx, - glu::RenderContext& renderCtx, - const glu::ContextInfo& contextInfo, - const char* caseName, - const char* description, - const glu::sl::ShaderCaseSpecification& specification); - virtual ~ShaderLibraryCase (void); + ShaderCase (tcu::TestContext& testCtx, + glu::RenderContext& renderCtx, + const glu::ContextInfo& contextInfo, + const char* caseName, + const char* description, + const ShaderCaseSpecification& specification); + ShaderCase (tcu::TestContext& testCtx, + glu::RenderContext& renderCtx, + const glu::ContextInfo& contextInfo, + const char* caseName, + const char* description, + const PipelineCaseSpecification& specification); + virtual ~ShaderCase (void); private: - void init (void); - bool execute (void); - IterateResult iterate (void); + void init (void); + bool execute (void); + IterateResult iterate (void); + + ShaderCase (const ShaderCase&); // not allowed! + ShaderCase& operator= (const ShaderCase&); // not allowed! + + std::string genVertexShader (const ValueBlock& valueBlock) const; + std::string genFragmentShader (const ValueBlock& valueBlock) const; + std::string specializeVertexShader (const char* src, const ValueBlock& valueBlock) const; + std::string specializeFragmentShader (const char* src, const ValueBlock& valueBlock) const; + void specializeVertexShaders (glu::ProgramSources& dst, const std::vector& sources, const ValueBlock& valueBlock, const std::vector& requirements) const; + void specializeFragmentShaders (glu::ProgramSources& dst, const std::vector& sources, const ValueBlock& valueBlock, const std::vector& requirements) const; + void specializeGeometryShaders (glu::ProgramSources& dst, const std::vector& sources, const ValueBlock& valueBlock, const std::vector& requirements) const; + void specializeTessControlShaders (glu::ProgramSources& dst, const std::vector& sources, const ValueBlock& valueBlock, const std::vector& requirements) const; + void specializeTessEvalShaders (glu::ProgramSources& dst, const std::vector& sources, const ValueBlock& valueBlock, const std::vector& requirements) const; + bool isTessellationPresent (void) const; + bool anyProgramRequiresFullGLSLES100Specification (void) const; + + void dumpValues (const ValueBlock& valueBlock, int arrayNdx); + + bool checkPixels (tcu::Surface& surface, int minX, int maxX, int minY, int maxY); - ShaderLibraryCase (const ShaderLibraryCase&); // not allowed! - ShaderLibraryCase& operator= (const ShaderLibraryCase&); // not allowed! + struct ProgramObject + { + glu::ProgramSources programSources; + PipelineProgram spec; + }; // Member variables. - glu::RenderContext& m_renderCtx; - const glu::ContextInfo& m_contextInfo; - const glu::sl::ShaderCaseSpecification m_spec; + glu::RenderContext& m_renderCtx; + const glu::ContextInfo& m_contextInfo; + const CaseType m_caseType; + const ExpectResult m_expectResult; + const glu::GLSLVersion m_targetVersion; + const bool m_separatePrograms; + std::vector m_valueBlocks; + std::vector m_programs; }; +} // sl } // gls } // deqp