framework/opengl/gluProgramInterfaceQuery.cpp \
framework/opengl/gluRenderConfig.cpp \
framework/opengl/gluRenderContext.cpp \
+ framework/opengl/gluShaderLibrary.cpp \
framework/opengl/gluShaderProgram.cpp \
framework/opengl/gluShaderUtil.cpp \
framework/opengl/gluStateReset.cpp \
case float_input
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 ];
- }
- 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 ];
+ 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 ];
}
both ""
version 300 es
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 ];
- }
- 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 ];
+ 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 ];
}
both ""
gluDummyRenderContext.hpp
gluPlatform.cpp
gluPlatform.hpp
+ gluShaderLibrary.cpp
+ gluShaderLibrary.hpp
)
set(GLUTIL_LIBS
--- /dev/null
+/*-------------------------------------------------------------------------
+ * 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 <sstream>
+#include <map>
+#include <cstdlib>
+
+#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<Value>& 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<string>& 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<tcu::TestNode*> 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<tcu::TestNode*>& shaderNodeList);
+ void parseShaderGroup (vector<tcu::TestNode*>& shaderNodeList);
+ void parseImport (vector<tcu::TestNode*>& shaderNodeList);
+
+ const tcu::Archive& m_archive;
+ const string m_filename;
+ ShaderCaseFactory* const m_caseFactory;
+
+ UniquePtr<tcu::Resource> m_resource;
+ vector<char> 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 = "<EOF>";
+ }
+ else if (isAlpha(*m_curPtr))
+ {
+ struct Named
+ {
+ const char* str;
+ Token token;
+ };
+
+ static const Named s_named[] =
+ {
+ { "true", TOKEN_TRUE },
+ { "false", TOKEN_FALSE },
+ { "desc", TOKEN_DESC },
+ { "expect", TOKEN_EXPECT },
+ { "group", TOKEN_GROUP },
+ { "case", TOKEN_CASE },
+ { "end", TOKEN_END },
+ { "values", TOKEN_VALUES },
+ { "both", TOKEN_BOTH },
+ { "vertex", TOKEN_VERTEX },
+ { "fragment", TOKEN_FRAGMENT },
+ { "uniform", TOKEN_UNIFORM },
+ { "input", TOKEN_INPUT },
+ { "output", TOKEN_OUTPUT },
+ { "float", TOKEN_FLOAT },
+ { "vec2", TOKEN_FLOAT_VEC2 },
+ { "vec3", TOKEN_FLOAT_VEC3 },
+ { "vec4", TOKEN_FLOAT_VEC4 },
+ { "mat2", TOKEN_FLOAT_MAT2 },
+ { "mat2x3", TOKEN_FLOAT_MAT2X3 },
+ { "mat2x4", TOKEN_FLOAT_MAT2X4 },
+ { "mat3x2", TOKEN_FLOAT_MAT3X2 },
+ { "mat3", TOKEN_FLOAT_MAT3 },
+ { "mat3x4", TOKEN_FLOAT_MAT3X4 },
+ { "mat4x2", TOKEN_FLOAT_MAT4X2 },
+ { "mat4x3", TOKEN_FLOAT_MAT4X3 },
+ { "mat4", TOKEN_FLOAT_MAT4 },
+ { "int", TOKEN_INT },
+ { "ivec2", TOKEN_INT_VEC2 },
+ { "ivec3", TOKEN_INT_VEC3 },
+ { "ivec4", TOKEN_INT_VEC4 },
+ { "uint", TOKEN_UINT },
+ { "uvec2", TOKEN_UINT_VEC2 },
+ { "uvec3", TOKEN_UINT_VEC3 },
+ { "uvec4", TOKEN_UINT_VEC4 },
+ { "bool", TOKEN_BOOL },
+ { "bvec2", TOKEN_BOOL_VEC2 },
+ { "bvec3", TOKEN_BOOL_VEC3 },
+ { "bvec4", TOKEN_BOOL_VEC4 },
+ { "version", TOKEN_VERSION },
+ { "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 "<invalid>";
+ case TOKEN_EOF: return "<eof>";
+ case TOKEN_STRING: return "<string>";
+ case TOKEN_SHADER_SOURCE: return "source";
+
+ case TOKEN_INT_LITERAL: return "<int>";
+ case TOKEN_FLOAT_LITERAL: return "<float>";
+
+ // identifiers
+ case TOKEN_IDENTIFIER: return "<identifier>";
+ case TOKEN_TRUE: return "true";
+ case TOKEN_FALSE: return "false";
+ case TOKEN_DESC: return "desc";
+ case TOKEN_EXPECT: return "expect";
+ case TOKEN_GROUP: return "group";
+ case TOKEN_CASE: return "case";
+ case TOKEN_END: return "end";
+ case TOKEN_VALUES: return "values";
+ case TOKEN_BOTH: return "both";
+ case TOKEN_VERTEX: return "vertex";
+ case TOKEN_FRAGMENT: return "fragment";
+ case TOKEN_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 "<unknown>";
+ }
+}
+
+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<Value>* 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<std::string> 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<tcu::TestNode*>& shaderNodeList)
+{
+ // Parse 'case'.
+ PARSE_DBG((" parseShaderCase()\n"));
+ advanceToken(TOKEN_CASE);
+
+ // Parse case name.
+ string caseName = m_curTokenStr;
+ advanceToken(); // \note [pyry] All token types are allowed here.
+
+ // \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<string> vertexSources;
+ vector<string> fragmentSources;
+ vector<string> tessellationCtrlSources;
+ vector<string> tessellationEvalSources;
+ vector<string> geometrySources;
+ ValueBlock valueBlock;
+ bool valueBlockSeen = false;
+ vector<CaseRequirement> requirements;
+ vector<ProgramSpecification> 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<RequiredCapability> requiredCaps;
+ vector<RequiredExtension> 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<tcu::TestNode*>& shaderNodeList)
+{
+ // Parse 'case'.
+ PARSE_DBG((" parseShaderGroup()\n"));
+ advanceToken(TOKEN_GROUP);
+
+ // Parse case name.
+ string name = m_curTokenStr;
+ advanceToken(); // \note [pyry] We don't want to check token type here (for instance to allow "uniform") group.
+
+ // Parse description.
+ assumeToken(TOKEN_STRING);
+ string description = parseStringLiteral(m_curTokenStr.c_str());
+ advanceToken(TOKEN_STRING);
+
+ std::vector<tcu::TestNode*> children;
+
+ // Parse group children.
+ for (;;)
+ {
+ if (m_curToken == TOKEN_END)
+ break;
+ else if (m_curToken == TOKEN_GROUP)
+ parseShaderGroup(children);
+ else if (m_curToken == TOKEN_CASE)
+ parseShaderCase(children);
+ else 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<tcu::TestNode*>& 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<tcu::TestNode*> importedCases = subParser.parse();
+
+ // \todo [2015-08-03 pyry] Not exception safe
+ shaderNodeList.insert(shaderNodeList.end(), importedCases.begin(), importedCases.end());
+ }
+}
+
+vector<tcu::TestNode*> 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<tcu::TestNode*> nodeList;
+
+ // Parse all cases.
+ PARSE_DBG(("parse()\n"));
+ for (;;)
+ {
+ if (m_curToken == TOKEN_CASE)
+ parseShaderCase(nodeList);
+ else if (m_curToken == TOKEN_GROUP)
+ parseShaderGroup(nodeList);
+ else if (m_curToken == TOKEN_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<tcu::TestNode*> 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<Value>& 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<RequiredExtension>& 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<RequiredExtension>& 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
--- /dev/null
+#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 <string>
+#include <vector>
+
+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<Element> elements; // Scalar values (variable.varType.getScalarSize() * #values).
+};
+
+struct ValueBlock
+{
+ std::vector<Value> inputs;
+ std::vector<Value> outputs;
+ std::vector<Value> 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<std::string> 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<std::string>& 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<RequiredExtension> 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<RequiredCapability> requiredCaps;
+ bool fullGLSLES100Required;
+
+ ValueBlock values;
+ std::vector<ProgramSpecification> 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<tcu::TestNode*>& children) = 0;
+ virtual tcu::TestCase* createCase (const std::string& name, const std::string& description, const ShaderCaseSpecification& spec) = 0;
+};
+
+std::vector<tcu::TestNode*> parseFile (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory);
+
+// Specialization utilties
+
+struct ProgramSpecializationParams
+{
+ const ShaderCaseSpecification& caseSpec;
+ const std::vector<RequiredExtension> 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<RequiredExtension>& 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<RequiredExtension>& extensions, ShaderType shaderType);
+
+// Other utilities
+
+void dumpValues (tcu::TestLog& log, const ValueBlock& values, int arrayNdx);
+
+} // sl
+} // glu
+
+#endif // _GLUSHADERLIBRARY_HPP
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<tcu::TestNode*> createTests (tcu::TestContext& testContext,
glu::RenderContext& renderContext,
const glu::ContextInfo& contextInfo,
{
using std::string;
using std::vector;
- using gls::sl::ShaderCase;
+ using gls::ShaderLibraryCase;
// Needed for autogenerating shader code for increased component counts
DE_STATIC_ASSERT(glu::TYPE_FLOAT+1 == glu::TYPE_FLOAT_VEC2);
" ${OUTPUT}\n"
"}\n";
- const tcu::StringTemplate shaderTemplate (shaderTemplateSrc);
- vector<tcu::TestNode*> ret;
- vector<ShaderCase::ValueBlock> 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());
+ const tcu::StringTemplate shaderTemplate (shaderTemplateSrc);
+ vector<tcu::TestNode*> ret;
for (int caseNdx = 0; caseNdx < numCases; caseNdx++)
{
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);
const string mapped = shaderTemplate.specialize(shaderTemplateParams);
if (testStage & SHADER_VERTEX)
- ret.push_back(new ShaderCase(testContext,
- renderContext,
- contextInfo,
- (caseName + "_vertex").c_str(),
- "",
- ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase(ShaderCase::EXPECT_PASS,
- version,
- shaderOutput,
- mapped)));
+ {
+ 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));
+ }
if (testStage & SHADER_FRAGMENT)
- ret.push_back(new ShaderCase(testContext,
- renderContext,
- contextInfo,
- (caseName + "_fragment").c_str(),
- "",
- ShaderCase::ShaderCaseSpecification::generateSharedSourceFragmentCase(ShaderCase::EXPECT_PASS,
- version,
- shaderOutput,
- mapped)));
+ {
+ 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));
+ }
}
// Deal with functions that allways accept one ore more scalar parameters even when others are vectors
const string mapped = shaderTemplate.specialize(shaderTemplateParams);
if (testStage & SHADER_VERTEX)
- ret.push_back(new ShaderCase(testContext,
- renderContext,
- contextInfo,
- (scalarCaseName + "_vertex").c_str(),
- "",
- ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase(ShaderCase::EXPECT_PASS,
- version,
- shaderOutput,
- mapped)));
+ {
+ 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));
+ }
if (testStage & SHADER_FRAGMENT)
- ret.push_back(new ShaderCase(testContext,
- renderContext,
- contextInfo,
- (scalarCaseName + "_fragment").c_str(),
- "",
- ShaderCase::ShaderCaseSpecification::generateSharedSourceFragmentCase(ShaderCase::EXPECT_PASS,
- version,
- shaderOutput,
- mapped)));
+ {
+ 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));
+ }
}
}
}
#include "glsShaderLibrary.hpp"
#include "glsShaderLibraryCase.hpp"
-#include "gluShaderUtil.hpp"
-#include "tcuResource.hpp"
-#include "glwEnums.hpp"
-
-#include "deInt32.h"
-
-#include <string>
-#include <vector>
-#include <fstream>
-#include <sstream>
-
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
-
-using std::string;
-using std::vector;
-using std::ostringstream;
-
-using namespace glu;
-
-#if 0
-# define PARSE_DBG(X) printf X
-#else
-# define PARSE_DBG(X) DE_NULL_STATEMENT
-#endif
namespace deqp
{
namespace 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');
-}
-DE_INLINE deBool isCaseNameChar (char c)
+namespace
{
- return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z') || deInRange32(c, '0', '9') || (c == '_') || (c == '-') || (c == '.');
-}
-// \todo [2011-02-11 pyry] Should not depend on Context or TestContext!
-class ShaderParser
+class CaseFactory : public glu::sl::ShaderCaseFactory
{
public:
- ShaderParser (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* currentDir = DE_NULL);
- ~ShaderParser (void);
-
- vector<tcu::TestNode*> parse (const char* input);
-
-private:
- enum Token
- {
- TOKEN_INVALID = 0,
- TOKEN_EOF,
- TOKEN_STRING,
- TOKEN_SHADER_SOURCE,
-
- TOKEN_INT_LITERAL,
- TOKEN_FLOAT_LITERAL,
-
- // identifiers
- TOKEN_IDENTIFIER,
- TOKEN_TRUE,
- TOKEN_FALSE,
- TOKEN_DESC,
- TOKEN_EXPECT,
- TOKEN_GROUP,
- TOKEN_CASE,
- TOKEN_END,
- TOKEN_VALUES,
- TOKEN_BOTH,
- TOKEN_VERTEX,
- TOKEN_FRAGMENT,
- TOKEN_UNIFORM,
- TOKEN_INPUT,
- TOKEN_OUTPUT,
- TOKEN_FLOAT,
- TOKEN_FLOAT_VEC2,
- TOKEN_FLOAT_VEC3,
- TOKEN_FLOAT_VEC4,
- TOKEN_FLOAT_MAT2,
- TOKEN_FLOAT_MAT2X3,
- TOKEN_FLOAT_MAT2X4,
- TOKEN_FLOAT_MAT3X2,
- TOKEN_FLOAT_MAT3,
- TOKEN_FLOAT_MAT3X4,
- TOKEN_FLOAT_MAT4X2,
- TOKEN_FLOAT_MAT4X3,
- TOKEN_FLOAT_MAT4,
- TOKEN_INT,
- TOKEN_INT_VEC2,
- TOKEN_INT_VEC3,
- TOKEN_INT_VEC4,
- TOKEN_UINT,
- TOKEN_UINT_VEC2,
- TOKEN_UINT_VEC3,
- TOKEN_UINT_VEC4,
- TOKEN_BOOL,
- TOKEN_BOOL_VEC2,
- TOKEN_BOOL_VEC3,
- TOKEN_BOOL_VEC4,
- TOKEN_VERSION,
- 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<tcu::TestNode*>& shaderNodeList);
- void parseShaderGroup (vector<tcu::TestNode*>& shaderNodeList);
- void parseImport (vector<tcu::TestNode*>& 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++;
- }
-
- 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;
+ CaseFactory (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo)
+ : m_testCtx (testCtx)
+ , m_renderCtx (renderCtx)
+ , m_contextInfo (contextInfo)
{
- 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] != '"'))
+ tcu::TestCaseGroup* createGroup (const std::string& name, const std::string& description, const std::vector<tcu::TestNode*>& children)
{
- 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 new tcu::TestCaseGroup(m_testCtx, name.c_str(), description.c_str(), children);
}
- 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 (;;)
+ tcu::TestCase* createCase (const std::string& name, const std::string& description, const glu::sl::ShaderCaseSpecification& spec)
{
- while (isWhitespace(*m_curPtr))
- m_curPtr++;
-
- // Check for EOL comment.
- if (*m_curPtr == '#')
- {
- while (*m_curPtr && !isEOL(*m_curPtr))
- m_curPtr++;
- }
- else
- break;
+ return new ShaderLibraryCase(m_testCtx, m_renderCtx, m_contextInfo, name.c_str(), description.c_str(), spec);
}
- if (!*m_curPtr)
- {
- m_curToken = TOKEN_EOF;
- m_curTokenStr = "<EOF>";
- }
- else if (isAlpha(*m_curPtr))
- {
- struct Named
- {
- const char* str;
- Token token;
- };
-
- static const Named s_named[] =
- {
- { "true", TOKEN_TRUE },
- { "false", TOKEN_FALSE },
- { "desc", TOKEN_DESC },
- { "expect", TOKEN_EXPECT },
- { "group", TOKEN_GROUP },
- { "case", TOKEN_CASE },
- { "end", TOKEN_END },
- { "values", TOKEN_VALUES },
- { "both", TOKEN_BOTH },
- { "vertex", TOKEN_VERTEX },
- { "fragment", TOKEN_FRAGMENT },
- { "uniform", TOKEN_UNIFORM },
- { "input", TOKEN_INPUT },
- { "output", TOKEN_OUTPUT },
- { "float", TOKEN_FLOAT },
- { "vec2", TOKEN_FLOAT_VEC2 },
- { "vec3", TOKEN_FLOAT_VEC3 },
- { "vec4", TOKEN_FLOAT_VEC4 },
- { "mat2", TOKEN_FLOAT_MAT2 },
- { "mat2x3", TOKEN_FLOAT_MAT2X3 },
- { "mat2x4", TOKEN_FLOAT_MAT2X4 },
- { "mat3x2", TOKEN_FLOAT_MAT3X2 },
- { "mat3", TOKEN_FLOAT_MAT3 },
- { "mat3x4", TOKEN_FLOAT_MAT3X4 },
- { "mat4x2", TOKEN_FLOAT_MAT4X2 },
- { "mat4x3", TOKEN_FLOAT_MAT4X3 },
- { "mat4", TOKEN_FLOAT_MAT4 },
- { "int", TOKEN_INT },
- { "ivec2", TOKEN_INT_VEC2 },
- { "ivec3", TOKEN_INT_VEC3 },
- { "ivec4", TOKEN_INT_VEC4 },
- { "uint", TOKEN_UINT },
- { "uvec2", TOKEN_UINT_VEC2 },
- { "uvec3", TOKEN_UINT_VEC3 },
- { "uvec4", TOKEN_UINT_VEC4 },
- { "bool", TOKEN_BOOL },
- { "bvec2", TOKEN_BOOL_VEC2 },
- { "bvec3", TOKEN_BOOL_VEC3 },
- { "bvec4", TOKEN_BOOL_VEC4 },
- { "version", TOKEN_VERSION },
- { "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 "<invalid>";
- case TOKEN_EOF: return "<eof>";
- case TOKEN_STRING: return "<string>";
- case TOKEN_SHADER_SOURCE: return "source";
-
- case TOKEN_INT_LITERAL: return "<int>";
- case TOKEN_FLOAT_LITERAL: return "<float>";
-
- // identifiers
- case TOKEN_IDENTIFIER: return "<identifier>";
- case TOKEN_TRUE: return "true";
- case TOKEN_FALSE: return "false";
- case TOKEN_DESC: return "desc";
- case TOKEN_EXPECT: return "expect";
- case TOKEN_GROUP: return "group";
- case TOKEN_CASE: return "case";
- case TOKEN_END: return "end";
- case TOKEN_VALUES: return "values";
- case TOKEN_BOTH: return "both";
- case TOKEN_VERTEX: return "vertex";
- case TOKEN_FRAGMENT: return "fragment";
- case TOKEN_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 "<unknown>";
- }
-}
-
-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"));
-
- // Parsed results.
- ShaderCase::Value result;
-
- // Parse storage.
- if (m_curToken == TOKEN_UNIFORM)
- result.storageType = ShaderCase::Value::STORAGE_UNIFORM;
- else if (m_curToken == TOKEN_INPUT)
- result.storageType = ShaderCase::Value::STORAGE_INPUT;
- else if (m_curToken == TOKEN_OUTPUT)
- result.storageType = ShaderCase::Value::STORAGE_OUTPUT;
- else
- parseError(string("unexpected token encountered when parsing value classifier"));
- advanceToken();
-
- // Parse data type.
- result.dataType = mapDataTypeToken(m_curToken);
- if (result.dataType == TYPE_INVALID)
- parseError(string("unexpected token when parsing value data type: " + m_curTokenStr));
- advanceToken();
-
- // Parse value name.
- if (m_curToken == TOKEN_IDENTIFIER || m_curToken == TOKEN_STRING)
- {
- if (m_curToken == TOKEN_IDENTIFIER)
- result.valueName = m_curTokenStr;
- else
- result.valueName = parseStringLiteral(m_curTokenStr.c_str());
- }
- else
- parseError(string("unexpected token when parsing value name: " + m_curTokenStr));
- advanceToken();
-
- // Parse assignment operator.
- advanceToken(TOKEN_ASSIGN);
-
- // Parse actual value.
- if (m_curToken == TOKEN_LEFT_BRACKET) // value list
- {
- advanceToken(TOKEN_LEFT_BRACKET);
- result.arrayLength = 0;
-
- for (;;)
- {
- parseValueElement(result.dataType, result);
- result.arrayLength++;
-
- if (m_curToken == TOKEN_RIGHT_BRACKET)
- break;
- else if (m_curToken == TOKEN_VERTICAL_BAR)
- {
- advanceToken();
- continue;
- }
- else
- parseError(string("unexpected token in value element array: " + m_curTokenStr));
- }
-
- advanceToken(TOKEN_RIGHT_BRACKET);
- }
- else // arrays, single elements
- {
- parseValueElement(result.dataType, result);
- result.arrayLength = 1;
- }
-
- advanceToken(TOKEN_SEMI_COLON); // end of declaration
-
- valueBlock.values.push_back(result);
-}
-
-void ShaderParser::parseValueBlock (ShaderCase::ValueBlock& valueBlock)
-{
- PARSE_DBG((" parseValueBlock()\n"));
- advanceToken(TOKEN_VALUES);
- advanceToken(TOKEN_LEFT_BRACE);
-
- for (;;)
- {
- if (m_curToken == TOKEN_UNIFORM || m_curToken == TOKEN_INPUT || m_curToken == TOKEN_OUTPUT)
- parseValue(valueBlock);
- else if (m_curToken == TOKEN_RIGHT_BRACE)
- break;
- else
- parseError(string("unexpected token when parsing a value block: " + m_curTokenStr));
- }
-
- advanceToken(TOKEN_RIGHT_BRACE);
-
- // Compute combined array length of value block.
- int arrayLength = 1;
- for (int valueNdx = 0; valueNdx < (int)valueBlock.values.size(); valueNdx++)
- {
- const ShaderCase::Value& val = valueBlock.values[valueNdx];
- if (val.arrayLength > 1)
- {
- DE_ASSERT(arrayLength == 1 || arrayLength == val.arrayLength);
- arrayLength = val.arrayLength;
- }
- }
- valueBlock.arrayLength = arrayLength;
-}
-
-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<std::string> 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<string> vertexSources;
- vector<string> fragmentSources;
- vector<string> tessellationCtrlSources;
- vector<string> tessellationEvalSources;
- vector<string> geometrySources;
- vector<ShaderCase::CaseRequirement> 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<tcu::TestNode*>& shaderNodeList)
-{
- // Parse 'case'.
- PARSE_DBG((" parseShaderCase()\n"));
- advanceToken(TOKEN_CASE);
-
- // Parse case name.
- string caseName = m_curTokenStr;
- advanceToken(); // \note [pyry] All token types are allowed here.
-
- // Setup case.
- GLSLVersion version = DEFAULT_GLSL_VERSION;
- ShaderCase::ExpectResult expectResult = ShaderCase::EXPECT_PASS;
- string description;
- string bothSource;
- vector<string> vertexSources;
- vector<string> fragmentSources;
- vector<string> tessellationCtrlSources;
- vector<string> tessellationEvalSources;
- vector<string> geometrySources;
- vector<ShaderCase::ValueBlock> valueBlockList;
- vector<ShaderCase::CaseRequirement> requirements;
- vector<ShaderCase::PipelineProgram> 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<tcu::TestNode*>& shaderNodeList)
-{
- // Parse 'case'.
- PARSE_DBG((" parseShaderGroup()\n"));
- advanceToken(TOKEN_GROUP);
-
- // Parse case name.
- string name = m_curTokenStr;
- advanceToken(); // \note [pyry] We don't want to check token type here (for instance to allow "uniform") group.
-
- // Parse description.
- assumeToken(TOKEN_STRING);
- string description = parseStringLiteral(m_curTokenStr.c_str());
- advanceToken(TOKEN_STRING);
-
- std::vector<tcu::TestNode*> children;
-
- // Parse group children.
- for (;;)
- {
- if (m_curToken == TOKEN_END)
- break;
- else if (m_curToken == TOKEN_GROUP)
- parseShaderGroup(children);
- else if (m_curToken == TOKEN_CASE)
- parseShaderCase(children);
- else 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<tcu::TestNode*>& shaderNodeList)
-{
- ShaderLibrary subLibrary (m_testCtx, m_renderCtx, m_contextInfo);
- vector<tcu::TestNode*> 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<tcu::TestNode*> ShaderParser::parse (const char* input)
-{
- // Initialize parser.
- m_input = input;
- m_curPtr = m_input.c_str();
- m_curToken = TOKEN_INVALID;
- m_curTokenStr = "";
- advanceToken();
-
- vector<tcu::TestNode*> nodeList;
-
- // Parse all cases.
- PARSE_DBG(("parse()\n"));
- for (;;)
- {
- if (m_curToken == TOKEN_CASE)
- parseShaderCase(nodeList);
- else if (m_curToken == TOKEN_GROUP)
- parseShaderGroup(nodeList);
- else if (m_curToken == TOKEN_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('/');
+private:
+ tcu::TestContext& m_testCtx;
+ glu::RenderContext& m_renderCtx;
+ const glu::ContextInfo& m_contextInfo;
+};
- if (lastDelim == std::string::npos)
- return "";
- else
- return filePath.substr(0, lastDelim+1);
-}
+} // anonymous
-ShaderLibrary::ShaderLibrary (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo)
+ShaderLibrary::ShaderLibrary (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo)
: m_testCtx (testCtx)
, m_renderCtx (renderCtx)
, m_contextInfo (contextInfo)
{
}
-vector<tcu::TestNode*> ShaderLibrary::loadShaderFile (const char* fileName)
-{
- tcu::Resource* resource = m_testCtx.getArchive().getResource(fileName);
- std::string fileDirectory = getFileDirectory(fileName);
- std::vector<char> buf;
-
-/* printf(" loading '%s'\n", fileName);*/
-
- try
- {
- int size = resource->getSize();
- buf.resize(size + 1);
- resource->read((deUint8*)&buf[0], size);
- buf[size] = '\0';
- }
- catch (const std::exception&)
- {
- delete resource;
- throw;
- }
-
- delete resource;
-
- sl::ShaderParser parser(m_testCtx, m_renderCtx, m_contextInfo, fileDirectory.c_str());
- vector<tcu::TestNode*> nodes = parser.parse(&buf[0]);
-
- return nodes;
-}
-
-vector<tcu::TestNode*> ShaderLibrary::parseShader (const char* shaderSource)
+std::vector<tcu::TestNode*> ShaderLibrary::loadShaderFile (const char* fileName)
{
- sl::ShaderParser parser(m_testCtx, m_renderCtx, m_contextInfo);
- vector<tcu::TestNode*> nodes = parser.parse(shaderSource);
+ CaseFactory caseFactory (m_testCtx, m_renderCtx, m_contextInfo);
- return nodes;
+ return glu::sl::parseFile(m_testCtx.getArchive(), fileName, &caseFactory);
}
} // gls
~ShaderLibrary (void);
std::vector<tcu::TestNode*> loadShaderFile (const char* fileName);
- std::vector<tcu::TestNode*> 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;
#include "tcuTestLog.hpp"
#include "tcuRenderTarget.hpp"
+#include "tcuTextureUtil.hpp"
+#include "tcuSurface.hpp"
#include "tcuStringTemplate.hpp"
#include "gluShaderProgram.hpp"
#include <string>
#include <sstream>
-using namespace std;
-using namespace tcu;
-using namespace glu;
-
namespace deqp
{
namespace gls
{
-namespace sl
-{
-
-enum
-{
- VIEWPORT_WIDTH = 128,
- VIEWPORT_HEIGHT = 128
-};
-
-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;
-
- default:
- return true;
- }
-}
-
-static inline bool supportsFragmentHighp (glu::GLSLVersion version)
-{
- return version != glu::GLSL_VERSION_100_ES;
-}
-
-static int queryGLInt (const glu::RenderContext& renderCtx, deUint32 pname)
-{
- const glw::Functions& gl = renderCtx.getFunctions();
- glw::GLint value = 0;
-
- gl.getIntegerv(pname, &value);
- GLU_EXPECT_NO_ERROR(gl.getError(), ("query " + de::toString(glu::getGettableStateStr(pname))).c_str());
-
- return value;
-}
-
-ShaderCase::ValueBlock::ValueBlock (void)
- : arrayLength(0)
-{
-}
-
-ShaderCase::CaseRequirement::CaseRequirement (void)
- : m_type (REQUIREMENTTYPE_LAST)
- , m_supportedExtensionNdx (-1)
- , m_effectiveShaderStageFlags (-1)
- , m_enumName (-1)
- , m_referenceValue (-1)
-{
-}
-
-ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createAnyExtensionRequirement (const std::vector<std::string>& requirements, deUint32 effectiveShaderStageFlags)
-{
- CaseRequirement retVal;
-
- retVal.m_type = REQUIREMENTTYPE_EXTENSION;
- retVal.m_extensions = requirements;
- retVal.m_effectiveShaderStageFlags = effectiveShaderStageFlags;
-
- return retVal;
-}
-
-ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createLimitRequirement (deUint32 enumName, int ref)
-{
- CaseRequirement retVal;
-
- retVal.m_type = REQUIREMENTTYPE_IMPLEMENTATION_LIMIT;
- retVal.m_enumName = enumName;
- retVal.m_referenceValue = ref;
- return retVal;
-}
+using namespace tcu;
+using namespace glu;
+using namespace glu::sl;
-ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createFullGLSLES100SpecificationRequirement (void)
-{
- CaseRequirement retVal;
+using std::vector;
+using std::string;
+using std::ostringstream;
+using std::map;
+using std::pair;
- retVal.m_type = REQUIREMENTTYPE_FULL_GLSL_ES_100_SPEC;
+using de::SharedPtr;
- return retVal;
-}
+// OpenGL-specific specialization utils
-void ShaderCase::CaseRequirement::checkRequirements (glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo)
+static vector<RequiredExtension> checkAndSpecializeExtensions (const vector<RequiredExtension>& src,
+ const ContextInfo& ctxInfo)
{
- DE_UNREF(renderCtx);
+ vector<RequiredExtension> specialized;
- switch (m_type)
+ for (size_t extNdx = 0; extNdx < src.size(); ++extNdx)
{
- 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;
- }
- }
+ const RequiredExtension& extension = src[extNdx];
+ int supportedAltNdx = -1;
- // no extension(s). Make a nice output
+ for (size_t alternativeNdx = 0; alternativeNdx < extension.alternatives.size(); ++alternativeNdx)
+ {
+ if (ctxInfo.isExtensionSupported(extension.alternatives[alternativeNdx].c_str()))
{
- std::ostringstream extensionList;
-
- for (int ndx = 0; ndx < (int)m_extensions.size(); ++ndx)
- {
- if (!extensionList.str().empty())
- extensionList << ", ";
- extensionList << m_extensions[ndx];
- }
-
- if (m_extensions.size() == 1)
- throw tcu::NotSupportedError("Test requires extension " + extensionList.str());
- else
- throw tcu::NotSupportedError("Test requires any extension of " + extensionList.str());
+ supportedAltNdx = (int)alternativeNdx;
+ break;
}
-
- // cannot be reached
}
- case REQUIREMENTTYPE_IMPLEMENTATION_LIMIT:
+ if (supportedAltNdx >= 0)
{
- const glw::Functions& gl = renderCtx.getFunctions();
- glw::GLint value = 0;
- glw::GLenum error;
-
- gl.getIntegerv(m_enumName, &value);
- error = gl.getError();
-
- if (error != GL_NO_ERROR)
- throw tcu::TestError("Query for " + de::toString(glu::getGettableStateStr(m_enumName)) + " generated " + de::toString(glu::getErrorStr(error)));
-
- if (!(value > m_referenceValue))
- throw tcu::NotSupportedError("Test requires " + de::toString(glu::getGettableStateStr(m_enumName)) + " (" + de::toString(value) + ") > " + de::toString(m_referenceValue));
-
- return;
+ specialized.push_back(RequiredExtension(extension.alternatives[supportedAltNdx], extension.effectiveStages));
}
-
- case REQUIREMENTTYPE_FULL_GLSL_ES_100_SPEC:
+ else
{
- // cannot be queried
- return;
- }
+ // no extension(s). Make a nice output
+ std::ostringstream extensionList;
- default:
- DE_ASSERT(false);
+ for (size_t ndx = 0; ndx < extension.alternatives.size(); ++ndx)
+ {
+ if (!extensionList.str().empty())
+ extensionList << ", ";
+ extensionList << extension.alternatives[ndx];
+ }
+
+ 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::ShaderCaseSpecification::ShaderCaseSpecification (void)
- : expectResult (EXPECT_LAST)
- , targetVersion (glu::GLSL_VERSION_LAST)
- , caseType (CASETYPE_COMPLETE)
-{
+ return specialized;
}
-ShaderCase::ShaderCaseSpecification ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase (ExpectResult expectResult_, glu::GLSLVersion targetVersion_, const std::vector<ValueBlock>& values, const std::string& sharedSource)
+static void checkImplementationLimits (const vector<RequiredCapability>& requiredCaps,
+ const ContextInfo& ctxInfo)
{
- ShaderCaseSpecification retVal;
- retVal.expectResult = expectResult_;
- retVal.targetVersion = targetVersion_;
- retVal.caseType = CASETYPE_VERTEX_ONLY;
- retVal.valueBlocks = values;
- retVal.vertexSources.push_back(sharedSource);
- return retVal;
-}
+ 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);
-ShaderCase::ShaderCaseSpecification ShaderCase::ShaderCaseSpecification::generateSharedSourceFragmentCase (ExpectResult expectResult_, glu::GLSLVersion targetVersion_, const std::vector<ValueBlock>& 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 (supportedValue <= requiredValue)
+ throw tcu::NotSupportedError("Test requires " + de::toString(glu::getGettableStateStr(pname)) + " (" + de::toString(supportedValue) + ") >= " + de::toString(requiredValue));
+ }
}
-class BeforeDrawValidator : public glu::DrawUtilCallback
+// Shader source specialization
+
+// 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)
{
-public:
- enum TargetType
- {
- TARGETTYPE_PROGRAM = 0,
- TARGETTYPE_PIPELINE,
+ ostringstream res;
+ const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion);
+ const char* const vtxIn = usesInout ? "in" : "attribute";
+ const char* const vtxOut = usesInout ? "out" : "varying";
- TARGETTYPE_LAST
- };
+ res << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n";
- BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType);
+ // 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";
- void beforeDrawCall (void);
+ 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 typeStr = getDataTypeName(floatType);
- const std::string& getInfoLog (void) const;
- glw::GLint getValidateStatus (void) const;
+ res << vtxIn << " " << typeStr << " a_" << val.name << ";\n";
-private:
- const glw::Functions& m_gl;
- const glw::GLuint m_target;
- const TargetType m_targetType;
+ if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
+ res << vtxOut << " " << typeStr << " " << val.name << ";\n";
+ else
+ res << vtxOut << " " << typeStr << " v_" << val.name << ";\n";
+ }
+ res << "\n";
- glw::GLint m_validateStatus;
- std::string m_logMessage;
-};
+ // 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;
-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);
+ if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT)
+ res << " " << name << " = a_" << name << ";\n";
+ else
+ res << " v_" << name << " = a_" << name << ";\n";
+ }
+
+ res << "}\n";
+ return res.str();
}
-void BeforeDrawValidator::beforeDrawCall (void)
+static void genCompareOp (ostringstream& output, const char* dstVec4Var, const ValueBlock& valueBlock, const char* nonFloatNamePrefix, const char* checkVarName)
{
- glw::GLint bytesWritten = 0;
- glw::GLint infoLogLength;
- std::vector<glw::GLchar> 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
+ bool isFirstOutput = true;
- infoLogLength = 0;
+ for (size_t ndx = 0; ndx < valueBlock.outputs.size(); ndx++)
+ {
+ const Value& val = valueBlock.outputs[ndx];
- 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);
+ // Check if we're only interested in one variable (then skip if not the right one).
+ if (checkVarName && val.name != checkVarName)
+ continue;
- GLU_EXPECT_NO_ERROR(m_gl.getError(), "get info log length");
+ // Prefix.
+ if (isFirstOutput)
+ {
+ output << "bool RES = ";
+ isFirstOutput = false;
+ }
+ else
+ output << "RES = RES && ";
- if (infoLogLength <= 0)
- {
- m_logMessage.clear();
- return;
+ // 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";
}
- 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]);
+ if (isFirstOutput)
+ output << dstVec4Var << " = vec4(1.0);\n"; // \todo [petri] Should we give warning if not expect-failure case?
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);
+ output << dstVec4Var << " = vec4(RES, RES, RES, 1.0);\n";
}
-const std::string& BeforeDrawValidator::getInfoLog (void) const
+static inline bool supportsFragmentHighp (glu::GLSLVersion version)
{
- return m_logMessage;
+ return version != glu::GLSL_VERSION_100_ES;
}
-glw::GLint BeforeDrawValidator::getValidateStatus (void) const
+static string genFragmentShader (const ShaderCaseSpecification& spec)
{
- return m_validateStatus;
-}
-
-// ShaderCase.
+ 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";
-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)
- {
- // 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());
- }
- else if (m_caseType == CASETYPE_FRAGMENT_ONLY)
- {
- // 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());
- }
+ shader << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\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());
- }
+ shader << "precision " << prec << " float;\n";
+ shader << "precision " << prec << " int;\n";
+ shader << "\n";
- // single program object
+ if (customColorOut)
{
- 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);
+ shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
+ shader << "\n";
}
-}
-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)
-{
- deUint32 totalActiveMask = 0;
-
- DE_ASSERT(m_caseType == CASETYPE_COMPLETE);
-
- // validate
+ genCompareFunctions(shader, spec.values, true);
+ shader << "\n";
- for (int pipelineProgramNdx = 0; pipelineProgramNdx < (int)specification.programs.size(); ++pipelineProgramNdx)
+ // Declarations (varying, reference for each output).
+ for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
{
- // 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;
+ 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";
+
+ shader << "uniform " << refTypeStr << " ref_" << val.name << ";\n";
}
- // create ProgramObjects
+ shader << "\n";
+ shader << "void main()\n";
+ shader << "{\n";
- for (int pipelineProgramNdx = 0; pipelineProgramNdx < (int)specification.programs.size(); ++pipelineProgramNdx)
- {
- ProgramObject program;
- program.spec = specification.programs[pipelineProgramNdx];
- m_programs.push_back(program);
- }
-}
+ shader << " ";
+ genCompareOp(shader, customColorOut ? "dEQP_FragColor" : "gl_FragColor", spec.values, "v_", DE_NULL);
-ShaderCase::~ShaderCase (void)
-{
+ shader << "}\n";
+ return shader.str();
}
-void ShaderCase::init (void)
+// Specialize a shader for the vertex shader test case.
+static string specializeVertexShader (const ShaderCaseSpecification& spec, const std::string& src)
{
- // If no value blocks given, use an empty one.
- if (m_valueBlocks.empty())
- m_valueBlocks.push_back(ValueBlock());
+ 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";
+
+ // generated from "both" case
+ DE_ASSERT(spec.caseType == CASETYPE_VERTEX_ONLY);
- // Use first value block to specialize shaders.
- const ValueBlock& valueBlock = m_valueBlocks[0];
+ // Output (write out position).
+ output << "gl_Position = dEQP_Position;\n";
- // \todo [2010-04-01 petri] Check that all value blocks have matching values.
+ // Declarations (position + attribute for each input, varying for each output).
+ decl << vtxIn << " highp vec4 dEQP_Position;\n";
- // prepare programs
- for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
+ for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
{
- // 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);
+ 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);
- // Generate specialized shader sources.
- if (m_caseType == CASETYPE_COMPLETE)
+ if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
{
- // 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);
+ decl << vtxIn << " " << floatTypeStr << " " << val.name << ";\n";
}
- else if (m_caseType == CASETYPE_VERTEX_ONLY)
+ else
{
- DE_ASSERT(m_programs.size() == 1);
- DE_ASSERT(!m_separatePrograms);
-
- // 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));
+ decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n";
+ setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(a_" << val.name << ");\n";
}
- else if (m_caseType == CASETYPE_FRAGMENT_ONLY)
+ }
+
+ // \todo [2015-07-24 pyry] Why are uniforms missing?
+
+ 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
{
- DE_ASSERT(m_programs.size() == 1);
- DE_ASSERT(!m_separatePrograms);
+ decl << vtxOut << " " << floatTypeStr << " v_" << val.name << ";\n";
+ decl << refTypeStr << " " << val.name << ";\n";
- // 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));
+ output << "v_" << val.name << " = " << floatTypeStr << "(" << val.name << ");\n";
}
-
- m_programs[programNdx].programSources << glu::ProgramSeparable(m_separatePrograms);
}
- // log the expected result
- switch (m_expectResult)
- {
- case EXPECT_PASS:
- // Don't write anything
- break;
+ // Shader specialization.
+ map<string, string> params;
+ params.insert(pair<string, string>("DECLARATIONS", decl.str()));
+ params.insert(pair<string, string>("SETUP", setup.str()));
+ params.insert(pair<string, string>("OUTPUT", output.str()));
+ params.insert(pair<string, string>("POSITION_FRAG_COLOR", "gl_Position"));
- case EXPECT_COMPILE_FAIL:
- m_testCtx.getLog() << tcu::TestLog::Message << "Expecting shader compilation to fail." << tcu::TestLog::EndMessage;
- break;
+ StringTemplate tmpl (src);
+ const string baseSrc = tmpl.specialize(params);
+ const string withExt = injectExtensionRequirements(baseSrc, spec.programs[0].requiredExtensions, SHADERTYPE_VERTEX);
- case EXPECT_LINK_FAIL:
- m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program linking to fail." << tcu::TestLog::EndMessage;
- break;
+ return withExt;
+}
- case EXPECT_COMPILE_LINK_FAIL:
- m_testCtx.getLog() << tcu::TestLog::Message << "Expecting either shader compilation or program linking to fail." << tcu::TestLog::EndMessage;
- break;
+// Specialize a shader for the fragment shader test case.
+static string specializeFragmentShader (const ShaderCaseSpecification& spec, const std::string& src)
+{
+ ostringstream decl;
+ ostringstream setup;
+ ostringstream output;
- case EXPECT_VALIDATION_FAIL:
- m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program validation to fail." << tcu::TestLog::EndMessage;
- break;
+ 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";
- case EXPECT_BUILD_SUCCESSFUL:
- m_testCtx.getLog() << tcu::TestLog::Message << "Expecting shader compilation and program linking to succeed. Resulting program will not be executed." << tcu::TestLog::EndMessage;
- break;
+ // generated from "both" case
+ DE_ASSERT(spec.caseType == CASETYPE_FRAGMENT_ONLY);
- default:
- DE_ASSERT(false);
- break;
- }
+ genCompareFunctions(decl, spec.values, false);
+ genCompareOp(output, fragColor, spec.values, "", DE_NULL);
- // sanity of arguments
+ if (customColorOut)
+ decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
- if (anyProgramRequiresFullGLSLES100Specification())
+ for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
{
- // 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);
+ 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";
+ }
}
-}
-static void setUniformValue (const glw::Functions& gl, const std::vector<deUint32>& pipelinePrograms, const std::string& name, const ShaderCase::Value& val, int arrayNdx, tcu::TestLog& log)
-{
- bool foundAnyMatch = false;
+ // \todo [2015-07-24 pyry] Why are uniforms missing?
- for (int programNdx = 0; programNdx < (int)pipelinePrograms.size(); ++programNdx)
+ for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
{
- 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;
+ const Value& val = spec.values.outputs[ndx];
+ const DataType basicType = val.type.getBasicType();
+ const char* const refTypeStr = getDataTypeName(basicType);
- foundAnyMatch = true;
-
- DE_STATIC_ASSERT(sizeof(ShaderCase::Value::Element) == sizeof(glw::GLfloat));
- DE_STATIC_ASSERT(sizeof(ShaderCase::Value::Element) == sizeof(glw::GLint));
-
- gl.useProgram(pipelinePrograms[programNdx]);
+ decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n";
+ decl << refTypeStr << " " << val.name << ";\n";
+ }
- 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;
- case TYPE_FLOAT_VEC3: gl.uniform3fv(loc, 1, &val.elements[elemNdx].float32); break;
- case TYPE_FLOAT_VEC4: gl.uniform4fv(loc, 1, &val.elements[elemNdx].float32); break;
- case TYPE_FLOAT_MAT2: gl.uniformMatrix2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
- case TYPE_FLOAT_MAT3: gl.uniformMatrix3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
- case TYPE_FLOAT_MAT4: gl.uniformMatrix4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
- case TYPE_INT: gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32); break;
- case TYPE_INT_VEC2: gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32); break;
- case TYPE_INT_VEC3: gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32); break;
- case TYPE_INT_VEC4: gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32); break;
- case TYPE_BOOL: gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32); break;
- case TYPE_BOOL_VEC2: gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32); break;
- case TYPE_BOOL_VEC3: gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32); break;
- case TYPE_BOOL_VEC4: gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32); break;
- case TYPE_UINT: gl.uniform1uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
- case TYPE_UINT_VEC2: gl.uniform2uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
- case TYPE_UINT_VEC3: gl.uniform3uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
- case TYPE_UINT_VEC4: gl.uniform4uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
- case TYPE_FLOAT_MAT2X3: gl.uniformMatrix2x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
- case TYPE_FLOAT_MAT2X4: gl.uniformMatrix2x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
- case TYPE_FLOAT_MAT3X2: gl.uniformMatrix3x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
- case TYPE_FLOAT_MAT3X4: gl.uniformMatrix3x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
- case TYPE_FLOAT_MAT4X2: gl.uniformMatrix4x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
- case TYPE_FLOAT_MAT4X3: gl.uniformMatrix4x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
+ /* \todo [2010-04-01 petri] Check all outputs. */
- case TYPE_SAMPLER_2D:
- case TYPE_SAMPLER_CUBE:
- DE_FATAL("implement!");
- break;
+ // Shader specialization.
+ map<string, string> params;
+ params.insert(pair<string, string>("DECLARATIONS", decl.str()));
+ params.insert(pair<string, string>("SETUP", setup.str()));
+ params.insert(pair<string, string>("OUTPUT", output.str()));
+ params.insert(pair<string, string>("POSITION_FRAG_COLOR", fragColor));
- default:
- DE_ASSERT(false);
- }
- }
+ StringTemplate tmpl (src);
+ const string baseSrc = tmpl.specialize(params);
+ const string withExt = injectExtensionRequirements(baseSrc, spec.programs[0].requiredExtensions, SHADERTYPE_FRAGMENT);
- if (!foundAnyMatch)
- log << tcu::TestLog::Message << "WARNING // Uniform \"" << name << "\" location is not valid, location = -1. Cannot set value to the uniform." << tcu::TestLog::EndMessage;
+ return withExt;
}
-bool ShaderCase::isTessellationPresent (void) const
+static void generateUniformDeclarations (std::ostream& dst, const ValueBlock& valueBlock)
{
- if (m_separatePrograms)
+ for (size_t ndx = 0; ndx < valueBlock.uniforms.size(); ndx++)
{
- const deUint32 tessellationBits = (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) |
- (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION);
+ const Value& val = valueBlock.uniforms[ndx];
+ const char* const typeStr = getDataTypeName(val.type.getBasicType());
- for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
- if (m_programs[programNdx].spec.activeStageBits & tessellationBits)
- return true;
- return false;
+ if (val.name.find('.') == string::npos)
+ dst << "uniform " << typeStr << " " << val.name << ";\n";
}
- else
- return !m_programs[0].programSources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty() ||
- !m_programs[0].programSources.sources[glu::SHADERTYPE_TESSELLATION_EVALUATION].empty();
}
-bool ShaderCase::anyProgramRequiresFullGLSLES100Specification (void) const
+static map<string, string> generateVertexSpecialization (const ProgramSpecializationParams& specParams)
{
- for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
- for (int requirementNdx = 0; requirementNdx < (int)m_programs[programNdx].spec.requirements.size(); ++requirementNdx)
+ const bool usesInout = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion);
+ const char* vtxIn = usesInout ? "in" : "attribute";
+ ostringstream decl;
+ ostringstream setup;
+ map<string, string> params;
+
+ decl << vtxIn << " highp vec4 dEQP_Position;\n";
+
+ for (size_t ndx = 0; ndx < specParams.caseSpec.values.inputs.size(); ndx++)
{
- if (m_programs[programNdx].spec.requirements[requirementNdx].getType() == CaseRequirement::REQUIREMENTTYPE_FULL_GLSL_ES_100_SPEC)
- return true;
+ const Value& val = specParams.caseSpec.values.inputs[ndx];
+ const DataType basicType = val.type.getBasicType();
+ const char* const typeStr = getDataTypeName(val.type.getBasicType());
+
+ if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
+ {
+ decl << vtxIn << " " << typeStr << " " << val.name << ";\n";
+ }
+ else
+ {
+ const DataType floatType = getDataTypeFloatScalars(basicType);
+ const char* const floatTypeStr = getDataTypeName(floatType);
+
+ decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n";
+ setup << typeStr << " " << val.name << " = " << typeStr << "(a_" << val.name << ");\n";
+ }
}
- return false;
+
+ generateUniformDeclarations(decl, specParams.caseSpec.values);
+
+ params.insert(pair<string, string>("VERTEX_DECLARATIONS", decl.str()));
+ params.insert(pair<string, string>("VERTEX_SETUP", setup.str()));
+ params.insert(pair<string, string>("VERTEX_OUTPUT", string("gl_Position = dEQP_Position;\n")));
+
+ return params;
}
-bool ShaderCase::checkPixels (Surface& surface, int minX, int maxX, int minY, int maxY)
+static map<string, string> generateFragmentSpecialization (const ProgramSpecializationParams& specParams)
{
- TestLog& log = m_testCtx.getLog();
- bool allWhite = true;
- bool allBlack = true;
- bool anyUnexpected = false;
+ 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<string, string> params;
- DE_ASSERT((maxX > minX) && (maxY > minY));
+ 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 (int y = minY; y <= maxY; y++)
+ for (size_t ndx = 0; ndx < specParams.caseSpec.values.outputs.size(); ndx++)
{
- for (int x = minX; x <= maxX; x++)
- {
- 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.
- bool isWhite = (pixel.getRed() == 255) && (pixel.getGreen() == 255) && (pixel.getBlue() == 255);
- bool isBlack = (pixel.getRed() == 0) && (pixel.getGreen() == 0) && (pixel.getBlue() == 0);
+ const Value& val = specParams.caseSpec.values.outputs[ndx];
+ const char* const refTypeStr = getDataTypeName(val.type.getBasicType());
- allWhite = allWhite && isWhite;
- allBlack = allBlack && isBlack;
- anyUnexpected = anyUnexpected || (!isWhite && !isBlack);
- }
+ decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n";
+ decl << refTypeStr << " " << val.name << ";\n";
}
- if (!allWhite)
- {
- if (anyUnexpected)
- log << TestLog::Message << "WARNING: expecting all rendered pixels to be white or black, but got other colors as well!" << TestLog::EndMessage;
- else if (!allBlack)
- log << TestLog::Message << "WARNING: got inconsistent results over the image, when all pixels should be the same color!" << TestLog::EndMessage;
+ generateUniformDeclarations(decl, specParams.caseSpec.values);
- return false;
- }
- return true;
+ params.insert(pair<string, string>("FRAGMENT_DECLARATIONS", decl.str()));
+ params.insert(pair<string, string>("FRAGMENT_OUTPUT", output.str()));
+ params.insert(pair<string, string>("FRAG_COLOR", fragColor));
+
+ return params;
}
-bool ShaderCase::execute (void)
+static map<string, string> generateGeometrySpecialization (const ProgramSpecializationParams& specParams)
{
- const float quadSize = 1.0f;
- static const float s_positions[4*4] =
- {
- -quadSize, -quadSize, 0.0f, 1.0f,
- -quadSize, +quadSize, 0.0f, 1.0f,
- +quadSize, -quadSize, 0.0f, 1.0f,
- +quadSize, +quadSize, 0.0f, 1.0f
- };
+ ostringstream decl;
+ map<string, string> params;
- static const deUint16 s_indices[2*3] =
- {
- 0, 1, 2,
- 1, 3, 2
- };
+ decl << "layout (triangles) in;\n";
+ decl << "layout (triangle_strip, max_vertices=3) out;\n";
+ decl << "\n";
- TestLog& log = m_testCtx.getLog();
- const glw::Functions& gl = m_renderCtx.getFunctions();
+ generateUniformDeclarations(decl, specParams.caseSpec.values);
- // 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();
- const bool requiresFullGLSLES100 = anyProgramRequiresFullGLSLES100Specification();
-
- bool allCompilesOk = true;
- bool allLinksOk = true;
- const char* failReason = DE_NULL;
-
- deUint32 vertexProgramID = -1;
- std::vector<deUint32> pipelineProgramIDs;
- std::vector<de::SharedPtr<glu::ShaderProgram> > programs;
- de::SharedPtr<glu::ProgramPipeline> programPipeline;
+ params.insert(pair<string, string>("GEOMETRY_DECLARATIONS", decl.str()));
- GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): start");
+ return params;
+}
- if (!m_separatePrograms)
- {
- de::SharedPtr<glu::ShaderProgram> program (new glu::ShaderProgram(m_renderCtx, m_programs[0].programSources));
+static map<string, string> generateTessControlSpecialization (const ProgramSpecializationParams& specParams)
+{
+ ostringstream decl;
+ ostringstream output;
+ map<string, string> params;
- vertexProgramID = program->getProgram();
- pipelineProgramIDs.push_back(program->getProgram());
- programs.push_back(program);
+ decl << "layout (vertices=3) out;\n";
+ decl << "\n";
- // Check that compile/link results are what we expect.
+ generateUniformDeclarations(decl, specParams.caseSpec.values);
- DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0);
- for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
- if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk)
- allCompilesOk = false;
+ 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;";
- if (!program->getProgramInfo().linkOk)
- allLinksOk = false;
+ params.insert(pair<string, string>("TESSELLATION_CONTROL_DECLARATIONS", decl.str()));
+ params.insert(pair<string, string>("TESSELLATION_CONTROL_OUTPUT", output.str()));
+ params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices)));
- log << *program;
- }
- else
- {
- // Separate programs
- for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
- {
- de::SharedPtr<glu::ShaderProgram> program(new glu::ShaderProgram(m_renderCtx, m_programs[programNdx].programSources));
+ return params;
+}
- if (m_programs[programNdx].spec.activeStageBits & (1 << glu::SHADERTYPE_VERTEX))
- vertexProgramID = program->getProgram();
+static map<string, string> generateTessEvalSpecialization (const ProgramSpecializationParams& specParams)
+{
+ ostringstream decl;
+ ostringstream output;
+ map<string, string> params;
- pipelineProgramIDs.push_back(program->getProgram());
- programs.push_back(program);
+ decl << "layout (triangles) in;\n";
+ decl << "\n";
- // Check that compile/link results are what we expect.
+ generateUniformDeclarations(decl, specParams.caseSpec.values);
- DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0);
- for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
- if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk)
- allCompilesOk = false;
+ 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 (!program->getProgramInfo().linkOk)
- allLinksOk = false;
+ params.insert(pair<string, string>("TESSELLATION_EVALUATION_DECLARATIONS", decl.str()));
+ params.insert(pair<string, string>("TESSELLATION_EVALUATION_OUTPUT", output.str()));
+ params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices)));
- // Log program and active stages
- {
- const tcu::ScopedLogSection section (log, "Program", "Program " + de::toString(programNdx+1));
- tcu::MessageBuilder builder (&log);
- bool firstStage = true;
+ return params;
+}
- builder << "Pipeline uses stages: ";
- for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
- {
- if (m_programs[programNdx].spec.activeStageBits & (1 << stage))
- {
- if (!firstStage)
- builder << ", ";
- builder << glu::getShaderTypeName((glu::ShaderType)stage);
- firstStage = true;
- }
- }
- builder << tcu::TestLog::EndMessage;
+static void specializeShaderSources (ProgramSources& dst,
+ const ProgramSources& src,
+ const ProgramSpecializationParams& specParams,
+ glu::ShaderType shaderType,
+ map<string, string> (*specializationGenerator) (const ProgramSpecializationParams& specParams))
+{
+ if (!src.sources[shaderType].empty())
+ {
+ const map<string, string> tmplParams = specializationGenerator(specParams);
- log << *program;
- }
+ for (size_t ndx = 0; ndx < src.sources[shaderType].size(); ++ndx)
+ {
+ 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);
}
}
+}
- switch (m_expectResult)
- {
- case EXPECT_PASS:
- case EXPECT_VALIDATION_FAIL:
- case EXPECT_BUILD_SUCCESSFUL:
- if (!allCompilesOk)
- failReason = "expected shaders to compile and link properly, but failed to compile.";
- else if (!allLinksOk)
- failReason = "expected shaders to compile and link properly, but failed to link.";
- break;
-
- case EXPECT_COMPILE_FAIL:
- if (allCompilesOk && !allLinksOk)
- failReason = "expected compilation to fail, but shaders compiled and link failed.";
- else if (allCompilesOk)
- failReason = "expected compilation to fail, but shaders compiled correctly.";
- break;
-
- case EXPECT_LINK_FAIL:
- if (!allCompilesOk)
- failReason = "expected linking to fail, but unable to compile.";
- else if (allLinksOk)
- failReason = "expected linking to fail, but passed.";
- break;
+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);
- case EXPECT_COMPILE_LINK_FAIL:
- if (allCompilesOk && allLinksOk)
- failReason = "expected compile or link to fail, but passed.";
- break;
+ dst << ProgramSeparable(src.separable);
+}
- default:
- DE_ASSERT(false);
- return false;
- }
+enum
+{
+ VIEWPORT_WIDTH = 128,
+ VIEWPORT_HEIGHT = 128
+};
- if (failReason != DE_NULL)
+class BeforeDrawValidator : public glu::DrawUtilCallback
+{
+public:
+ enum TargetType
{
- // \todo [2010-06-07 petri] These should be handled in the test case?
- log << TestLog::Message << "ERROR: " << failReason << TestLog::EndMessage;
-
- if (requiresFullGLSLES100)
- {
- log << TestLog::Message
- << "Assuming build failure is caused by implementation not supporting full GLSL ES 100 specification, which is not required."
- << TestLog::EndMessage;
-
- if (allCompilesOk && !allLinksOk)
- {
- // Used features are detectable at compile time. If implementation parses shader
- // at link time, report it as quality warning.
- m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason);
- }
- else
- m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Full GLSL ES 100 is not supported");
- }
- 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);
- }
- else
- m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failReason);
- return false;
- }
-
- // Return if shader is not intended to be run
- if (m_expectResult == EXPECT_COMPILE_FAIL ||
- m_expectResult == EXPECT_COMPILE_LINK_FAIL ||
- m_expectResult == EXPECT_LINK_FAIL ||
- m_expectResult == EXPECT_BUILD_SUCCESSFUL)
- return true;
+ TARGETTYPE_PROGRAM = 0,
+ TARGETTYPE_PIPELINE,
- // Setup viewport.
- gl.viewport(viewportX, viewportY, width, height);
+ TARGETTYPE_LAST
+ };
- if (m_separatePrograms)
- {
- programPipeline = de::SharedPtr<glu::ProgramPipeline>(new glu::ProgramPipeline(m_renderCtx));
+ BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType);
- // Setup pipeline
- gl.bindProgramPipeline(programPipeline->getPipeline());
- 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_programs[programNdx].spec.activeStageBits & (1 << stage))
- shaderFlags |= glu::getGLShaderTypeBit((glu::ShaderType)stage);
+ void beforeDrawCall (void);
- programPipeline->useProgramStages(shaderFlags, pipelineProgramIDs[programNdx]);
- }
+ const std::string& getInfoLog (void) const;
+ glw::GLint getValidateStatus (void) const;
- programPipeline->activeShaderProgram(vertexProgramID);
- GLU_EXPECT_NO_ERROR(gl.getError(), "setup pipeline");
- }
- else
- {
- // Start using program
- gl.useProgram(vertexProgramID);
- GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()");
- }
+private:
+ const glw::Functions& m_gl;
+ const glw::GLuint m_target;
+ const TargetType m_targetType;
- // Fetch location for positions positions.
- int positionLoc = gl.getAttribLocation(vertexProgramID, "dEQP_Position");
- if (positionLoc == -1)
- {
- string errStr = string("no location found for attribute 'dEQP_Position'");
- TCU_FAIL(errStr.c_str());
- }
+ glw::GLint m_validateStatus;
+ std::string m_logMessage;
+};
- // Iterate all value blocks.
- for (int blockNdx = 0; blockNdx < (int)m_valueBlocks.size(); blockNdx++)
- {
- const ValueBlock& valueBlock = m_valueBlocks[blockNdx];
+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);
+}
- // always render at least one pass even if there is no input/output data
- const int numRenderPasses = (valueBlock.arrayLength == 0) ? (1) : (valueBlock.arrayLength);
+void BeforeDrawValidator::beforeDrawCall (void)
+{
+ glw::GLint bytesWritten = 0;
+ glw::GLint infoLogLength;
+ std::vector<glw::GLchar> logBuffer;
+ int stringLength;
- // Iterate all array sub-cases.
- for (int arrayNdx = 0; arrayNdx < numRenderPasses; arrayNdx++)
- {
- int numValues = (int)valueBlock.values.size();
- vector<VertexArrayBinding> vertexArrays;
- int attribValueNdx = 0;
- vector<vector<float> > attribValues (numValues);
- glw::GLenum postDrawError;
- BeforeDrawValidator beforeDrawValidator (gl,
- (m_separatePrograms) ? (programPipeline->getPipeline()) : (vertexProgramID),
- (m_separatePrograms) ? (BeforeDrawValidator::TARGETTYPE_PIPELINE) : (BeforeDrawValidator::TARGETTYPE_PROGRAM));
+ // 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);
- vertexArrays.push_back(va::Float(positionLoc, 4, numVerticesPerDraw, 0, &s_positions[0]));
+ GLU_EXPECT_NO_ERROR(m_gl.getError(), "validate");
- // Collect VA pointer for inputs
- for (int valNdx = 0; valNdx < numValues; valNdx++)
- {
- 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);
+ // check status
+ m_validateStatus = -1;
- if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
- {
- // Replicate values four times.
- std::vector<float>& 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
- {
- // convert to floats.
- for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
- {
- 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;
- }
- }
- }
+ 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);
- // 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_";
+ GLU_EXPECT_NO_ERROR(m_gl.getError(), "get validate status");
+ TCU_CHECK(m_validateStatus == GL_TRUE || m_validateStatus == GL_FALSE);
- // 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;
- }
+ // read log
- if (isDataTypeMatrix(dataType))
- {
- int numCols = getDataTypeMatrixNumColumns(dataType);
- int numRows = getDataTypeMatrixNumRows(dataType);
- DE_ASSERT(scalarSize == numCols*numRows);
+ infoLogLength = 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]));
- }
+ 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(gl.getError(), "set vertex attrib array");
- }
- }
+ GLU_EXPECT_NO_ERROR(m_gl.getError(), "get info log length");
- GLU_EXPECT_NO_ERROR(gl.getError(), "before set uniforms");
+ if (infoLogLength <= 0)
+ {
+ m_logMessage.clear();
+ return;
+ }
- // set uniform values for outputs (refs).
- for (int valNdx = 0; valNdx < numValues; valNdx++)
- {
- const ShaderCase::Value& val = valueBlock.values[valNdx];
- const char* const valueName = val.valueName.c_str();
+ 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 (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");
- }
- }
+ 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);
- // Clear.
- gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
- gl.clear(GL_COLOR_BUFFER_BIT);
- GLU_EXPECT_NO_ERROR(gl.getError(), "clear buffer");
+ // 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);
+}
- // Use program or pipeline
- if (m_separatePrograms)
- gl.useProgram(0);
- else
- gl.useProgram(vertexProgramID);
+const std::string& BeforeDrawValidator::getInfoLog (void) const
+{
+ return m_logMessage;
+}
- // Draw.
- if (tessellationPresent)
- {
- gl.patchParameteri(GL_PATCH_VERTICES, 3);
- GLU_EXPECT_NO_ERROR(gl.getError(), "set patchParameteri(PATCH_VERTICES, 3)");
- }
+glw::GLint BeforeDrawValidator::getValidateStatus (void) const
+{
+ return m_validateStatus;
+}
- draw(m_renderCtx,
- vertexProgramID,
- (int)vertexArrays.size(),
- &vertexArrays[0],
- (tessellationPresent) ?
- (pr::Patches(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])) :
- (pr::Triangles(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])),
- (m_expectResult == EXPECT_VALIDATION_FAIL) ?
- (&beforeDrawValidator) :
- (DE_NULL));
+// ShaderCase.
- postDrawError = gl.getError();
+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)
+{
+}
- if (m_expectResult == EXPECT_PASS)
- {
- // Read back results.
- Surface surface (width, height);
- const float w = s_positions[3];
- const int minY = deCeilFloatToInt32 (((-quadSize / w) * 0.5f + 0.5f) * (float)height + 1.0f);
- const int maxY = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)height - 0.5f);
- const int minX = deCeilFloatToInt32 (((-quadSize / w) * 0.5f + 0.5f) * (float)width + 1.0f);
- const int maxX = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)width - 0.5f);
+ShaderLibraryCase::~ShaderLibraryCase (void)
+{
+}
- GLU_EXPECT_NO_ERROR(postDrawError, "draw");
+void ShaderLibraryCase::init (void)
+{
+ DE_ASSERT(isValid(m_spec));
- glu::readPixels(m_renderCtx, viewportX, viewportY, surface.getAccess());
- GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
+ checkImplementationLimits(m_spec.requiredCaps, m_contextInfo);
- if (!checkPixels(surface, minX, maxX, minY, maxY))
- {
- 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 the expected result
+ switch (m_spec.expectResult)
+ {
+ case EXPECT_PASS:
+ // Don't write anything
+ break;
- log << TestLog::Message << "Failing shader input/output values:" << TestLog::EndMessage;
- dumpValues(valueBlock, arrayNdx);
+ case EXPECT_COMPILE_FAIL:
+ m_testCtx.getLog() << tcu::TestLog::Message << "Expecting shader compilation to fail." << tcu::TestLog::EndMessage;
+ break;
- // Dump image on failure.
- log << TestLog::Image("Result", "Rendered result image", surface);
+ case EXPECT_LINK_FAIL:
+ m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program linking to fail." << tcu::TestLog::EndMessage;
+ break;
- gl.useProgram(0);
- m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
- return false;
- }
- }
- else if (m_expectResult == EXPECT_VALIDATION_FAIL)
- {
- log << TestLog::Message
- << "Draw call generated error: "
- << glu::getErrorStr(postDrawError) << " "
- << ((postDrawError == GL_INVALID_OPERATION) ? ("(expected)") : ("(unexpected)")) << "\n"
- << "Validate status: "
- << glu::getBooleanStr(beforeDrawValidator.getValidateStatus()) << " "
- << ((beforeDrawValidator.getValidateStatus() == GL_FALSE) ? ("(expected)") : ("(unexpected)")) << "\n"
- << "Info log: "
- << ((beforeDrawValidator.getInfoLog().empty()) ? ("[empty string]") : (beforeDrawValidator.getInfoLog())) << "\n"
- << TestLog::EndMessage;
+ case EXPECT_COMPILE_LINK_FAIL:
+ m_testCtx.getLog() << tcu::TestLog::Message << "Expecting either shader compilation or program linking to fail." << tcu::TestLog::EndMessage;
+ break;
- // test result
+ case EXPECT_VALIDATION_FAIL:
+ m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program validation to fail." << tcu::TestLog::EndMessage;
+ break;
- if (postDrawError != GL_NO_ERROR && postDrawError != GL_INVALID_OPERATION)
- {
- m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, ("Draw: got unexpected error: " + de::toString(glu::getErrorStr(postDrawError))).c_str());
- return false;
- }
+ case EXPECT_BUILD_SUCCESSFUL:
+ m_testCtx.getLog() << tcu::TestLog::Message << "Expecting shader compilation and program linking to succeed. Resulting program will not be executed." << tcu::TestLog::EndMessage;
+ break;
- if (beforeDrawValidator.getValidateStatus() == GL_TRUE)
- {
- if (postDrawError == GL_NO_ERROR)
- m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but validation and rendering succeeded");
- else if (postDrawError == GL_INVALID_OPERATION)
- m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but validation succeeded (rendering failed as expected)");
- else
- DE_ASSERT(false);
- return false;
- }
- else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_NO_ERROR)
- {
- m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but rendering succeeded (validation failed as expected)");
- return false;
- }
- else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_INVALID_OPERATION)
- {
- // Validation does not depend on input values, no need to test all values
- return true;
- }
- else
- DE_ASSERT(false);
- }
- else
- DE_ASSERT(false);
- }
+ default:
+ DE_ASSERT(false);
+ break;
}
-
- gl.useProgram(0);
- if (m_separatePrograms)
- gl.bindProgramPipeline(0);
-
- GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): end");
- return true;
-}
-
-TestCase::IterateResult ShaderCase::iterate (void)
-{
- // Initialize state to pass.
- m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
-
- bool executeOk = execute();
-
- DE_ASSERT(executeOk ? m_testCtx.getTestResult() == QP_TEST_RESULT_PASS : m_testCtx.getTestResult() != QP_TEST_RESULT_PASS);
- DE_UNREF(executeOk);
- return TestCase::STOP;
-}
-
-static void generateExtensionStatements (std::ostringstream& buf, const std::vector<ShaderCase::CaseRequirement>& 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<ShaderCase::CaseRequirement>& requirements)
+static void setUniformValue (const glw::Functions& gl, const std::vector<deUint32>& pipelinePrograms, const std::string& name, const Value& val, int arrayNdx, tcu::TestLog& log)
{
- 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;
+ bool foundAnyMatch = false;
- while (std::getline(baseCodeBuf, line))
+ for (int programNdx = 0; programNdx < (int)pipelinePrograms.size(); ++programNdx)
{
- // begins with '#'?
- const std::string::size_type firstNonWhitespace = line.find_first_not_of("\t ");
- const bool isPreprocessorDirective = (firstNonWhitespace != std::string::npos && line.at(firstNonWhitespace) == '#');
+ 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;
- // Inject #extensions
- if (!isPreprocessorDirective && firstNonPreprocessorLine)
- {
- firstNonPreprocessorLine = false;
- resultBuf << extensions.str();
- }
+ DE_ASSERT(elemNdx+scalarSize <= (int)val.elements.size());
- resultBuf << line << "\n";
- }
+ if (loc == -1)
+ continue;
- return resultBuf.str();
-}
+ foundAnyMatch = true;
-// 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";
+ DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLfloat));
+ DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLint));
- res << glu::getGLSLVersionDeclaration(m_targetVersion) << "\n";
+ gl.useProgram(pipelinePrograms[programNdx]);
- // 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)
+ switch (dataType)
{
- DataType floatType = getDataTypeFloatScalars(val.dataType);
- const char* typeStr = getDataTypeName(floatType);
- res << vtxIn << " " << typeStr << " a_" << val.valueName << ";\n";
+ 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;
+ case TYPE_FLOAT_VEC3: gl.uniform3fv(loc, 1, &val.elements[elemNdx].float32); break;
+ case TYPE_FLOAT_VEC4: gl.uniform4fv(loc, 1, &val.elements[elemNdx].float32); break;
+ case TYPE_FLOAT_MAT2: gl.uniformMatrix2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
+ case TYPE_FLOAT_MAT3: gl.uniformMatrix3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
+ case TYPE_FLOAT_MAT4: gl.uniformMatrix4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
+ case TYPE_INT: gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32); break;
+ case TYPE_INT_VEC2: gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32); break;
+ case TYPE_INT_VEC3: gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32); break;
+ case TYPE_INT_VEC4: gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32); break;
+ case TYPE_BOOL: gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32); break;
+ case TYPE_BOOL_VEC2: gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32); break;
+ case TYPE_BOOL_VEC3: gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32); break;
+ case TYPE_BOOL_VEC4: gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32); break;
+ case TYPE_UINT: gl.uniform1uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
+ case TYPE_UINT_VEC2: gl.uniform2uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
+ case TYPE_UINT_VEC3: gl.uniform3uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
+ case TYPE_UINT_VEC4: gl.uniform4uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break;
+ case TYPE_FLOAT_MAT2X3: gl.uniformMatrix2x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
+ case TYPE_FLOAT_MAT2X4: gl.uniformMatrix2x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
+ case TYPE_FLOAT_MAT3X2: gl.uniformMatrix3x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
+ case TYPE_FLOAT_MAT3X4: gl.uniformMatrix3x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
+ case TYPE_FLOAT_MAT4X2: gl.uniformMatrix4x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
+ case TYPE_FLOAT_MAT4X3: gl.uniformMatrix4x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break;
- if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
- res << vtxOut << " " << typeStr << " " << val.valueName << ";\n";
- else
- res << vtxOut << " " << typeStr << " v_" << val.valueName << ";\n";
- }
- }
- res << "\n";
+ case TYPE_SAMPLER_2D:
+ case TYPE_SAMPLER_CUBE:
+ DE_FATAL("implement!");
+ break;
- // 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";
+ default:
+ DE_ASSERT(false);
}
}
- res << "}\n";
- return res.str();
+ if (!foundAnyMatch)
+ log << tcu::TestLog::Message << "WARNING // Uniform \"" << name << "\" location is not valid, location = -1. Cannot set value to the uniform." << tcu::TestLog::EndMessage;
}
-static void genCompareFunctions (ostringstream& stream, const ShaderCase::ValueBlock& valueBlock, bool useFloatTypes)
+static bool isTessellationPresent (const ShaderCaseSpecification& spec)
{
- 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++)
+ if (spec.programs[0].sources.separable)
{
- const ShaderCase::Value& val = valueBlock.values[valueNdx];
- if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
- cmpTypeFound[(int)val.dataType] = true;
- }
+ const deUint32 tessellationBits = (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) |
+ (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION);
- 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";
+ for (int programNdx = 0; programNdx < (int)spec.programs.size(); ++programNdx)
+ if (spec.programs[programNdx].activeStages & tessellationBits)
+ return true;
+ return false;
}
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";
+ return !spec.programs[0].sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty() ||
+ !spec.programs[0].sources.sources[glu::SHADERTYPE_TESSELLATION_EVALUATION].empty();
}
-static void genCompareOp (ostringstream& output, const char* dstVec4Var, const ShaderCase::ValueBlock& valueBlock, const char* nonFloatNamePrefix, const char* checkVarName)
+static bool checkPixels (tcu::TestLog& log, const tcu::ConstPixelBufferAccess& surface)
{
- bool isFirstOutput = true;
+ bool allWhite = true;
+ bool allBlack = true;
+ bool anyUnexpected = false;
- for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+ for (int y = 0; y < surface.getHeight(); y++)
{
- const ShaderCase::Value& val = valueBlock.values[ndx];
- const char* valueName = val.valueName.c_str();
-
- if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
+ for (int x = 0; x < surface.getWidth(); x++)
{
- // 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 && ";
+ const tcu::IVec4 pixel = surface.getPixelInt(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);
- // 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";
+ allWhite = allWhite && isWhite;
+ allBlack = allBlack && isBlack;
+ anyUnexpected = anyUnexpected || (!isWhite && !isBlack);
}
- // \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";
+ if (!allWhite)
+ {
+ if (anyUnexpected)
+ log << TestLog::Message << "WARNING: expecting all rendered pixels to be white or black, but got other colors as well!" << TestLog::EndMessage;
+ else if (!allBlack)
+ log << TestLog::Message << "WARNING: got inconsistent results over the image, when all pixels should be the same color!" << TestLog::EndMessage;
+
+ return false;
+ }
+
+ return true;
}
-string ShaderCase::genFragmentShader (const ValueBlock& valueBlock) const
+bool ShaderLibraryCase::execute (void)
{
- 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";
+ const float quadSize = 1.0f;
+ static const float s_positions[4*4] =
+ {
+ -quadSize, -quadSize, 0.0f, 1.0f,
+ -quadSize, +quadSize, 0.0f, 1.0f,
+ +quadSize, -quadSize, 0.0f, 1.0f,
+ +quadSize, +quadSize, 0.0f, 1.0f
+ };
- shader << glu::getGLSLVersionDeclaration(m_targetVersion) << "\n";
+ static const deUint16 s_indices[2*3] =
+ {
+ 0, 1, 2,
+ 1, 3, 2
+ };
- shader << "precision " << prec << " float;\n";
- shader << "precision " << prec << " int;\n";
- shader << "\n";
+ TestLog& log = m_testCtx.getLog();
+ const glw::Functions& gl = m_renderCtx.getFunctions();
- if (customColorOut)
- {
- shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
- shader << "\n";
- }
+ // 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<ProgramSources> specializedSources (m_spec.programs.size());
+
+ deUint32 vertexProgramID = -1;
+ vector<deUint32> pipelineProgramIDs;
+ vector<SharedPtr<ShaderProgram> > programs;
+ SharedPtr<ProgramPipeline> programPipeline;
- genCompareFunctions(shader, valueBlock, true);
- shader << "\n";
+ GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): start");
- // Declarations (varying, reference for each output).
- for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+ // 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
{
- const ShaderCase::Value& val = valueBlock.values[ndx];
- DataType floatType = getDataTypeFloatScalars(val.dataType);
- const char* floatTypeStr = getDataTypeName(floatType);
- const char* refTypeStr = getDataTypeName(val.dataType);
+ DE_ASSERT(m_spec.caseType == CASETYPE_COMPLETE);
- if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
+ 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++)
{
- if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
- shader << fragIn << " " << floatTypeStr << " " << val.valueName << ";\n";
- else
- shader << fragIn << " " << floatTypeStr << " v_" << val.valueName << ";\n";
+ const ProgramSpecializationParams progSpecParams (m_spec, checkAndSpecializeExtensions(m_spec.programs[progNdx].requiredExtensions, m_contextInfo), maxPatchVertices);
- shader << "uniform " << refTypeStr << " ref_" << val.valueName << ";\n";
+ specializeProgramSources(specializedSources[progNdx], m_spec.programs[progNdx].sources, progSpecParams);
}
}
- shader << "\n";
- shader << "void main()\n";
- shader << "{\n";
-
- shader << " ";
- genCompareOp(shader, customColorOut ? "dEQP_FragColor" : "gl_FragColor", valueBlock, "v_", DE_NULL);
+ if (!separablePrograms)
+ {
+ de::SharedPtr<glu::ShaderProgram> program (new glu::ShaderProgram(m_renderCtx, specializedSources[0]));
- shader << "}\n";
- return shader.str();
-}
+ vertexProgramID = program->getProgram();
+ pipelineProgramIDs.push_back(program->getProgram());
+ programs.push_back(program);
-// 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";
+ // Check that compile/link results are what we expect.
- // generated from "both" case
- DE_ASSERT(m_caseType == CASETYPE_VERTEX_ONLY);
+ DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0);
+ for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
+ if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk)
+ allCompilesOk = false;
- // Output (write out position).
- output << "gl_Position = dEQP_Position;\n";
+ if (!program->getProgramInfo().linkOk)
+ allLinksOk = false;
- // 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++)
+ log << *program;
+ }
+ else
{
- 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)
+ // Separate programs
+ for (size_t programNdx = 0; programNdx < m_spec.programs.size(); ++programNdx)
{
- 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<string, string> params;
- params.insert(pair<string, string>("DECLARATIONS", decl.str()));
- params.insert(pair<string, string>("SETUP", setup.str()));
- params.insert(pair<string, string>("OUTPUT", output.str()));
- params.insert(pair<string, string>("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);
+ de::SharedPtr<glu::ShaderProgram> program(new glu::ShaderProgram(m_renderCtx, specializedSources[programNdx]));
- return withExt;
-}
+ if (m_spec.programs[programNdx].activeStages & (1u << glu::SHADERTYPE_VERTEX))
+ vertexProgramID = program->getProgram();
-// 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;
+ pipelineProgramIDs.push_back(program->getProgram());
+ programs.push_back(program);
- const bool usesInout = usesShaderInoutQualifiers(m_targetVersion);
- const bool customColorOut = usesInout;
- const char* fragIn = usesInout ? "in" : "varying";
- const char* fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor";
+ // Check that compile/link results are what we expect.
- // generated from "both" case
- DE_ASSERT(m_caseType == CASETYPE_FRAGMENT_ONLY);
+ DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0);
+ for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
+ if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk)
+ allCompilesOk = false;
- genCompareFunctions(decl, valueBlock, false);
- genCompareOp(output, fragColor, valueBlock, "", DE_NULL);
+ if (!program->getProgramInfo().linkOk)
+ allLinksOk = false;
- if (customColorOut)
- decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
+ // Log program and active stages
+ {
+ const tcu::ScopedLogSection section (log, "Program", "Program " + de::toString(programNdx+1));
+ tcu::MessageBuilder builder (&log);
+ bool firstStage = 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();
- DataType floatType = getDataTypeFloatScalars(val.dataType);
- const char* floatTypeStr = getDataTypeName(floatType);
- const char* refTypeStr = getDataTypeName(val.dataType);
+ builder << "Pipeline uses stages: ";
+ for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
+ {
+ if (m_spec.programs[programNdx].activeStages & (1u << stage))
+ {
+ if (!firstStage)
+ builder << ", ";
+ builder << glu::getShaderTypeName((glu::ShaderType)stage);
+ firstStage = true;
+ }
+ }
+ builder << tcu::TestLog::EndMessage;
- 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";
+ log << *program;
}
}
- 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<string, string> params;
- params.insert(pair<string, string>("DECLARATIONS", decl.str()));
- params.insert(pair<string, string>("SETUP", setup.str()));
- params.insert(pair<string, string>("OUTPUT", output.str()));
- params.insert(pair<string, string>("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);
+ switch (m_spec.expectResult)
+ {
+ case EXPECT_PASS:
+ case EXPECT_VALIDATION_FAIL:
+ case EXPECT_BUILD_SUCCESSFUL:
+ if (!allCompilesOk)
+ failReason = "expected shaders to compile and link properly, but failed to compile.";
+ else if (!allLinksOk)
+ failReason = "expected shaders to compile and link properly, but failed to link.";
+ break;
- return withExt;
-}
+ case EXPECT_COMPILE_FAIL:
+ if (allCompilesOk && !allLinksOk)
+ failReason = "expected compilation to fail, but shaders compiled and link failed.";
+ else if (allCompilesOk)
+ failReason = "expected compilation to fail, but shaders compiled correctly.";
+ break;
-static map<string, string> 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<string, string> params;
+ case EXPECT_LINK_FAIL:
+ if (!allCompilesOk)
+ failReason = "expected linking to fail, but unable to compile.";
+ else if (allLinksOk)
+ failReason = "expected linking to fail, but passed.";
+ break;
- DE_UNREF(renderCtx);
+ case EXPECT_COMPILE_LINK_FAIL:
+ if (allCompilesOk && allLinksOk)
+ failReason = "expected compile or link to fail, but passed.";
+ break;
- decl << vtxIn << " highp vec4 dEQP_Position;\n";
+ default:
+ DE_ASSERT(false);
+ return false;
+ }
- for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+ if (failReason != DE_NULL)
{
- const ShaderCase::Value& val = valueBlock.values[ndx];
- const char* typeStr = getDataTypeName(val.dataType);
+ // \todo [2010-06-07 petri] These should be handled in the test case?
+ log << TestLog::Message << "ERROR: " << failReason << TestLog::EndMessage;
- if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
+ if (m_spec.fullGLSLES100Required)
{
- if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
+ log << TestLog::Message
+ << "Assuming build failure is caused by implementation not supporting full GLSL ES 100 specification, which is not required."
+ << TestLog::EndMessage;
+
+ if (allCompilesOk && !allLinksOk)
{
- decl << vtxIn << " " << typeStr << " " << val.valueName << ";\n";
+ // Used features are detectable at compile time. If implementation parses shader
+ // at link time, report it as quality warning.
+ m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason);
}
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";
- }
+ 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)
+ {
+ // If implementation parses shader at link time, report it as quality warning.
+ m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason);
}
- else if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM &&
- val.valueName.find('.') == string::npos)
- decl << "uniform " << typeStr << " " << val.valueName << ";\n";
+ else
+ m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failReason);
+ return false;
}
- params.insert(pair<string, string>("VERTEX_DECLARATIONS", decl.str()));
- params.insert(pair<string, string>("VERTEX_SETUP", setup.str()));
- params.insert(pair<string, string>("VERTEX_OUTPUT", string("gl_Position = dEQP_Position;\n")));
- return params;
-}
-
-static map<string, string> 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<string, string> params;
-
- DE_UNREF(renderCtx);
-
- genCompareFunctions(decl, valueBlock, false);
- genCompareOp(output, fragColor, valueBlock, "", DE_NULL);
+ // 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)
+ return true;
- if (customColorOut)
- decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
+ // Setup viewport.
+ gl.viewport(viewportX, viewportY, width, height);
- for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+ if (separablePrograms)
{
- const ShaderCase::Value& val = valueBlock.values[ndx];
- const char* valueName = val.valueName.c_str();
- const char* refTypeStr = getDataTypeName(val.dataType);
+ programPipeline = de::SharedPtr<glu::ProgramPipeline>(new glu::ProgramPipeline(m_renderCtx));
- 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)
+ // Setup pipeline
+ gl.bindProgramPipeline(programPipeline->getPipeline());
+ for (int programNdx = 0; programNdx < (int)m_spec.programs.size(); ++programNdx)
{
- decl << "uniform " << refTypeStr << " " << valueName << ";\n";
- }
- }
-
- params.insert(pair<string, string>("FRAGMENT_DECLARATIONS", decl.str()));
- params.insert(pair<string, string>("FRAGMENT_OUTPUT", output.str()));
- params.insert(pair<string, string>("FRAG_COLOR", fragColor));
- return params;
-}
+ deUint32 shaderFlags = 0;
+ for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
+ if (m_spec.programs[programNdx].activeStages & (1u << stage))
+ shaderFlags |= glu::getGLShaderTypeBit((glu::ShaderType)stage);
-static map<string, string> generateGeometrySpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock)
-{
- ostringstream decl;
- map<string, string> params;
+ programPipeline->useProgramStages(shaderFlags, pipelineProgramIDs[programNdx]);
+ }
- DE_UNREF(renderCtx);
- DE_UNREF(targetVersion);
+ programPipeline->activeShaderProgram(vertexProgramID);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "setup pipeline");
+ }
+ else
+ {
+ // Start using program
+ gl.useProgram(vertexProgramID);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()");
+ }
- decl << "layout (triangles) in;\n";
- decl << "layout (triangle_strip, max_vertices=3) out;\n";
- decl << "\n";
+ // Fetch location for positions positions.
+ int positionLoc = gl.getAttribLocation(vertexProgramID, "dEQP_Position");
+ if (positionLoc == -1)
+ {
+ string errStr = string("no location found for attribute 'dEQP_Position'");
+ TCU_FAIL(errStr.c_str());
+ }
- for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+ // Iterate all value blocks.
{
- const ShaderCase::Value& val = valueBlock.values[ndx];
- const char* valueName = val.valueName.c_str();
- const char* refTypeStr = getDataTypeName(val.dataType);
+ const ValueBlock& valueBlock = m_spec.values;
+
+ // 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();
- if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM &&
- val.valueName.find('.') == string::npos)
+ // Iterate all array sub-cases.
+ for (int arrayNdx = 0; arrayNdx < numRenderPasses; arrayNdx++)
{
- decl << "uniform " << refTypeStr << " " << valueName << ";\n";
- }
- }
+ vector<VertexArrayBinding> vertexArrays;
+ int attribValueNdx = 0;
+ vector<vector<float> > attribValues (valueBlock.inputs.size());
+ glw::GLenum postDrawError;
+ BeforeDrawValidator beforeDrawValidator (gl,
+ (separablePrograms) ? (programPipeline->getPipeline()) : (vertexProgramID),
+ (separablePrograms) ? (BeforeDrawValidator::TARGETTYPE_PIPELINE) : (BeforeDrawValidator::TARGETTYPE_PROGRAM));
- params.insert(pair<string, string>("GEOMETRY_DECLARATIONS", decl.str()));
- return params;
-}
+ vertexArrays.push_back(va::Float(positionLoc, 4, numVerticesPerDraw, 0, &s_positions[0]));
-static map<string, string> generateTessControlSpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock)
-{
- ostringstream decl;
- ostringstream output;
- map<string, string> params;
+ // Collect VA pointer for inputs
+ for (size_t valNdx = 0; valNdx < valueBlock.inputs.size(); 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<float>& 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
+ {
+ // convert to floats.
+ for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
+ {
+ 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;
+ }
+ }
+ }
- DE_UNREF(targetVersion);
+ // 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_";
- decl << "layout (vertices=3) out;\n";
- decl << "\n";
+ // 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;
+ }
- 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 (isDataTypeMatrix(dataType))
+ {
+ int numCols = getDataTypeMatrixNumColumns(dataType);
+ int numRows = getDataTypeMatrixNumRows(dataType);
+ DE_ASSERT(scalarSize == numCols*numRows);
- if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM &&
- val.valueName.find('.') == string::npos)
- {
- decl << "uniform " << refTypeStr << " " << valueName << ";\n";
- }
- }
+ 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]));
+ }
- 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;";
+ GLU_EXPECT_NO_ERROR(gl.getError(), "set vertex attrib array");
+ }
- params.insert(pair<string, string>("TESSELLATION_CONTROL_DECLARATIONS", decl.str()));
- params.insert(pair<string, string>("TESSELLATION_CONTROL_OUTPUT", output.str()));
- params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(queryGLInt(renderCtx, GL_MAX_PATCH_VERTICES))));
- return params;
-}
+ GLU_EXPECT_NO_ERROR(gl.getError(), "before set uniforms");
-static map<string, string> generateTessEvalSpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock)
-{
- ostringstream decl;
- ostringstream output;
- map<string, string> params;
+ // 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();
- DE_UNREF(targetVersion);
+ // 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");
+ }
- decl << "layout (triangles) in;\n";
- decl << "\n";
+ // set uniform values
+ for (size_t valNdx = 0; valNdx < valueBlock.uniforms.size(); valNdx++)
+ {
+ const Value& val = valueBlock.uniforms[valNdx];
+ const char* const valueName = val.name.c_str();
- 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);
+ setUniformValue(gl, pipelineProgramIDs, valueName, val, arrayNdx, m_testCtx.getLog());
+ GLU_EXPECT_NO_ERROR(gl.getError(), "set uniforms");
+ }
- if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM &&
- val.valueName.find('.') == string::npos)
- {
- decl << "uniform " << refTypeStr << " " << valueName << ";\n";
- }
- }
+ // Clear.
+ gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
+ gl.clear(GL_COLOR_BUFFER_BIT);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "clear buffer");
- 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";
+ // Use program or pipeline
+ if (separablePrograms)
+ gl.useProgram(0);
+ else
+ gl.useProgram(vertexProgramID);
- params.insert(pair<string, string>("TESSELLATION_EVALUATION_DECLARATIONS", decl.str()));
- params.insert(pair<string, string>("TESSELLATION_EVALUATION_OUTPUT", output.str()));
- params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(queryGLInt(renderCtx, GL_MAX_PATCH_VERTICES))));
- return params;
-}
+ // Draw.
+ if (tessellationPresent)
+ {
+ gl.patchParameteri(GL_PATCH_VERTICES, 3);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "set patchParameteri(PATCH_VERTICES, 3)");
+ }
-static void specializeShaders (const glu::RenderContext& renderCtx,
- glu::ProgramSources& dst,
- glu::ShaderType shaderType,
- const std::vector<std::string>& sources,
- const ShaderCase::ValueBlock& valueBlock,
- glu::GLSLVersion targetVersion,
- const std::vector<ShaderCase::CaseRequirement>& requirements,
- std::map<std::string, std::string> (*specializationGenerator)(const glu::RenderContext&, glu::GLSLVersion, const ShaderCase::ValueBlock&))
-{
- if (!sources.empty())
- {
- const std::map<std::string, std::string> specializationParams = specializationGenerator(renderCtx, targetVersion, valueBlock);
+ draw(m_renderCtx,
+ vertexProgramID,
+ (int)vertexArrays.size(),
+ &vertexArrays[0],
+ (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) ?
+ (&beforeDrawValidator) :
+ (DE_NULL));
- 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);
+ postDrawError = gl.getError();
- dst << glu::ShaderSource(shaderType, glslSource);
- }
- }
-}
+ if (m_spec.expectResult == EXPECT_PASS)
+ {
+ // Read back results.
+ Surface surface (width, height);
+ const float w = s_positions[3];
+ const int minY = deCeilFloatToInt32 (((-quadSize / w) * 0.5f + 0.5f) * (float)height + 1.0f);
+ const int maxY = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)height - 0.5f);
+ const int minX = deCeilFloatToInt32 (((-quadSize / w) * 0.5f + 0.5f) * (float)width + 1.0f);
+ const int maxX = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)width - 0.5f);
-void ShaderCase::specializeVertexShaders (glu::ProgramSources& dst, const std::vector<std::string>& sources, const ValueBlock& valueBlock, const std::vector<ShaderCase::CaseRequirement>& requirements) const
-{
- specializeShaders(m_renderCtx, dst, glu::SHADERTYPE_VERTEX, sources, valueBlock, m_targetVersion, requirements, generateVertexSpecialization);
-}
+ GLU_EXPECT_NO_ERROR(postDrawError, "draw");
-void ShaderCase::specializeFragmentShaders (glu::ProgramSources& dst, const std::vector<std::string>& sources, const ValueBlock& valueBlock, const std::vector<ShaderCase::CaseRequirement>& requirements) const
-{
- specializeShaders(m_renderCtx, dst, glu::SHADERTYPE_FRAGMENT, sources, valueBlock, m_targetVersion, requirements, generateFragmentSpecialization);
-}
+ glu::readPixels(m_renderCtx, viewportX, viewportY, surface.getAccess());
+ GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
-void ShaderCase::specializeGeometryShaders (glu::ProgramSources& dst, const std::vector<std::string>& sources, const ValueBlock& valueBlock, const std::vector<ShaderCase::CaseRequirement>& requirements) const
-{
- specializeShaders(m_renderCtx, dst, glu::SHADERTYPE_GEOMETRY, sources, valueBlock, m_targetVersion, requirements, generateGeometrySpecialization);
-}
+ if (!checkPixels(log, tcu::getSubregion(surface.getAccess(), minX, minY, maxX-minX+1, maxY-minY+1)))
+ {
+ log << TestLog::Message << "INCORRECT RESULT for sub-case " << arrayNdx+1 << " of " << numRenderPasses << "):"
+ << TestLog::EndMessage;
-void ShaderCase::specializeTessControlShaders (glu::ProgramSources& dst, const std::vector<std::string>& sources, const ValueBlock& valueBlock, const std::vector<ShaderCase::CaseRequirement>& requirements) const
-{
- specializeShaders(m_renderCtx, dst, glu::SHADERTYPE_TESSELLATION_CONTROL, sources, valueBlock, m_targetVersion, requirements, generateTessControlSpecialization);
-}
+ log << TestLog::Message << "Failing shader input/output values:" << TestLog::EndMessage;
+ dumpValues(log, valueBlock, arrayNdx);
-void ShaderCase::specializeTessEvalShaders (glu::ProgramSources& dst, const std::vector<std::string>& sources, const ValueBlock& valueBlock, const std::vector<ShaderCase::CaseRequirement>& requirements) const
-{
- specializeShaders(m_renderCtx, dst, glu::SHADERTYPE_TESSELLATION_EVALUATION, sources, valueBlock, m_targetVersion, requirements, generateTessEvalSpecialization);
-}
+ // Dump image on failure.
+ log << TestLog::Image("Result", "Rendered result image", surface);
-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");
+ gl.useProgram(0);
+ m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
+ return false;
+ }
}
- }
- else if (isDataTypeMatrix(dataType))
- {
- int numRows = getDataTypeMatrixNumRows(dataType);
- int numCols = getDataTypeMatrixNumColumns(dataType);
- for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
+ else if (m_spec.expectResult == EXPECT_VALIDATION_FAIL)
{
- result << " [ ";
- for (int colNdx = 0; colNdx < numCols; colNdx++)
+ log << TestLog::Message
+ << "Draw call generated error: "
+ << glu::getErrorStr(postDrawError) << " "
+ << ((postDrawError == GL_INVALID_OPERATION) ? ("(expected)") : ("(unexpected)")) << "\n"
+ << "Validate status: "
+ << glu::getBooleanStr(beforeDrawValidator.getValidateStatus()) << " "
+ << ((beforeDrawValidator.getValidateStatus() == GL_FALSE) ? ("(expected)") : ("(unexpected)")) << "\n"
+ << "Info log: "
+ << ((beforeDrawValidator.getInfoLog().empty()) ? ("[empty string]") : (beforeDrawValidator.getInfoLog())) << "\n"
+ << TestLog::EndMessage;
+
+ // test result
+
+ if (postDrawError != GL_NO_ERROR && postDrawError != GL_INVALID_OPERATION)
+ {
+ m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, ("Draw: got unexpected error: " + de::toString(glu::getErrorStr(postDrawError))).c_str());
+ return false;
+ }
+
+ if (beforeDrawValidator.getValidateStatus() == GL_TRUE)
+ {
+ if (postDrawError == GL_NO_ERROR)
+ m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but validation and rendering succeeded");
+ else if (postDrawError == GL_INVALID_OPERATION)
+ m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but validation succeeded (rendering failed as expected)");
+ else
+ DE_ASSERT(false);
+ return false;
+ }
+ else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_NO_ERROR)
+ {
+ m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but rendering succeeded (validation failed as expected)");
+ return false;
+ }
+ else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_INVALID_OPERATION)
{
- int elemNdx = (val.arrayLength == 1) ? 0 : arrayNdx;
- float v = val.elements[elemNdx*scalarSize + rowNdx*numCols + colNdx].float32;
- result << ((colNdx==0) ? "" : ", ") << v;
+ // Validation does not depend on input values, no need to test all values
+ return true;
}
- result << " ]\n";
+ else
+ DE_ASSERT(false);
}
+ else
+ DE_ASSERT(false);
}
+ }
- if (isDataTypeScalar(dataType))
- result << "\n";
- else if (isDataTypeVector(dataType))
- result << " ]\n";
+ gl.useProgram(0);
+ if (separablePrograms)
+ gl.bindProgramPipeline(0);
- m_testCtx.getLog() << TestLog::Message << result.str() << TestLog::EndMessage;
- }
+ GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): end");
+ return true;
+}
+
+TestCase::IterateResult ShaderLibraryCase::iterate (void)
+{
+ // Initialize state to pass.
+ m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
+
+ bool executeOk = execute();
+
+ DE_ASSERT(executeOk ? m_testCtx.getTestResult() == QP_TEST_RESULT_PASS : m_testCtx.getTestResult() != QP_TEST_RESULT_PASS);
+ DE_UNREF(executeOk);
+ return TestCase::STOP;
}
-} // sl
} // gls
} // deqp
*//*--------------------------------------------------------------------*/
#include "gluDefs.hpp"
-#include "gluShaderUtil.hpp"
-#include "gluRenderContext.hpp"
-#include "gluShaderProgram.hpp"
+#include "gluShaderLibrary.hpp"
#include "tcuTestCase.hpp"
-#include "tcuSurface.hpp"
-#include <string>
-#include <vector>
+namespace glu
+{
+class RenderContext;
+class ContextInfo;
+}
namespace deqp
{
namespace gls
{
-namespace sl
-{
// ShaderCase node.
-class ShaderCase : public tcu::TestCase
+class ShaderLibraryCase : 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<Element> 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<Value> 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<std::string>& 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<std::string> 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<ValueBlock>& values, const std::string& sharedSource);
- static ShaderCaseSpecification generateSharedSourceFragmentCase (ExpectResult expectResult_, glu::GLSLVersion targetVersion_, const std::vector<ValueBlock>& values, const std::string& sharedSource);
-
- ExpectResult expectResult;
- glu::GLSLVersion targetVersion;
- CaseType caseType;
- std::vector<CaseRequirement> requirements;
- std::vector<ValueBlock> valueBlocks;
- std::vector<std::string> vertexSources;
- std::vector<std::string> fragmentSources;
- std::vector<std::string> tessCtrlSources;
- std::vector<std::string> tessEvalSources;
- std::vector<std::string> geometrySources;
- };
-
- struct PipelineProgram
- {
- deUint32 activeStageBits;
- std::vector<CaseRequirement> requirements;
- std::vector<std::string> vertexSources;
- std::vector<std::string> fragmentSources;
- std::vector<std::string> tessCtrlSources;
- std::vector<std::string> tessEvalSources;
- std::vector<std::string> geometrySources;
- };
-
- struct PipelineCaseSpecification
- {
- ExpectResult expectResult;
- glu::GLSLVersion targetVersion;
- CaseType caseType;
- std::vector<ValueBlock> valueBlocks;
- std::vector<PipelineProgram> programs;
- };
-
// Methods.
- 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);
+ 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);
private:
- 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<std::string>& sources, const ValueBlock& valueBlock, const std::vector<ShaderCase::CaseRequirement>& requirements) const;
- void specializeFragmentShaders (glu::ProgramSources& dst, const std::vector<std::string>& sources, const ValueBlock& valueBlock, const std::vector<ShaderCase::CaseRequirement>& requirements) const;
- void specializeGeometryShaders (glu::ProgramSources& dst, const std::vector<std::string>& sources, const ValueBlock& valueBlock, const std::vector<ShaderCase::CaseRequirement>& requirements) const;
- void specializeTessControlShaders (glu::ProgramSources& dst, const std::vector<std::string>& sources, const ValueBlock& valueBlock, const std::vector<ShaderCase::CaseRequirement>& requirements) const;
- void specializeTessEvalShaders (glu::ProgramSources& dst, const std::vector<std::string>& sources, const ValueBlock& valueBlock, const std::vector<ShaderCase::CaseRequirement>& 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);
+ void init (void);
+ bool execute (void);
+ IterateResult iterate (void);
- struct ProgramObject
- {
- glu::ProgramSources programSources;
- PipelineProgram spec;
- };
+ ShaderLibraryCase (const ShaderLibraryCase&); // not allowed!
+ ShaderLibraryCase& operator= (const ShaderLibraryCase&); // not allowed!
// Member variables.
- 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<ValueBlock> m_valueBlocks;
- std::vector<ProgramObject> m_programs;
+ glu::RenderContext& m_renderCtx;
+ const glu::ContextInfo& m_contextInfo;
+ const glu::sl::ShaderCaseSpecification m_spec;
};
-} // sl
} // gls
} // deqp