Refactor ShaderLibrary
authorPyry Haulos <phaulos@google.com>
Tue, 1 Sep 2015 20:56:41 +0000 (13:56 -0700)
committerPyry Haulos <phaulos@google.com>
Wed, 2 Sep 2015 16:43:38 +0000 (09:43 -0700)
 * Refactor ShaderLibraryCase interface into more concise and standalone
ShaderCaseSpecification

 * Move .test file parser and common utilities to glutil

Change-Id: Iadf6e1baaf65f69df15d57301b1175c112bd6469

data/gles2/shaders/constants.test
data/gles3/shaders/constants.test
framework/opengl/CMakeLists.txt
framework/opengl/gluShaderLibrary.cpp [new file with mode: 0644]
framework/opengl/gluShaderLibrary.hpp [new file with mode: 0644]
modules/glshared/glsShaderConstExprTests.cpp
modules/glshared/glsShaderLibrary.cpp
modules/glshared/glsShaderLibrary.hpp
modules/glshared/glsShaderLibraryCase.cpp
modules/glshared/glsShaderLibraryCase.hpp

index 7564a5e..b2cb1b7 100644 (file)
@@ -1,13 +1,8 @@
 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 ""
index d8752a1..43a035c 100644 (file)
@@ -2,13 +2,8 @@ case float_input
        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 ""
index 13b7db2..810fb38 100644 (file)
@@ -52,6 +52,8 @@ set(GLUTIL_SRCS
        gluDummyRenderContext.hpp
        gluPlatform.cpp
        gluPlatform.hpp
+       gluShaderLibrary.cpp
+       gluShaderLibrary.hpp
        )
 
 set(GLUTIL_LIBS
diff --git a/framework/opengl/gluShaderLibrary.cpp b/framework/opengl/gluShaderLibrary.cpp
new file mode 100644 (file)
index 0000000..0e8530b
--- /dev/null
@@ -0,0 +1,1847 @@
+/*-------------------------------------------------------------------------
+ * 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
diff --git a/framework/opengl/gluShaderLibrary.hpp b/framework/opengl/gluShaderLibrary.hpp
new file mode 100644 (file)
index 0000000..ce05193
--- /dev/null
@@ -0,0 +1,198 @@
+#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
index f15fa74..3495baf 100644 (file)
@@ -37,6 +37,41 @@ namespace gls
 namespace ShaderConstExpr
 {
 
+static void addOutputVar (glu::sl::ValueBlock* dst, glu::DataType type, float output)
+{
+       dst->outputs.push_back(glu::sl::Value());
+
+       {
+               glu::sl::Value& value   = dst->outputs.back();
+
+               value.name      = "out0";
+               value.type      = glu::VarType(type, glu::PRECISION_LAST);
+               value.elements.resize(1);
+
+               switch (type)
+               {
+                       case glu::TYPE_INT:
+                               value.elements[0].int32 = (int)output;
+                               break;
+
+                       case glu::TYPE_UINT:
+                               value.elements[0].int32 = (unsigned int)output;
+                               break;
+
+                       case glu::TYPE_BOOL:
+                               value.elements[0].bool32 = output!=0.0f;
+                               break;
+
+                       case glu::TYPE_FLOAT:
+                               value.elements[0].float32 = output;
+                               break;
+
+                       default:
+                               DE_ASSERT(false);
+               }
+       }
+}
+
 std::vector<tcu::TestNode*> createTests (tcu::TestContext&                     testContext,
                                                                                 glu::RenderContext&            renderContext,
                                                                                 const glu::ContextInfo&        contextInfo,
@@ -47,7 +82,7 @@ std::vector<tcu::TestNode*> createTests (tcu::TestContext&                    testContext,
 {
        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);
@@ -80,17 +115,8 @@ std::vector<tcu::TestNode*> createTests (tcu::TestContext&                  testContext,
                "       ${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++)
        {
@@ -102,31 +128,6 @@ std::vector<tcu::TestNode*> createTests (tcu::TestContext&                 testContext,
                const string                            expression              = cases[caseNdx].expression;
                // Check for presence of func(vec, scalar) style specialization, use as gatekeeper for applying said specialization
                const bool                                      alwaysScalar    = expression.find("${MT}")!=string::npos;
-               ShaderCase::Value&                      expected                = shaderOutput[0].values[0];
-
-               switch (outType)
-               {
-                       case glu::TYPE_INT:
-                               expected.elements[0].int32 = (int)cases[caseNdx].output;
-                               break;
-
-                       case glu::TYPE_UINT:
-                               expected.elements[0].int32 = (unsigned int)cases[caseNdx].output;
-                               break;
-
-                       case glu::TYPE_BOOL:
-                               expected.elements[0].bool32 = cases[caseNdx].output!=0.0f;
-                               break;
-
-                       case glu::TYPE_FLOAT:
-                               expected.elements[0].float32 = cases[caseNdx].output;
-                               break;
-
-                       default:
-                               DE_ASSERT(false);
-               }
-
-               expected.dataType = outType;
 
                shaderTemplateParams["GLES_VERSION"]    = version == glu::GLSL_VERSION_300_ES ? "300 es" : "100";
                shaderTemplateParams["CASE_BASE_TYPE"]  = glu::getDataTypeName(outType);
@@ -153,26 +154,46 @@ std::vector<tcu::TestNode*> createTests (tcu::TestContext&                        testContext,
                                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
@@ -187,26 +208,46 @@ std::vector<tcu::TestNode*> createTests (tcu::TestContext&                        testContext,
                                        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));
+                                       }
                                }
                        }
                }
index 3956628..9877180 100644 (file)
 
 #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)
@@ -1421,41 +71,11 @@ ShaderLibrary::~ShaderLibrary (void)
 {
 }
 
-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
index 3639394..2040cd3 100644 (file)
@@ -42,13 +42,11 @@ public:
                                                                ~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;
index a37bb47..db8e498 100644 (file)
@@ -25,6 +25,8 @@
 
 #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
index 162ea0f..a1ae5a6 100644 (file)
  *//*--------------------------------------------------------------------*/
 
 #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