Revert "Refactor ShaderLibrary"
authorPyry Haulos <phaulos@google.com>
Wed, 2 Sep 2015 17:47:12 +0000 (17:47 +0000)
committerPyry Haulos <phaulos@google.com>
Wed, 2 Sep 2015 17:47:12 +0000 (17:47 +0000)
This reverts commit 877323dd00656b1045e89b4cc27d2e3ab9ecac2c.

Change-Id: I65ba2756b218d76f1189d82287dafbdb6316d725

data/gles2/shaders/constants.test
data/gles3/shaders/constants.test
framework/opengl/CMakeLists.txt
framework/opengl/gluShaderLibrary.cpp [deleted file]
framework/opengl/gluShaderLibrary.hpp [deleted file]
modules/glshared/glsShaderConstExprTests.cpp
modules/glshared/glsShaderLibrary.cpp
modules/glshared/glsShaderLibrary.hpp
modules/glshared/glsShaderLibraryCase.cpp
modules/glshared/glsShaderLibraryCase.hpp

index b2cb1b7..7564a5e 100644 (file)
@@ -1,8 +1,13 @@
 case float_input
        values
        {
-               input float in0         = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 | -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ];
-               output float out0       = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 | -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ];
+               input float in0         = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 ];
+               output float out0       = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 ];
+       }
+       values
+       {
+               input float in0         = [ -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ];
+               output float out0       = [ -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ];
        }
 
        both ""
index 43a035c..d8752a1 100644 (file)
@@ -2,8 +2,13 @@ case float_input
        version 300 es
        values
        {
-               input float in0         = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 | -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ];
-               output float out0       = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 | -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ];
+               input float in0         = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 ];
+               output float out0       = [ 1.123 | 0.75 | -512.0 | -72.13 | 199.91 ];
+       }
+       values
+       {
+               input float in0         = [ -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ];
+               output float out0       = [ -1.123 | -0.75 | 512.0 | -72.13 | -199.91 ];
        }
 
        both ""
index 810fb38..13b7db2 100644 (file)
@@ -52,8 +52,6 @@ set(GLUTIL_SRCS
        gluDummyRenderContext.hpp
        gluPlatform.cpp
        gluPlatform.hpp
-       gluShaderLibrary.cpp
-       gluShaderLibrary.hpp
        )
 
 set(GLUTIL_LIBS
diff --git a/framework/opengl/gluShaderLibrary.cpp b/framework/opengl/gluShaderLibrary.cpp
deleted file mode 100644 (file)
index 0e8530b..0000000
+++ /dev/null
@@ -1,1847 +0,0 @@
-/*-------------------------------------------------------------------------
- * drawElements Quality Program OpenGL ES Utilities
- * ------------------------------------------------
- *
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief Shader .test file utilities.
- *//*--------------------------------------------------------------------*/
-
-#include "gluShaderLibrary.hpp"
-
-#include "tcuStringTemplate.hpp"
-#include "tcuResource.hpp"
-#include "tcuTestLog.hpp"
-
-#include "deStringUtil.hpp"
-#include "deUniquePtr.hpp"
-#include "deFilePath.hpp"
-
-#include "glwEnums.hpp"
-
-#include <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
deleted file mode 100644 (file)
index ce05193..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-#ifndef _GLUSHADERLIBRARY_HPP
-#define _GLUSHADERLIBRARY_HPP
-/*-------------------------------------------------------------------------
- * drawElements Quality Program OpenGL ES Utilities
- * ------------------------------------------------
- *
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief Shader .test file utilities.
- *//*--------------------------------------------------------------------*/
-
-#include "gluDefs.hpp"
-#include "gluVarType.hpp"
-#include "gluShaderProgram.hpp"
-#include "tcuTestCase.hpp"
-
-#include <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 3495baf..f15fa74 100644 (file)
@@ -37,41 +37,6 @@ namespace gls
 namespace ShaderConstExpr
 {
 
-static void addOutputVar (glu::sl::ValueBlock* dst, glu::DataType type, float output)
-{
-       dst->outputs.push_back(glu::sl::Value());
-
-       {
-               glu::sl::Value& value   = dst->outputs.back();
-
-               value.name      = "out0";
-               value.type      = glu::VarType(type, glu::PRECISION_LAST);
-               value.elements.resize(1);
-
-               switch (type)
-               {
-                       case glu::TYPE_INT:
-                               value.elements[0].int32 = (int)output;
-                               break;
-
-                       case glu::TYPE_UINT:
-                               value.elements[0].int32 = (unsigned int)output;
-                               break;
-
-                       case glu::TYPE_BOOL:
-                               value.elements[0].bool32 = output!=0.0f;
-                               break;
-
-                       case glu::TYPE_FLOAT:
-                               value.elements[0].float32 = output;
-                               break;
-
-                       default:
-                               DE_ASSERT(false);
-               }
-       }
-}
-
 std::vector<tcu::TestNode*> createTests (tcu::TestContext&                     testContext,
                                                                                 glu::RenderContext&            renderContext,
                                                                                 const glu::ContextInfo&        contextInfo,
@@ -82,7 +47,7 @@ std::vector<tcu::TestNode*> createTests (tcu::TestContext&                    testContext,
 {
        using std::string;
        using std::vector;
-       using gls::ShaderLibraryCase;
+       using gls::sl::ShaderCase;
 
        // Needed for autogenerating shader code for increased component counts
        DE_STATIC_ASSERT(glu::TYPE_FLOAT+1 == glu::TYPE_FLOAT_VEC2);
@@ -115,8 +80,17 @@ std::vector<tcu::TestNode*> createTests (tcu::TestContext&                  testContext,
                "       ${OUTPUT}\n"
                "}\n";
 
-       const tcu::StringTemplate       shaderTemplate  (shaderTemplateSrc);
-       vector<tcu::TestNode*>          ret;
+       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());
 
        for (int caseNdx = 0; caseNdx < numCases; caseNdx++)
        {
@@ -128,6 +102,31 @@ 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);
@@ -154,46 +153,26 @@ std::vector<tcu::TestNode*> createTests (tcu::TestContext&                        testContext,
                                const string mapped = shaderTemplate.specialize(shaderTemplateParams);
 
                                if (testStage & SHADER_VERTEX)
-                               {
-                                       glu::sl::ShaderCaseSpecification        spec;
-
-                                       spec.targetVersion      = version;
-                                       spec.expectResult       = glu::sl::EXPECT_PASS;
-                                       spec.caseType           = glu::sl::CASETYPE_VERTEX_ONLY;
-                                       spec.programs.resize(1);
-
-                                       spec.programs[0].sources << glu::VertexSource(mapped);
-
-                                       addOutputVar(&spec.values, outType, cases[caseNdx].output);
-
-                                       ret.push_back(new ShaderLibraryCase(testContext,
-                                                                                                               renderContext,
-                                                                                                               contextInfo,
-                                                                                                               (caseName + "_vertex").c_str(),
-                                                                                                               "",
-                                                                                                               spec));
-                               }
+                                       ret.push_back(new ShaderCase(testContext,
+                                                                                                renderContext,
+                                                                                                contextInfo,
+                                                                                                (caseName + "_vertex").c_str(),
+                                                                                                "",
+                                                                                                ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase(ShaderCase::EXPECT_PASS,
+                                                                                                                                                                                                                                        version,
+                                                                                                                                                                                                                                        shaderOutput,
+                                                                                                                                                                                                                                        mapped)));
 
                                if (testStage & SHADER_FRAGMENT)
-                               {
-                                       glu::sl::ShaderCaseSpecification        spec;
-
-                                       spec.targetVersion      = version;
-                                       spec.expectResult       = glu::sl::EXPECT_PASS;
-                                       spec.caseType           = glu::sl::CASETYPE_FRAGMENT_ONLY;
-                                       spec.programs.resize(1);
-
-                                       spec.programs[0].sources << glu::FragmentSource(mapped);
-
-                                       addOutputVar(&spec.values, outType, cases[caseNdx].output);
-
-                                       ret.push_back(new ShaderLibraryCase(testContext,
-                                                                                                               renderContext,
-                                                                                                               contextInfo,
-                                                                                                               (caseName + "_fragment").c_str(),
-                                                                                                               "",
-                                                                                                               spec));
-                               }
+                                       ret.push_back(new ShaderCase(testContext,
+                                                                                                renderContext,
+                                                                                                contextInfo,
+                                                                                                (caseName + "_fragment").c_str(),
+                                                                                                "",
+                                                                                                ShaderCase::ShaderCaseSpecification::generateSharedSourceFragmentCase(ShaderCase::EXPECT_PASS,
+                                                                                                                                                                                                                                          version,
+                                                                                                                                                                                                                                          shaderOutput,
+                                                                                                                                                                                                                                          mapped)));
                        }
 
                        // Deal with functions that allways accept one ore more scalar parameters even when others are vectors
@@ -208,46 +187,26 @@ std::vector<tcu::TestNode*> createTests (tcu::TestContext&                        testContext,
                                        const string mapped = shaderTemplate.specialize(shaderTemplateParams);
 
                                        if (testStage & SHADER_VERTEX)
-                                       {
-                                               glu::sl::ShaderCaseSpecification        spec;
-
-                                               spec.targetVersion      = version;
-                                               spec.expectResult       = glu::sl::EXPECT_PASS;
-                                               spec.caseType           = glu::sl::CASETYPE_VERTEX_ONLY;
-                                               spec.programs.resize(1);
-
-                                               spec.programs[0].sources << glu::VertexSource(mapped);
-
-                                               addOutputVar(&spec.values, outType, cases[caseNdx].output);
-
-                                               ret.push_back(new ShaderLibraryCase(testContext,
-                                                                                                                       renderContext,
-                                                                                                                       contextInfo,
-                                                                                                                       (scalarCaseName + "_vertex").c_str(),
-                                                                                                                       "",
-                                                                                                                       spec));
-                                       }
+                                               ret.push_back(new ShaderCase(testContext,
+                                                                                                        renderContext,
+                                                                                                        contextInfo,
+                                                                                                        (scalarCaseName + "_vertex").c_str(),
+                                                                                                        "",
+                                                                                                        ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase(ShaderCase::EXPECT_PASS,
+                                                                                                                                                                                                                                                version,
+                                                                                                                                                                                                                                                shaderOutput,
+                                                                                                                                                                                                                                                mapped)));
 
                                        if (testStage & SHADER_FRAGMENT)
-                                       {
-                                               glu::sl::ShaderCaseSpecification        spec;
-
-                                               spec.targetVersion      = version;
-                                               spec.expectResult       = glu::sl::EXPECT_PASS;
-                                               spec.caseType           = glu::sl::CASETYPE_FRAGMENT_ONLY;
-                                               spec.programs.resize(1);
-
-                                               spec.programs[0].sources << glu::FragmentSource(mapped);
-
-                                               addOutputVar(&spec.values, outType, cases[caseNdx].output);
-
-                                               ret.push_back(new ShaderLibraryCase(testContext,
-                                                                                                                       renderContext,
-                                                                                                                       contextInfo,
-                                                                                                                       (scalarCaseName + "_fragment").c_str(),
-                                                                                                                       "",
-                                                                                                                       spec));
-                                       }
+                                               ret.push_back(new ShaderCase(testContext,
+                                                                                                        renderContext,
+                                                                                                        contextInfo,
+                                                                                                        (scalarCaseName + "_fragment").c_str(),
+                                                                                                        "",
+                                                                                                        ShaderCase::ShaderCaseSpecification::generateSharedSourceFragmentCase(ShaderCase::EXPECT_PASS,
+                                                                                                                                                                                                                                                  version,
+                                                                                                                                                                                                                                                  shaderOutput,
+                                                                                                                                                                                                                                                  mapped)));
                                }
                        }
                }
index 9877180..3956628 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');
+}
 
-namespace
+DE_INLINE deBool isCaseNameChar (char c)
 {
+       return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z') || deInRange32(c, '0', '9') || (c == '_') || (c == '-') || (c == '.');
+}
 
-class CaseFactory : public glu::sl::ShaderCaseFactory
+// \todo [2011-02-11 pyry] Should not depend on Context or TestContext!
+class ShaderParser
 {
 public:
-       CaseFactory (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo)
-               : m_testCtx                     (testCtx)
-               , m_renderCtx           (renderCtx)
-               , m_contextInfo         (contextInfo)
+                                                       ShaderParser                    (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* currentDir = DE_NULL);
+                                                       ~ShaderParser                   (void);
+
+       vector<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++;
        }
 
-       tcu::TestCaseGroup* createGroup (const std::string& name, const std::string& description, const std::vector<tcu::TestNode*>& children)
+       return o.str();
+}
+
+static string removeExtraIndentation (const string& source)
+{
+       // Detect indentation from first line.
+       int numIndentChars = 0;
+       for (int ndx = 0; ndx < (int)source.length() && isWhitespace(source[ndx]); ndx++)
+               numIndentChars += source[ndx] == '\t' ? 4 : 1;
+
+       // Process all lines and remove preceding indentation.
+       ostringstream processed;
        {
-               return new tcu::TestCaseGroup(m_testCtx, name.c_str(), description.c_str(), children);
+               bool    atLineStart                     = true;
+               int             indentCharsOmitted      = 0;
+
+               for (int pos = 0; pos < (int)source.length(); pos++)
+               {
+                       char c = source[pos];
+
+                       if (atLineStart && indentCharsOmitted < numIndentChars && (c == ' ' || c == '\t'))
+                       {
+                               indentCharsOmitted += c == '\t' ? 4 : 1;
+                       }
+                       else if (isEOL(c))
+                       {
+                               if (source[pos] == '\r' && source[pos+1] == '\n')
+                               {
+                                       pos += 1;
+                                       processed << '\n';
+                               }
+                               else
+                                       processed << c;
+
+                               atLineStart                     = true;
+                               indentCharsOmitted      = 0;
+                       }
+                       else
+                       {
+                               processed << c;
+                               atLineStart = false;
+                       }
+               }
        }
 
-       tcu::TestCase* createCase (const std::string& name, const std::string& description, const glu::sl::ShaderCaseSpecification& spec)
+       return processed.str();
+}
+
+string ShaderParser::parseShaderSource (const char* str)
+{
+       const char*             p = str+2;
+       ostringstream   o;
+
+       // Eat first empty line from beginning.
+       while (*p == ' ') p++;
+       if (*p == '\r') p++;
+       if (*p == '\n') p++;
+
+       while ((p[0] != '"') || (p[1] != '"'))
        {
-               return new ShaderLibraryCase(m_testCtx, m_renderCtx, m_contextInfo, name.c_str(), description.c_str(), spec);
+               if (*p == '\\')
+               {
+                       switch (p[1])
+                       {
+                               case 0:         DE_ASSERT(DE_FALSE);    break;
+                               case 'n':       o << '\n';                              break;
+                               case 't':       o << '\t';                              break;
+                               default:        o << p[1];                              break;
+                       }
+
+                       p += 2;
+               }
+               else
+                       o << *p++;
        }
 
-private:
-       tcu::TestContext&                       m_testCtx;
-       glu::RenderContext&                     m_renderCtx;
-       const glu::ContextInfo&         m_contextInfo;
-};
+       return removeExtraIndentation(o.str());
+}
+
+void ShaderParser::advanceToken (void)
+{
+       // Skip old token.
+       m_curPtr += m_curTokenStr.length();
+
+       // Reset token (for safety).
+       m_curToken              = TOKEN_INVALID;
+       m_curTokenStr   = "";
+
+       // Eat whitespace & comments while they last.
+       for (;;)
+       {
+               while (isWhitespace(*m_curPtr))
+                       m_curPtr++;
+
+               // Check for EOL comment.
+               if (*m_curPtr == '#')
+               {
+                       while (*m_curPtr && !isEOL(*m_curPtr))
+                               m_curPtr++;
+               }
+               else
+                       break;
+       }
+
+       if (!*m_curPtr)
+       {
+               m_curToken = TOKEN_EOF;
+               m_curTokenStr = "<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"));
 
