Import dEQP.

Import drawElements Quality Program from an internal repository.

Bug: 17388917
Change-Id: Ic109fe4a57e31b2a816113d90fbdf51a43e7abeb
diff --git a/modules/glshared/glsShaderLibraryCase.cpp b/modules/glshared/glsShaderLibraryCase.cpp
new file mode 100644
index 0000000..c313058
--- /dev/null
+++ b/modules/glshared/glsShaderLibraryCase.cpp
@@ -0,0 +1,1719 @@
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program OpenGL (ES) Module
+ * -----------------------------------------------
+ *
+ * Copyright 2014 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 Compiler test case.
+ *//*--------------------------------------------------------------------*/
+
+#include "glsShaderLibraryCase.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuRenderTarget.hpp"
+
+#include "tcuStringTemplate.hpp"
+#include "gluShaderProgram.hpp"
+#include "gluPixelTransfer.hpp"
+#include "gluDrawUtil.hpp"
+#include "gluContextInfo.hpp"
+#include "gluStrUtil.hpp"
+
+#include "glwFunctions.hpp"
+#include "glwEnums.hpp"
+
+#include "deRandom.hpp"
+#include "deInt32.h"
+#include "deMath.h"
+#include "deString.h"
+#include "deStringUtil.hpp"
+#include "deSharedPtr.hpp"
+
+#include <map>
+#include <vector>
+#include <string>
+#include <sstream>
+
+using namespace std;
+using namespace tcu;
+using namespace glu;
+
+namespace deqp
+{
+namespace gls
+{
+namespace sl
+{
+
+enum
+{
+	VIEWPORT_WIDTH		= 128,
+	VIEWPORT_HEIGHT		= 128
+};
+
+static inline bool usesShaderInoutQualifiers (glu::GLSLVersion version)
+{
+	switch (version)
+	{
+		case glu::GLSL_VERSION_100_ES:
+		case glu::GLSL_VERSION_130:
+		case glu::GLSL_VERSION_140:
+		case glu::GLSL_VERSION_150:
+			return false;
+
+		default:
+			return true;
+	}
+}
+
+static inline bool supportsFragmentHighp (glu::GLSLVersion version)
+{
+	return version != glu::GLSL_VERSION_100_ES;
+}
+
+ShaderCase::ValueBlock::ValueBlock (void)
+	: arrayLength(0)
+{
+}
+
+ShaderCase::CaseRequirement::CaseRequirement (void)
+	: m_type						(REQUIREMENTTYPE_LAST)
+	, m_supportedExtensionNdx		(-1)
+	, m_effectiveShaderStageFlags	(-1)
+	, m_enumName					(-1)
+	, m_referenceValue				(-1)
+{
+}
+
+ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createAnyExtensionRequirement (const std::vector<std::string>& requirements, deUint32 effectiveShaderStageFlags)
+{
+	CaseRequirement retVal;
+
+	retVal.m_type = REQUIREMENTTYPE_EXTENSION;
+	retVal.m_extensions = requirements;
+	retVal.m_effectiveShaderStageFlags = effectiveShaderStageFlags;
+
+	return retVal;
+}
+
+ShaderCase::CaseRequirement ShaderCase::CaseRequirement::createLimitRequirement (deUint32 enumName, int ref)
+{
+	CaseRequirement retVal;
+
+	retVal.m_type = REQUIREMENTTYPE_IMPLEMENTATION_LIMIT;
+	retVal.m_enumName = enumName;
+	retVal.m_referenceValue = ref;
+
+	return retVal;
+}
+
+void ShaderCase::CaseRequirement::checkRequirements (glu::RenderContext& renderCtx, const glu::ContextInfo& contextInfo)
+{
+	DE_UNREF(renderCtx);
+
+	switch (m_type)
+	{
+		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;
+				}
+			}
+
+			// no extension(s). Make a nice output
+			{
+				std::ostringstream extensionList;
+
+				for (int ndx = 0; ndx < (int)m_extensions.size(); ++ndx)
+				{
+					if (!extensionList.str().empty())
+						extensionList << ", ";
+					extensionList << m_extensions[ndx];
+				}
+
+				if (m_extensions.size() == 1)
+					throw tcu::NotSupportedError("Test requires extension " + extensionList.str());
+				else
+					throw tcu::NotSupportedError("Test requires any extension of " + extensionList.str());
+			}
+
+			// cannot be reached
+		}
+
+		case REQUIREMENTTYPE_IMPLEMENTATION_LIMIT:
+		{
+			const glw::Functions&	gl		= renderCtx.getFunctions();
+			glw::GLint				value	= 0;
+			glw::GLenum				error;
+
+			gl.getIntegerv(m_enumName, &value);
+			error = gl.getError();
+
+			if (error != GL_NO_ERROR)
+				throw tcu::TestError("Query for " + de::toString(glu::getGettableStateStr(m_enumName)) +  " generated " + de::toString(glu::getErrorStr(error)));
+
+			if (!(value > m_referenceValue))
+				throw tcu::NotSupportedError("Test requires " + de::toString(glu::getGettableStateStr(m_enumName)) + " (" + de::toString(value) + ") > " + de::toString(m_referenceValue));
+
+			return;
+		}
+
+		default:
+			DE_ASSERT(false);
+	}
+}
+
+ShaderCase::ShaderCaseSpecification::ShaderCaseSpecification (void)
+	: expectResult	(EXPECT_LAST)
+	, targetVersion	(glu::GLSL_VERSION_LAST)
+	, caseType		(CASETYPE_COMPLETE)
+{
+}
+
+ShaderCase::ShaderCaseSpecification ShaderCase::ShaderCaseSpecification::generateSharedSourceVertexCase (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_VERTEX_ONLY;
+	retVal.valueBlocks		= values;
+	retVal.vertexSources.push_back(sharedSource);
+	return retVal;
+}
+
+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;
+}
+
+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;
+
+	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;
+	}
+
+	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.
+
+ShaderCase::ShaderCase (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* name, const char* description, const ShaderCaseSpecification& specification)
+	: tcu::TestCase				(testCtx, name, description)
+	, m_renderCtx				(renderCtx)
+	, m_contextInfo				(contextInfo)
+	, m_caseType				(specification.caseType)
+	, m_expectResult			(specification.expectResult)
+	, m_targetVersion			(specification.targetVersion)
+	, m_separatePrograms		(false)
+	, m_valueBlocks				(specification.valueBlocks)
+{
+	if (m_caseType == CASETYPE_VERTEX_ONLY)
+	{
+		// case generated from "both" target, vertex case
+		DE_ASSERT(specification.vertexSources.size() == 1);
+		DE_ASSERT(specification.fragmentSources.empty());
+		DE_ASSERT(specification.tessCtrlSources.empty());
+		DE_ASSERT(specification.tessEvalSources.empty());
+		DE_ASSERT(specification.geometrySources.empty());
+	}
+	else if (m_caseType == CASETYPE_FRAGMENT_ONLY)
+	{
+		// case generated from "both" target, fragment case
+		DE_ASSERT(specification.vertexSources.empty());
+		DE_ASSERT(specification.fragmentSources.size() == 1);
+		DE_ASSERT(specification.tessCtrlSources.empty());
+		DE_ASSERT(specification.tessEvalSources.empty());
+		DE_ASSERT(specification.geometrySources.empty());
+	}
+
+	// single program object
+	{
+		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);
+	}
+}
+
+ShaderCase::ShaderCase (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* name, const char* description, const PipelineCaseSpecification& specification)
+	: tcu::TestCase				(testCtx, name, description)
+	, m_renderCtx				(renderCtx)
+	, m_contextInfo				(contextInfo)
+	, m_caseType				(specification.caseType)
+	, m_expectResult			(specification.expectResult)
+	, m_targetVersion			(specification.targetVersion)
+	, m_separatePrograms		(true)
+	, m_valueBlocks				(specification.valueBlocks)
+{
+	deUint32 totalActiveMask = 0;
+
+	DE_ASSERT(m_caseType == CASETYPE_COMPLETE);
+
+	// validate
+
+	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;
+	}
+
+	// create ProgramObjects
+
+	for (int pipelineProgramNdx = 0; pipelineProgramNdx < (int)specification.programs.size(); ++pipelineProgramNdx)
+	{
+		ProgramObject program;
+		program.spec = specification.programs[pipelineProgramNdx];
+		m_programs.push_back(program);
+	}
+}
+
+ShaderCase::~ShaderCase (void)
+{
+}
+
+void ShaderCase::init (void)
+{
+	// If no value blocks given, use an empty one.
+	if (m_valueBlocks.empty())
+		m_valueBlocks.push_back(ValueBlock());
+
+	// Use first value block to specialize shaders.
+	const ValueBlock& valueBlock = m_valueBlocks[0];
+
+	// \todo [2010-04-01 petri] Check that all value blocks have matching values.
+
+	// prepare programs
+	for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
+	{
+		// 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);
+
+		// Generate specialized shader sources.
+		if (m_caseType == CASETYPE_COMPLETE)
+		{
+			// 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);
+		}
+		else if (m_caseType == CASETYPE_VERTEX_ONLY)
+		{
+			DE_ASSERT(m_programs.size() == 1);
+			DE_ASSERT(!m_separatePrograms);
+
+			// case generated from "both" target, vertex case
+			m_programs[0].programSources << glu::VertexSource(specializeVertexShader(m_programs[0].spec.vertexSources[0].c_str(), valueBlock));
+			m_programs[0].programSources << glu::FragmentSource(genFragmentShader(valueBlock));
+		}
+		else if (m_caseType == CASETYPE_FRAGMENT_ONLY)
+		{
+			DE_ASSERT(m_programs.size() == 1);
+			DE_ASSERT(!m_separatePrograms);
+
+			// 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));
+		}
+
+		m_programs[programNdx].programSources << glu::ProgramSeparable(m_separatePrograms);
+	}
+
+	// log the expected result
+	switch (m_expectResult)
+	{
+		case EXPECT_PASS:
+			// Don't write anything
+			break;
+
+		case EXPECT_COMPILE_FAIL:
+			m_testCtx.getLog() << tcu::TestLog::Message << "Expecting shader compilation to fail." << tcu::TestLog::EndMessage;
+			break;
+
+		case EXPECT_LINK_FAIL:
+			m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program linking to fail." << tcu::TestLog::EndMessage;
+			break;
+
+		case EXPECT_COMPILE_LINK_FAIL:
+			m_testCtx.getLog() << tcu::TestLog::Message << "Expecting either shader compilation or program linking to fail." << tcu::TestLog::EndMessage;
+			break;
+
+		case EXPECT_VALIDATION_FAIL:
+			m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program validation to fail." << tcu::TestLog::EndMessage;
+			break;
+
+		default:
+			DE_ASSERT(false);
+			break;
+	}
+}
+
+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 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(ShaderCase::Value::Element) == sizeof(glw::GLfloat));
+		DE_STATIC_ASSERT(sizeof(ShaderCase::Value::Element) == sizeof(glw::GLint));
+
+		gl.useProgram(pipelinePrograms[programNdx]);
+
+		switch (val.dataType)
+		{
+			case TYPE_FLOAT:		gl.uniform1fv(loc, 1, &val.elements[elemNdx].float32);						break;
+			case TYPE_FLOAT_VEC2:	gl.uniform2fv(loc, 1, &val.elements[elemNdx].float32);						break;
+			case TYPE_FLOAT_VEC3:	gl.uniform3fv(loc, 1, &val.elements[elemNdx].float32);						break;
+			case TYPE_FLOAT_VEC4:	gl.uniform4fv(loc, 1, &val.elements[elemNdx].float32);						break;
+			case TYPE_FLOAT_MAT2:	gl.uniformMatrix2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);		break;
+			case TYPE_FLOAT_MAT3:	gl.uniformMatrix3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);		break;
+			case TYPE_FLOAT_MAT4:	gl.uniformMatrix4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);		break;
+			case TYPE_INT:			gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32);						break;
+			case TYPE_INT_VEC2:		gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32);						break;
+			case TYPE_INT_VEC3:		gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32);						break;
+			case TYPE_INT_VEC4:		gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32);						break;
+			case TYPE_BOOL:			gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32);						break;
+			case TYPE_BOOL_VEC2:	gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32);						break;
+			case TYPE_BOOL_VEC3:	gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32);						break;
+			case TYPE_BOOL_VEC4:	gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32);						break;
+			case TYPE_UINT:			gl.uniform1uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32);		break;
+			case TYPE_UINT_VEC2:	gl.uniform2uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32);		break;
+			case TYPE_UINT_VEC3:	gl.uniform3uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32);		break;
+			case TYPE_UINT_VEC4:	gl.uniform4uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32);		break;
+			case TYPE_FLOAT_MAT2X3:	gl.uniformMatrix2x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);	break;
+			case TYPE_FLOAT_MAT2X4:	gl.uniformMatrix2x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);	break;
+			case TYPE_FLOAT_MAT3X2:	gl.uniformMatrix3x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);	break;
+			case TYPE_FLOAT_MAT3X4:	gl.uniformMatrix3x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);	break;
+			case TYPE_FLOAT_MAT4X2:	gl.uniformMatrix4x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);	break;
+			case TYPE_FLOAT_MAT4X3:	gl.uniformMatrix4x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);	break;
+
+			case TYPE_SAMPLER_2D:
+			case TYPE_SAMPLER_CUBE:
+				DE_ASSERT(!"implement!");
+				break;
+
+			default:
+				DE_ASSERT(false);
+		}
+	}
+
+	if (!foundAnyMatch)
+		log << tcu::TestLog::Message << "WARNING // Uniform \"" << name << "\" location is not valid, location = -1. Cannot set value to the uniform." << tcu::TestLog::EndMessage;
+}
+
+bool ShaderCase::isTessellationPresent (void) const
+{
+	if (m_separatePrograms)
+	{
+		const deUint32 tessellationBits =	(1 << glu::SHADERTYPE_TESSELLATION_CONTROL)		|
+											(1 << glu::SHADERTYPE_TESSELLATION_EVALUATION);
+
+		for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
+			if (m_programs[programNdx].spec.activeStageBits & tessellationBits)
+				return true;
+		return false;
+	}
+	else
+		return	!m_programs[0].programSources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty() ||
+				!m_programs[0].programSources.sources[glu::SHADERTYPE_TESSELLATION_EVALUATION].empty();
+}
+
+bool ShaderCase::checkPixels (Surface& surface, int minX, int maxX, int minY, int maxY)
+{
+	TestLog&	log				= m_testCtx.getLog();
+	bool		allWhite		= true;
+	bool		allBlack		= true;
+	bool		anyUnexpected	= false;
+
+	DE_ASSERT((maxX > minX) && (maxY > minY));
+
+	for (int y = minY; y <= maxY; y++)
+	{
+		for (int x = minX; x <= maxX; x++)
+		{
+			RGBA		pixel		 = surface.getPixel(x, y);
+			// Note: we really do not want to involve alpha in the check comparison
+			// \todo [2010-09-22 kalle] Do we know that alpha would be one? If yes, could use color constants white and black.
+			bool		isWhite		 = (pixel.getRed() == 255) && (pixel.getGreen() == 255) && (pixel.getBlue() == 255);
+			bool		isBlack		 = (pixel.getRed() == 0) && (pixel.getGreen() == 0) && (pixel.getBlue() == 0);
+
+			allWhite		= allWhite && isWhite;
+			allBlack		= allBlack && isBlack;
+			anyUnexpected	= anyUnexpected || (!isWhite && !isBlack);
+		}
+	}
+
+	if (!allWhite)
+	{
+		if (anyUnexpected)
+			log << TestLog::Message << "WARNING: expecting all rendered pixels to be white or black, but got other colors as well!" << TestLog::EndMessage;
+		else if (!allBlack)
+			log << TestLog::Message << "WARNING: got inconsistent results over the image, when all pixels should be the same color!" << TestLog::EndMessage;
+
+		return false;
+	}
+	return true;
+}
+
+bool ShaderCase::execute (void)
+{
+	const float										quadSize			= 1.0f;
+	static const float								s_positions[4*4]	=
+	{
+		-quadSize, -quadSize, 0.0f, 1.0f,
+		-quadSize, +quadSize, 0.0f, 1.0f,
+		+quadSize, -quadSize, 0.0f, 1.0f,
+		+quadSize, +quadSize, 0.0f, 1.0f
+	};
+
+	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();
+
+	// 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();
+
+	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");
+
+	if (!m_separatePrograms)
+	{
+		de::SharedPtr<glu::ShaderProgram>	program		(new glu::ShaderProgram(m_renderCtx, m_programs[0].programSources));
+
+		vertexProgramID = program->getProgram();
+		pipelineProgramIDs.push_back(program->getProgram());
+		programs.push_back(program);
+
+		// Check that compile/link results are what we expect.
+
+		DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0);
+		for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
+			if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk)
+				allCompilesOk = false;
+
+		if (!program->getProgramInfo().linkOk)
+			allLinksOk = false;
+
+		log << *program;
+	}
+	else
+	{
+		// Separate programs
+		for (int programNdx = 0; programNdx < (int)m_programs.size(); ++programNdx)
+		{
+			de::SharedPtr<glu::ShaderProgram> program(new glu::ShaderProgram(m_renderCtx, m_programs[programNdx].programSources));
+
+			if (m_programs[programNdx].spec.activeStageBits & (1 << glu::SHADERTYPE_VERTEX))
+				vertexProgramID = program->getProgram();
+
+			pipelineProgramIDs.push_back(program->getProgram());
+			programs.push_back(program);
+
+			// Check that compile/link results are what we expect.
+
+			DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0);
+			for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
+				if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk)
+					allCompilesOk = false;
+
+			if (!program->getProgramInfo().linkOk)
+				allLinksOk = false;
+
+			// Log program and active stages
+			{
+				const tcu::ScopedLogSection	section		(log, "Program", "Program " + de::toString(programNdx+1));
+				tcu::MessageBuilder			builder		(&log);
+				bool						firstStage	= true;
+
+				builder << "Pipeline uses stages: ";
+				for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
+				{
+					if (m_programs[programNdx].spec.activeStageBits & (1 << stage))
+					{
+						if (!firstStage)
+							builder << ", ";
+						builder << glu::getShaderTypeName((glu::ShaderType)stage);
+						firstStage = true;
+					}
+				}
+				builder << tcu::TestLog::EndMessage;
+
+				log << *program;
+			}
+		}
+	}
+
+	switch (m_expectResult)
+	{
+		case EXPECT_PASS:
+		case EXPECT_VALIDATION_FAIL:
+			if (!allCompilesOk)
+				failReason = "expected shaders to compile and link properly, but failed to compile.";
+			else if (!allLinksOk)
+				failReason = "expected shaders to compile and link properly, but failed to link.";
+			break;
+
+		case EXPECT_COMPILE_FAIL:
+			if (allCompilesOk && !allLinksOk)
+				failReason = "expected compilation to fail, but shaders compiled and link failed.";
+			else if (allCompilesOk)
+				failReason = "expected compilation to fail, but shaders compiled correctly.";
+			break;
+
+		case EXPECT_LINK_FAIL:
+			if (!allCompilesOk)
+				failReason = "expected linking to fail, but unable to compile.";
+			else if (allLinksOk)
+				failReason = "expected linking to fail, but passed.";
+			break;
+
+		case EXPECT_COMPILE_LINK_FAIL:
+			if (allCompilesOk && allLinksOk)
+				failReason = "expected compile or link to fail, but passed.";
+			break;
+
+		default:
+			DE_ASSERT(false);
+			return false;
+	}
+
+	if (failReason != DE_NULL)
+	{
+		// \todo [2010-06-07 petri] These should be handled in the test case?
+		log << TestLog::Message << "ERROR: " << failReason << TestLog::EndMessage;
+
+		// If implementation parses shader at link time, report it as quality warning.
+		if (m_expectResult == EXPECT_COMPILE_FAIL && allCompilesOk && !allLinksOk)
+			m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason);
+		else
+			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failReason);
+		return false;
+	}
+
+	// Return if compile/link expected to fail.
+	if (m_expectResult == EXPECT_COMPILE_FAIL		||
+		m_expectResult == EXPECT_COMPILE_LINK_FAIL	||
+		m_expectResult == EXPECT_LINK_FAIL)
+		return (failReason == DE_NULL);
+
+	// Setup viewport.
+	gl.viewport(viewportX, viewportY, width, height);
+
+	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_programs.size(); ++programNdx)
+		{
+			deUint32 shaderFlags = 0;
+			for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage)
+				if (m_programs[programNdx].spec.activeStageBits & (1 << stage))
+					shaderFlags |= glu::getGLShaderTypeBit((glu::ShaderType)stage);
+
+			programPipeline->useProgramStages(shaderFlags, pipelineProgramIDs[programNdx]);
+		}
+
+		programPipeline->activeShaderProgram(vertexProgramID);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "setup pipeline");
+	}
+	else
+	{
+		// Start using program
+		gl.useProgram(vertexProgramID);
+		GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()");
+	}
+
+	// Fetch location for positions positions.
+	int positionLoc = gl.getAttribLocation(vertexProgramID, "dEQP_Position");
+	if (positionLoc == -1)
+	{
+		string errStr = string("no location found for attribute 'dEQP_Position'");
+		TCU_FAIL(errStr.c_str());
+	}
+
+	// Iterate all value blocks.
+	for (int blockNdx = 0; blockNdx < (int)m_valueBlocks.size(); blockNdx++)
+	{
+		const ValueBlock&	valueBlock		= m_valueBlocks[blockNdx];
+
+		// always render at least one pass even if there is no input/output data
+		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		(numValues);
+			glw::GLenum					postDrawError;
+			BeforeDrawValidator			beforeDrawValidator	(gl,
+															 (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 (int valNdx = 0; valNdx < numValues; valNdx++)
+			{
+				const ShaderCase::Value&	val			= valueBlock.values[valNdx];
+				const char* const			valueName	= val.valueName.c_str();
+				const DataType				dataType	= val.dataType;
+				const int					scalarSize	= getDataTypeScalarSize(val.dataType);
+
+				if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
+				{
+					// Replicate values four times.
+					std::vector<float>& scalars = attribValues[attribValueNdx++];
+					scalars.resize(numVerticesPerDraw * scalarSize);
+					if (isDataTypeFloatOrVec(dataType) || isDataTypeMatrix(dataType))
+					{
+						for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
+							for (int ndx = 0; ndx < scalarSize; ndx++)
+								scalars[repNdx*scalarSize + ndx] = val.elements[arrayNdx*scalarSize + ndx].float32;
+					}
+					else
+					{
+						// convert to floats.
+						for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
+						{
+							for (int ndx = 0; ndx < scalarSize; ndx++)
+							{
+								float v = (float)val.elements[arrayNdx*scalarSize + ndx].int32;
+								DE_ASSERT(val.elements[arrayNdx*scalarSize + ndx].int32 == (int)v);
+								scalars[repNdx*scalarSize + ndx] = v;
+							}
+						}
+					}
+
+					// 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;
+					}
+
+					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*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(), "before set uniforms");
+
+			// set uniform values for outputs (refs).
+			for (int valNdx = 0; valNdx < numValues; valNdx++)
+			{
+				const ShaderCase::Value&	val			= valueBlock.values[valNdx];
+				const char* const			valueName	= val.valueName.c_str();
+
+				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.
+			gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
+			gl.clear(GL_COLOR_BUFFER_BIT);
+			GLU_EXPECT_NO_ERROR(gl.getError(), "clear buffer");
+
+			// Use program or pipeline
+			if (m_separatePrograms)
+				gl.useProgram(0);
+			else
+				gl.useProgram(vertexProgramID);
+
+			// Draw.
+			if (tessellationPresent)
+			{
+				gl.patchParameteri(GL_PATCH_VERTICES, 3);
+				GLU_EXPECT_NO_ERROR(gl.getError(), "set patchParameteri(PATCH_VERTICES, 3)");
+			}
+
+			draw(m_renderCtx,
+				 vertexProgramID,
+				 (int)vertexArrays.size(),
+				 &vertexArrays[0],
+				 (tessellationPresent) ?
+					(pr::Patches(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])) :
+					(pr::Triangles(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])),
+				 (m_expectResult == EXPECT_VALIDATION_FAIL) ?
+					(&beforeDrawValidator) :
+					(DE_NULL));
+
+			postDrawError = gl.getError();
+
+			if (m_expectResult == EXPECT_PASS)
+			{
+				// Read back results.
+				Surface			surface			(width, height);
+				const float		w				= s_positions[3];
+				const int		minY			= deCeilFloatToInt32 (((-quadSize / w) * 0.5f + 0.5f) * height + 1.0f);
+				const int		maxY			= deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * height - 0.5f);
+				const int		minX			= deCeilFloatToInt32 (((-quadSize / w) * 0.5f + 0.5f) * width + 1.0f);
+				const int		maxX			= deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * width - 0.5f);
+
+				GLU_EXPECT_NO_ERROR(postDrawError, "draw");
+
+				glu::readPixels(m_renderCtx, viewportX, viewportY, surface.getAccess());
+				GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
+
+				if (!checkPixels(surface, minX, maxX, minY, maxY))
+				{
+					log << TestLog::Message << "INCORRECT RESULT for (value block " << (blockNdx+1) << " of " <<  (int)m_valueBlocks.size()
+											<< ", sub-case " << arrayNdx+1 << " of " << valueBlock.arrayLength << "):"
+						<< TestLog::EndMessage;
+
+					log << TestLog::Message << "Failing shader input/output values:" << TestLog::EndMessage;
+					dumpValues(valueBlock, arrayNdx);
+
+					// Dump image on failure.
+					log << TestLog::Image("Result", "Rendered result image", surface);
+
+					gl.useProgram(0);
+					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
+					return false;
+				}
+			}
+			else if (m_expectResult == EXPECT_VALIDATION_FAIL)
+			{
+				log	<< TestLog::Message
+					<< "Draw call generated error: "
+					<< glu::getErrorStr(postDrawError) << " "
+					<< ((postDrawError == GL_INVALID_OPERATION) ? ("(expected)") : ("(unexpected)")) << "\n"
+					<< "Validate status: "
+					<< glu::getBooleanStr(beforeDrawValidator.getValidateStatus()) << " "
+					<< ((beforeDrawValidator.getValidateStatus() == GL_FALSE) ? ("(expected)") : ("(unexpected)")) << "\n"
+					<< "Info log: "
+					<< ((beforeDrawValidator.getInfoLog().empty()) ? ("[empty string]") : (beforeDrawValidator.getInfoLog())) << "\n"
+					<< TestLog::EndMessage;
+
+				// test result
+
+				if (postDrawError != GL_NO_ERROR && postDrawError != GL_INVALID_OPERATION)
+				{
+					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, ("Draw: got unexpected error: " + de::toString(glu::getErrorStr(postDrawError))).c_str());
+					return false;
+				}
+
+				if (beforeDrawValidator.getValidateStatus() == GL_TRUE)
+				{
+					if (postDrawError == GL_NO_ERROR)
+						m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but validation and rendering succeeded");
+					else if (postDrawError == GL_INVALID_OPERATION)
+						m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but validation succeeded (rendering failed as expected)");
+					else
+						DE_ASSERT(false);
+					return false;
+				}
+				else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_NO_ERROR)
+				{
+					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but rendering succeeded (validation failed as expected)");
+					return false;
+				}
+				else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_INVALID_OPERATION)
+				{
+					// Validation does not depend on input values, no need to test all values
+					return true;
+				}
+				else
+					DE_ASSERT(false);
+			}
+			else
+				DE_ASSERT(false);
+		}
+	}
+
+	gl.useProgram(0);
+	if (m_separatePrograms)
+		gl.bindProgramPipeline(0);
+
+	GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): end");
+	return true;
+}
+
+TestCase::IterateResult ShaderCase::iterate (void)
+{
+	// Initialize state to pass.
+	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
+
+	bool executeOk = execute();
+
+	DE_ASSERT(executeOk ? m_testCtx.getTestResult() == QP_TEST_RESULT_PASS : m_testCtx.getTestResult() != QP_TEST_RESULT_PASS);
+	DE_UNREF(executeOk);
+	return TestCase::STOP;
+}
+
+static void generateExtensionStatements (std::ostringstream& buf, const std::vector<ShaderCase::CaseRequirement>& requirements, glu::ShaderType type)
+{
+	for (int ndx = 0; ndx < (int)requirements.size(); ++ndx)
+		if (requirements[ndx].getType() == ShaderCase::CaseRequirement::REQUIREMENTTYPE_EXTENSION &&
+			(requirements[ndx].getAffectedExtensionStageFlags() & (1 << (deUint32)type)) != 0)
+			buf << "#extension " << requirements[ndx].getSupportedExtension() << " : require\n";
+}
+
+// Injects #extension XXX : require lines after the last preprocessor directive in the shader code. Does not support line continuations
+static std::string injectExtensionRequirements (const std::string& baseCode, glu::ShaderType shaderType, const std::vector<ShaderCase::CaseRequirement>& requirements)
+{
+	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 (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;
+
+	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 (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;
+
+	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 (glu::GLSLVersion targetVersion, const ShaderCase::ValueBlock& valueBlock)
+{
+	ostringstream		decl;
+	map<string, string>	params;
+
+	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 (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()));
+	return params;
+}
+
+static map<string, string> generateTessEvalSpecialization (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()));
+	return params;
+}
+
+static void specializeShaders (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)(glu::GLSLVersion, const ShaderCase::ValueBlock&))
+{
+	if (!sources.empty())
+	{
+		const std::map<std::string, std::string> specializationParams = specializationGenerator(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(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(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(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(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(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