-} // anonymous
+       // Parsed results.
+       ShaderCase::Value result;
 
-ShaderLibrary::ShaderLibrary (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo)
+       // Parse storage.
+       if (m_curToken == TOKEN_UNIFORM)
+               result.storageType = ShaderCase::Value::STORAGE_UNIFORM;
+       else if (m_curToken == TOKEN_INPUT)
+               result.storageType = ShaderCase::Value::STORAGE_INPUT;
+       else if (m_curToken == TOKEN_OUTPUT)
+               result.storageType = ShaderCase::Value::STORAGE_OUTPUT;
+       else
+               parseError(string("unexpected token encountered when parsing value classifier"));
+       advanceToken();
+
+       // Parse data type.
+       result.dataType = mapDataTypeToken(m_curToken);
+       if (result.dataType == TYPE_INVALID)
+               parseError(string("unexpected token when parsing value data type: " + m_curTokenStr));
+       advanceToken();
+
+       // Parse value name.
+       if (m_curToken == TOKEN_IDENTIFIER || m_curToken == TOKEN_STRING)
+       {
+               if (m_curToken == TOKEN_IDENTIFIER)
+                       result.valueName = m_curTokenStr;
+               else
+                       result.valueName = parseStringLiteral(m_curTokenStr.c_str());
+       }
+       else
+               parseError(string("unexpected token when parsing value name: " + m_curTokenStr));
+       advanceToken();
+
+       // Parse assignment operator.
+       advanceToken(TOKEN_ASSIGN);
+
+       // Parse actual value.
+       if (m_curToken == TOKEN_LEFT_BRACKET) // value list
+       {
+               advanceToken(TOKEN_LEFT_BRACKET);
+               result.arrayLength = 0;
+
+               for (;;)
+               {
+                       parseValueElement(result.dataType, result);
+                       result.arrayLength++;
+
+                       if (m_curToken == TOKEN_RIGHT_BRACKET)
+                               break;
+                       else if (m_curToken == TOKEN_VERTICAL_BAR)
+                       {
+                               advanceToken();
+                               continue;
+                       }
+                       else
+                               parseError(string("unexpected token in value element array: " + m_curTokenStr));
+               }
+
+               advanceToken(TOKEN_RIGHT_BRACKET);
+       }
+       else // arrays, single elements
+       {
+               parseValueElement(result.dataType, result);
+               result.arrayLength = 1;
+       }
+
+       advanceToken(TOKEN_SEMI_COLON); // end of declaration
+
+       valueBlock.values.push_back(result);
+}
+
+void ShaderParser::parseValueBlock (ShaderCase::ValueBlock& valueBlock)
+{
+       PARSE_DBG(("    parseValueBlock()\n"));
+       advanceToken(TOKEN_VALUES);
+       advanceToken(TOKEN_LEFT_BRACE);
+
+       for (;;)
+       {
+               if (m_curToken == TOKEN_UNIFORM || m_curToken == TOKEN_INPUT || m_curToken == TOKEN_OUTPUT)
+                       parseValue(valueBlock);
+               else if (m_curToken == TOKEN_RIGHT_BRACE)
+                       break;
+               else
+                       parseError(string("unexpected token when parsing a value block: " + m_curTokenStr));
+       }
+
+       advanceToken(TOKEN_RIGHT_BRACE);
+
+       // Compute combined array length of value block.
+       int arrayLength = 1;
+       for (int valueNdx = 0; valueNdx < (int)valueBlock.values.size(); valueNdx++)
+       {
+               const ShaderCase::Value& val = valueBlock.values[valueNdx];
+               if (val.arrayLength > 1)
+               {
+                       DE_ASSERT(arrayLength == 1 || arrayLength == val.arrayLength);
+                       arrayLength = val.arrayLength;
+               }
+       }
+       valueBlock.arrayLength = arrayLength;
+}
+
+deUint32 ShaderParser::parseShaderStageList (void)
+{
+       deUint32 mask = 0;
+
+       assumeToken(TOKEN_LEFT_BRACE);
+
+       // don't allow 0-sized lists
+       advanceToken();
+       mask |= getShaderStageLiteralFlag();
+       advanceToken();
+
+       for (;;)
+       {
+               if (m_curToken == TOKEN_RIGHT_BRACE)
+                       break;
+               else if (m_curToken == TOKEN_COMMA)
+               {
+                       deUint32 stageFlag;
+                       advanceToken();
+
+                       stageFlag = getShaderStageLiteralFlag();
+                       if (stageFlag & mask)
+                               parseError(string("stage already set in the shader stage set: " + m_curTokenStr));
+
+                       mask |= stageFlag;
+                       advanceToken();
+               }
+               else
+                       parseError(string("invalid shader stage set token: " + m_curTokenStr));
+       }
+       advanceToken(TOKEN_RIGHT_BRACE);
+
+       return mask;
+}
+
+void ShaderParser::parseRequirement (ShaderCase::CaseRequirement& valueBlock)
+{
+       PARSE_DBG(("    parseRequirement()\n"));
+
+       advanceToken();
+       assumeToken(TOKEN_IDENTIFIER);
+
+       if (m_curTokenStr == "extension")
+       {
+               std::vector<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('/');
+
+       if (lastDelim == std::string::npos)
+               return "";
+       else
+               return filePath.substr(0, lastDelim+1);
+}
+
+ShaderLibrary::ShaderLibrary (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo)
        : m_testCtx                     (testCtx)
        , m_renderCtx           (renderCtx)
        , m_contextInfo         (contextInfo)
@@ -71,11 +1421,41 @@ ShaderLibrary::~ShaderLibrary (void)
 {
 }
 
-std::vector<tcu::TestNode*> ShaderLibrary::loadShaderFile (const char* fileName)
+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)
 {
-       CaseFactory     caseFactory     (m_testCtx, m_renderCtx, m_contextInfo);
+       sl::ShaderParser parser(m_testCtx, m_renderCtx, m_contextInfo);
+       vector<tcu::TestNode*> nodes = parser.parse(shaderSource);
 
-       return glu::sl::parseFile(m_testCtx.getArchive(), fileName, &caseFactory);
+       return nodes;
 }
 
 } // gls
index 2040cd3..3639394 100644 (file)
@@ -42,11 +42,13 @@ 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 db8e498..a37bb47 100644 (file)
@@ -25,8 +25,6 @@
 
 #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
+{
 
-using namespace tcu;
-using namespace glu;
-using namespace glu::sl;
+enum
+{
+       VIEWPORT_WIDTH          = 128,
+       VIEWPORT_HEIGHT         = 128
+};
 
-using std::vector;
-using std::string;
-using std::ostringstream;
-using std::map;
-using std::pair;
+static inline bool usesShaderInoutQualifiers (glu::GLSLVersion version)
+{
+       switch (version)
+       {
+               case glu::GLSL_VERSION_100_ES:
+               case glu::GLSL_VERSION_130:
+               case glu::GLSL_VERSION_140:
+               case glu::GLSL_VERSION_150:
+                       return false;
 
-using de::SharedPtr;
+               default:
+                       return true;
+       }
+}
 
-// OpenGL-specific specialization utils
+static inline bool supportsFragmentHighp (glu::GLSLVersion version)
+{
+       return version != glu::GLSL_VERSION_100_ES;
+}
 
-static vector<RequiredExtension> checkAndSpecializeExtensions (const vector<RequiredExtension>&        src,
-                                                                                                                          const ContextInfo&                           ctxInfo)
+static int queryGLInt (const glu::RenderContext& renderCtx, deUint32 pname)
 {
-       vector<RequiredExtension>       specialized;
+       const glw::Functions&   gl              = renderCtx.getFunctions();
+       glw::GLint                              value   = 0;
 
-       for (size_t extNdx = 0; extNdx < src.size(); ++extNdx)
-       {
-               const RequiredExtension&        extension               = src[extNdx];
-               int                                                     supportedAltNdx = -1;
+       gl.getIntegerv(pname, &value);
+       GLU_EXPECT_NO_ERROR(gl.getError(), ("query " + de::toString(glu::getGettableStateStr(pname))).c_str());
 
-               for (size_t alternativeNdx = 0; alternativeNdx < extension.alternatives.size(); ++alternativeNdx)
-               {
-                       if (ctxInfo.isExtensionSupported(extension.alternatives[alternativeNdx].c_str()))
-                       {
-                               supportedAltNdx = (int)alternativeNdx;
-                               break;
-                       }
-               }
+       return value;
+}
 
-               if (supportedAltNdx >= 0)
-               {
-                       specialized.push_back(RequiredExtension(extension.alternatives[supportedAltNdx], extension.effectiveStages));
-               }
-               else
-               {
-                       // no extension(s). Make a nice output
-                       std::ostringstream extensionList;
+ShaderCase::ValueBlock::ValueBlock (void)
+       : arrayLength(0)
+{
+}
 
-                       for (size_t ndx = 0; ndx < extension.alternatives.size(); ++ndx)
-                       {
-                               if (!extensionList.str().empty())
-                                       extensionList << ", ";
-                               extensionList << extension.alternatives[ndx];
-                       }
+ShaderCase::CaseRequirement::CaseRequirement (void)
+       : m_type                                                (REQUIREMENTTYPE_LAST)
+       , m_supportedExtensionNdx               (-1)
+       , m_effectiveShaderStageFlags   (-1)
+       , m_enumName                                    (-1)
+       , m_referenceValue                              (-1)
+{
+}
 
-                       if (extension.alternatives.size() == 1)
-                               throw tcu::NotSupportedError("Test requires extension " + extensionList.str());
-                       else
-                               throw tcu::NotSupportedError("Test requires any extension of " + extensionList.str());
-               }
-       }
+ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createAnyExtensionRequirement (const std::vector<std::string>& requirements, deUint32 effectiveShaderStageFlags)
+{
+       CaseRequirement retVal;
+
+       retVal.m_type = REQUIREMENTTYPE_EXTENSION;
+       retVal.m_extensions = requirements;
+       retVal.m_effectiveShaderStageFlags = effectiveShaderStageFlags;
 
-       return specialized;
+       return retVal;
 }
 
-static void checkImplementationLimits (const vector<RequiredCapability>&       requiredCaps,
-                                                                          const ContextInfo&                                   ctxInfo)
+ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createLimitRequirement (deUint32 enumName, int ref)
 {
-       for (size_t capNdx = 0; capNdx < requiredCaps.size(); ++capNdx)
-       {
-               const deUint32  pname                   = requiredCaps[capNdx].enumName;
-               const int               requiredValue   = requiredCaps[capNdx].referenceValue;
-               const int               supportedValue  = ctxInfo.getInt((int)pname);
+       CaseRequirement retVal;
 
-               if (supportedValue <= requiredValue)
-                       throw tcu::NotSupportedError("Test requires " + de::toString(glu::getGettableStateStr(pname)) + " (" + de::toString(supportedValue) + ") >= " + de::toString(requiredValue));
-       }
-}
+       retVal.m_type = REQUIREMENTTYPE_IMPLEMENTATION_LIMIT;
+       retVal.m_enumName = enumName;
+       retVal.m_referenceValue = ref;
 
-// Shader source specialization
+       return retVal;
+}
 
-// This functions builds a matching vertex shader for a 'both' case, when
-// the fragment shader is being tested.
-// We need to build attributes and varyings for each 'input'.
-static string genVertexShader (const ShaderCaseSpecification& spec)
+ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createFullGLSLES100SpecificationRequirement (void)
 {
-       ostringstream           res;
-       const bool                      usesInout       = glslVersionUsesInOutQualifiers(spec.targetVersion);
-       const char* const       vtxIn           = usesInout ? "in"      : "attribute";
-       const char* const       vtxOut          = usesInout ? "out"     : "varying";
+       CaseRequirement retVal;
 
-       res << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n";
+       retVal.m_type = REQUIREMENTTYPE_FULL_GLSL_ES_100_SPEC;
 
-       // Declarations (position + attribute/varying for each input).
-       res << "precision highp float;\n";
-       res << "precision highp int;\n";
-       res << "\n";
-       res << vtxIn << " highp vec4 dEQP_Position;\n";
+       return retVal;
+}
+
+void ShaderCase::CaseRequirement::checkRequirements (glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo)
+{
+       DE_UNREF(renderCtx);
 
-       for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
+       switch (m_type)
        {
-               const Value&            val                     = spec.values.inputs[ndx];
-               const DataType          basicType       = val.type.getBasicType();
-               const DataType          floatType       = getDataTypeFloatScalars(basicType);
-               const char* const       typeStr         = getDataTypeName(floatType);
+               case REQUIREMENTTYPE_EXTENSION:
+               {
+                       for (int ndx = 0; ndx < (int)m_extensions.size(); ++ndx)
+                       {
+                               if (contextInfo.isExtensionSupported(m_extensions[ndx].c_str()))
+                               {
+                                       m_supportedExtensionNdx = ndx;
+                                       return;
+                               }
+                       }
 
-               res << vtxIn << " " << typeStr << " a_" << val.name << ";\n";
+                       // no extension(s). Make a nice output
+                       {
+                               std::ostringstream extensionList;
 
-               if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
-                       res << vtxOut << " " << typeStr << " " << val.name << ";\n";
-               else
-                       res << vtxOut << " " << typeStr << " v_" << val.name << ";\n";
-       }
-       res << "\n";
+                               for (int ndx = 0; ndx < (int)m_extensions.size(); ++ndx)
+                               {
+                                       if (!extensionList.str().empty())
+                                               extensionList << ", ";
+                                       extensionList << m_extensions[ndx];
+                               }
 
-       // Main function.
-       // - gl_Position = dEQP_Position;
-       // - for each input: write attribute directly to varying
-       res << "void main()\n";
-       res << "{\n";
-       res << "        gl_Position = dEQP_Position;\n";
-       for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
-       {
-               const Value&    val             = spec.values.inputs[ndx];
-               const string&   name    = val.name;
+                               if (m_extensions.size() == 1)
+                                       throw tcu::NotSupportedError("Test requires extension " + extensionList.str());
+                               else
+                                       throw tcu::NotSupportedError("Test requires any extension of " + extensionList.str());
+                       }
 
-               if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT)
-                       res << "        " << name << " = a_" << name << ";\n";
-               else
-                       res << "        v_" << name << " = a_" << name << ";\n";
-       }
+                       // cannot be reached
+               }
 
-       res << "}\n";
-       return res.str();
-}
+               case REQUIREMENTTYPE_IMPLEMENTATION_LIMIT:
+               {
+                       const glw::Functions&   gl              = renderCtx.getFunctions();
+                       glw::GLint                              value   = 0;
+                       glw::GLenum                             error;
 
-static void genCompareOp (ostringstream& output, const char* dstVec4Var, const ValueBlock& valueBlock, const char* nonFloatNamePrefix, const char* checkVarName)
-{
-       bool isFirstOutput = true;
+                       gl.getIntegerv(m_enumName, &value);
+                       error = gl.getError();
 
-       for (size_t ndx = 0; ndx < valueBlock.outputs.size(); ndx++)
-       {
-               const Value&    val             = valueBlock.outputs[ndx];
+                       if (error != GL_NO_ERROR)
+                               throw tcu::TestError("Query for " + de::toString(glu::getGettableStateStr(m_enumName)) +  " generated " + de::toString(glu::getErrorStr(error)));
 
-               // Check if we're only interested in one variable (then skip if not the right one).
-               if (checkVarName && val.name != checkVarName)
-                       continue;
+                       if (!(value > m_referenceValue))
+                               throw tcu::NotSupportedError("Test requires " + de::toString(glu::getGettableStateStr(m_enumName)) + " (" + de::toString(value) + ") > " + de::toString(m_referenceValue));
+
+                       return;
+               }
 
-               // Prefix.
-               if (isFirstOutput)
+               case REQUIREMENTTYPE_FULL_GLSL_ES_100_SPEC:
                {
-                       output << "bool RES = ";
-                       isFirstOutput = false;
+                       // cannot be queried
+                       return;
                }
-               else
-                       output << "RES = RES && ";
 
-               // Generate actual comparison.
-               if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT)
-                       output << "isOk(" << val.name << ", ref_" << val.name << ", 0.05);\n";
-               else
-                       output << "isOk(" << nonFloatNamePrefix << val.name << ", ref_" << val.name << ");\n";
+               default:
+                       DE_ASSERT(false);
        }
-
-       if (isFirstOutput)
-               output << dstVec4Var << " = vec4(1.0);\n";      // \todo [petri] Should we give warning if not expect-failure case?
-       else
-               output << dstVec4Var << " = vec4(RES, RES, RES, 1.0);\n";
 }
 
-static inline bool supportsFragmentHighp (glu::GLSLVersion version)
+ShaderCase::ShaderCaseSpecification::ShaderCaseSpecification (void)
+       : expectResult  (EXPECT_LAST)
+       , targetVersion (glu::GLSL_VERSION_LAST)
+       , caseType              (CASETYPE_COMPLETE)
 {
-       return version != glu::GLSL_VERSION_100_ES;
 }
 
-static string genFragmentShader (const ShaderCaseSpecification& spec)
+ShaderCase::ShaderCaseSpecification ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase (ExpectResult expectResult_, glu::GLSLVersion targetVersion_, const std::vector<ValueBlock>& values, const std::string& sharedSource)
 {
-       ostringstream           shader;
-       const bool                      usesInout               = glslVersionUsesInOutQualifiers(spec.targetVersion);
-       const bool                      customColorOut  = usesInout;
-       const char*     const   fragIn                  = usesInout ? "in" : "varying";
-       const char*     const   prec                    = supportsFragmentHighp(spec.targetVersion) ? "highp" : "mediump";
-
-       shader << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n";
+       ShaderCaseSpecification retVal;
+       retVal.expectResult             = expectResult_;
+       retVal.targetVersion    = targetVersion_;
+       retVal.caseType                 = CASETYPE_VERTEX_ONLY;
+       retVal.valueBlocks              = values;
+       retVal.vertexSources.push_back(sharedSource);
+       return retVal;
+}
 
-       shader << "precision " << prec << " float;\n";
-       shader << "precision " << prec << " int;\n";
-       shader << "\n";
+ShaderCase::ShaderCaseSpecification ShaderCase::ShaderCaseSpecification::generateSharedSourceFragmentCase (ExpectResult expectResult_, glu::GLSLVersion targetVersion_, const std::vector<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 (customColorOut)
+class BeforeDrawValidator : public glu::DrawUtilCallback
+{
+public:
+       enum TargetType
        {
-               shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
-               shader << "\n";
-       }
+               TARGETTYPE_PROGRAM = 0,
+               TARGETTYPE_PIPELINE,
 
-       genCompareFunctions(shader, spec.values, true);
-       shader << "\n";
+               TARGETTYPE_LAST
+       };
 
-       // Declarations (varying, reference for each output).
-       for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
-       {
-               const Value&            val                             = spec.values.outputs[ndx];
-               const DataType          basicType               = val.type.getBasicType();
-               const DataType          floatType               = getDataTypeFloatScalars(basicType);
-               const char* const       floatTypeStr    = getDataTypeName(floatType);
-               const char* const       refTypeStr              = getDataTypeName(basicType);
-
-               if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
-                       shader << fragIn << " " << floatTypeStr << " " << val.name << ";\n";
-               else
-                       shader << fragIn << " " << floatTypeStr << " v_" << val.name << ";\n";
+                                                       BeforeDrawValidator     (const glw::Functions& gl, glw::GLuint target, TargetType targetType);
 
-               shader << "uniform " << refTypeStr << " ref_" << val.name << ";\n";
-       }
+       void                                    beforeDrawCall          (void);
 
-       shader << "\n";
-       shader << "void main()\n";
-       shader << "{\n";
+       const std::string&              getInfoLog                      (void) const;
+       glw::GLint                              getValidateStatus       (void) const;
 
-       shader << "     ";
-       genCompareOp(shader, customColorOut ? "dEQP_FragColor" : "gl_FragColor", spec.values, "v_", DE_NULL);
+private:
+       const glw::Functions&   m_gl;
+       const glw::GLuint               m_target;
+       const TargetType                m_targetType;
 
-       shader << "}\n";
-       return shader.str();
+       glw::GLint                              m_validateStatus;
+       std::string                             m_logMessage;
+};
+
+BeforeDrawValidator::BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType)
+       : m_gl                          (gl)
+       , m_target                      (target)
+       , m_targetType          (targetType)
+       , m_validateStatus      (-1)
+{
+       DE_ASSERT(targetType < TARGETTYPE_LAST);
 }
 
-// Specialize a shader for the vertex shader test case.
-static string specializeVertexShader (const ShaderCaseSpecification& spec, const std::string& src)
+void BeforeDrawValidator::beforeDrawCall (void)
 {
-       ostringstream           decl;
-       ostringstream           setup;
-       ostringstream           output;
-       const bool                      usesInout       = glslVersionUsesInOutQualifiers(spec.targetVersion);
-       const char* const       vtxIn           = usesInout ? "in"      : "attribute";
-       const char* const       vtxOut          = usesInout ? "out"     : "varying";
+       glw::GLint                                      bytesWritten    = 0;
+       glw::GLint                                      infoLogLength;
+       std::vector<glw::GLchar>        logBuffer;
+       int                                                     stringLength;
 
-       // generated from "both" case
-       DE_ASSERT(spec.caseType == CASETYPE_VERTEX_ONLY);
+       // validate
+       if (m_targetType == TARGETTYPE_PROGRAM)
+               m_gl.validateProgram(m_target);
+       else if (m_targetType == TARGETTYPE_PIPELINE)
+               m_gl.validateProgramPipeline(m_target);
+       else
+               DE_ASSERT(false);
 
-       // Output (write out position).
-       output << "gl_Position = dEQP_Position;\n";
+       GLU_EXPECT_NO_ERROR(m_gl.getError(), "validate");
 
-       // Declarations (position + attribute for each input, varying for each output).
-       decl << vtxIn << " highp vec4 dEQP_Position;\n";
+       // check status
+       m_validateStatus = -1;
 
-       for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
-       {
-               const Value&            val                             = spec.values.inputs[ndx];
-               const DataType          basicType               = val.type.getBasicType();
-               const DataType          floatType               = getDataTypeFloatScalars(basicType);
-               const char* const       floatTypeStr    = getDataTypeName(floatType);
-               const char* const       refTypeStr              = getDataTypeName(basicType);
+       if (m_targetType == TARGETTYPE_PROGRAM)
+               m_gl.getProgramiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus);
+       else if (m_targetType == TARGETTYPE_PIPELINE)
+               m_gl.getProgramPipelineiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus);
+       else
+               DE_ASSERT(false);
 
-               if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
-               {
-                       decl << vtxIn << " " << floatTypeStr << " " << val.name << ";\n";
-               }
-               else
-               {
-                       decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n";
-                       setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(a_" << val.name << ");\n";
-               }
-       }
+       GLU_EXPECT_NO_ERROR(m_gl.getError(), "get validate status");
+       TCU_CHECK(m_validateStatus == GL_TRUE || m_validateStatus == GL_FALSE);
 
-       // \todo [2015-07-24 pyry] Why are uniforms missing?
+       // read log
 
-       for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
-       {
-               const Value&            val                             = spec.values.outputs[ndx];
-               const DataType          basicType               = val.type.getBasicType();
-               const DataType          floatType               = getDataTypeFloatScalars(basicType);
-               const char* const       floatTypeStr    = getDataTypeName(floatType);
-               const char* const       refTypeStr              = getDataTypeName(basicType);
-
-               if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
-                       decl << vtxOut << " " << floatTypeStr << " " << val.name << ";\n";
-               else
-               {
-                       decl << vtxOut << " " << floatTypeStr << " v_" << val.name << ";\n";
-                       decl << refTypeStr << " " << val.name << ";\n";
+       infoLogLength = 0;
 
-                       output << "v_" << val.name << " = " << floatTypeStr << "(" << val.name << ");\n";
-               }
+       if (m_targetType == TARGETTYPE_PROGRAM)
+               m_gl.getProgramiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength);
+       else if (m_targetType == TARGETTYPE_PIPELINE)
+               m_gl.getProgramPipelineiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength);
+       else
+               DE_ASSERT(false);
+
+       GLU_EXPECT_NO_ERROR(m_gl.getError(), "get info log length");
+
+       if (infoLogLength <= 0)
+       {
+               m_logMessage.clear();
+               return;
        }
 
-       // Shader specialization.
-       map<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"));
+       logBuffer.resize(infoLogLength + 2, '0'); // +1 for zero terminator (infoLogLength should include it, but better play it safe), +1 to make sure buffer is always larger
 
-       StringTemplate  tmpl    (src);
-       const string    baseSrc = tmpl.specialize(params);
-       const string    withExt = injectExtensionRequirements(baseSrc, spec.programs[0].requiredExtensions, SHADERTYPE_VERTEX);
+       if (m_targetType == TARGETTYPE_PROGRAM)
+               m_gl.getProgramInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]);
+       else if (m_targetType == TARGETTYPE_PIPELINE)
+               m_gl.getProgramPipelineInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]);
+       else
+               DE_ASSERT(false);
 
-       return withExt;
+       // just ignore bytesWritten to be safe, find the null terminator
+       stringLength = (int)(std::find(logBuffer.begin(), logBuffer.end(), '0') - logBuffer.begin());
+       m_logMessage.assign(&logBuffer[0], stringLength);
 }
 
-// Specialize a shader for the fragment shader test case.
-static string specializeFragmentShader (const ShaderCaseSpecification& spec, const std::string& src)
+const std::string& BeforeDrawValidator::getInfoLog (void) const
 {
-       ostringstream           decl;
-       ostringstream           setup;
-       ostringstream           output;
-
-       const bool                      usesInout               = glslVersionUsesInOutQualifiers(spec.targetVersion);
-       const bool                      customColorOut  = usesInout;
-       const char* const       fragIn                  = usesInout                     ? "in"                          : "varying";
-       const char* const       fragColor               = customColorOut        ? "dEQP_FragColor"      : "gl_FragColor";
-
-       // generated from "both" case
-       DE_ASSERT(spec.caseType == CASETYPE_FRAGMENT_ONLY);
+       return m_logMessage;
+}
 
-       genCompareFunctions(decl, spec.values, false);
-       genCompareOp(output, fragColor, spec.values, "", DE_NULL);
+glw::GLint BeforeDrawValidator::getValidateStatus (void) const
+{
+       return m_validateStatus;
+}
 
-       if (customColorOut)
-               decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
+// ShaderCase.
 
-       for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
+ShaderCase::ShaderCase (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* name, const char* description, const ShaderCaseSpecification& specification)
+       : tcu::TestCase                         (testCtx, name, description)
+       , m_renderCtx                           (renderCtx)
+       , m_contextInfo                         (contextInfo)
+       , m_caseType                            (specification.caseType)
+       , m_expectResult                        (specification.expectResult)
+       , m_targetVersion                       (specification.targetVersion)
+       , m_separatePrograms            (false)
+       , m_valueBlocks                         (specification.valueBlocks)
+{
+       if (m_caseType == CASETYPE_VERTEX_ONLY)
        {
-               const Value&            val                             = spec.values.inputs[ndx];
-               const DataType          basicType               = val.type.getBasicType();
-               const DataType          floatType               = getDataTypeFloatScalars(basicType);
-               const char* const       floatTypeStr    = getDataTypeName(floatType);
-               const char* const       refTypeStr              = getDataTypeName(basicType);
-
-               if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
-                       decl << fragIn << " " << floatTypeStr << " " << val.name << ";\n";
-               else
-               {
-                       decl << fragIn << " " << floatTypeStr << " v_" << val.name << ";\n";
-                       std::string offset = isDataTypeIntOrIVec(basicType) ? " * 1.0025" : ""; // \todo [petri] bit of a hack to avoid errors in chop() due to varying interpolation
-                       setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(v_" << val.name << offset << ");\n";
-               }
+               // case generated from "both" target, vertex case
+               DE_ASSERT(specification.vertexSources.size() == 1);
+               DE_ASSERT(specification.fragmentSources.empty());
+               DE_ASSERT(specification.tessCtrlSources.empty());
+               DE_ASSERT(specification.tessEvalSources.empty());
+               DE_ASSERT(specification.geometrySources.empty());
        }
-
-       // \todo [2015-07-24 pyry] Why are uniforms missing?
-
-       for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
+       else if (m_caseType == CASETYPE_FRAGMENT_ONLY)
        {
-               const Value&            val                             = spec.values.outputs[ndx];
-               const DataType          basicType               = val.type.getBasicType();
-               const char* const       refTypeStr              = getDataTypeName(basicType);
+               // case generated from "both" target, fragment case
+               DE_ASSERT(specification.vertexSources.empty());
+               DE_ASSERT(specification.fragmentSources.size() == 1);
+               DE_ASSERT(specification.tessCtrlSources.empty());
+               DE_ASSERT(specification.tessEvalSources.empty());
+               DE_ASSERT(specification.geometrySources.empty());
+       }
 
-               decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n";
-               decl << refTypeStr << " " << val.name << ";\n";
+       if (m_expectResult == EXPECT_BUILD_SUCCESSFUL)
+       {
+               // Shader is never executed. Presense of input/output values is likely an error
+               DE_ASSERT(m_valueBlocks.empty());
        }
 
-       /* \todo [2010-04-01 petri] Check all outputs. */
-
-       // Shader specialization.
-       map<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, spec.programs[0].requiredExtensions, SHADERTYPE_FRAGMENT);
-
-       return withExt;
-}
-
-static void generateUniformDeclarations (std::ostream& dst, const ValueBlock& valueBlock)
-{
-       for (size_t ndx = 0; ndx < valueBlock.uniforms.size(); ndx++)
+       // single program object
        {
-               const Value&            val             = valueBlock.uniforms[ndx];
-               const char* const       typeStr = getDataTypeName(val.type.getBasicType());
-
-               if (val.name.find('.') == string::npos)
-                       dst << "uniform " << typeStr << " " << val.name << ";\n";
+               ProgramObject program;
+               program.spec.requirements               = specification.requirements;
+               program.spec.vertexSources              = specification.vertexSources;
+               program.spec.fragmentSources    = specification.fragmentSources;
+               program.spec.tessCtrlSources    = specification.tessCtrlSources;
+               program.spec.tessEvalSources    = specification.tessEvalSources;
+               program.spec.geometrySources    = specification.geometrySources;
+
+               m_programs.push_back(program);
        }
 }
 
-static map<string, string> generateVertexSpecialization (const ProgramSpecializationParams& specParams)
+ShaderCase::ShaderCase (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* name, const char* description, const PipelineCaseSpecification& specification)
+       : tcu::TestCase                         (testCtx, name, description)
+       , m_renderCtx                           (renderCtx)
+       , m_contextInfo                         (contextInfo)
+       , m_caseType                            (specification.caseType)
+       , m_expectResult                        (specification.expectResult)
+       , m_targetVersion                       (specification.targetVersion)
+       , m_separatePrograms            (true)
+       , m_valueBlocks                         (specification.valueBlocks)
 {
-       const bool                              usesInout       = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion);
-       const char*                             vtxIn           = usesInout ? "in" : "attribute";
-       ostringstream                   decl;
-       ostringstream                   setup;
-       map<string, string>             params;
+       deUint32 totalActiveMask = 0;
 
-       decl << vtxIn << " highp vec4 dEQP_Position;\n";
-
-       for (size_t ndx = 0; ndx < specParams.caseSpec.values.inputs.size(); ndx++)
-       {
-               const Value&            val                     = specParams.caseSpec.values.inputs[ndx];
-               const DataType          basicType       = val.type.getBasicType();
-               const char* const       typeStr         = getDataTypeName(val.type.getBasicType());
+       DE_ASSERT(m_caseType == CASETYPE_COMPLETE);
 
-               if (getDataTypeScalarType(basicType) == TYPE_FLOAT)
-               {
-                       decl << vtxIn << " " << typeStr << " " << val.name << ";\n";
-               }
-               else
-               {
-                       const DataType          floatType               = getDataTypeFloatScalars(basicType);
-                       const char* const       floatTypeStr    = getDataTypeName(floatType);
+       // validate
 
-                       decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n";
-                       setup << typeStr << " " << val.name << " = " << typeStr << "(a_" << val.name << ");\n";
-               }
+       for (int pipelineProgramNdx = 0; pipelineProgramNdx < (int)specification.programs.size(); ++pipelineProgramNdx)
+       {
+               // program with an active stage must contain executable code for that stage
+               DE_ASSERT(((specification.programs[pipelineProgramNdx].activeStageBits & (1 << glu::SHADERTYPE_VERTEX))                                         == 0) || !specification.programs[pipelineProgramNdx].vertexSources.empty());
+               DE_ASSERT(((specification.programs[pipelineProgramNdx].activeStageBits & (1 << glu::SHADERTYPE_FRAGMENT))                                       == 0) || !specification.programs[pipelineProgramNdx].fragmentSources.empty());
+               DE_ASSERT(((specification.programs[pipelineProgramNdx].activeStageBits & (1 << glu::SHADERTYPE_TESSELLATION_CONTROL))           == 0) || !specification.programs[pipelineProgramNdx].tessCtrlSources.empty());
+               DE_ASSERT(((specification.programs[pipelineProgramNdx].activeStageBits & (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION))        == 0) || !specification.programs[pipelineProgramNdx].tessEvalSources.empty());
+               DE_ASSERT(((specification.programs[pipelineProgramNdx].activeStageBits & (1 << glu::SHADERTYPE_GEOMETRY))                                       == 0) || !specification.programs[pipelineProgramNdx].geometrySources.empty());
+
+               // no two programs with with the same stage active
+               DE_ASSERT((totalActiveMask & specification.programs[pipelineProgramNdx].activeStageBits) == 0);
+               totalActiveMask |= specification.programs[pipelineProgramNdx].activeStageBits;
        }
 
-       generateUniformDeclarations(decl, specParams.caseSpec.values);
-
-       params.insert(pair<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 ProgramSpecializationParams& specParams)
-{
-       const bool                      usesInout               = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion);
-       const bool                      customColorOut  = usesInout;
-       const char* const       fragColor               = customColorOut ? "dEQP_FragColor"     : "gl_FragColor";
-       ostringstream           decl;
-       ostringstream           output;
-       map<string, string>     params;
+       // create ProgramObjects
 
-       genCompareFunctions(decl, specParams.caseSpec.values, false);
-       genCompareOp(output, fragColor, specParams.caseSpec.values, "", DE_NULL);
-
-       if (customColorOut)
-               decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
-
-       for (size_t ndx = 0; ndx < specParams.caseSpec.values.outputs.size(); ndx++)
+       for (int pipelineProgramNdx = 0; pipelineProgramNdx < (int)specification.programs.size(); ++pipelineProgramNdx)
        {
-               const Value&            val                     = specParams.caseSpec.values.outputs[ndx];
-               const char*     const   refTypeStr      = getDataTypeName(val.type.getBasicType());
-
-               decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n";
-               decl << refTypeStr << " " << val.name << ";\n";
+               ProgramObject program;
+               program.spec = specification.programs[pipelineProgramNdx];
+               m_programs.push_back(program);
        }
-
-       generateUniformDeclarations(decl, specParams.caseSpec.values);
-
-       params.insert(pair<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;
-}
-
-static map<string, string> generateGeometrySpecialization (const ProgramSpecializationParams& specParams)
-{
-       ostringstream           decl;
-       map<string, string>     params;
-
-       decl << "layout (triangles) in;\n";
-       decl << "layout (triangle_strip, max_vertices=3) out;\n";
-       decl << "\n";
-
-       generateUniformDeclarations(decl, specParams.caseSpec.values);
-
-       params.insert(pair<string, string>("GEOMETRY_DECLARATIONS",             decl.str()));
-
-       return params;
 }
 
-static map<string, string> generateTessControlSpecialization (const ProgramSpecializationParams& specParams)
+ShaderCase::~ShaderCase (void)
 {
-       ostringstream           decl;
-       ostringstream           output;
-       map<string, string>     params;
-
-       decl << "layout (vertices=3) out;\n";
-       decl << "\n";
-
-       generateUniformDeclarations(decl, specParams.caseSpec.values);
-
-       output <<       "gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
-                               "gl_TessLevelInner[0] = 2.0;\n"
-                               "gl_TessLevelInner[1] = 2.0;\n"
-                               "gl_TessLevelOuter[0] = 2.0;\n"
-                               "gl_TessLevelOuter[1] = 2.0;\n"
-                               "gl_TessLevelOuter[2] = 2.0;\n"
-                               "gl_TessLevelOuter[3] = 2.0;";
-
-       params.insert(pair<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)));
-
-       return params;
 }
 
-static map<string, string> generateTessEvalSpecialization (const ProgramSpecializationParams& specParams)
+void ShaderCase::init (void)
 {
-       ostringstream           decl;
-       ostringstream           output;
-       map<string, string>     params;
-
-       decl << "layout (triangles) in;\n";
-       decl << "\n";
-
-       generateUniformDeclarations(decl, specParams.caseSpec.values);
-
-       output <<       "gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + gl_TessCoord[2] * gl_in[2].gl_Position;\n";
+       // If no value blocks given, use an empty one.
+       if (m_valueBlocks.empty())
+               m_valueBlocks.push_back(ValueBlock());
 
-       params.insert(pair<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)));
+       // Use first value block to specialize shaders.
+       const ValueBlock& valueBlock = m_valueBlocks[0];
 
-       return params;
-}
+       // \todo [2010-04-01 petri] Check that all value blocks have matching values.
 
-static void specializeShaderSources (ProgramSources&                                   dst,
-                                                                        const ProgramSources&                          src,
-                                                                        const ProgramSpecializationParams&     specParams,
-                                                                        glu::ShaderType                                        shaderType,
-                                                                        map<string, string>                            (*specializationGenerator) (const ProgramSpecializationParams& specParams))
-{
-       if (!src.sources[shaderType].empty())
+       // prepare programs
+       for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
        {
-               const map<string, string>       tmplParams      = specializationGenerator(specParams);
+               // Check requirements
+               for (int ndx = 0; ndx < (int)m_programs[programNdx].spec.requirements.size(); ++ndx)
+                       m_programs[programNdx].spec.requirements[ndx].checkRequirements(m_renderCtx, m_contextInfo);
 
-               for (size_t ndx = 0; ndx < src.sources[shaderType].size(); ++ndx)
+               // Generate specialized shader sources.
+               if (m_caseType == CASETYPE_COMPLETE)
                {
-                       const StringTemplate    tmpl                    (src.sources[shaderType][ndx]);
-                       const std::string               baseGLSLCode    = tmpl.specialize(tmplParams);
-                       const std::string               sourceWithExts  = injectExtensionRequirements(baseGLSLCode, specParams.requiredExtensions, shaderType);
-
-                       dst << glu::ShaderSource(shaderType, sourceWithExts);
+                       // all shaders specified separately
+                       specializeVertexShaders         (m_programs[programNdx].programSources, m_programs[programNdx].spec.vertexSources,              valueBlock,     m_programs[programNdx].spec.requirements);
+                       specializeFragmentShaders       (m_programs[programNdx].programSources, m_programs[programNdx].spec.fragmentSources,    valueBlock,     m_programs[programNdx].spec.requirements);
+                       specializeGeometryShaders       (m_programs[programNdx].programSources, m_programs[programNdx].spec.geometrySources,    valueBlock,     m_programs[programNdx].spec.requirements);
+                       specializeTessControlShaders(m_programs[programNdx].programSources,     m_programs[programNdx].spec.tessCtrlSources,    valueBlock,     m_programs[programNdx].spec.requirements);
+                       specializeTessEvalShaders       (m_programs[programNdx].programSources, m_programs[programNdx].spec.tessEvalSources,    valueBlock,     m_programs[programNdx].spec.requirements);
                }
-       }
-}
-
-static void specializeProgramSources (glu::ProgramSources&                                     dst,
-                                                                         const glu::ProgramSources&                    src,
-                                                                         const ProgramSpecializationParams&    specParams)
-{
-       specializeShaderSources(dst, src, specParams, SHADERTYPE_VERTEX,                                        generateVertexSpecialization);
-       specializeShaderSources(dst, src, specParams, SHADERTYPE_FRAGMENT,                                      generateFragmentSpecialization);
-       specializeShaderSources(dst, src, specParams, SHADERTYPE_GEOMETRY,                                      generateGeometrySpecialization);
-       specializeShaderSources(dst, src, specParams, SHADERTYPE_TESSELLATION_CONTROL,          generateTessControlSpecialization);
-       specializeShaderSources(dst, src, specParams, SHADERTYPE_TESSELLATION_EVALUATION,       generateTessEvalSpecialization);
-
-       dst << ProgramSeparable(src.separable);
-}
-
-enum
-{
-       VIEWPORT_WIDTH          = 128,
-       VIEWPORT_HEIGHT         = 128
-};
-
-class BeforeDrawValidator : public glu::DrawUtilCallback
-{
-public:
-       enum TargetType
-       {
-               TARGETTYPE_PROGRAM = 0,
-               TARGETTYPE_PIPELINE,
-
-               TARGETTYPE_LAST
-       };
-
-                                                       BeforeDrawValidator     (const glw::Functions& gl, glw::GLuint target, TargetType targetType);
-
-       void                                    beforeDrawCall          (void);
-
-       const std::string&              getInfoLog                      (void) const;
-       glw::GLint                              getValidateStatus       (void) const;
-
-private:
-       const glw::Functions&   m_gl;
-       const glw::GLuint               m_target;
-       const TargetType                m_targetType;
-
-       glw::GLint                              m_validateStatus;
-       std::string                             m_logMessage;
-};
-
-BeforeDrawValidator::BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType)
-       : m_gl                          (gl)
-       , m_target                      (target)
-       , m_targetType          (targetType)
-       , m_validateStatus      (-1)
-{
-       DE_ASSERT(targetType < TARGETTYPE_LAST);
-}
-
-void BeforeDrawValidator::beforeDrawCall (void)
-{
-       glw::GLint                                      bytesWritten    = 0;
-       glw::GLint                                      infoLogLength;
-       std::vector<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
-
-       infoLogLength = 0;
+               else if (m_caseType == CASETYPE_VERTEX_ONLY)
+               {
+                       DE_ASSERT(m_programs.size() == 1);
+                       DE_ASSERT(!m_separatePrograms);
 
-       if (m_targetType == TARGETTYPE_PROGRAM)
-               m_gl.getProgramiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength);
-       else if (m_targetType == TARGETTYPE_PIPELINE)
-               m_gl.getProgramPipelineiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength);
-       else
-               DE_ASSERT(false);
+                       // case generated from "both" target, vertex case
+                       m_programs[0].programSources << glu::VertexSource(specializeVertexShader(m_programs[0].spec.vertexSources[0].c_str(), valueBlock));
+                       m_programs[0].programSources << glu::FragmentSource(genFragmentShader(valueBlock));
+               }
+               else if (m_caseType == CASETYPE_FRAGMENT_ONLY)
+               {
+                       DE_ASSERT(m_programs.size() == 1);
+                       DE_ASSERT(!m_separatePrograms);
 
-       GLU_EXPECT_NO_ERROR(m_gl.getError(), "get info log length");
+                       // case generated from "both" target, fragment case
+                       m_programs[0].programSources << glu::VertexSource(genVertexShader(valueBlock));
+                       m_programs[0].programSources << glu::FragmentSource(specializeFragmentShader(m_programs[0].spec.fragmentSources[0].c_str(), valueBlock));
+               }
 
-       if (infoLogLength <= 0)
-       {
-               m_logMessage.clear();
-               return;
+               m_programs[programNdx].programSources << glu::ProgramSeparable(m_separatePrograms);
        }
 
-       logBuffer.resize(infoLogLength + 2, '0'); // +1 for zero terminator (infoLogLength should include it, but better play it safe), +1 to make sure buffer is always larger
-
-       if (m_targetType == TARGETTYPE_PROGRAM)
-               m_gl.getProgramInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]);
-       else if (m_targetType == TARGETTYPE_PIPELINE)
-               m_gl.getProgramPipelineInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]);
-       else
-               DE_ASSERT(false);
-
-       // just ignore bytesWritten to be safe, find the null terminator
-       stringLength = (int)(std::find(logBuffer.begin(), logBuffer.end(), '0') - logBuffer.begin());
-       m_logMessage.assign(&logBuffer[0], stringLength);
-}
-
-const std::string& BeforeDrawValidator::getInfoLog (void) const
-{
-       return m_logMessage;
-}
-
-glw::GLint BeforeDrawValidator::getValidateStatus (void) const
-{
-       return m_validateStatus;
-}
-
-// ShaderCase.
-
-ShaderLibraryCase::ShaderLibraryCase (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* name, const char* description, const ShaderCaseSpecification& specification)
-       : tcu::TestCase (testCtx, name, description)
-       , m_renderCtx   (renderCtx)
-       , m_contextInfo (contextInfo)
-       , m_spec                (specification)
-{
-}
-
-ShaderLibraryCase::~ShaderLibraryCase (void)
-{
-}
-
-void ShaderLibraryCase::init (void)
-{
-       DE_ASSERT(isValid(m_spec));
-
-       checkImplementationLimits(m_spec.requiredCaps, m_contextInfo);
-
        // log the expected result
-       switch (m_spec.expectResult)
+       switch (m_expectResult)
        {
                case EXPECT_PASS:
                        // Don't write anything
@@ -761,32 +521,42 @@ void ShaderLibraryCase::init (void)
                        DE_ASSERT(false);
                        break;
        }
+
+       // sanity of arguments
+
+       if (anyProgramRequiresFullGLSLES100Specification())
+       {
+               // makes only sense in tests where shader compiles
+               DE_ASSERT(m_expectResult == EXPECT_PASS                         ||
+                                 m_expectResult == EXPECT_VALIDATION_FAIL      ||
+                                 m_expectResult == EXPECT_BUILD_SUCCESSFUL);
+
+               // only makes sense for ES 100 programs
+               DE_ASSERT(m_targetVersion == glu::GLSL_VERSION_100_ES);
+       }
 }
 
-static void setUniformValue (const glw::Functions& gl, const std::vector<deUint32>& pipelinePrograms, const std::string& name, const Value& val, int arrayNdx, tcu::TestLog& log)
+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;
 
        for (int programNdx = 0; programNdx < (int)pipelinePrograms.size(); ++programNdx)
        {
-               const DataType  dataType        = val.type.getBasicType();
-               const int               scalarSize      = getDataTypeScalarSize(dataType);
-               const int               loc                     = gl.getUniformLocation(pipelinePrograms[programNdx], name.c_str());
-               const int               elemNdx         = arrayNdx * scalarSize;
-
-               DE_ASSERT(elemNdx+scalarSize <= (int)val.elements.size());
+               const int scalarSize    = getDataTypeScalarSize(val.dataType);
+               const int loc                   = gl.getUniformLocation(pipelinePrograms[programNdx], name.c_str());
+               const int elemNdx               = (val.arrayLength == 1) ? (0) : (arrayNdx * scalarSize);
 
                if (loc == -1)
                        continue;
 
                foundAnyMatch = true;
 
-               DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLfloat));
-               DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLint));
+               DE_STATIC_ASSERT(sizeof(ShaderCase::Value::Element) == sizeof(glw::GLfloat));
+               DE_STATIC_ASSERT(sizeof(ShaderCase::Value::Element) == sizeof(glw::GLint));
 
                gl.useProgram(pipelinePrograms[programNdx]);
 
-               switch (dataType)
+               switch (val.dataType)
                {
                        case TYPE_FLOAT:                gl.uniform1fv(loc, 1, &val.elements[elemNdx].float32);                                          break;
                        case TYPE_FLOAT_VEC2:   gl.uniform2fv(loc, 1, &val.elements[elemNdx].float32);                                          break;
@@ -828,38 +598,52 @@ static void setUniformValue (const glw::Functions& gl, const std::vector<deUint3
                log << tcu::TestLog::Message << "WARNING // Uniform \"" << name << "\" location is not valid, location = -1. Cannot set value to the uniform." << tcu::TestLog::EndMessage;
 }
 
-static bool isTessellationPresent (const ShaderCaseSpecification& spec)
+bool ShaderCase::isTessellationPresent (void) const
 {
-       if (spec.programs[0].sources.separable)
+       if (m_separatePrograms)
        {
                const deUint32 tessellationBits =       (1 << glu::SHADERTYPE_TESSELLATION_CONTROL)             |
                                                                                        (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION);
 
-               for (int programNdx = 0; programNdx < (int)spec.programs.size(); ++programNdx)
-                       if (spec.programs[programNdx].activeStages & tessellationBits)
+               for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
+                       if (m_programs[programNdx].spec.activeStageBits & tessellationBits)
                                return true;
                return false;
        }
        else
-               return !spec.programs[0].sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty() ||
-                          !spec.programs[0].sources.sources[glu::SHADERTYPE_TESSELLATION_EVALUATION].empty();
+               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
+{
+       for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
+       for (int requirementNdx = 0; requirementNdx < (int)m_programs[programNdx].spec.requirements.size(); ++requirementNdx)
+       {
+               if (m_programs[programNdx].spec.requirements[requirementNdx].getType() == CaseRequirement::REQUIREMENTTYPE_FULL_GLSL_ES_100_SPEC)
+                       return true;
+       }
+       return false;
 }
 
-static bool checkPixels (tcu::TestLog& log, const tcu::ConstPixelBufferAccess& surface)
+bool ShaderCase::checkPixels (Surface& surface, int minX, int maxX, int minY, int maxY)
 {
-       bool    allWhite                = true;
-       bool    allBlack                = true;
-       bool    anyUnexpected   = false;
+       TestLog&        log                             = m_testCtx.getLog();
+       bool            allWhite                = true;
+       bool            allBlack                = true;
+       bool            anyUnexpected   = false;
+
+       DE_ASSERT((maxX > minX) && (maxY > minY));
 
-       for (int y = 0; y < surface.getHeight(); y++)
+       for (int y = minY; y <= maxY; y++)
        {
-               for (int x = 0; x < surface.getWidth(); x++)
+               for (int x = minX; x <= maxX; x++)
                {
-                       const tcu::IVec4        pixel            = surface.getPixelInt(x, y);
+                       RGBA            pixel            = surface.getPixel(x, y);
                        // Note: we really do not want to involve alpha in the check comparison
                        // \todo [2010-09-22 kalle] Do we know that alpha would be one? If yes, could use color constants white and black.
-                       const bool                      isWhite          = (pixel[0] == 255) && (pixel[1] == 255) && (pixel[2] == 255);
-                       const bool                      isBlack          = (pixel[0] ==   0) && (pixel[1] ==   0) && (pixel[2] ==   0);
+                       bool            isWhite          = (pixel.getRed() == 255) && (pixel.getGreen() == 255) && (pixel.getBlue() == 255);
+                       bool            isBlack          = (pixel.getRed() == 0) && (pixel.getGreen() == 0) && (pixel.getBlue() == 0);
 
                        allWhite                = allWhite && isWhite;
                        allBlack                = allBlack && isBlack;
@@ -876,14 +660,13 @@ static bool checkPixels (tcu::TestLog& log, const tcu::ConstPixelBufferAccess& s
 
                return false;
        }
-
        return true;
 }
 
-bool ShaderLibraryCase::execute (void)
+bool ShaderCase::execute (void)
 {
-       const float                                                     quadSize                                = 1.0f;
-       static const float                                      s_positions[4*4]                =
+       const float                                                                             quadSize                                = 1.0f;
+       static const float                                                              s_positions[4*4]                =
        {
                -quadSize, -quadSize, 0.0f, 1.0f,
                -quadSize, +quadSize, 0.0f, 1.0f,
@@ -891,69 +674,40 @@ bool ShaderLibraryCase::execute (void)
                +quadSize, +quadSize, 0.0f, 1.0f
        };
 
-       static const deUint16                           s_indices[2*3]                  =
+       static const deUint16                                                   s_indices[2*3]                  =
        {
                0, 1, 2,
                1, 3, 2
        };
 
-       TestLog&                                                        log                                             = m_testCtx.getLog();
-       const glw::Functions&                           gl                                              = m_renderCtx.getFunctions();
+       TestLog&                                                                                log                                             = m_testCtx.getLog();
+       const glw::Functions&                                                   gl                                              = m_renderCtx.getFunctions();
 
        // Compute viewport.
-       const tcu::RenderTarget&                        renderTarget                    = m_renderCtx.getRenderTarget();
-       de::Random                                                      rnd                                             (deStringHash(getName()));
-       const int                                                       width                                   = deMin32(renderTarget.getWidth(),      VIEWPORT_WIDTH);
-       const int                                                       height                                  = deMin32(renderTarget.getHeight(),     VIEWPORT_HEIGHT);
-       const int                                                       viewportX                               = rnd.getInt(0, renderTarget.getWidth()  - width);
-       const int                                                       viewportY                               = rnd.getInt(0, renderTarget.getHeight() - height);
-       const int                                                       numVerticesPerDraw              = 4;
-       const bool                                                      tessellationPresent             = isTessellationPresent(m_spec);
-       const bool                                                      separablePrograms               = m_spec.programs[0].sources.separable;
-
-       bool                                                            allCompilesOk                   = true;
-       bool                                                            allLinksOk                              = true;
-       const char*                                                     failReason                              = DE_NULL;
-
-       vector<ProgramSources>                          specializedSources              (m_spec.programs.size());
-
-       deUint32                                                        vertexProgramID                 = -1;
-       vector<deUint32>                                        pipelineProgramIDs;
-       vector<SharedPtr<ShaderProgram> >       programs;
-       SharedPtr<ProgramPipeline>                      programPipeline;
+       const tcu::RenderTarget&                                                renderTarget                    = m_renderCtx.getRenderTarget();
+       de::Random                                                                              rnd                                             (deStringHash(getName()));
+       const int                                                                               width                                   = deMin32(renderTarget.getWidth(),      VIEWPORT_WIDTH);
+       const int                                                                               height                                  = deMin32(renderTarget.getHeight(),     VIEWPORT_HEIGHT);
+       const int                                                                               viewportX                               = rnd.getInt(0, renderTarget.getWidth()  - width);
+       const int                                                                               viewportY                               = rnd.getInt(0, renderTarget.getHeight() - height);
+       const int                                                                               numVerticesPerDraw              = 4;
+       const bool                                                                              tessellationPresent             = isTessellationPresent();
+       const bool                                                                              requiresFullGLSLES100   = anyProgramRequiresFullGLSLES100Specification();
+
+       bool                                                                                    allCompilesOk                   = true;
+       bool                                                                                    allLinksOk                              = true;
+       const char*                                                                             failReason                              = DE_NULL;
+
+       deUint32                                                                                vertexProgramID                 = -1;
+       std::vector<deUint32>                                                   pipelineProgramIDs;
+       std::vector<de::SharedPtr<glu::ShaderProgram> > programs;
+       de::SharedPtr<glu::ProgramPipeline>                             programPipeline;
 
        GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): start");
 
-       // Specialize shaders
-       if (m_spec.caseType == CASETYPE_VERTEX_ONLY)
-       {
-               DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[SHADERTYPE_VERTEX].size() == 1);
-               specializedSources[0] << glu::VertexSource(specializeVertexShader(m_spec, m_spec.programs[0].sources.sources[SHADERTYPE_VERTEX][0]))
-                                                         << glu::FragmentSource(genFragmentShader(m_spec));
-       }
-       else if (m_spec.caseType == CASETYPE_FRAGMENT_ONLY)
-       {
-               DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].size() == 1);
-               specializedSources[0] << glu::VertexSource(genVertexShader(m_spec))
-                                                         << glu::FragmentSource(specializeFragmentShader(m_spec, m_spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT][0]));
-       }
-       else
-       {
-               DE_ASSERT(m_spec.caseType == CASETYPE_COMPLETE);
-
-               const int       maxPatchVertices        = isTessellationPresent(m_spec) ? m_contextInfo.getInt(GL_MAX_PATCH_VERTICES) : 0;
-
-               for (size_t progNdx = 0; progNdx < m_spec.programs.size(); progNdx++)
-               {
-                       const ProgramSpecializationParams       progSpecParams  (m_spec, checkAndSpecializeExtensions(m_spec.programs[progNdx].requiredExtensions, m_contextInfo), maxPatchVertices);
-
-                       specializeProgramSources(specializedSources[progNdx], m_spec.programs[progNdx].sources, progSpecParams);
-               }
-       }
-
-       if (!separablePrograms)
+       if (!m_separatePrograms)
        {
-               de::SharedPtr<glu::ShaderProgram>       program         (new glu::ShaderProgram(m_renderCtx, specializedSources[0]));
+               de::SharedPtr<glu::ShaderProgram>       program         (new glu::ShaderProgram(m_renderCtx, m_programs[0].programSources));
 
                vertexProgramID = program->getProgram();
                pipelineProgramIDs.push_back(program->getProgram());
@@ -974,11 +728,11 @@ bool ShaderLibraryCase::execute (void)
        else
        {
                // Separate programs
-               for (size_t programNdx = 0; programNdx < m_spec.programs.size(); ++programNdx)
+               for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
                {
-                       de::SharedPtr<glu::ShaderProgram> program(new glu::ShaderProgram(m_renderCtx, specializedSources[programNdx]));
+                       de::SharedPtr<glu::ShaderProgram> program(new glu::ShaderProgram(m_renderCtx, m_programs[programNdx].programSources));
 
-                       if (m_spec.programs[programNdx].activeStages & (1u << glu::SHADERTYPE_VERTEX))
+                       if (m_programs[programNdx].spec.activeStageBits & (1 << glu::SHADERTYPE_VERTEX))
                                vertexProgramID = program->getProgram();
 
                        pipelineProgramIDs.push_back(program->getProgram());
@@ -1003,7 +757,7 @@ bool ShaderLibraryCase::execute (void)
                                builder << "Pipeline uses stages: ";
                                for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
                                {
-                                       if (m_spec.programs[programNdx].activeStages & (1u << stage))
+                                       if (m_programs[programNdx].spec.activeStageBits & (1 << stage))
                                        {
                                                if (!firstStage)
                                                        builder << ", ";
@@ -1018,7 +772,7 @@ bool ShaderLibraryCase::execute (void)
                }
        }
 
-       switch (m_spec.expectResult)
+       switch (m_expectResult)
        {
                case EXPECT_PASS:
                case EXPECT_VALIDATION_FAIL:
@@ -1058,7 +812,7 @@ bool ShaderLibraryCase::execute (void)
                // \todo [2010-06-07 petri] These should be handled in the test case?
                log << TestLog::Message << "ERROR: " << failReason << TestLog::EndMessage;
 
-               if (m_spec.fullGLSLES100Required)
+               if (requiresFullGLSLES100)
                {
                        log     << TestLog::Message
                                << "Assuming build failure is caused by implementation not supporting full GLSL ES 100 specification, which is not required."
@@ -1073,7 +827,7 @@ bool ShaderLibraryCase::execute (void)
                        else
                                m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Full GLSL ES 100 is not supported");
                }
-               else if (m_spec.expectResult == EXPECT_COMPILE_FAIL && allCompilesOk && !allLinksOk)
+               else if (m_expectResult == EXPECT_COMPILE_FAIL && allCompilesOk && !allLinksOk)
                {
                        // If implementation parses shader at link time, report it as quality warning.
                        m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason);
@@ -1084,26 +838,26 @@ bool ShaderLibraryCase::execute (void)
        }
 
        // Return if shader is not intended to be run
-       if (m_spec.expectResult == EXPECT_COMPILE_FAIL          ||
-               m_spec.expectResult == EXPECT_COMPILE_LINK_FAIL ||
-               m_spec.expectResult == EXPECT_LINK_FAIL                 ||
-               m_spec.expectResult == EXPECT_BUILD_SUCCESSFUL)
+       if (m_expectResult == EXPECT_COMPILE_FAIL               ||
+               m_expectResult == EXPECT_COMPILE_LINK_FAIL      ||
+               m_expectResult == EXPECT_LINK_FAIL                      ||
+               m_expectResult == EXPECT_BUILD_SUCCESSFUL)
                return true;
 
        // Setup viewport.
        gl.viewport(viewportX, viewportY, width, height);
 
-       if (separablePrograms)
+       if (m_separatePrograms)
        {
                programPipeline = de::SharedPtr<glu::ProgramPipeline>(new glu::ProgramPipeline(m_renderCtx));
 
                // Setup pipeline
                gl.bindProgramPipeline(programPipeline->getPipeline());
-               for (int programNdx = 0; programNdx < (int)m_spec.programs.size(); ++programNdx)
+               for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
                {
                        deUint32 shaderFlags = 0;
                        for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
-                               if (m_spec.programs[programNdx].activeStages & (1u << stage))
+                               if (m_programs[programNdx].spec.activeStageBits & (1 << stage))
                                        shaderFlags |= glu::getGLShaderTypeBit((glu::ShaderType)stage);
 
                        programPipeline->useProgramStages(shaderFlags, pipelineProgramIDs[programNdx]);
@@ -1128,111 +882,114 @@ bool ShaderLibraryCase::execute (void)
        }
 
        // Iterate all value blocks.
+       for (int blockNdx = 0; blockNdx < (int)m_valueBlocks.size(); blockNdx++)
        {
-               const ValueBlock&       valueBlock              = m_spec.values;
+               const ValueBlock&       valueBlock              = m_valueBlocks[blockNdx];
 
                // always render at least one pass even if there is no input/output data
-               const int                       numRenderPasses = valueBlock.outputs.empty() ? 1 : (int)valueBlock.outputs[0].elements.size() / valueBlock.outputs[0].type.getScalarSize();
+               const int                       numRenderPasses = (valueBlock.arrayLength == 0) ? (1) : (valueBlock.arrayLength);
 
                // Iterate all array sub-cases.
                for (int arrayNdx = 0; arrayNdx < numRenderPasses; arrayNdx++)
                {
+                       int                                                     numValues                       = (int)valueBlock.values.size();
                        vector<VertexArrayBinding>      vertexArrays;
                        int                                                     attribValueNdx          = 0;
-                       vector<vector<float> >          attribValues            (valueBlock.inputs.size());
+                       vector<vector<float> >          attribValues            (numValues);
                        glw::GLenum                                     postDrawError;
                        BeforeDrawValidator                     beforeDrawValidator     (gl,
-                                                                                                                        (separablePrograms) ? (programPipeline->getPipeline())                 : (vertexProgramID),
-                                                                                                                        (separablePrograms) ? (BeforeDrawValidator::TARGETTYPE_PIPELINE)       : (BeforeDrawValidator::TARGETTYPE_PROGRAM));
+                                                                                                                        (m_separatePrograms) ? (programPipeline->getPipeline())                        : (vertexProgramID),
+                                                                                                                        (m_separatePrograms) ? (BeforeDrawValidator::TARGETTYPE_PIPELINE)      : (BeforeDrawValidator::TARGETTYPE_PROGRAM));
 
                        vertexArrays.push_back(va::Float(positionLoc, 4, numVerticesPerDraw, 0, &s_positions[0]));
 
                        // Collect VA pointer for inputs
-                       for (size_t valNdx = 0; valNdx < valueBlock.inputs.size(); valNdx++)
+                       for (int valNdx = 0; valNdx < numValues; valNdx++)
                        {
-                               const Value&            val                     = valueBlock.inputs[valNdx];
-                               const char* const       valueName       = val.name.c_str();
-                               const DataType          dataType        = val.type.getBasicType();
-                               const int                       scalarSize      = getDataTypeScalarSize(dataType);
-
-                               // Replicate values four times.
-                               std::vector<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
+                               const ShaderCase::Value&        val                     = valueBlock.values[valNdx];
+                               const char* const                       valueName       = val.valueName.c_str();
+                               const DataType                          dataType        = val.dataType;
+                               const int                                       scalarSize      = getDataTypeScalarSize(val.dataType);
+
+                               if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
                                {
-                                       // convert to floats.
-                                       for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
+                                       // Replicate values four times.
+                                       std::vector<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
                                        {
-                                               for (int ndx = 0; ndx < scalarSize; ndx++)
+                                               // convert to floats.
+                                               for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
                                                {
-                                                       float v = (float)val.elements[arrayNdx*scalarSize + ndx].int32;
-                                                       DE_ASSERT(val.elements[arrayNdx*scalarSize + ndx].int32 == (int)v);
-                                                       scalars[repNdx*scalarSize + ndx] = v;
+                                                       for (int ndx = 0; ndx < scalarSize; ndx++)
+                                                       {
+                                                               float v = (float)val.elements[arrayNdx*scalarSize + ndx].int32;
+                                                               DE_ASSERT(val.elements[arrayNdx*scalarSize + ndx].int32 == (int)v);
+                                                               scalars[repNdx*scalarSize + ndx] = v;
+                                                       }
                                                }
                                        }
-                               }
 
-                               // Attribute name prefix.
-                               string attribPrefix = "";
-                               // \todo [2010-05-27 petri] Should latter condition only apply for vertex cases (or actually non-fragment cases)?
-                               if ((m_spec.caseType == CASETYPE_FRAGMENT_ONLY) || (getDataTypeScalarType(dataType) != TYPE_FLOAT))
-                                       attribPrefix = "a_";
+                                       // Attribute name prefix.
+                                       string attribPrefix = "";
+                                       // \todo [2010-05-27 petri] Should latter condition only apply for vertex cases (or actually non-fragment cases)?
+                                       if ((m_caseType == CASETYPE_FRAGMENT_ONLY) || (getDataTypeScalarType(dataType) != TYPE_FLOAT))
+                                               attribPrefix = "a_";
 
-                               // Input always given as attribute.
-                               string attribName = attribPrefix + valueName;
-                               int attribLoc = gl.getAttribLocation(vertexProgramID, attribName.c_str());
-                               if (attribLoc == -1)
-                               {
-                                       log << TestLog::Message << "Warning: no location found for attribute '" << attribName << "'" << TestLog::EndMessage;
-                                       continue;
-                               }
+                                       // Input always given as attribute.
+                                       string attribName = attribPrefix + valueName;
+                                       int attribLoc = gl.getAttribLocation(vertexProgramID, attribName.c_str());
+                                       if (attribLoc == -1)
+                                       {
+                                               log << TestLog::Message << "Warning: no location found for attribute '" << attribName << "'" << TestLog::EndMessage;
+                                               continue;
+                                       }
 
-                               if (isDataTypeMatrix(dataType))
-                               {
-                                       int numCols = getDataTypeMatrixNumColumns(dataType);
-                                       int numRows = getDataTypeMatrixNumRows(dataType);
-                                       DE_ASSERT(scalarSize == numCols*numRows);
+                                       if (isDataTypeMatrix(dataType))
+                                       {
+                                               int numCols = getDataTypeMatrixNumColumns(dataType);
+                                               int numRows = getDataTypeMatrixNumRows(dataType);
+                                               DE_ASSERT(scalarSize == numCols*numRows);
 
-                                       for (int i = 0; i < numCols; i++)
-                                               vertexArrays.push_back(va::Float(attribLoc + i, numRows, numVerticesPerDraw, scalarSize*(int)sizeof(float), &scalars[i * numRows]));
-                               }
-                               else
-                               {
-                                       DE_ASSERT(isDataTypeFloatOrVec(dataType) || isDataTypeIntOrIVec(dataType) || isDataTypeUintOrUVec(dataType) || isDataTypeBoolOrBVec(dataType));
-                                       vertexArrays.push_back(va::Float(attribLoc, scalarSize, numVerticesPerDraw, 0, &scalars[0]));
-                               }
+                                               for (int i = 0; i < numCols; i++)
+                                                       vertexArrays.push_back(va::Float(attribLoc + i, numRows, numVerticesPerDraw, scalarSize*(int)sizeof(float), &scalars[i * numRows]));
+                                       }
+                                       else
+                                       {
+                                               DE_ASSERT(isDataTypeFloatOrVec(dataType) || isDataTypeIntOrIVec(dataType) || isDataTypeUintOrUVec(dataType) || isDataTypeBoolOrBVec(dataType));
+                                               vertexArrays.push_back(va::Float(attribLoc, scalarSize, numVerticesPerDraw, 0, &scalars[0]));
+                                       }
 
-                               GLU_EXPECT_NO_ERROR(gl.getError(), "set vertex attrib array");
+                                       GLU_EXPECT_NO_ERROR(gl.getError(), "set vertex attrib array");
+                               }
                        }
 
                        GLU_EXPECT_NO_ERROR(gl.getError(), "before set uniforms");
 
-                       // set reference values for outputs.
-                       for (size_t valNdx = 0; valNdx < valueBlock.outputs.size(); valNdx++)
-                       {
-                               const Value&            val                     = valueBlock.outputs[valNdx];
-                               const char* const       valueName       = val.name.c_str();
-
-                               // Set reference value.
-                               string refName = string("ref_") + valueName;
-                               setUniformValue(gl, pipelineProgramIDs, refName, val, arrayNdx, m_testCtx.getLog());
-                               GLU_EXPECT_NO_ERROR(gl.getError(), "set reference uniforms");
-                       }
-
-                       // set uniform values
-                       for (size_t valNdx = 0; valNdx < valueBlock.uniforms.size(); valNdx++)
+                       // set uniform values for outputs (refs).
+                       for (int valNdx = 0; valNdx < numValues; valNdx++)
                        {
-                               const Value&            val                     = valueBlock.uniforms[valNdx];
-                               const char* const       valueName       = val.name.c_str();
+                               const ShaderCase::Value&        val                     = valueBlock.values[valNdx];
+                               const char* const                       valueName       = val.valueName.c_str();
 
-                               setUniformValue(gl, pipelineProgramIDs, valueName, val, arrayNdx, m_testCtx.getLog());
-                               GLU_EXPECT_NO_ERROR(gl.getError(), "set uniforms");
+                               if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
+                               {
+                                       // Set reference value.
+                                       string refName = string("ref_") + valueName;
+                                       setUniformValue(gl, pipelineProgramIDs, refName, val, arrayNdx, m_testCtx.getLog());
+                                       GLU_EXPECT_NO_ERROR(gl.getError(), "set reference uniforms");
+                               }
+                               else if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM)
+                               {
+                                       setUniformValue(gl, pipelineProgramIDs, valueName, val, arrayNdx, m_testCtx.getLog());
+                                       GLU_EXPECT_NO_ERROR(gl.getError(), "set uniforms");
+                               }
                        }
 
                        // Clear.
@@ -1241,7 +998,7 @@ bool ShaderLibraryCase::execute (void)
                        GLU_EXPECT_NO_ERROR(gl.getError(), "clear buffer");
 
                        // Use program or pipeline
-                       if (separablePrograms)
+                       if (m_separatePrograms)
                                gl.useProgram(0);
                        else
                                gl.useProgram(vertexProgramID);
@@ -1260,13 +1017,13 @@ bool ShaderLibraryCase::execute (void)
                                 (tessellationPresent) ?
                                        (pr::Patches(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])) :
                                        (pr::Triangles(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])),
-                                (m_spec.expectResult == EXPECT_VALIDATION_FAIL) ?
+                                (m_expectResult == EXPECT_VALIDATION_FAIL) ?
                                        (&beforeDrawValidator) :
                                        (DE_NULL));
 
                        postDrawError = gl.getError();
 
-                       if (m_spec.expectResult == EXPECT_PASS)
+                       if (m_expectResult == EXPECT_PASS)
                        {
                                // Read back results.
                                Surface                 surface                 (width, height);
@@ -1281,13 +1038,14 @@ bool ShaderLibraryCase::execute (void)
                                glu::readPixels(m_renderCtx, viewportX, viewportY, surface.getAccess());
                                GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
 
-                               if (!checkPixels(log, tcu::getSubregion(surface.getAccess(), minX, minY, maxX-minX+1, maxY-minY+1)))
+                               if (!checkPixels(surface, minX, maxX, minY, maxY))
                                {
-                                       log << TestLog::Message << "INCORRECT RESULT for sub-case " << arrayNdx+1 << " of " << numRenderPasses << "):"
+                                       log << TestLog::Message << "INCORRECT RESULT for (value block " << (blockNdx+1) << " of " <<  (int)m_valueBlocks.size()
+                                                                                       << ", sub-case " << arrayNdx+1 << " of " << valueBlock.arrayLength << "):"
                                                << TestLog::EndMessage;
 
                                        log << TestLog::Message << "Failing shader input/output values:" << TestLog::EndMessage;
-                                       dumpValues(log, valueBlock, arrayNdx);
+                                       dumpValues(valueBlock, arrayNdx);
 
                                        // Dump image on failure.
                                        log << TestLog::Image("Result", "Rendered result image", surface);
@@ -1297,7 +1055,7 @@ bool ShaderLibraryCase::execute (void)
                                        return false;
                                }
                        }
-                       else if (m_spec.expectResult == EXPECT_VALIDATION_FAIL)
+                       else if (m_expectResult == EXPECT_VALIDATION_FAIL)
                        {
                                log     << TestLog::Message
                                        << "Draw call generated error: "
@@ -1347,14 +1105,14 @@ bool ShaderLibraryCase::execute (void)
        }
 
        gl.useProgram(0);
-       if (separablePrograms)
+       if (m_separatePrograms)
                gl.bindProgramPipeline(0);
 
        GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): end");
        return true;
 }
 
-TestCase::IterateResult ShaderLibraryCase::iterate (void)
+TestCase::IterateResult ShaderCase::iterate (void)
 {
        // Initialize state to pass.
        m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
@@ -1366,5 +1124,690 @@ TestCase::IterateResult ShaderLibraryCase::iterate (void)
        return TestCase::STOP;
 }
 
+static void generateExtensionStatements (std::ostringstream& buf, const std::vector<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)
+{
+       std::istringstream      baseCodeBuf(baseCode);
+       std::ostringstream      resultBuf;
+       std::string                     line;
+       bool                            firstNonPreprocessorLine = true;
+       std::ostringstream      extensions;
+
+       generateExtensionStatements(extensions, requirements, shaderType);
+
+       // skip if no requirements
+       if (extensions.str().empty())
+               return baseCode;
+
+       while (std::getline(baseCodeBuf, line))
+       {
+               // begins with '#'?
+               const std::string::size_type    firstNonWhitespace              = line.find_first_not_of("\t ");
+               const bool                                              isPreprocessorDirective = (firstNonWhitespace != std::string::npos && line.at(firstNonWhitespace) == '#');
+
+               // Inject #extensions
+               if (!isPreprocessorDirective && firstNonPreprocessorLine)
+               {
+                       firstNonPreprocessorLine = false;
+                       resultBuf << extensions.str();
+               }
+
+               resultBuf << line << "\n";
+       }
+
+       return resultBuf.str();
+}
+
+// This functions builds a matching vertex shader for a 'both' case, when
+// the fragment shader is being tested.
+// We need to build attributes and varyings for each 'input'.
+string ShaderCase::genVertexShader (const ValueBlock& valueBlock) const
+{
+       ostringstream   res;
+       const bool              usesInout       = usesShaderInoutQualifiers(m_targetVersion);
+       const char*             vtxIn           = usesInout ? "in"      : "attribute";
+       const char*             vtxOut          = usesInout ? "out"     : "varying";
+
+       res << glu::getGLSLVersionDeclaration(m_targetVersion) << "\n";
+
+       // Declarations (position + attribute/varying for each input).
+       res << "precision highp float;\n";
+       res << "precision highp int;\n";
+       res << "\n";
+       res << vtxIn << " highp vec4 dEQP_Position;\n";
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value& val = valueBlock.values[ndx];
+               if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
+               {
+                       DataType        floatType       = getDataTypeFloatScalars(val.dataType);
+                       const char*     typeStr         = getDataTypeName(floatType);
+                       res << vtxIn << " " << typeStr << " a_" << val.valueName << ";\n";
+
+                       if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
+                               res << vtxOut << " " << typeStr << " " << val.valueName << ";\n";
+                       else
+                               res << vtxOut << " " << typeStr << " v_" << val.valueName << ";\n";
+               }
+       }
+       res << "\n";
+
+       // Main function.
+       // - gl_Position = dEQP_Position;
+       // - for each input: write attribute directly to varying
+       res << "void main()\n";
+       res << "{\n";
+       res << "        gl_Position = dEQP_Position;\n";
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value& val = valueBlock.values[ndx];
+               if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
+               {
+                       const string& name = val.valueName;
+                       if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
+                               res << "        " << name << " = a_" << name << ";\n";
+                       else
+                               res << "        v_" << name << " = a_" << name << ";\n";
+               }
+       }
+
+       res << "}\n";
+       return res.str();
+}
+
+static void genCompareFunctions (ostringstream& stream, const ShaderCase::ValueBlock& valueBlock, bool useFloatTypes)
+{
+       bool cmpTypeFound[TYPE_LAST];
+       for (int i = 0; i < TYPE_LAST; i++)
+               cmpTypeFound[i] = false;
+
+       for (int valueNdx = 0; valueNdx < (int)valueBlock.values.size(); valueNdx++)
+       {
+               const ShaderCase::Value& val = valueBlock.values[valueNdx];
+               if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
+                       cmpTypeFound[(int)val.dataType] = true;
+       }
+
+       if (useFloatTypes)
+       {
+               if (cmpTypeFound[TYPE_BOOL])            stream << "bool isOk (float a, bool b) { return ((a > 0.5) == b); }\n";
+               if (cmpTypeFound[TYPE_BOOL_VEC2])       stream << "bool isOk (vec2 a, bvec2 b) { return (greaterThan(a, vec2(0.5)) == b); }\n";
+               if (cmpTypeFound[TYPE_BOOL_VEC3])       stream << "bool isOk (vec3 a, bvec3 b) { return (greaterThan(a, vec3(0.5)) == b); }\n";
+               if (cmpTypeFound[TYPE_BOOL_VEC4])       stream << "bool isOk (vec4 a, bvec4 b) { return (greaterThan(a, vec4(0.5)) == b); }\n";
+               if (cmpTypeFound[TYPE_INT])                     stream << "bool isOk (float a, int b)  { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1)); }\n";
+               if (cmpTypeFound[TYPE_INT_VEC2])        stream << "bool isOk (vec2 a, ivec2 b) { return (ivec2(floor(a + 0.5)) == b); }\n";
+               if (cmpTypeFound[TYPE_INT_VEC3])        stream << "bool isOk (vec3 a, ivec3 b) { return (ivec3(floor(a + 0.5)) == b); }\n";
+               if (cmpTypeFound[TYPE_INT_VEC4])        stream << "bool isOk (vec4 a, ivec4 b) { return (ivec4(floor(a + 0.5)) == b); }\n";
+               if (cmpTypeFound[TYPE_UINT])            stream << "bool isOk (float a, uint b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1u)); }\n";
+               if (cmpTypeFound[TYPE_UINT_VEC2])       stream << "bool isOk (vec2 a, uvec2 b) { return (uvec2(floor(a + 0.5)) == b); }\n";
+               if (cmpTypeFound[TYPE_UINT_VEC3])       stream << "bool isOk (vec3 a, uvec3 b) { return (uvec3(floor(a + 0.5)) == b); }\n";
+               if (cmpTypeFound[TYPE_UINT_VEC4])       stream << "bool isOk (vec4 a, uvec4 b) { return (uvec4(floor(a + 0.5)) == b); }\n";
+       }
+       else
+       {
+               if (cmpTypeFound[TYPE_BOOL])            stream << "bool isOk (bool a, bool b)   { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_BOOL_VEC2])       stream << "bool isOk (bvec2 a, bvec2 b) { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_BOOL_VEC3])       stream << "bool isOk (bvec3 a, bvec3 b) { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_BOOL_VEC4])       stream << "bool isOk (bvec4 a, bvec4 b) { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_INT])                     stream << "bool isOk (int a, int b)     { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_INT_VEC2])        stream << "bool isOk (ivec2 a, ivec2 b) { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_INT_VEC3])        stream << "bool isOk (ivec3 a, ivec3 b) { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_INT_VEC4])        stream << "bool isOk (ivec4 a, ivec4 b) { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_UINT])            stream << "bool isOk (uint a, uint b)   { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_UINT_VEC2])       stream << "bool isOk (uvec2 a, uvec2 b) { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_UINT_VEC3])       stream << "bool isOk (uvec3 a, uvec3 b) { return (a == b); }\n";
+               if (cmpTypeFound[TYPE_UINT_VEC4])       stream << "bool isOk (uvec4 a, uvec4 b) { return (a == b); }\n";
+       }
+
+       if (cmpTypeFound[TYPE_FLOAT])           stream << "bool isOk (float a, float b, float eps) { return (abs(a-b) <= (eps*abs(b) + eps)); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_VEC2])      stream << "bool isOk (vec2 a, vec2 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_VEC3])      stream << "bool isOk (vec3 a, vec3 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_VEC4])      stream << "bool isOk (vec4 a, vec4 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
+
+       if (cmpTypeFound[TYPE_FLOAT_MAT2])              stream << "bool isOk (mat2 a, mat2 b, float eps) { vec2 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec2(eps))); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_MAT2X3])    stream << "bool isOk (mat2x3 a, mat2x3 b, float eps) { vec3 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec3(eps))); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_MAT2X4])    stream << "bool isOk (mat2x4 a, mat2x4 b, float eps) { vec4 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec4(eps))); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_MAT3X2])    stream << "bool isOk (mat3x2 a, mat3x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec2(eps))); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_MAT3])              stream << "bool isOk (mat3 a, mat3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec3(eps))); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_MAT3X4])    stream << "bool isOk (mat3x4 a, mat3x4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec4(eps))); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_MAT4X2])    stream << "bool isOk (mat4x2 a, mat4x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec2(eps))); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_MAT4X3])    stream << "bool isOk (mat4x3 a, mat4x3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec3(eps))); }\n";
+       if (cmpTypeFound[TYPE_FLOAT_MAT4])              stream << "bool isOk (mat4 a, mat4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec4(eps))); }\n";
+}
+
+static void genCompareOp (ostringstream& output, const char* dstVec4Var, const ShaderCase::ValueBlock& valueBlock, const char* nonFloatNamePrefix, const char* checkVarName)
+{
+       bool isFirstOutput = true;
+
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value&        val                     = valueBlock.values[ndx];
+               const char*                                     valueName       = val.valueName.c_str();
+
+               if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
+               {
+                       // Check if we're only interested in one variable (then skip if not the right one).
+                       if (checkVarName && !deStringEqual(valueName, checkVarName))
+                               continue;
+
+                       // Prefix.
+                       if (isFirstOutput)
+                       {
+                               output << "bool RES = ";
+                               isFirstOutput = false;
+                       }
+                       else
+                               output << "RES = RES && ";
+
+                       // Generate actual comparison.
+                       if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
+                               output << "isOk(" << valueName << ", ref_" << valueName << ", 0.05);\n";
+                       else
+                               output << "isOk(" << nonFloatNamePrefix << valueName << ", ref_" << valueName << ");\n";
+               }
+               // \note Uniforms are already declared in shader.
+       }
+
+       if (isFirstOutput)
+               output << dstVec4Var << " = vec4(1.0);\n";      // \todo [petri] Should we give warning if not expect-failure case?
+       else
+               output << dstVec4Var << " = vec4(RES, RES, RES, 1.0);\n";
+}
+
+string ShaderCase::genFragmentShader (const ValueBlock& valueBlock) const
+{
+       ostringstream   shader;
+       const bool              usesInout               = usesShaderInoutQualifiers(m_targetVersion);
+       const bool              customColorOut  = usesInout;
+       const char*             fragIn                  = usesInout ? "in" : "varying";
+       const char*             prec                    = supportsFragmentHighp(m_targetVersion) ? "highp" : "mediump";
+
+       shader << glu::getGLSLVersionDeclaration(m_targetVersion) << "\n";
+
+       shader << "precision " << prec << " float;\n";
+       shader << "precision " << prec << " int;\n";
+       shader << "\n";
+
+       if (customColorOut)
+       {
+               shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
+               shader << "\n";
+       }
+
+       genCompareFunctions(shader, valueBlock, true);
+       shader << "\n";
+
+       // Declarations (varying, reference for each output).
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value& val = valueBlock.values[ndx];
+               DataType        floatType               = getDataTypeFloatScalars(val.dataType);
+               const char*     floatTypeStr    = getDataTypeName(floatType);
+               const char*     refTypeStr              = getDataTypeName(val.dataType);
+
+               if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
+               {
+                       if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
+                               shader << fragIn << " " << floatTypeStr << " " << val.valueName << ";\n";
+                       else
+                               shader << fragIn << " " << floatTypeStr << " v_" << val.valueName << ";\n";
+
+                       shader << "uniform " << refTypeStr << " ref_" << val.valueName << ";\n";
+               }
+       }
+
+       shader << "\n";
+       shader << "void main()\n";
+       shader << "{\n";
+
+       shader << "     ";
+       genCompareOp(shader, customColorOut ? "dEQP_FragColor" : "gl_FragColor", valueBlock, "v_", DE_NULL);
+
+       shader << "}\n";
+       return shader.str();
+}
+
+// Specialize a shader for the vertex shader test case.
+string ShaderCase::specializeVertexShader (const char* src, const ValueBlock& valueBlock) const
+{
+       ostringstream   decl;
+       ostringstream   setup;
+       ostringstream   output;
+       const bool              usesInout       = usesShaderInoutQualifiers(m_targetVersion);
+       const char*             vtxIn           = usesInout ? "in"      : "attribute";
+       const char*             vtxOut          = usesInout ? "out"     : "varying";
+
+       // generated from "both" case
+       DE_ASSERT(m_caseType == CASETYPE_VERTEX_ONLY);
+
+       // Output (write out position).
+       output << "gl_Position = dEQP_Position;\n";
+
+       // Declarations (position + attribute for each input, varying for each output).
+       decl << vtxIn << " highp vec4 dEQP_Position;\n";
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value& val = valueBlock.values[ndx];
+               const char*     valueName               = val.valueName.c_str();
+               DataType        floatType               = getDataTypeFloatScalars(val.dataType);
+               const char*     floatTypeStr    = getDataTypeName(floatType);
+               const char*     refTypeStr              = getDataTypeName(val.dataType);
+
+               if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
+               {
+                       if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
+                       {
+                               decl << vtxIn << " " << floatTypeStr << " " << valueName << ";\n";
+                       }
+                       else
+                       {
+                               decl << vtxIn << " " << floatTypeStr << " a_" << valueName << ";\n";
+                               setup << refTypeStr << " " << valueName << " = " << refTypeStr << "(a_" << valueName << ");\n";
+                       }
+               }
+               else if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
+               {
+                       if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
+                               decl << vtxOut << " " << floatTypeStr << " " << valueName << ";\n";
+                       else
+                       {
+                               decl << vtxOut << " " << floatTypeStr << " v_" << valueName << ";\n";
+                               decl << refTypeStr << " " << valueName << ";\n";
+
+                               output << "v_" << valueName << " = " << floatTypeStr << "(" << valueName << ");\n";
+                       }
+               }
+       }
+
+       // Shader specialization.
+       map<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);
+
+       return withExt;
+}
+
+// Specialize a shader for the fragment shader test case.
+string ShaderCase::specializeFragmentShader (const char* src, const ValueBlock& valueBlock) const
+{
+       ostringstream   decl;
+       ostringstream   setup;
+       ostringstream   output;
+
+       const bool              usesInout               = usesShaderInoutQualifiers(m_targetVersion);
+       const bool              customColorOut  = usesInout;
+       const char*             fragIn                  = usesInout                     ? "in"                          : "varying";
+       const char*             fragColor               = customColorOut        ? "dEQP_FragColor"      : "gl_FragColor";
+
+       // generated from "both" case
+       DE_ASSERT(m_caseType == CASETYPE_FRAGMENT_ONLY);
+
+       genCompareFunctions(decl, valueBlock, false);
+       genCompareOp(output, fragColor, valueBlock, "", DE_NULL);
+
+       if (customColorOut)
+               decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
+
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value&        val                             = valueBlock.values[ndx];
+               const char*                                     valueName               = val.valueName.c_str();
+               DataType                                        floatType               = getDataTypeFloatScalars(val.dataType);
+               const char*                                     floatTypeStr    = getDataTypeName(floatType);
+               const char*                                     refTypeStr              = getDataTypeName(val.dataType);
+
+               if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
+               {
+                       if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
+                               decl << fragIn << " " << floatTypeStr << " " << valueName << ";\n";
+                       else
+                       {
+                               decl << fragIn << " " << floatTypeStr << " v_" << valueName << ";\n";
+                               std::string offset = isDataTypeIntOrIVec(val.dataType) ? " * 1.0025" : ""; // \todo [petri] bit of a hack to avoid errors in chop() due to varying interpolation
+                               setup << refTypeStr << " " << valueName << " = " << refTypeStr << "(v_" << valueName << offset << ");\n";
+                       }
+               }
+               else if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
+               {
+                       decl << "uniform " << refTypeStr << " ref_" << valueName << ";\n";
+                       decl << refTypeStr << " " << valueName << ";\n";
+               }
+       }
+
+       /* \todo [2010-04-01 petri] Check all outputs. */
+
+       // Shader specialization.
+       map<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);
+
+       return withExt;
+}
+
+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;
+
+       DE_UNREF(renderCtx);
+
+       decl << vtxIn << " highp vec4 dEQP_Position;\n";
+
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value&        val             = valueBlock.values[ndx];
+               const char*                                     typeStr = getDataTypeName(val.dataType);
+
+               if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
+               {
+                       if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
+                       {
+                               decl << vtxIn << " " << typeStr << " " << val.valueName << ";\n";
+                       }
+                       else
+                       {
+                               DataType        floatType               = getDataTypeFloatScalars(val.dataType);
+                               const char*     floatTypeStr    = getDataTypeName(floatType);
+
+                               decl << vtxIn << " " << floatTypeStr << " a_" << val.valueName << ";\n";
+                               setup << typeStr << " " << val.valueName << " = " << typeStr << "(a_" << val.valueName << ");\n";
+                       }
+               }
+               else if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM &&
+                                       val.valueName.find('.') == string::npos)
+                       decl << "uniform " << typeStr << " " << val.valueName << ";\n";
+       }
+
+       params.insert(pair<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);
+
+       if (customColorOut)
+               decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
+
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value&        val                             = valueBlock.values[ndx];
+               const char*                                     valueName               = val.valueName.c_str();
+               const char*                                     refTypeStr              = getDataTypeName(val.dataType);
+
+               if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
+               {
+                       decl << "uniform " << refTypeStr << " ref_" << valueName << ";\n";
+                       decl << refTypeStr << " " << valueName << ";\n";
+               }
+               else if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM &&
+                                       val.valueName.find('.') == string::npos)
+               {
+                       decl << "uniform " << refTypeStr << " " << valueName << ";\n";
+               }
+       }
+
+       params.insert(pair<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;
+}
+
+static map<string, string> generateGeometrySpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock)
+{
+       ostringstream           decl;
+       map<string, string>     params;
+
+       DE_UNREF(renderCtx);
+       DE_UNREF(targetVersion);
+
+       decl << "layout (triangles) in;\n";
+       decl << "layout (triangle_strip, max_vertices=3) out;\n";
+       decl << "\n";
+
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value&        val                             = valueBlock.values[ndx];
+               const char*                                     valueName               = val.valueName.c_str();
+               const char*                                     refTypeStr              = getDataTypeName(val.dataType);
+
+               if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM &&
+                       val.valueName.find('.') == string::npos)
+               {
+                       decl << "uniform " << refTypeStr << " " << valueName << ";\n";
+               }
+       }
+
+       params.insert(pair<string, string>("GEOMETRY_DECLARATIONS",             decl.str()));
+       return params;
+}
+
+static map<string, string> generateTessControlSpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock)
+{
+       ostringstream           decl;
+       ostringstream           output;
+       map<string, string>     params;
+
+       DE_UNREF(targetVersion);
+
+       decl << "layout (vertices=3) out;\n";
+       decl << "\n";
+
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value&        val                             = valueBlock.values[ndx];
+               const char*                                     valueName               = val.valueName.c_str();
+               const char*                                     refTypeStr              = getDataTypeName(val.dataType);
+
+               if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM &&
+                       val.valueName.find('.') == string::npos)
+               {
+                       decl << "uniform " << refTypeStr << " " << valueName << ";\n";
+               }
+       }
+
+       output <<       "gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
+                               "gl_TessLevelInner[0] = 2.0;\n"
+                               "gl_TessLevelInner[1] = 2.0;\n"
+                               "gl_TessLevelOuter[0] = 2.0;\n"
+                               "gl_TessLevelOuter[1] = 2.0;\n"
+                               "gl_TessLevelOuter[2] = 2.0;\n"
+                               "gl_TessLevelOuter[3] = 2.0;";
+
+       params.insert(pair<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;
+}
+
+static map<string, string> generateTessEvalSpecialization (const glu::RenderContext& renderCtx, glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock)
+{
+       ostringstream           decl;
+       ostringstream           output;
+       map<string, string>     params;
+
+       DE_UNREF(targetVersion);
+
+       decl << "layout (triangles) in;\n";
+       decl << "\n";
+
+       for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
+       {
+               const ShaderCase::Value&        val                             = valueBlock.values[ndx];
+               const char*                                     valueName               = val.valueName.c_str();
+               const char*                                     refTypeStr              = getDataTypeName(val.dataType);
+
+               if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM &&
+                       val.valueName.find('.') == string::npos)
+               {
+                       decl << "uniform " << refTypeStr << " " << valueName << ";\n";
+               }
+       }
+
+       output <<       "gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + gl_TessCoord[2] * gl_in[2].gl_Position;\n";
+
+       params.insert(pair<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;
+}
+
+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);
+
+               for (int ndx = 0; ndx < (int)sources.size(); ++ndx)
+               {
+                       const StringTemplate    tmpl                    (sources[ndx]);
+                       const std::string               baseGLSLCode    = tmpl.specialize(specializationParams);
+                       const std::string               glslSource              = injectExtensionRequirements(baseGLSLCode, shaderType, requirements);
+
+                       dst << glu::ShaderSource(shaderType, glslSource);
+               }
+       }
+}
+
+void ShaderCase::specializeVertexShaders (glu::ProgramSources& dst, const std::vector<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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+void ShaderCase::dumpValues (const ValueBlock& valueBlock, int arrayNdx)
+{
+       int numValues = (int)valueBlock.values.size();
+       for (int valNdx = 0; valNdx < numValues; valNdx++)
+       {
+               const ShaderCase::Value&        val                             = valueBlock.values[valNdx];
+               const char*                                     valueName               = val.valueName.c_str();
+               DataType                                        dataType                = val.dataType;
+               int                                                     scalarSize              = getDataTypeScalarSize(val.dataType);
+               ostringstream                           result;
+
+               result << "    ";
+               if (val.storageType == Value::STORAGE_INPUT)
+                       result << "input ";
+               else if (val.storageType == Value::STORAGE_UNIFORM)
+                       result << "uniform ";
+               else if (val.storageType == Value::STORAGE_OUTPUT)
+                       result << "expected ";
+
+               result << getDataTypeName(dataType) << " " << valueName << ":";
+
+               if (isDataTypeScalar(dataType))
+                       result << " ";
+               if (isDataTypeVector(dataType))
+                       result << " [ ";
+               else if (isDataTypeMatrix(dataType))
+                       result << "\n";
+
+               if (isDataTypeScalarOrVector(dataType))
+               {
+                       for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
+                       {
+                               int                                             elemNdx = (val.arrayLength == 1) ? 0 : arrayNdx;
+                               const Value::Element&   e               = val.elements[elemNdx*scalarSize + scalarNdx];
+                               result << ((scalarNdx != 0) ? ", " : "");
+
+                               if (isDataTypeFloatOrVec(dataType))
+                                       result << e.float32;
+                               else if (isDataTypeIntOrIVec(dataType))
+                                       result << e.int32;
+                               else if (isDataTypeUintOrUVec(dataType))
+                                       result << (deUint32)e.int32;
+                               else if (isDataTypeBoolOrBVec(dataType))
+                                       result << (e.bool32 ? "true" : "false");
+                       }
+               }
+               else if (isDataTypeMatrix(dataType))
+               {
+                       int numRows = getDataTypeMatrixNumRows(dataType);
+                       int numCols = getDataTypeMatrixNumColumns(dataType);
+                       for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
+                       {
+                               result << "       [ ";
+                               for (int colNdx = 0; colNdx < numCols; colNdx++)
+                               {
+                                       int             elemNdx = (val.arrayLength == 1) ? 0 : arrayNdx;
+                                       float   v               = val.elements[elemNdx*scalarSize + rowNdx*numCols + colNdx].float32;
+                                       result << ((colNdx==0) ? "" : ", ") << v;
+                               }
+                               result << " ]\n";
+                       }
+               }
+
+               if (isDataTypeScalar(dataType))
+                       result << "\n";
+               else if (isDataTypeVector(dataType))
+                       result << " ]\n";
+
+               m_testCtx.getLog() << TestLog::Message << result.str() << TestLog::EndMessage;
+       }
+}
+
+} // sl
 } // gls
 } // deqp
index a1ae5a6..162ea0f 100644 (file)
  *//*--------------------------------------------------------------------*/
 
 #include "gluDefs.hpp"
-#include "gluShaderLibrary.hpp"
+#include "gluShaderUtil.hpp"
+#include "gluRenderContext.hpp"
+#include "gluShaderProgram.hpp"
 #include "tcuTestCase.hpp"
+#include "tcuSurface.hpp"
 
-namespace glu
-{
-class RenderContext;
-class ContextInfo;
-}
+#include <string>
+#include <vector>
 
 namespace deqp
 {
 namespace gls
 {
+namespace sl
+{
 
 // ShaderCase node.
 
-class ShaderLibraryCase : public tcu::TestCase
+class ShaderCase : public tcu::TestCase
 {
 public:
+       enum CaseType
+       {
+               CASETYPE_COMPLETE = 0,          //!< Has all shaders specified separately.
+               CASETYPE_VERTEX_ONLY,           //!< "Both" case, vertex shader sub case.
+               CASETYPE_FRAGMENT_ONLY,         //!< "Both" case, fragment shader sub case.
+
+               CASETYPE_LAST
+       };
+
+       enum ExpectResult
+       {
+               EXPECT_PASS = 0,
+               EXPECT_COMPILE_FAIL,
+               EXPECT_LINK_FAIL,
+               EXPECT_COMPILE_LINK_FAIL,
+               EXPECT_VALIDATION_FAIL,
+               EXPECT_BUILD_SUCCESSFUL,
+
+               EXPECT_LAST
+       };
+
+       struct Value
+       {
+               enum StorageType
+               {
+                       STORAGE_UNIFORM,
+                       STORAGE_INPUT,
+                       STORAGE_OUTPUT,
+
+                       STORAGE_LAST
+               };
+
+               /* \todo [2010-03-31 petri] Replace with another vector to allow a) arrays, b) compact representation */
+               union Element
+               {
+                       float           float32;
+                       deInt32         int32;
+                       deInt32         bool32;
+               };
+
+               StorageType                             storageType;
+               std::string                             valueName;
+               glu::DataType                   dataType;
+               int                                             arrayLength;    // Number of elements in array (currently always 1).
+               std::vector<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.
-                                                                                       ShaderLibraryCase                                                               (tcu::TestContext&                                                      testCtx,
-                                                                                                                                                                                        glu::RenderContext&                                            renderCtx,
-                                                                                                                                                                                        const glu::ContextInfo&                                        contextInfo,
-                                                                                                                                                                                        const char*                                                            caseName,
-                                                                                                                                                                                        const char*                                                            description,
-                                                                                                                                                                                        const glu::sl::ShaderCaseSpecification&        specification);
-       virtual                                                                 ~ShaderLibraryCase                                                              (void);
+                                                                       ShaderCase                                                                              (tcu::TestContext&                              testCtx,
+                                                                                                                                                                        glu::RenderContext&                    renderCtx,
+                                                                                                                                                                        const glu::ContextInfo&                contextInfo,
+                                                                                                                                                                        const char*                                    caseName,
+                                                                                                                                                                        const char*                                    description,
+                                                                                                                                                                        const ShaderCaseSpecification& specification);
+                                                                       ShaderCase                                                                              (tcu::TestContext&                                      testCtx,
+                                                                                                                                                                        glu::RenderContext&                            renderCtx,
+                                                                                                                                                                        const glu::ContextInfo&                        contextInfo,
+                                                                                                                                                                        const char*                                            caseName,
+                                                                                                                                                                        const char*                                            description,
+                                                                                                                                                                        const PipelineCaseSpecification&       specification);
+       virtual                                                 ~ShaderCase                                                                             (void);
 
 private:
-       void                                                                    init                                                                                    (void);
-       bool                                                                    execute                                                                                 (void);
-       IterateResult                                                   iterate                                                                                 (void);
+       void                                                    init                                                                                    (void);
+       bool                                                    execute                                                                                 (void);
+       IterateResult                                   iterate                                                                                 (void);
+
+                                                                       ShaderCase                                                                              (const ShaderCase&);            // not allowed!
+       ShaderCase&                                             operator=                                                                               (const ShaderCase&);            // not allowed!
+
+       std::string                                             genVertexShader                                                                 (const ValueBlock& valueBlock) const;
+       std::string                                             genFragmentShader                                                               (const ValueBlock& valueBlock) const;
+       std::string                                             specializeVertexShader                                                  (const char* src, const ValueBlock& valueBlock) const;
+       std::string                                             specializeFragmentShader                                                (const char* src, const ValueBlock& valueBlock) const;
+       void                                                    specializeVertexShaders                                                 (glu::ProgramSources& dst, const std::vector<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);
 
-                                                                                       ShaderLibraryCase                                                               (const ShaderLibraryCase&); // not allowed!
-       ShaderLibraryCase&                                              operator=                                                                               (const ShaderLibraryCase&); // not allowed!
+       struct ProgramObject
+       {
+               glu::ProgramSources             programSources;
+               PipelineProgram                 spec;
+       };
 
        // Member variables.
-       glu::RenderContext&                                             m_renderCtx;
-       const glu::ContextInfo&                                 m_contextInfo;
-       const glu::sl::ShaderCaseSpecification  m_spec;
+       glu::RenderContext&                             m_renderCtx;
+       const glu::ContextInfo&                 m_contextInfo;
+       const CaseType                                  m_caseType;
+       const ExpectResult                              m_expectResult;
+       const glu::GLSLVersion                  m_targetVersion;
+       const bool                                              m_separatePrograms;
+       std::vector<ValueBlock>                 m_valueBlocks;
+       std::vector<ProgramObject>              m_programs;
 };
 
+} // sl
 } // gls
 } // deqp