/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.0 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 Shader compilation performance tests.
 *//*--------------------------------------------------------------------*/

#include "es3pShaderCompilationCases.hpp"
#include "tcuTestLog.hpp"
#include "tcuVector.hpp"
#include "tcuMatrix.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuPlatform.hpp"
#include "tcuCommandLine.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuCPUWarmup.hpp"
#include "tcuStringTemplate.hpp"
#include "gluTexture.hpp"
#include "gluPixelTransfer.hpp"
#include "gluRenderContext.hpp"
#include "deStringUtil.hpp"
#include "deRandom.hpp"
#include "deClock.h"
#include "deMath.h"

#include "glwEnums.hpp"
#include "glwFunctions.hpp"

#include <map>
#include <algorithm>
#include <limits>
#include <iomanip>

using tcu::TestLog;
using tcu::Vec3;
using tcu::Vec4;
using tcu::Mat3;
using tcu::Mat4;
using std::string;
using std::vector;
using namespace glw; // GL types

namespace deqp
{

namespace gles3
{

namespace Performance
{

static const bool	WARMUP_CPU_AT_BEGINNING_OF_CASE					= false;
static const bool	WARMUP_CPU_BEFORE_EACH_MEASUREMENT				= true;

static const int	MAX_VIEWPORT_WIDTH								= 64;
static const int	MAX_VIEWPORT_HEIGHT								= 64;

static const int	DEFAULT_MINIMUM_MEASUREMENT_COUNT				= 15;
static const float	RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD	= 0.05f;

// Texture size for the light shader and texture lookup shader cases.
static const int	TEXTURE_WIDTH									= 64;
static const int	TEXTURE_HEIGHT									= 64;

template <typename T>
inline string toStringWithPadding (T value, int minLength)
{
	std::ostringstream s;
	s << std::setfill('0') << std::setw(minLength) << value;
	return s.str();
}

// Add some whitespace and comments to str. They should depend on uniqueNumber.
static string strWithWhiteSpaceAndComments (const string& str, deUint32 uniqueNumber)
{
	string res("");

	// Find the first newline.
	int firstLineEndNdx = 0;
	while (firstLineEndNdx < (int)str.size() && str[firstLineEndNdx] != '\n')
	{
		res += str[firstLineEndNdx];
		firstLineEndNdx++;
	}
	res += '\n';
	DE_ASSERT(firstLineEndNdx < (int)str.size());

	// Add the whitespaces and comments just after the first line.

	de::Random		rnd		(uniqueNumber);
	int				numWS	= rnd.getInt(10, 20);

	for (int i = 0; i < numWS; i++)
		res += " \t\n"[rnd.getInt(0, 2)];

	res += "/* unique comment " + de::toString(uniqueNumber) + " */\n";
	res += "// unique comment " + de::toString(uniqueNumber) + "\n";

	for (int i = 0; i < numWS; i++)
		res += " \t\n"[rnd.getInt(0, 2)];

	// Add the rest of the string.
	res.append(&str.c_str()[firstLineEndNdx + 1]);

	return res;
}

//! Helper for computing relative magnitudes while avoiding division by zero.
static float hackySafeRelativeResult (float x, float y)
{
	// \note A possible case is that x is standard deviation, and y is average
	//		 (or similarly for median or some such). So, if y is 0, that
	//		 probably means that x is also 0(ish) (because in practice we're
	//		 dealing with non-negative values, in which case an average of 0
	//		 implies that the samples are all 0 - note that the same isn't
	//		 strictly true for things like median) so a relative result of 0
	//		 wouldn't be that far from the truth.
	return y == 0.0f ? 0.0f : x/y;
}

template <typename T>
static float vectorFloatAverage (const vector<T>& v)
{
	DE_ASSERT(!v.empty());
	float result = 0.0f;
	for (int i = 0; i < (int)v.size(); i++)
		result += (float)v[i];
	return result / (float)v.size();
}

template <typename T>
static float vectorFloatMedian (const vector<T>& v)
{
	DE_ASSERT(!v.empty());
	vector<T> temp = v;
	std::sort(temp.begin(), temp.end());
	return temp.size() % 2 == 0
		   ? 0.5f * ((float)temp[temp.size()/2-1] + (float)temp[temp.size()/2])
		   : (float)temp[temp.size()/2];
}

template <typename T>
static float vectorFloatMinimum (const vector<T>& v)
{
	DE_ASSERT(!v.empty());
	return (float)*std::min_element(v.begin(), v.end());
}

template <typename T>
static float vectorFloatMaximum (const vector<T>& v)
{
	DE_ASSERT(!v.empty());
	return (float)*std::max_element(v.begin(), v.end());
}

template <typename T>
static float vectorFloatStandardDeviation (const vector<T>& v)
{
	float average	= vectorFloatAverage(v);
	float result	= 0.0f;
	for (int i = 0; i < (int)v.size(); i++)
	{
		float d = (float)v[i] - average;
		result += d*d;
	}
	return deFloatSqrt(result/(float)v.size());
}

template <typename T>
static float vectorFloatRelativeStandardDeviation (const vector<T>& v)
{
	return hackySafeRelativeResult(vectorFloatStandardDeviation(v), vectorFloatAverage(v));
}

template <typename T>
static float vectorFloatMedianAbsoluteDeviation (const vector<T>& v)
{
	float			median				= vectorFloatMedian(v);
	vector<float>	absoluteDeviations	(v.size());

	for (int i = 0; i < (int)v.size(); i++)
		absoluteDeviations[i] = deFloatAbs((float)v[i] - median);

	return vectorFloatMedian(absoluteDeviations);
}

template <typename T>
static float vectorFloatRelativeMedianAbsoluteDeviation (const vector<T>& v)
{
	return hackySafeRelativeResult(vectorFloatMedianAbsoluteDeviation(v), vectorFloatMedian(v));
}

template <typename T>
static float vectorFloatMaximumMinusMinimum (const vector<T>& v)
{
	return vectorFloatMaximum(v) - vectorFloatMinimum(v);
}

template <typename T>
static float vectorFloatRelativeMaximumMinusMinimum (const vector<T>& v)
{
	return hackySafeRelativeResult(vectorFloatMaximumMinusMinimum(v), vectorFloatMaximum(v));
}

template <typename T>
static vector<T> vectorLowestPercentage (const vector<T>& v, float factor)
{
	DE_ASSERT(0.0f < factor && factor <= 1.0f);

	int			targetSize	= (int)(deFloatCeil(factor*(float)v.size()));
	vector<T>	temp		= v;
	std::sort(temp.begin(), temp.end());

	while ((int)temp.size() > targetSize)
		temp.pop_back();

	return temp;
}

template <typename T>
static float vectorFloatFirstQuartile (const vector<T>& v)
{
	return vectorFloatMedian(vectorLowestPercentage(v, 0.5f));
}

// Helper function for combining 4 tcu::Vec4's into one tcu::Vector<float, 16>.
static tcu::Vector<float, 16> combineVec4ToVec16 (const Vec4& a0, const Vec4& a1, const Vec4& a2, const Vec4& a3)
{
	tcu::Vector<float, 16> result;

	for (int vecNdx = 0; vecNdx < 4; vecNdx++)
	{
		const Vec4& srcVec = vecNdx == 0 ? a0 : vecNdx == 1 ? a1 : vecNdx == 2 ? a2 : a3;
		for (int i = 0; i < 4; i++)
			result[vecNdx*4 + i] = srcVec[i];
	}

	return result;
}

// Helper function for extending an n-sized (n <= 16) vector to a 16-sized vector (padded with zeros).
template <int Size>
static tcu::Vector<float, 16> vecTo16 (const tcu::Vector<float, Size>& vec)
{
	DE_STATIC_ASSERT(Size <= 16);

	tcu::Vector<float, 16> res(0.0f);

	for (int i = 0; i < Size; i++)
		res[i] = vec[i];

	return res;
}

// Helper function for extending an n-sized (n <= 16) array to a 16-sized vector (padded with zeros).
template <int Size>
static tcu::Vector<float, 16> arrTo16 (const tcu::Array<float, Size>& arr)
{
	DE_STATIC_ASSERT(Size <= 16);

	tcu::Vector<float, 16> res(0.0f);

	for(int i = 0; i < Size; i++)
		res[i] = arr[i];

	return res;
}

static string getShaderInfoLog (const glw::Functions& gl, deUint32 shader)
{
	string			result;
	int				infoLogLen;
	vector<char>	infoLogBuf;

	gl.getShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen);
	infoLogBuf.resize(infoLogLen + 1);
	gl.getShaderInfoLog(shader, infoLogLen + 1, DE_NULL, &infoLogBuf[0]);
	result = &infoLogBuf[0];

	return result;
}

static string getProgramInfoLog (const glw::Functions& gl, deUint32 program)
{
	string			result;
	int				infoLogLen;
	vector<char>	infoLogBuf;

	gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen);
	infoLogBuf.resize(infoLogLen + 1);
	gl.getProgramInfoLog(program, infoLogLen + 1, DE_NULL, &infoLogBuf[0]);
	result = &infoLogBuf[0];

	return result;
}

enum LightType
{
	LIGHT_DIRECTIONAL = 0,
	LIGHT_POINT,

	LIGHT_LAST,
};

enum LoopType
{
	LOOP_TYPE_STATIC = 0,
	LOOP_TYPE_UNIFORM,
	LOOP_TYPE_DYNAMIC,

	LOOP_LAST
};

// For texture lookup cases: which texture lookups are inside a conditional statement.
enum ConditionalUsage
{
	CONDITIONAL_USAGE_NONE = 0,		// No conditional statements.
	CONDITIONAL_USAGE_FIRST_HALF,	// First numLookUps/2 lookups are inside a conditional statement.
	CONDITIONAL_USAGE_EVERY_OTHER,	// First, third etc. lookups are inside conditional statements.

	CONDITIONAL_USAGE_LAST
};

enum ConditionalType
{
	CONDITIONAL_TYPE_STATIC = 0,
	CONDITIONAL_TYPE_UNIFORM,
	CONDITIONAL_TYPE_DYNAMIC,

	CONDITIONAL_TYPE_LAST
};

// For the invalid shader compilation tests; what kind of invalidity a shader shall contain.
enum ShaderValidity
{
	SHADER_VALIDITY_VALID = 0,
	SHADER_VALIDITY_INVALID_CHAR,
	SHADER_VALIDITY_SEMANTIC_ERROR,

	SHADER_VALIDITY_LAST
};

class ShaderCompilerCase : public TestCase
{
public:
	struct AttribSpec
	{
		string					name;
		tcu::Vector<float, 16>	value;

		AttribSpec (const string& n, const tcu::Vector<float, 16>& v) : name(n), value(v) {}
	};

	struct UniformSpec
	{
		enum Type
		{
			TYPE_FLOAT = 0,
			TYPE_VEC2,
			TYPE_VEC3,
			TYPE_VEC4,

			TYPE_MAT3,
			TYPE_MAT4,

			TYPE_TEXTURE_UNIT,

			TYPE_LAST
		};

		string					name;
		Type					type;
		tcu::Vector<float, 16>	value;

		UniformSpec (const string& n, Type t, float v)							: name(n), type(t), value(v) {}
		UniformSpec (const string& n, Type t, const tcu::Vector<float, 16>& v)	: name(n), type(t), value(v) {}
	};

								ShaderCompilerCase		(Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments);
								~ShaderCompilerCase		(void);

	void						init					(void);

	IterateResult				iterate					(void);

protected:
	struct ProgramContext
	{
		string					vertShaderSource;
		string					fragShaderSource;
		vector<AttribSpec>		vertexAttributes;
		vector<UniformSpec>		uniforms;
	};

	deUint32					getSpecializationID		(int measurementNdx) const;		// Return an ID that depends on the case ID, current measurement index and time; used to specialize attribute names etc. (avoid shader caching).
	virtual ProgramContext		generateShaderData		(int measurementNdx) const = 0;	// Generate shader sources and inputs. Attribute etc. names depend on above name specialization.

private:
	struct Measurement
	{
		// \note All times in microseconds. 32-bit integers would probably suffice (would need over an hour of test case runtime to overflow), but better safe than sorry.
		deInt64 sourceSetTime;
		deInt64 vertexCompileTime;
		deInt64 fragmentCompileTime;
		deInt64 programLinkTime;
		deInt64 firstInputSetTime;
		deInt64 firstDrawTime;

		deInt64 secondInputSetTime;
		deInt64 secondDrawTime;

		deInt64 firstPhase				(void) const { return sourceSetTime + vertexCompileTime + fragmentCompileTime + programLinkTime + firstInputSetTime + firstDrawTime; }
		deInt64 secondPhase				(void) const { return secondInputSetTime + secondDrawTime; }

		deInt64 totalTimeWithoutDraw	(void) const { return firstPhase() - de::min(secondPhase(), firstInputSetTime + firstDrawTime); }

		Measurement (deInt64 sourceSetTime_,
					 deInt64 vertexCompileTime_,
					 deInt64 fragmentCompileTime_,
					 deInt64 programLinkTime_,
					 deInt64 firstInputSetTime_,
					 deInt64 firstDrawTime_,
					 deInt64 secondInputSetTime_,
					 deInt64 secondDrawTime_)
			: sourceSetTime			(sourceSetTime_)
			, vertexCompileTime		(vertexCompileTime_)
			, fragmentCompileTime	(fragmentCompileTime_)
			, programLinkTime		(programLinkTime_)
			, firstInputSetTime		(firstInputSetTime_)
			, firstDrawTime			(firstDrawTime_)
			, secondInputSetTime	(secondInputSetTime_)
			, secondDrawTime		(secondDrawTime_)
		{
		}
	};

	struct ShadersAndProgram
	{
		deUint32 vertShader;
		deUint32 fragShader;
		deUint32 program;
	};

	struct Logs
	{
		string vert;
		string frag;
		string link;
	};

	struct BuildInfo
	{
		bool vertCompileSuccess;
		bool fragCompileSuccess;
		bool linkSuccess;

		Logs logs;
	};

	ShadersAndProgram			createShadersAndProgram		(void) const;
	void						setShaderSources			(deUint32 vertShader, deUint32 fragShader, const ProgramContext&) const;
	bool						compileShader				(deUint32 shader) const;
	bool						linkAndUseProgram			(deUint32 program) const;
	void						setShaderInputs				(deUint32 program, const ProgramContext&) const;							// Set attribute pointers and uniforms.
	void						draw						(void) const;																// Clear, draw and finish.
	void						cleanup						(const ShadersAndProgram&, const ProgramContext&, bool linkSuccess) const;	// Do GL deinitializations.

	Logs						getLogs						(const ShadersAndProgram&) const;
	void						logProgramData				(const BuildInfo&, const ProgramContext&) const;
	bool						goodEnoughMeasurements		(const vector<Measurement>& measurements) const;

	int							m_viewportWidth;
	int							m_viewportHeight;

	bool						m_avoidCache;				// If true, avoid caching between measurements as well (and not only between test cases).
	bool						m_addWhitespaceAndComments;	// If true, add random whitespace and comments to the source (good caching should ignore those).
	deUint32					m_startHash;				// A hash from case id and time, at the time of construction.

	int							m_minimumMeasurementCount;
	int							m_maximumMeasurementCount;
};

class ShaderCompilerLightCase : public ShaderCompilerCase
{
public:
							ShaderCompilerLightCase		(Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, int numLights, LightType lightType);
							~ShaderCompilerLightCase	(void);

	void					init						(void);
	void					deinit						(void);

protected:
	ProgramContext			generateShaderData			(int measurementNdx) const;

private:
	int						m_numLights;
	bool					m_isVertexCase;
	LightType				m_lightType;
	glu::Texture2D*			m_texture;
};

class ShaderCompilerTextureCase : public ShaderCompilerCase
{
public:
									ShaderCompilerTextureCase	(Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType);
									~ShaderCompilerTextureCase	(void);

	void							init						(void);
	void							deinit						(void);

protected:
	ProgramContext					generateShaderData			(int measurementNdx) const;

private:
	int								m_numLookups;
	vector<glu::Texture2D*>			m_textures;
	ConditionalUsage				m_conditionalUsage;
	ConditionalType					m_conditionalType;
};

class ShaderCompilerLoopCase : public ShaderCompilerCase
{
public:
						ShaderCompilerLoopCase	(Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, LoopType type, int numLoopIterations, int nestingDepth);
						~ShaderCompilerLoopCase	(void);

protected:
	ProgramContext		generateShaderData		(int measurementNdx) const;

private:
	int					m_numLoopIterations;
	int					m_nestingDepth;
	bool				m_isVertexCase;
	LoopType			m_type;
};

class ShaderCompilerOperCase : public ShaderCompilerCase
{
public:
						ShaderCompilerOperCase	(Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, const char* oper, int numOperations);
						~ShaderCompilerOperCase	(void);

protected:
	ProgramContext		generateShaderData		(int measurementNdx) const;

private:
	string				m_oper;
	int					m_numOperations;
	bool				m_isVertexCase;
};

class ShaderCompilerMandelbrotCase : public ShaderCompilerCase
{
public:
						ShaderCompilerMandelbrotCase	(Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, int numFractalIterations);
						~ShaderCompilerMandelbrotCase	(void);

protected:
	ProgramContext		generateShaderData				(int measurementNdx) const;

private:
	int					m_numFractalIterations;
};

class InvalidShaderCompilerCase : public TestCase
{
public:
	// \note Similar to the ShaderValidity enum, but doesn't have a VALID type.
	enum InvalidityType
	{
		INVALIDITY_INVALID_CHAR = 0,
		INVALIDITY_SEMANTIC_ERROR,

		INVALIDITY_LAST
	};

						InvalidShaderCompilerCase	(Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType);
						~InvalidShaderCompilerCase	(void);

	IterateResult		iterate						(void);

protected:
	struct ProgramContext
	{
		string vertShaderSource;
		string fragShaderSource;
	};

	deUint32				getSpecializationID		(int measurementNdx) const;			// Return an ID that depends on the case ID, current measurement index and time; used to specialize attribute names etc. (avoid shader caching).
	virtual ProgramContext	generateShaderSources	(int measurementNdx) const = 0;		// Generate shader sources. Attribute etc. names depend on above name specialization.

	InvalidityType			m_invalidityType;

private:
	struct Measurement
	{
		// \note All times in microseconds. 32-bit integers would probably suffice (would need over an hour of test case runtime to overflow), but better safe than sorry.
		deInt64 sourceSetTime;
		deInt64 vertexCompileTime;
		deInt64 fragmentCompileTime;

		deInt64 totalTime (void) const { return sourceSetTime + vertexCompileTime + fragmentCompileTime; }

		Measurement (deInt64 sourceSetTime_,
					 deInt64 vertexCompileTime_,
					 deInt64 fragmentCompileTime_)
			: sourceSetTime			(sourceSetTime_)
			, vertexCompileTime		(vertexCompileTime_)
			, fragmentCompileTime	(fragmentCompileTime_)
		{
		}
	};

	struct Shaders
	{
		deUint32 vertShader;
		deUint32 fragShader;
	};

	struct Logs
	{
		string vert;
		string frag;
	};

	struct BuildInfo
	{
		bool vertCompileSuccess;
		bool fragCompileSuccess;

		Logs logs;
	};

	Shaders						createShaders			(void) const;
	void						setShaderSources		(const Shaders&, const ProgramContext&) const;
	bool						compileShader			(deUint32 shader) const;
	void						cleanup					(const Shaders&) const;

	Logs						getLogs					(const Shaders&) const;
	void						logProgramData			(const BuildInfo&, const ProgramContext&) const;
	bool						goodEnoughMeasurements	(const vector<Measurement>& measurements) const;

	deUint32					m_startHash; // A hash from case id and time, at the time of construction.

	int							m_minimumMeasurementCount;
	int							m_maximumMeasurementCount;
};

class InvalidShaderCompilerLightCase : public InvalidShaderCompilerCase
{
public:
							InvalidShaderCompilerLightCase	(Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool isVertexCase, int numLights, LightType lightType);
							~InvalidShaderCompilerLightCase	(void);

protected:
	ProgramContext			generateShaderSources			(int measurementNdx) const;

private:
	bool					m_isVertexCase;
	int						m_numLights;
	LightType				m_lightType;
};

class InvalidShaderCompilerTextureCase : public InvalidShaderCompilerCase
{
public:
							InvalidShaderCompilerTextureCase	(Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType);
							~InvalidShaderCompilerTextureCase	(void);

protected:
	ProgramContext			generateShaderSources				(int measurementNdx) const;

private:
	int						m_numLookups;
	ConditionalUsage		m_conditionalUsage;
	ConditionalType			m_conditionalType;
};

class InvalidShaderCompilerLoopCase : public InvalidShaderCompilerCase
{
public:
						InvalidShaderCompilerLoopCase	(Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool , LoopType type, int numLoopIterations, int nestingDepth);
						~InvalidShaderCompilerLoopCase	(void);

protected:
	ProgramContext		generateShaderSources			(int measurementNdx) const;

private:
	bool				m_isVertexCase;
	int					m_numLoopIterations;
	int					m_nestingDepth;
	LoopType			m_type;
};

class InvalidShaderCompilerOperCase : public InvalidShaderCompilerCase
{
public:
						InvalidShaderCompilerOperCase	(Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool isVertexCase, const char* oper, int numOperations);
						~InvalidShaderCompilerOperCase	(void);

protected:
	ProgramContext		generateShaderSources			(int measurementNdx) const;

private:
	bool				m_isVertexCase;
	string				m_oper;
	int					m_numOperations;
};

class InvalidShaderCompilerMandelbrotCase : public InvalidShaderCompilerCase
{
public:
						InvalidShaderCompilerMandelbrotCase		(Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, int numFractalIterations);
						~InvalidShaderCompilerMandelbrotCase	(void);

protected:
	ProgramContext		generateShaderSources					(int measurementNdx) const;

private:
	int					m_numFractalIterations;
};

static string getNameSpecialization (deUint32 id)
{
	return "_" + toStringWithPadding(id, 10);
}

// Substitute StringTemplate parameters for attribute/uniform/varying name and constant expression specialization as well as possible shader compilation error causes.
static string specializeShaderSource (const string& shaderSourceTemplate, deUint32 cacheAvoidanceID, ShaderValidity validity)
{
	std::map<string, string> params;
	params["NAME_SPEC"]			= getNameSpecialization(cacheAvoidanceID);
	params["FLOAT01"]			= de::floatToString((float)cacheAvoidanceID / (float)(std::numeric_limits<deUint32>::max()), 6);
	params["SEMANTIC_ERROR"]	= validity != SHADER_VALIDITY_SEMANTIC_ERROR	? "" : "\tfloat invalid = sin(1.0, 2.0);\n";
	params["INVALID_CHAR"]		= validity != SHADER_VALIDITY_INVALID_CHAR		? "" : "@\n"; // \note Some implementations crash when the invalid character is the last character in the source, so use newline.

	return tcu::StringTemplate(shaderSourceTemplate).specialize(params);
}

// Function for generating the vertex shader of a (directional or point) light case.
static string lightVertexTemplate (int numLights, bool isVertexCase, LightType lightType)
{
	string resultTemplate;

	resultTemplate +=
		"#version 300 es\n"
		"in highp vec4 a_position${NAME_SPEC};\n"
		"in mediump vec3 a_normal${NAME_SPEC};\n"
		"in mediump vec4 a_texCoord0${NAME_SPEC};\n"
		"uniform mediump vec3 u_material_ambientColor${NAME_SPEC};\n"
		"uniform mediump vec4 u_material_diffuseColor${NAME_SPEC};\n"
		"uniform mediump vec3 u_material_emissiveColor${NAME_SPEC};\n"
		"uniform mediump vec3 u_material_specularColor${NAME_SPEC};\n"
		"uniform mediump float u_material_shininess${NAME_SPEC};\n";

	for (int lightNdx = 0; lightNdx < numLights; lightNdx++)
	{
		string ndxStr = de::toString(lightNdx);

		resultTemplate +=
			"uniform mediump vec3 u_light" + ndxStr + "_color${NAME_SPEC};\n"
			"uniform mediump vec3 u_light" + ndxStr + "_direction${NAME_SPEC};\n";

		if (lightType == LIGHT_POINT)
			resultTemplate +=
				"uniform mediump vec4 u_light" + ndxStr + "_position${NAME_SPEC};\n"
				"uniform mediump float u_light" + ndxStr + "_constantAttenuation${NAME_SPEC};\n"
				"uniform mediump float u_light" + ndxStr + "_linearAttenuation${NAME_SPEC};\n"
				"uniform mediump float u_light" + ndxStr + "_quadraticAttenuation${NAME_SPEC};\n";
	}

	resultTemplate +=
		"uniform highp mat4 u_mvpMatrix${NAME_SPEC};\n"
		"uniform highp mat4 u_modelViewMatrix${NAME_SPEC};\n"
		"uniform mediump mat3 u_normalMatrix${NAME_SPEC};\n"
		"uniform mediump mat4 u_texCoordMatrix0${NAME_SPEC};\n"
		"out mediump vec4 v_color${NAME_SPEC};\n"
		"out mediump vec2 v_texCoord0${NAME_SPEC};\n";

	if (!isVertexCase)
	{
		resultTemplate += "out mediump vec3 v_eyeNormal${NAME_SPEC};\n";

		if (lightType == LIGHT_POINT)
			resultTemplate +=
				"out mediump vec3 v_directionToLight${NAME_SPEC}[" + de::toString(numLights) + "];\n"
				"out mediump float v_distanceToLight${NAME_SPEC}[" + de::toString(numLights) + "];\n";
	}

	resultTemplate +=
		"mediump vec3 direction (mediump vec4 from, mediump vec4 to)\n"
		"{\n"
		"	return vec3(to.xyz * from.w - from.xyz * to.w);\n"
		"}\n"
		"\n"
		"mediump vec3 computeLighting (\n"
		"	mediump vec3 directionToLight,\n"
		"	mediump vec3 halfVector,\n"
		"	mediump vec3 normal,\n"
		"	mediump vec3 lightColor,\n"
		"	mediump vec3 diffuseColor,\n"
		"	mediump vec3 specularColor,\n"
		"	mediump float shininess)\n"
		"{\n"
		"	mediump float normalDotDirection  = max(dot(normal, directionToLight), 0.0);\n"
		"	mediump vec3  color               = normalDotDirection * diffuseColor * lightColor;\n"
		"\n"
		"	if (normalDotDirection != 0.0)\n"
		"		color += pow(max(dot(normal, halfVector), 0.0), shininess) * specularColor * lightColor;\n"
		"\n"
		"	return color;\n"
		"}\n"
		"\n";

	if (lightType == LIGHT_POINT)
		resultTemplate +=
			"mediump float computeDistanceAttenuation (mediump float distToLight, mediump float constAtt, mediump float linearAtt, mediump float quadraticAtt)\n"
			"{\n"
			"	return 1.0 / (constAtt + linearAtt * distToLight + quadraticAtt * distToLight * distToLight);\n"
			"}\n"
			"\n";

	resultTemplate +=
		"void main (void)\n"
		"{\n"
		"	highp vec4 position = a_position${NAME_SPEC};\n"
		"	highp vec3 normal = a_normal${NAME_SPEC};\n"
		"	gl_Position = u_mvpMatrix${NAME_SPEC} * position * (0.95 + 0.05*${FLOAT01});\n"
		"	v_texCoord0${NAME_SPEC} = (u_texCoordMatrix0${NAME_SPEC} * a_texCoord0${NAME_SPEC}).xy;\n"
		"	mediump vec4 color = vec4(u_material_emissiveColor${NAME_SPEC}, u_material_diffuseColor${NAME_SPEC}.a);\n"
		"\n"
		"	highp vec4 eyePosition = u_modelViewMatrix${NAME_SPEC} * position;\n"
		"	mediump vec3 eyeNormal = normalize(u_normalMatrix${NAME_SPEC} * normal);\n";

	if (!isVertexCase)
		resultTemplate += "\tv_eyeNormal${NAME_SPEC} = eyeNormal;\n";

	resultTemplate += "\n";

	for (int lightNdx = 0; lightNdx < numLights; lightNdx++)
	{
		string ndxStr = de::toString(lightNdx);

		resultTemplate +=
			"	/* Light " + ndxStr + " */\n";

		if (lightType == LIGHT_POINT)
		{
			resultTemplate +=
				"	mediump float distanceToLight" + ndxStr + " = distance(eyePosition, u_light" + ndxStr + "_position${NAME_SPEC});\n"
				"	mediump vec3 directionToLight" + ndxStr + " = normalize(direction(eyePosition, u_light" + ndxStr + "_position${NAME_SPEC}));\n";

			if (isVertexCase)
				resultTemplate +=
					"	mediump vec3 halfVector" + ndxStr + " = normalize(directionToLight" + ndxStr + " + vec3(0.0, 0.0, 1.0));\n"
					"	color.rgb += computeLighting(directionToLight" + ndxStr + ", halfVector" + ndxStr + ", eyeNormal, u_light" + ndxStr + "_color${NAME_SPEC}, u_material_diffuseColor${NAME_SPEC}.rgb, "
					"u_material_specularColor${NAME_SPEC}, u_material_shininess${NAME_SPEC}) * computeDistanceAttenuation(distanceToLight" + ndxStr + ", u_light" + ndxStr + "_constantAttenuation${NAME_SPEC}, "
					"u_light" + ndxStr + "_linearAttenuation${NAME_SPEC}, u_light" + ndxStr + "_quadraticAttenuation${NAME_SPEC});\n";
			else
				resultTemplate +=
					"	v_directionToLight${NAME_SPEC}[" + ndxStr + "] = directionToLight" + ndxStr + ";\n"
					"	v_distanceToLight${NAME_SPEC}[" + ndxStr + "]  = distanceToLight" + ndxStr + ";\n";
		}
		else if (lightType == LIGHT_DIRECTIONAL)
		{
			if (isVertexCase)
				resultTemplate +=
					"	mediump vec3 directionToLight" + ndxStr + " = -u_light" + ndxStr + "_direction${NAME_SPEC};\n"
					"	mediump vec3 halfVector" + ndxStr + " = normalize(directionToLight" + ndxStr + " + vec3(0.0, 0.0, 1.0));\n"
					"	color.rgb += computeLighting(directionToLight" + ndxStr + ", halfVector" + ndxStr + ", eyeNormal, u_light" + ndxStr + "_color${NAME_SPEC}, u_material_diffuseColor${NAME_SPEC}.rgb, u_material_specularColor${NAME_SPEC}, u_material_shininess${NAME_SPEC});\n";
		}
		else
			DE_ASSERT(DE_FALSE);

		resultTemplate += "\n";
	}

	resultTemplate +=
		"	v_color${NAME_SPEC} = color;\n"
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

// Function for generating the fragment shader of a (directional or point) light case.
static string lightFragmentTemplate (int numLights, bool isVertexCase, LightType lightType)
{
	string resultTemplate;

	resultTemplate +=
		"#version 300 es\n"
		"layout(location = 0) out mediump vec4 o_color;\n";

	if (!isVertexCase)
	{
		resultTemplate +=
			"uniform mediump vec3 u_material_ambientColor${NAME_SPEC};\n"
			"uniform mediump vec4 u_material_diffuseColor${NAME_SPEC};\n"
			"uniform mediump vec3 u_material_emissiveColor${NAME_SPEC};\n"
			"uniform mediump vec3 u_material_specularColor${NAME_SPEC};\n"
			"uniform mediump float u_material_shininess${NAME_SPEC};\n";

		for (int lightNdx = 0; lightNdx < numLights; lightNdx++)
		{
			string ndxStr = de::toString(lightNdx);

			resultTemplate +=
				"uniform mediump vec3 u_light" + ndxStr + "_color${NAME_SPEC};\n"
				"uniform mediump vec3 u_light" + ndxStr + "_direction${NAME_SPEC};\n";

			if (lightType == LIGHT_POINT)
				resultTemplate +=
					"uniform mediump vec4 u_light" + ndxStr + "_position${NAME_SPEC};\n"
					"uniform mediump float u_light" + ndxStr + "_constantAttenuation${NAME_SPEC};\n"
					"uniform mediump float u_light" + ndxStr + "_linearAttenuation${NAME_SPEC};\n"
					"uniform mediump float u_light" + ndxStr + "_quadraticAttenuation${NAME_SPEC};\n";
		}
	}

	resultTemplate +=
		"uniform sampler2D u_sampler0${NAME_SPEC};\n"
		"in mediump vec4 v_color${NAME_SPEC};\n"
		"in mediump vec2 v_texCoord0${NAME_SPEC};\n";

	if (!isVertexCase)
	{
		resultTemplate +=
			"in mediump vec3 v_eyeNormal${NAME_SPEC};\n";

		if (lightType == LIGHT_POINT)
			resultTemplate +=
				"in mediump vec3 v_directionToLight${NAME_SPEC}[" + de::toString(numLights) + "];\n"
				"in mediump float v_distanceToLight${NAME_SPEC}[" + de::toString(numLights) + "];\n";

		resultTemplate +=
			"mediump vec3 direction (mediump vec4 from, mediump vec4 to)\n"
			"{\n"
			"	return vec3(to.xyz * from.w - from.xyz * to.w);\n"
			"}\n"
			"\n";

		resultTemplate +=
			"mediump vec3 computeLighting (\n"
			"	mediump vec3 directionToLight,\n"
			"	mediump vec3 halfVector,\n"
			"	mediump vec3 normal,\n"
			"	mediump vec3 lightColor,\n"
			"	mediump vec3 diffuseColor,\n"
			"	mediump vec3 specularColor,\n"
			"	mediump float shininess)\n"
			"{\n"
			"	mediump float normalDotDirection  = max(dot(normal, directionToLight), 0.0);\n"
			"	mediump vec3  color               = normalDotDirection * diffuseColor * lightColor;\n"
			"\n"
			"	if (normalDotDirection != 0.0)\n"
			"		color += pow(max(dot(normal, halfVector), 0.0), shininess) * specularColor * lightColor;\n"
			"\n"
			"	return color;\n"
			"}\n"
			"\n";

		if (lightType == LIGHT_POINT)
			resultTemplate +=
				"mediump float computeDistanceAttenuation (mediump float distToLight, mediump float constAtt, mediump float linearAtt, mediump float quadraticAtt)\n"
				"{\n"
				"	return 1.0 / (constAtt + linearAtt * distToLight + quadraticAtt * distToLight * distToLight);\n"
				"}\n"
				"\n";
	}

	resultTemplate +=
		"void main (void)\n"
		"{\n"
		"	mediump vec2 texCoord0 = v_texCoord0${NAME_SPEC}.xy;\n"
		"	mediump vec4 color = v_color${NAME_SPEC};\n";

	if (!isVertexCase)
	{
		resultTemplate +=
			"	mediump vec3 eyeNormal = normalize(v_eyeNormal${NAME_SPEC});\n"
			"\n";

		for (int lightNdx = 0; lightNdx < numLights; lightNdx++)
		{
			string ndxStr = de::toString(lightNdx);

			resultTemplate +=
				"	/* Light " + ndxStr + " */\n";

			if (lightType == LIGHT_POINT)
				resultTemplate +=
					"	mediump vec3 directionToLight" + ndxStr + " = normalize(v_directionToLight${NAME_SPEC}[" + ndxStr + "]);\n"
					"	mediump float distanceToLight" + ndxStr + " = v_distanceToLight${NAME_SPEC}[" + ndxStr + "];\n"
					"	mediump vec3 halfVector" + ndxStr + " = normalize(directionToLight" + ndxStr + " + vec3(0.0, 0.0, 1.0));\n"
					"	color.rgb += computeLighting(directionToLight" + ndxStr + ", halfVector" + ndxStr + ", eyeNormal, u_light" + ndxStr + "_color${NAME_SPEC}, u_material_diffuseColor${NAME_SPEC}.rgb, "
					"u_material_specularColor${NAME_SPEC}, u_material_shininess${NAME_SPEC}) * computeDistanceAttenuation(distanceToLight" + ndxStr + ", u_light" + ndxStr + "_constantAttenuation${NAME_SPEC}, "
					"u_light" + ndxStr + "_linearAttenuation${NAME_SPEC}, u_light" + ndxStr + "_quadraticAttenuation${NAME_SPEC});\n"
					"\n";
			else if (lightType == LIGHT_DIRECTIONAL)
				resultTemplate +=
					"	mediump vec3 directionToLight" + ndxStr + " = -u_light" + ndxStr + "_direction${NAME_SPEC};\n"
					"	mediump vec3 halfVector" + ndxStr + " = normalize(directionToLight" + ndxStr + " + vec3(0.0, 0.0, 1.0));\n"
					"	color.rgb += computeLighting(directionToLight" + ndxStr + ", halfVector" + ndxStr + ", eyeNormal, u_light" + ndxStr + "_color${NAME_SPEC}, u_material_diffuseColor${NAME_SPEC}.rgb, u_material_specularColor${NAME_SPEC}, u_material_shininess${NAME_SPEC});\n"
					"\n";
			else
				DE_ASSERT(DE_FALSE);
		}
	}

	resultTemplate +=
		"	color *= texture(u_sampler0${NAME_SPEC}, texCoord0);\n"
		"	o_color = color + ${FLOAT01};\n"
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

// Function for generating the shader attributes of a (directional or point) light case.
static vector<ShaderCompilerCase::AttribSpec> lightShaderAttributes (const string& nameSpecialization)
{
	vector<ShaderCompilerCase::AttribSpec> result;

	result.push_back(ShaderCompilerCase::AttribSpec("a_position" + nameSpecialization,
													combineVec4ToVec16(Vec4(-1.0f, -1.0f,  0.0f,  1.0f),
																	   Vec4(-1.0f,  1.0f,  0.0f,  1.0f),
																	   Vec4( 1.0f, -1.0f,  0.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  0.0f,  1.0f))));

	result.push_back(ShaderCompilerCase::AttribSpec("a_normal" + nameSpecialization,
													combineVec4ToVec16(Vec4(0.0f, 0.0f, -1.0f, 0.0f),
																	   Vec4(0.0f, 0.0f, -1.0f, 0.0f),
																	   Vec4(0.0f, 0.0f, -1.0f, 0.0f),
																	   Vec4(0.0f, 0.0f, -1.0f, 0.0f))));

	result.push_back(ShaderCompilerCase::AttribSpec("a_texCoord0" + nameSpecialization,
													combineVec4ToVec16(Vec4(0.0f, 0.0f, 0.0f, 0.0f),
																	   Vec4(1.0f, 0.0f, 0.0f, 0.0f),
																	   Vec4(0.0f, 1.0f, 0.0f, 0.0f),
																	   Vec4(1.0f, 1.0f, 0.0f, 0.0f))));

	return result;
}

// Function for generating the shader uniforms of a (directional or point) light case.
static vector<ShaderCompilerCase::UniformSpec> lightShaderUniforms (const string& nameSpecialization, int numLights, LightType lightType)
{
	vector<ShaderCompilerCase::UniformSpec> result;

	result.push_back(ShaderCompilerCase::UniformSpec("u_material_ambientColor" + nameSpecialization,
													 ShaderCompilerCase::UniformSpec::TYPE_VEC3,
													 vecTo16(Vec3(0.5f, 0.7f, 0.9f))));

	result.push_back(ShaderCompilerCase::UniformSpec("u_material_diffuseColor" + nameSpecialization,
													 ShaderCompilerCase:: UniformSpec::TYPE_VEC4,
													 vecTo16(Vec4(0.3f, 0.4f, 0.5f, 1.0f))));

	result.push_back(ShaderCompilerCase::UniformSpec("u_material_emissiveColor" + nameSpecialization,
													 ShaderCompilerCase::UniformSpec::TYPE_VEC3,
													 vecTo16(Vec3(0.7f, 0.2f, 0.2f))));

	result.push_back(ShaderCompilerCase::UniformSpec("u_material_specularColor" + nameSpecialization,
													 ShaderCompilerCase::UniformSpec::TYPE_VEC3,
													 vecTo16(Vec3(0.2f, 0.6f, 1.0f))));

	result.push_back(ShaderCompilerCase::UniformSpec("u_material_shininess" + nameSpecialization,
													 ShaderCompilerCase::UniformSpec::TYPE_FLOAT,
													 0.8f));

	for (int lightNdx = 0; lightNdx < numLights; lightNdx++)
	{
		string ndxStr = de::toString(lightNdx);

		result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_color" + nameSpecialization,
														 ShaderCompilerCase::UniformSpec::TYPE_VEC3,
														 vecTo16(Vec3(0.8f, 0.6f, 0.3f))));

		result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_direction" + nameSpecialization,
														 ShaderCompilerCase::UniformSpec::TYPE_VEC3,
														 vecTo16(Vec3(0.2f, 0.3f, 0.4f))));

		if (lightType == LIGHT_POINT)
		{
			result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_position" + nameSpecialization,
															 ShaderCompilerCase::UniformSpec::TYPE_VEC4,
															 vecTo16(Vec4(1.0f, 0.6f, 0.3f, 0.2f))));

			result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_constantAttenuation" + nameSpecialization,
															 ShaderCompilerCase::UniformSpec::TYPE_FLOAT,
															 0.6f));

			result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_linearAttenuation" + nameSpecialization,
															 ShaderCompilerCase::UniformSpec::TYPE_FLOAT,
															 0.5f));

			result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_quadraticAttenuation" + nameSpecialization,
															 ShaderCompilerCase::UniformSpec::TYPE_FLOAT,
															 0.4f));
		}
	}

	result.push_back(ShaderCompilerCase::UniformSpec("u_mvpMatrix" + nameSpecialization,
													 ShaderCompilerCase::UniformSpec::TYPE_MAT4,
													 arrTo16(Mat4(1.0f).getColumnMajorData())));

	result.push_back(ShaderCompilerCase::UniformSpec("u_modelViewMatrix" + nameSpecialization,
													 ShaderCompilerCase::UniformSpec::TYPE_MAT4,
													 arrTo16(Mat4(1.0f).getColumnMajorData())));

	result.push_back(ShaderCompilerCase::UniformSpec("u_normalMatrix" + nameSpecialization,
													 ShaderCompilerCase::UniformSpec::TYPE_MAT3,
													 arrTo16(Mat3(1.0f).getColumnMajorData())));

	result.push_back(ShaderCompilerCase::UniformSpec("u_texCoordMatrix0" + nameSpecialization,
													 ShaderCompilerCase::UniformSpec::TYPE_MAT4,
													 arrTo16(Mat4(1.0f).getColumnMajorData())));

	result.push_back(ShaderCompilerCase::UniformSpec("u_sampler0" + nameSpecialization,
													 ShaderCompilerCase::UniformSpec::TYPE_TEXTURE_UNIT,
													 0.0f));

	return result;
}

// Function for generating a vertex shader with a for loop.
static string loopVertexTemplate (LoopType type, bool isVertexCase, int numLoopIterations, int nestingDepth)
{
	string resultTemplate;
	string loopBound		= type == LOOP_TYPE_STATIC	? de::toString(numLoopIterations)
							: type == LOOP_TYPE_UNIFORM	? "int(u_loopBound${NAME_SPEC})"
							: type == LOOP_TYPE_DYNAMIC	? "int(a_loopBound${NAME_SPEC})"
							: "";

	DE_ASSERT(!loopBound.empty());

	resultTemplate +=
		"#version 300 es\n"
		"in highp vec4 a_position${NAME_SPEC};\n";

	if (type == LOOP_TYPE_DYNAMIC)
		resultTemplate +=
			"in mediump float a_loopBound${NAME_SPEC};\n";

	resultTemplate +=
		"in mediump vec4 a_value${NAME_SPEC};\n"
		"out mediump vec4 v_value${NAME_SPEC};\n";

	if (isVertexCase)
	{
		if (type == LOOP_TYPE_UNIFORM)
			resultTemplate += "uniform mediump float u_loopBound${NAME_SPEC};\n";

		resultTemplate +=
			"\n"
			"void main()\n"
			"{\n"
			"	gl_Position = a_position${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n"
			"	mediump vec4 value = a_value${NAME_SPEC};\n";

		for (int i = 0; i < nestingDepth; i++)
		{
			string iterName = "i" + de::toString(i);
			resultTemplate += string(i + 1, '\t') + "for (int " + iterName + " = 0; " + iterName + " < " + loopBound + "; " + iterName + "++)\n";
		}

		resultTemplate += string(nestingDepth + 1, '\t') + "value *= a_value${NAME_SPEC};\n";

		resultTemplate +=
			"	v_value${NAME_SPEC} = value;\n";
	}
	else
	{
		if (type == LOOP_TYPE_DYNAMIC)
			resultTemplate +=
				"out mediump float v_loopBound${NAME_SPEC};\n";

		resultTemplate +=
			"\n"
			"void main()\n"
			"{\n"
			"	gl_Position = a_position${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n"
			"	v_value${NAME_SPEC} = a_value${NAME_SPEC};\n";

		if (type == LOOP_TYPE_DYNAMIC)
			resultTemplate +=
				"	v_loopBound${NAME_SPEC} = a_loopBound${NAME_SPEC};\n";
	}

	resultTemplate +=
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

// Function for generating a fragment shader with a for loop.
static string loopFragmentTemplate (LoopType type, bool isVertexCase, int numLoopIterations, int nestingDepth)
{
	string resultTemplate;
	string loopBound		= type == LOOP_TYPE_STATIC	? de::toString(numLoopIterations)
							: type == LOOP_TYPE_UNIFORM	? "int(u_loopBound${NAME_SPEC})"
							: type == LOOP_TYPE_DYNAMIC	? "int(v_loopBound${NAME_SPEC})"
							: "";

	DE_ASSERT(!loopBound.empty());

	resultTemplate +=
		"#version 300 es\n"
		"layout(location = 0) out mediump vec4 o_color;\n"
		"in mediump vec4 v_value${NAME_SPEC};\n";

	if (!isVertexCase)
	{
		if (type == LOOP_TYPE_DYNAMIC)
			resultTemplate +=
				"in mediump float v_loopBound${NAME_SPEC};\n";
		else if (type == LOOP_TYPE_UNIFORM)
			resultTemplate +=
				"uniform mediump float u_loopBound${NAME_SPEC};\n";

		resultTemplate +=
			"\n"
			"void main()\n"
			"{\n"
			"	mediump vec4 value = v_value${NAME_SPEC};\n";

		for (int i = 0; i < nestingDepth; i++)
		{
			string iterName = "i" + de::toString(i);
			resultTemplate += string(i + 1, '\t') + "for (int " + iterName + " = 0; " + iterName + " < " + loopBound + "; " + iterName + "++)\n";
		}

		resultTemplate += string(nestingDepth + 1, '\t') + "value *= v_value${NAME_SPEC};\n";

		resultTemplate +=
			"	o_color = value + ${FLOAT01};\n";
	}
	else
		resultTemplate +=
			"\n"
			"void main()\n"
			"{\n"
			"	o_color = v_value${NAME_SPEC} + ${FLOAT01};\n";

	resultTemplate +=
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

// Function for generating the shader attributes for a loop case.
static vector<ShaderCompilerCase::AttribSpec> loopShaderAttributes (const string& nameSpecialization, LoopType type, int numLoopIterations)
{
	vector<ShaderCompilerCase::AttribSpec> result;

	result.push_back(ShaderCompilerCase::AttribSpec("a_position" + nameSpecialization,
													combineVec4ToVec16(Vec4(-1.0f, -1.0f,  0.0f,  1.0f),
																	   Vec4(-1.0f,  1.0f,  0.0f,  1.0f),
																	   Vec4( 1.0f, -1.0f,  0.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  0.0f,  1.0f))));

	result.push_back(ShaderCompilerCase::AttribSpec("a_value" + nameSpecialization,
													combineVec4ToVec16(Vec4( 1.0f,  1.0f,  1.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  1.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  1.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  1.0f,  1.0f))));

	if (type == LOOP_TYPE_DYNAMIC)
		result.push_back(ShaderCompilerCase::AttribSpec("a_loopBound" + nameSpecialization,
														combineVec4ToVec16(Vec4((float)numLoopIterations, 0.0f, 0.0f, 0.0f),
																		   Vec4((float)numLoopIterations, 0.0f, 0.0f, 0.0f),
																		   Vec4((float)numLoopIterations, 0.0f, 0.0f, 0.0f),
																		   Vec4((float)numLoopIterations, 0.0f, 0.0f, 0.0f))));

	return result;
}

static vector<ShaderCompilerCase::UniformSpec> loopShaderUniforms (const string& nameSpecialization, LoopType type, int numLoopIterations)
{
	vector<ShaderCompilerCase::UniformSpec> result;

	if (type == LOOP_TYPE_UNIFORM)
		result.push_back(ShaderCompilerCase::UniformSpec("u_loopBound" + nameSpecialization,
														 ShaderCompilerCase::UniformSpec::TYPE_FLOAT,
														 (float)numLoopIterations));

	return result;
}

// Function for generating the shader attributes for a case with only one attribute value in addition to the position attribute.
static vector<ShaderCompilerCase::AttribSpec> singleValueShaderAttributes (const string& nameSpecialization)
{
	vector<ShaderCompilerCase::AttribSpec> result;

	result.push_back(ShaderCompilerCase::AttribSpec("a_position" + nameSpecialization,
													combineVec4ToVec16(Vec4(-1.0f, -1.0f,  0.0f,  1.0f),
																	   Vec4(-1.0f,  1.0f,  0.0f,  1.0f),
																	   Vec4( 1.0f, -1.0f,  0.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  0.0f,  1.0f))));

	result.push_back(ShaderCompilerCase::AttribSpec("a_value" + nameSpecialization,
													combineVec4ToVec16(Vec4( 1.0f,  1.0f,  1.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  1.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  1.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  1.0f,  1.0f))));

	return result;
}

// Function for generating a vertex shader with a binary operation chain.
static string binaryOpVertexTemplate (int numOperations, const char* op)
{
	string resultTemplate;

	resultTemplate +=
		"#version 300 es\n"
		"in highp vec4 a_position${NAME_SPEC};\n"
		"in mediump vec4 a_value${NAME_SPEC};\n"
		"out mediump vec4 v_value${NAME_SPEC};\n"
		"\n"
		"void main()\n"
		"{\n"
		"	gl_Position = a_position${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n"
		"	mediump vec4 value = ";

	for (int i = 0; i < numOperations; i++)
		resultTemplate += string(i > 0 ? op : "") + "a_value${NAME_SPEC}";

	resultTemplate +=
		";\n"
		"	v_value${NAME_SPEC} = value;\n"
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

// Function for generating a fragment shader with a binary operation chain.
static string binaryOpFragmentTemplate (int numOperations, const char* op)
{
	string resultTemplate;

	resultTemplate +=
		"#version 300 es\n"
		"layout(location = 0) out mediump vec4 o_color;\n"
		"in mediump vec4 v_value${NAME_SPEC};\n"
		"\n"
		"void main()\n"
		"{\n"
		"	mediump vec4 value = ";

	for (int i = 0; i < numOperations; i++)
		resultTemplate += string(i > 0 ? op : "") + "v_value${NAME_SPEC}";

	resultTemplate +=
		";\n"
		"	o_color = value + ${FLOAT01};\n"
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

// Function for generating a vertex that takes one attribute in addition to position and just passes it to the fragment shader as a varying.
static string singleVaryingVertexTemplate (void)
{
	const char* resultTemplate =
		"#version 300 es\n"
		"in highp vec4 a_position${NAME_SPEC};\n"
		"in mediump vec4 a_value${NAME_SPEC};\n"
		"out mediump vec4 v_value${NAME_SPEC};\n"
		"\n"
		"void main()\n"
		"{\n"
		"	gl_Position = a_position${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n"
		"	v_value${NAME_SPEC} = a_value${NAME_SPEC};\n"
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

// Function for generating a fragment shader that takes a single varying and uses it as the color.
static string singleVaryingFragmentTemplate (void)
{
	const char* resultTemplate =
		"#version 300 es\n"
		"layout(location = 0) out mediump vec4 o_color;\n"
		"in mediump vec4 v_value${NAME_SPEC};\n"
		"\n"
		"void main()\n"
		"{\n"
		"	o_color = v_value${NAME_SPEC} + ${FLOAT01};\n"
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

// Function for generating the vertex shader of a texture lookup case.
static string textureLookupVertexTemplate (ConditionalUsage conditionalUsage, ConditionalType conditionalType)
{
	string	resultTemplate;
	bool	conditionVaryingNeeded = conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_DYNAMIC;

	resultTemplate +=
		"#version 300 es\n"
		"in highp vec4 a_position${NAME_SPEC};\n"
		"in mediump vec2 a_coords${NAME_SPEC};\n"
		"out mediump vec2 v_coords${NAME_SPEC};\n";

	if (conditionVaryingNeeded)
		resultTemplate +=
			"in mediump float a_condition${NAME_SPEC};\n"
			"out mediump float v_condition${NAME_SPEC};\n";

	resultTemplate +=
		"\n"
		"void main()\n"
		"{\n"
		"	gl_Position = a_position${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n"
		"	v_coords${NAME_SPEC} = a_coords${NAME_SPEC};\n";

	if (conditionVaryingNeeded)
		resultTemplate +=
			"	v_condition${NAME_SPEC} = a_condition${NAME_SPEC};\n";

	resultTemplate +=
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

// Function for generating the fragment shader of a texture lookup case.
static string textureLookupFragmentTemplate (int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType)
{
	string resultTemplate;

	resultTemplate +=
		"#version 300 es\n"
		"layout(location = 0) out mediump vec4 o_color;\n"
		"in mediump vec2 v_coords${NAME_SPEC};\n";

	if (conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_DYNAMIC)
		resultTemplate +=
			"in mediump float v_condition${NAME_SPEC};\n";

	for (int i = 0; i < numLookups; i++)
		resultTemplate +=
			"uniform sampler2D u_sampler" + de::toString(i) + "${NAME_SPEC};\n";

	if (conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_UNIFORM)
		resultTemplate +=
			"uniform mediump float u_condition${NAME_SPEC};\n";

	resultTemplate +=
		"\n"
		"void main()\n"
		"{\n"
		"	mediump vec4 color = vec4(0.0);\n";

	const char* conditionalTerm = conditionalType == CONDITIONAL_TYPE_STATIC	? "1.0 > 0.0"
								: conditionalType == CONDITIONAL_TYPE_UNIFORM	? "u_condition${NAME_SPEC} > 0.0"
								: conditionalType == CONDITIONAL_TYPE_DYNAMIC	? "v_condition${NAME_SPEC} > 0.0"
								: DE_NULL;

	DE_ASSERT(conditionalTerm != DE_NULL);

	if (conditionalUsage == CONDITIONAL_USAGE_FIRST_HALF)
		resultTemplate += string("") +
			"	if (" + conditionalTerm + ")\n"
			"	{\n";

	for (int i = 0; i < numLookups; i++)
	{
		if (conditionalUsage == CONDITIONAL_USAGE_FIRST_HALF)
		{
			if (i < (numLookups + 1) / 2)
				resultTemplate += "\t";
		}
		else if (conditionalUsage == CONDITIONAL_USAGE_EVERY_OTHER)
		{
			if (i % 2 == 0)
				resultTemplate += string("") +
					"	if (" + conditionalTerm + ")\n"
					"\t";
		}

		resultTemplate +=
			"	color += texture(u_sampler" + de::toString(i) + "${NAME_SPEC}, v_coords${NAME_SPEC});\n";

		if (conditionalUsage == CONDITIONAL_USAGE_FIRST_HALF && i == (numLookups - 1) / 2)
			resultTemplate += "\t}\n";
	}

	resultTemplate +=
		"	o_color = color/" + de::toString(numLookups) + ".0 + ${FLOAT01};\n" +
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

// Function for generating the shader attributes of a texture lookup case.
static vector<ShaderCompilerCase::AttribSpec> textureLookupShaderAttributes (const string& nameSpecialization, ConditionalUsage conditionalUsage, ConditionalType conditionalType)
{
	vector<ShaderCompilerCase::AttribSpec> result;

	result.push_back(ShaderCompilerCase::AttribSpec("a_position" + nameSpecialization,
													combineVec4ToVec16(Vec4(-1.0f, -1.0f,  0.0f,  1.0f),
																	   Vec4(-1.0f,  1.0f,  0.0f,  1.0f),
																	   Vec4( 1.0f, -1.0f,  0.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  0.0f,  1.0f))));

	result.push_back(ShaderCompilerCase::AttribSpec("a_coords" + nameSpecialization,
													combineVec4ToVec16(Vec4(0.0f, 0.0f, 0.0f, 0.0f),
																	   Vec4(0.0f, 1.0f, 0.0f, 0.0f),
																	   Vec4(1.0f, 0.0f, 0.0f, 0.0f),
																	   Vec4(1.0f, 1.0f, 0.0f, 0.0f))));

	if (conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_DYNAMIC)
		result.push_back(ShaderCompilerCase::AttribSpec("a_condition" + nameSpecialization,
														combineVec4ToVec16(Vec4(1.0f), Vec4(1.0f), Vec4(1.0f), Vec4(1.0f))));

	return result;
}

// Function for generating the shader uniforms of a texture lookup case.
static vector<ShaderCompilerCase::UniformSpec> textureLookupShaderUniforms (const string& nameSpecialization, int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType)
{
	vector<ShaderCompilerCase::UniformSpec> result;

	for (int i = 0; i < numLookups; i++)
		result.push_back(ShaderCompilerCase::UniformSpec("u_sampler" + de::toString(i) + nameSpecialization,
														 ShaderCompilerCase::UniformSpec::TYPE_TEXTURE_UNIT,
														 (float)i));

	if (conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_UNIFORM)
		result.push_back(ShaderCompilerCase::UniformSpec("u_condition" + nameSpecialization,
														 ShaderCompilerCase::UniformSpec::TYPE_FLOAT,
														 1.0f));

	return result;
}

static string mandelbrotVertexTemplate (void)
{
	const char* resultTemplate =
		"#version 300 es\n"
		"uniform highp mat4 u_mvp${NAME_SPEC};\n"
		"\n"
		"in highp vec4 a_vertex${NAME_SPEC};\n"
		"in highp vec4 a_coord${NAME_SPEC};\n"
		"\n"
		"out mediump vec2 v_coord${NAME_SPEC};\n"
		"\n"
		"void main(void)\n"
		"{\n"
		"	gl_Position = u_mvp${NAME_SPEC} * a_vertex${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n"
		"\n"
		"	float xMin = -2.0;\n"
		"	float xMax = +0.5;\n"
		"	float yMin = -1.5;\n"
		"	float yMax = +1.5;\n"
		"\n"
		"	v_coord${NAME_SPEC}.x = a_coord${NAME_SPEC}.x * (xMax - xMin) + xMin;\n"
		"	v_coord${NAME_SPEC}.y = a_coord${NAME_SPEC}.y * (yMax - yMin) + yMin;\n"
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

static string mandelbrotFragmentTemplate (int numFractalIterations)
{
	string resultTemplate =
		"#version 300 es\n"
		"layout(location = 0) out mediump vec4 o_color;\n"
		"in mediump vec2 v_coord${NAME_SPEC};\n"
		"\n"
		"precision mediump float;\n"
		"\n"
		"#define NUM_ITERS " + de::toString(numFractalIterations) + "\n"
		"\n"
		"void main (void)\n"
		"{\n"
		"	vec2 coords = v_coord${NAME_SPEC};\n"
		"	float u_limit = 2.0 * 2.0;\n"
		"	vec2 tmp = vec2(0, 0);\n"
		"	int iter;\n"
		"\n"
		"	for (iter = 0; iter < NUM_ITERS; iter++)\n"
		"	{\n"
		"		tmp = vec2((tmp.x + tmp.y) * (tmp.x - tmp.y), 2.0 * (tmp.x * tmp.y)) + coords;\n"
		"\n"
		"		if (dot(tmp, tmp) > u_limit)\n"
		"			break;\n"
		"	}\n"
		"\n"
		"	vec3 color = vec3(float(iter) * (1.0 / float(NUM_ITERS)));\n"
		"\n"
		"	o_color = vec4(color, 1.0) + ${FLOAT01};\n"
		"${SEMANTIC_ERROR}"
		"}\n"
		"${INVALID_CHAR}";

	return resultTemplate;
}

static vector<ShaderCompilerCase::AttribSpec> mandelbrotShaderAttributes (const string& nameSpecialization)
{
	vector<ShaderCompilerCase::AttribSpec> result;

	result.push_back(ShaderCompilerCase::AttribSpec("a_vertex" + nameSpecialization,
													combineVec4ToVec16(Vec4(-1.0f, -1.0f,  0.0f,  1.0f),
																	   Vec4(-1.0f,  1.0f,  0.0f,  1.0f),
																	   Vec4( 1.0f, -1.0f,  0.0f,  1.0f),
																	   Vec4( 1.0f,  1.0f,  0.0f,  1.0f))));

	result.push_back(ShaderCompilerCase::AttribSpec("a_coord" + nameSpecialization,
													combineVec4ToVec16(Vec4(0.0f, 0.0f, 0.0f, 1.0f),
																	   Vec4(0.0f, 1.0f, 0.0f, 1.0f),
																	   Vec4(1.0f, 0.0f, 0.0f, 1.0f),
																	   Vec4(1.0f, 1.0f, 0.0f, 1.0f))));

	return result;
}

static vector<ShaderCompilerCase::UniformSpec> mandelbrotShaderUniforms (const string& nameSpecialization)
{
	vector<ShaderCompilerCase::UniformSpec> result;

	result.push_back(ShaderCompilerCase::UniformSpec("u_mvp" + nameSpecialization,
													 ShaderCompilerCase::UniformSpec::TYPE_MAT4,
													 arrTo16(Mat4(1.0f).getColumnMajorData())));

	return result;
}

ShaderCompilerCase::ShaderCompilerCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments)
	: TestCase								(context, tcu::NODETYPE_PERFORMANCE, name, description)
	, m_viewportWidth						(0)
	, m_viewportHeight						(0)
	, m_avoidCache							(avoidCache)
	, m_addWhitespaceAndComments			(addWhitespaceAndComments)
	, m_startHash							((deUint32)(deUint64Hash(deGetTime()) ^ deUint64Hash(deGetMicroseconds()) ^ deInt32Hash(caseID)))
{
	int cmdLineIterCount = context.getTestContext().getCommandLine().getTestIterationCount();
	m_minimumMeasurementCount = cmdLineIterCount > 0 ? cmdLineIterCount : DEFAULT_MINIMUM_MEASUREMENT_COUNT;
	m_maximumMeasurementCount = m_minimumMeasurementCount*3;
}

ShaderCompilerCase::~ShaderCompilerCase (void)
{
}

deUint32 ShaderCompilerCase::getSpecializationID (int measurementNdx) const
{
	if (m_avoidCache)
		return m_startHash ^ (deUint32)deInt32Hash((deInt32)measurementNdx);
	else
		return m_startHash;
}

void ShaderCompilerCase::init (void)
{
	const glw::Functions&		gl				= m_context.getRenderContext().getFunctions();
	const tcu::RenderTarget&	renderTarget	= m_context.getRenderContext().getRenderTarget();

	m_viewportWidth		= deMin32(MAX_VIEWPORT_WIDTH, renderTarget.getWidth());
	m_viewportHeight	= deMin32(MAX_VIEWPORT_HEIGHT, renderTarget.getHeight());

	gl.viewport(0, 0, m_viewportWidth, m_viewportHeight);
}

ShaderCompilerCase::ShadersAndProgram ShaderCompilerCase::createShadersAndProgram (void) const
{
	const glw::Functions&	gl = m_context.getRenderContext().getFunctions();
	ShadersAndProgram		result;

	result.vertShader	= gl.createShader(GL_VERTEX_SHADER);
	result.fragShader	= gl.createShader(GL_FRAGMENT_SHADER);
	result.program		= gl.createProgram();

	gl.attachShader(result.program, result.vertShader);
	gl.attachShader(result.program, result.fragShader);

	return result;
}

void ShaderCompilerCase::setShaderSources (deUint32 vertShader, deUint32 fragShader, const ProgramContext& progCtx) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
	const char* vertShaderSourceCStr = progCtx.vertShaderSource.c_str();
	const char* fragShaderSourceCStr = progCtx.fragShaderSource.c_str();
	gl.shaderSource(vertShader, 1, &vertShaderSourceCStr, DE_NULL);
	gl.shaderSource(fragShader, 1, &fragShaderSourceCStr, DE_NULL);
}

bool ShaderCompilerCase::compileShader (deUint32 shader) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
	GLint status;
	gl.compileShader(shader);
	gl.getShaderiv(shader, GL_COMPILE_STATUS, &status);
	return status != 0;
}

bool ShaderCompilerCase::linkAndUseProgram (deUint32 program) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
	GLint linkStatus;

	gl.linkProgram(program);
	gl.getProgramiv(program, GL_LINK_STATUS, &linkStatus);

	if (linkStatus != 0)
		gl.useProgram(program);

	return linkStatus != 0;
}

void ShaderCompilerCase::setShaderInputs (deUint32 program, const ProgramContext& progCtx) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	// Setup attributes.

	for (int attribNdx = 0; attribNdx < (int)progCtx.vertexAttributes.size(); attribNdx++)
	{
		int location = gl.getAttribLocation(program, progCtx.vertexAttributes[attribNdx].name.c_str());
		if (location >= 0)
		{
			gl.enableVertexAttribArray(location);
			gl.vertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, 0, progCtx.vertexAttributes[attribNdx].value.getPtr());
		}
	}

	// Setup uniforms.

	for (int uniformNdx = 0; uniformNdx < (int)progCtx.uniforms.size(); uniformNdx++)
	{
		int location = gl.getUniformLocation(program, progCtx.uniforms[uniformNdx].name.c_str());
		if (location >= 0)
		{
			const float* floatPtr = progCtx.uniforms[uniformNdx].value.getPtr();

			switch (progCtx.uniforms[uniformNdx].type)
			{
				case UniformSpec::TYPE_FLOAT:			gl.uniform1fv(location, 1, floatPtr);								break;
				case UniformSpec::TYPE_VEC2:			gl.uniform2fv(location, 1, floatPtr);								break;
				case UniformSpec::TYPE_VEC3:			gl.uniform3fv(location, 1, floatPtr);								break;
				case UniformSpec::TYPE_VEC4:			gl.uniform4fv(location, 1, floatPtr);								break;
				case UniformSpec::TYPE_MAT3:			gl.uniformMatrix3fv(location, 1, GL_FALSE, floatPtr);				break;
				case UniformSpec::TYPE_MAT4:			gl.uniformMatrix4fv(location, 1, GL_FALSE, floatPtr);				break;
				case UniformSpec::TYPE_TEXTURE_UNIT:	gl.uniform1i(location, (GLint)deRoundFloatToInt32(*floatPtr));		break;
				default:
					DE_ASSERT(DE_FALSE);
			}
		}
	}
}

void ShaderCompilerCase::draw (void) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	static const deUint8 indices[] =
	{
		0, 1, 2,
		2, 1, 3
	};

	gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
	gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(indices), GL_UNSIGNED_BYTE, indices);

	// \note Read one pixel to force compilation.
	deUint32 pixel;
	gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
}

void ShaderCompilerCase::cleanup (const ShadersAndProgram& shadersAndProgram, const ProgramContext& progCtx, bool linkSuccess) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	if (linkSuccess)
	{
		for (int attribNdx = 0; attribNdx < (int)progCtx.vertexAttributes.size(); attribNdx++)
		{
			int location = gl.getAttribLocation(shadersAndProgram.program, progCtx.vertexAttributes[attribNdx].name.c_str());
			if (location >= 0)
				gl.disableVertexAttribArray(location);
		}
	}

	gl.useProgram(0);
	gl.detachShader(shadersAndProgram.program, shadersAndProgram.vertShader);
	gl.detachShader(shadersAndProgram.program, shadersAndProgram.fragShader);
	gl.deleteShader(shadersAndProgram.vertShader);
	gl.deleteShader(shadersAndProgram.fragShader);
	gl.deleteProgram(shadersAndProgram.program);
}

void ShaderCompilerCase::logProgramData (const BuildInfo& buildInfo, const ProgramContext& progCtx) const
{
	m_testCtx.getLog() << TestLog::ShaderProgram(buildInfo.linkSuccess, buildInfo.logs.link)
					   << TestLog::Shader(QP_SHADER_TYPE_VERTEX,	progCtx.vertShaderSource, buildInfo.vertCompileSuccess, buildInfo.logs.vert)
					   << TestLog::Shader(QP_SHADER_TYPE_FRAGMENT,	progCtx.fragShaderSource, buildInfo.fragCompileSuccess, buildInfo.logs.frag)
					   << TestLog::EndShaderProgram;
}

ShaderCompilerCase::Logs ShaderCompilerCase::getLogs (const ShadersAndProgram& shadersAndProgram) const
{
	const glw::Functions&	gl = m_context.getRenderContext().getFunctions();
	Logs					result;

	result.vert = getShaderInfoLog(gl, shadersAndProgram.vertShader);
	result.frag = getShaderInfoLog(gl, shadersAndProgram.fragShader);
	result.link = getProgramInfoLog(gl, shadersAndProgram.program);

	return result;
}

bool ShaderCompilerCase::goodEnoughMeasurements (const vector<Measurement>& measurements) const
{
	if ((int)measurements.size() < m_minimumMeasurementCount)
		return false;
	else
	{
		if ((int)measurements.size() >= m_maximumMeasurementCount)
			return true;
		else
		{
			vector<deInt64> totalTimesWithoutDraw;
			for (int i = 0; i < (int)measurements.size(); i++)
				totalTimesWithoutDraw.push_back(measurements[i].totalTimeWithoutDraw());
			return vectorFloatRelativeMedianAbsoluteDeviation(vectorLowestPercentage(totalTimesWithoutDraw, 0.5f)) < RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD;
		}
	}
}

ShaderCompilerCase::IterateResult ShaderCompilerCase::iterate (void)
{
	// Before actual measurements, compile and draw with a dummy shader to avoid possible initial slowdowns in the actual test.
	{
		deUint32		specID = getSpecializationID(0);
		ProgramContext	progCtx;
		progCtx.vertShaderSource = specializeShaderSource(singleVaryingVertexTemplate(), specID, SHADER_VALIDITY_VALID);
		progCtx.fragShaderSource = specializeShaderSource(singleVaryingFragmentTemplate(), specID, SHADER_VALIDITY_VALID);
		progCtx.vertexAttributes = singleValueShaderAttributes(getNameSpecialization(specID));

		ShadersAndProgram shadersAndProgram = createShadersAndProgram();
		setShaderSources(shadersAndProgram.vertShader, shadersAndProgram.fragShader, progCtx);

		BuildInfo buildInfo;
		buildInfo.vertCompileSuccess	= compileShader(shadersAndProgram.vertShader);
		buildInfo.fragCompileSuccess	= compileShader(shadersAndProgram.fragShader);
		buildInfo.linkSuccess			= linkAndUseProgram(shadersAndProgram.program);
		if (!(buildInfo.vertCompileSuccess && buildInfo.fragCompileSuccess && buildInfo.linkSuccess))
		{
			buildInfo.logs = getLogs(shadersAndProgram);
			logProgramData(buildInfo, progCtx);
			cleanup(shadersAndProgram, progCtx, buildInfo.linkSuccess);
			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compilation failed");
			return STOP;
		}
		setShaderInputs(shadersAndProgram.program, progCtx);
		draw();
		cleanup(shadersAndProgram, progCtx, buildInfo.linkSuccess);
	}

	vector<Measurement>		measurements;
	// \note These are logged after measurements are done.
	ProgramContext			latestProgramContext;
	BuildInfo				latestBuildInfo;

	if (WARMUP_CPU_AT_BEGINNING_OF_CASE)
		tcu::warmupCPU();

	// Actual test measurements.
	while (!goodEnoughMeasurements(measurements))
	{
		// Create shaders, compile & link, set shader inputs and draw. Time measurement is done at relevant points.
		// \note Setting inputs and drawing are done twice in order to find out the time for actual compiling.

		// \note Shader data (sources and inputs) are generated and GL shader and program objects are created before any time measurements.
		ProgramContext		progCtx				= generateShaderData((int)measurements.size());
		ShadersAndProgram	shadersAndProgram	= createShadersAndProgram();
		BuildInfo			buildInfo;

		if (m_addWhitespaceAndComments)
		{
			const deUint32 hash = m_startHash ^ (deUint32)deInt32Hash((deInt32)measurements.size());
			progCtx.vertShaderSource = strWithWhiteSpaceAndComments(progCtx.vertShaderSource, hash);
			progCtx.fragShaderSource = strWithWhiteSpaceAndComments(progCtx.fragShaderSource, hash);
		}

		if (WARMUP_CPU_BEFORE_EACH_MEASUREMENT)
			tcu::warmupCPU();

		// \note Do NOT do anything too hefty between the first and last deGetMicroseconds() here (other than the gl calls); it would disturb the measurement.

		deUint64 startTime = deGetMicroseconds();

		setShaderSources(shadersAndProgram.vertShader, shadersAndProgram.fragShader, progCtx);
		deUint64 shaderSourceSetEndTime = deGetMicroseconds();

		buildInfo.vertCompileSuccess = compileShader(shadersAndProgram.vertShader);
		deUint64 vertexShaderCompileEndTime = deGetMicroseconds();

		buildInfo.fragCompileSuccess = compileShader(shadersAndProgram.fragShader);
		deUint64 fragmentShaderCompileEndTime = deGetMicroseconds();

		buildInfo.linkSuccess = linkAndUseProgram(shadersAndProgram.program);
		deUint64 programLinkEndTime = deGetMicroseconds();

		// Check compilation and linking status here, after all compilation and linking gl calls are made.
		if (!(buildInfo.vertCompileSuccess && buildInfo.fragCompileSuccess && buildInfo.linkSuccess))
		{
			buildInfo.logs = getLogs(shadersAndProgram);
			logProgramData(buildInfo, progCtx);
			cleanup(shadersAndProgram, progCtx, buildInfo.linkSuccess);
			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compilation failed");
			return STOP;
		}

		setShaderInputs(shadersAndProgram.program, progCtx);
		deUint64 firstShaderInputSetEndTime = deGetMicroseconds();

		// Draw for the first time.
		draw();
		deUint64 firstDrawEndTime = deGetMicroseconds();

		// Set inputs and draw again.

		setShaderInputs(shadersAndProgram.program, progCtx);
		deUint64 secondShaderInputSetEndTime = deGetMicroseconds();

		draw();
		deUint64 secondDrawEndTime = deGetMicroseconds();

		// De-initializations (detach shaders etc.).

		buildInfo.logs = getLogs(shadersAndProgram);
		cleanup(shadersAndProgram, progCtx, buildInfo.linkSuccess);

		// Output measurement log later (after last measurement).

		measurements.push_back(Measurement((deInt64)(shaderSourceSetEndTime			- startTime),
										   (deInt64)(vertexShaderCompileEndTime		- shaderSourceSetEndTime),
										   (deInt64)(fragmentShaderCompileEndTime	- vertexShaderCompileEndTime),
										   (deInt64)(programLinkEndTime				- fragmentShaderCompileEndTime),
										   (deInt64)(firstShaderInputSetEndTime		- programLinkEndTime),
										   (deInt64)(firstDrawEndTime				- firstShaderInputSetEndTime),
										   (deInt64)(secondShaderInputSetEndTime	- firstDrawEndTime),
										   (deInt64)(secondDrawEndTime				- secondShaderInputSetEndTime)));

		latestBuildInfo			= buildInfo;
		latestProgramContext	= progCtx;

		m_testCtx.touchWatchdog(); // \note Measurements may take a while in a bad case.
	}

	// End of test case, log information about measurements.
	{
		TestLog& log = m_testCtx.getLog();

		vector<deInt64> sourceSetTimes;
		vector<deInt64> vertexCompileTimes;
		vector<deInt64> fragmentCompileTimes;
		vector<deInt64> programLinkTimes;
		vector<deInt64> firstInputSetTimes;
		vector<deInt64> firstDrawTimes;
		vector<deInt64> secondInputTimes;
		vector<deInt64> secondDrawTimes;
		vector<deInt64> firstPhaseTimes;
		vector<deInt64> secondPhaseTimes;
		vector<deInt64> totalTimesWithoutDraw;
		vector<deInt64> specializationTimes;

		if (!m_avoidCache)
			log << TestLog::Message << "Note: Testing cache hits, so the medians and averages exclude the first iteration." << TestLog::EndMessage;

		log << TestLog::Message << "Note: \"Specialization time\" means first draw time minus second draw time." << TestLog::EndMessage
			<< TestLog::Message << "Note: \"Compilation time\" means the time up to (and including) linking, plus specialization time." << TestLog::EndMessage;

		log << TestLog::Section("IterationMeasurements", "Iteration measurements of compilation and linking times");

		DE_ASSERT((int)measurements.size() > (m_avoidCache ? 0 : 1));

		for (int ndx = 0; ndx < (int)measurements.size(); ndx++)
		{
			const Measurement& curMeas = measurements[ndx];

			// Subtract time of second phase (second input setup and draw) from first (from start to end of first draw).
			// \note Cap if second phase seems unreasonably high (higher than first input set and draw).
			deInt64 timeWithoutDraw		= curMeas.totalTimeWithoutDraw();

			// Specialization time = first draw - second draw time. Again, cap at 0 if second draw was longer than first draw.
			deInt64 specializationTime	= de::max<deInt64>(0, curMeas.firstDrawTime - curMeas.secondDrawTime);

			if (ndx > 0 || m_avoidCache) // \note When allowing cache hits, don't account for the first measurement when calculating median or average.
			{
				sourceSetTimes.push_back		(curMeas.sourceSetTime);
				vertexCompileTimes.push_back	(curMeas.vertexCompileTime);
				fragmentCompileTimes.push_back	(curMeas.fragmentCompileTime);
				programLinkTimes.push_back		(curMeas.programLinkTime);
				firstInputSetTimes.push_back	(curMeas.firstInputSetTime);
				firstDrawTimes.push_back		(curMeas.firstDrawTime);
				firstPhaseTimes.push_back		(curMeas.firstPhase());
				secondDrawTimes.push_back		(curMeas.secondDrawTime);
				secondInputTimes.push_back		(curMeas.secondInputSetTime);
				secondPhaseTimes.push_back		(curMeas.secondPhase());
				totalTimesWithoutDraw.push_back	(timeWithoutDraw);
				specializationTimes.push_back	(specializationTime);
			}

			// Log this measurement.
			log << TestLog::Float("Measurement" + de::toString(ndx) + "CompilationTime",
								  "Measurement " + de::toString(ndx) + " compilation time",
								  "ms", QP_KEY_TAG_TIME, timeWithoutDraw / 1000.0f)
				<< TestLog::Float("Measurement" + de::toString(ndx) + "SpecializationTime",
								  "Measurement " + de::toString(ndx) + " specialization time",
								  "ms", QP_KEY_TAG_TIME, specializationTime / 1000.0f);
		}

		// Log some statistics.

		for (int entireRangeOrLowestHalf = 0; entireRangeOrLowestHalf < 2; entireRangeOrLowestHalf++)
		{
			bool				isEntireRange				= entireRangeOrLowestHalf == 0;
			string				statNamePrefix				= isEntireRange ? "" : "LowestHalf";
			vector<deInt64>		rangeTotalTimes				= isEntireRange ? totalTimesWithoutDraw	: vectorLowestPercentage(totalTimesWithoutDraw,	0.5f);
			vector<deInt64>		rangeSpecializationTimes	= isEntireRange ? specializationTimes	: vectorLowestPercentage(specializationTimes,	0.5f);

#define LOG_COMPILE_SPECIALIZE_TIME_STAT(NAME, DESC, FUNC)																													\
	log << TestLog::Float(statNamePrefix + "CompilationTime" + (NAME), (DESC) + string(" of compilation time"), "ms", QP_KEY_TAG_TIME, (FUNC)(rangeTotalTimes)/1000.0f)		\
		<< TestLog::Float(statNamePrefix + "SpecializationTime" + (NAME), (DESC) + string(" of specialization time"), "ms", QP_KEY_TAG_TIME, (FUNC)(rangeSpecializationTimes)/1000.0f)

#define LOG_COMPILE_SPECIALIZE_RELATIVE_STAT(NAME, DESC, FUNC)																										\
	log << TestLog::Float(statNamePrefix + "CompilationTime" + (NAME), (DESC) + string(" of compilation time"), "", QP_KEY_TAG_NONE, (FUNC)(rangeTotalTimes))		\
		<< TestLog::Float(statNamePrefix + "SpecializationTime" + (NAME), (DESC) + string(" of specialization time"), "", QP_KEY_TAG_NONE, (FUNC)(rangeSpecializationTimes))

			log << TestLog::Message << "\nStatistics computed from "
									<< (isEntireRange ? "all" : "only the lowest 50%")
									<< " of the above measurements:"
									<< TestLog::EndMessage;

			LOG_COMPILE_SPECIALIZE_TIME_STAT		("Median",							"Median",								vectorFloatMedian);
			LOG_COMPILE_SPECIALIZE_TIME_STAT		("Average",							"Average",								vectorFloatAverage);
			LOG_COMPILE_SPECIALIZE_TIME_STAT		("Minimum",							"Minimum",								vectorFloatMinimum);
			LOG_COMPILE_SPECIALIZE_TIME_STAT		("Maximum",							"Maximum",								vectorFloatMaximum);
			LOG_COMPILE_SPECIALIZE_TIME_STAT		("MedianAbsoluteDeviation",			"Median absolute deviation",			vectorFloatMedianAbsoluteDeviation);
			LOG_COMPILE_SPECIALIZE_RELATIVE_STAT	("RelativeMedianAbsoluteDeviation",	"Relative median absolute deviation",	vectorFloatRelativeMedianAbsoluteDeviation);
			LOG_COMPILE_SPECIALIZE_TIME_STAT		("StandardDeviation",				"Standard deviation",					vectorFloatStandardDeviation);
			LOG_COMPILE_SPECIALIZE_RELATIVE_STAT	("RelativeStandardDeviation",		"Relative standard deviation",			vectorFloatRelativeStandardDeviation);
			LOG_COMPILE_SPECIALIZE_TIME_STAT		("MaxMinusMin",						"Max-min",								vectorFloatMaximumMinusMinimum);
			LOG_COMPILE_SPECIALIZE_RELATIVE_STAT	("RelativeMaxMinusMin",				"Relative max-min",						vectorFloatRelativeMaximumMinusMinimum);

#undef LOG_COMPILE_SPECIALIZE_RELATIVE_STAT
#undef LOG_COMPILE_SPECIALIZE_TIME_STAT

			if (!isEntireRange && vectorFloatRelativeMedianAbsoluteDeviation(rangeTotalTimes) > RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD)
				log << TestLog::Message << "\nWARNING: couldn't achieve relative median absolute deviation under threshold value "
										<< RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD
										<< " for compilation time of the lowest 50% of measurements" << TestLog::EndMessage;
		}

		log << TestLog::EndSection; // End section IterationMeasurements

		for (int medianOrAverage = 0; medianOrAverage < 2; medianOrAverage++)
		{
			typedef float (*VecFunc)(const vector<deInt64>&);

			bool	isMedian						= medianOrAverage == 0;
			string	singular						= isMedian ? "Median" : "Average";
			string	plural							= singular + "s";
			VecFunc func							= isMedian ? (VecFunc) vectorFloatMedian<deInt64> : (VecFunc) vectorFloatAverage<deInt64>;

			log << TestLog::Section(plural + "PerPhase", plural + " per phase");

			for (int entireRangeOrLowestHalf = 0; entireRangeOrLowestHalf < 2; entireRangeOrLowestHalf++)
			{
				bool	isEntireRange	= entireRangeOrLowestHalf == 0;
				string	statNamePrefix	= isEntireRange ? "" : "LowestHalf";
				float	rangeSizeRatio	= isEntireRange ? 1.0f : 0.5f;

#define LOG_TIME(NAME, DESC, DATA) log << TestLog::Float(statNamePrefix + (NAME) + singular, singular + " of " + (DESC), "ms", QP_KEY_TAG_TIME, func(vectorLowestPercentage((DATA), rangeSizeRatio))/1000.0f);

				log << TestLog::Message << (isEntireRange ? "For all measurements:" : "\nFor only the lowest 50% of the measurements:") << TestLog::EndMessage;
				LOG_TIME("ShaderSourceSetTime",			"shader source set time",			sourceSetTimes);
				LOG_TIME("VertexShaderCompileTime",		"vertex shader compile time",		vertexCompileTimes);
				LOG_TIME("FragmentShaderCompileTime",	"fragment shader compile time",		fragmentCompileTimes);
				LOG_TIME("ProgramLinkTime",				"program link time",				programLinkTimes);
				LOG_TIME("FirstShaderInputSetTime",		"first shader input set time",		firstInputSetTimes);
				LOG_TIME("FirstDrawTime",				"first draw time",					firstDrawTimes);
				LOG_TIME("SecondShaderInputSetTime",	"second shader input set time",		secondInputTimes);
				LOG_TIME("SecondDrawTime",				"second draw time",					secondDrawTimes);

#undef LOG_TIME
			}

			log << TestLog::EndSection;
		}

		// Set result.

		{
			log << TestLog::Message << "Note: test result is the first quartile (i.e. median of the lowest half of measurements) of compilation times" << TestLog::EndMessage;
			float result = vectorFloatFirstQuartile(totalTimesWithoutDraw) / 1000.0f;
			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString(result, 2).c_str());
		}

		// Log shaders.

		if (m_avoidCache || m_addWhitespaceAndComments)
		{
			string msg = "Note: the following shaders are the ones from the last iteration; ";

			if (m_avoidCache)
				msg += "variables' names and some constant expressions";
			if (m_addWhitespaceAndComments)
				msg += string(m_avoidCache ? " as well as " : "") + "whitespace and comments";

			msg += " differ between iterations.";

			log << TestLog::Message << msg.c_str() << TestLog::EndMessage;
		}

		logProgramData(latestBuildInfo, latestProgramContext);

		return STOP;
	}
}

ShaderCompilerLightCase::ShaderCompilerLightCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, int numLights, LightType lightType)
	: ShaderCompilerCase	(context, name, description, caseID, avoidCache, addWhitespaceAndComments)
	, m_numLights			(numLights)
	, m_isVertexCase		(isVertexCase)
	, m_lightType			(lightType)
	, m_texture				(DE_NULL)
{
}

ShaderCompilerLightCase::~ShaderCompilerLightCase (void)
{
	ShaderCompilerLightCase::deinit();
}

void ShaderCompilerLightCase::deinit (void)
{
	delete m_texture;
	m_texture = DE_NULL;
}

void ShaderCompilerLightCase::init (void)
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	// Setup texture.

	DE_ASSERT(m_texture == DE_NULL);

	m_texture = new glu::Texture2D(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, TEXTURE_WIDTH, TEXTURE_HEIGHT);

	tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_texture->getRefTexture().getFormat());

	m_texture->getRefTexture().allocLevel(0);
	tcu::fillWithComponentGradients(m_texture->getRefTexture().getLevel(0), fmtInfo.valueMin, fmtInfo.valueMax);

	gl.activeTexture(GL_TEXTURE0);
	gl.bindTexture(GL_TEXTURE_2D, m_texture->getGLTexture());
	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	m_texture->upload();

	ShaderCompilerCase::init();
}

ShaderCompilerCase::ProgramContext ShaderCompilerLightCase::generateShaderData (int measurementNdx) const
{
	deUint32		specID		= getSpecializationID(measurementNdx);
	string			nameSpec	= getNameSpecialization(specID);
	ProgramContext	result;

	result.vertShaderSource		= specializeShaderSource(lightVertexTemplate(m_numLights, m_isVertexCase, m_lightType), specID, SHADER_VALIDITY_VALID);
	result.fragShaderSource		= specializeShaderSource(lightFragmentTemplate(m_numLights, m_isVertexCase, m_lightType), specID, SHADER_VALIDITY_VALID);
	result.vertexAttributes		= lightShaderAttributes(nameSpec);
	result.uniforms				= lightShaderUniforms(nameSpec, m_numLights, m_lightType);

	return result;
}

ShaderCompilerTextureCase::ShaderCompilerTextureCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType)
	: ShaderCompilerCase	(context, name, description, caseID, avoidCache, addWhitespaceAndComments)
	, m_numLookups			(numLookups)
	, m_conditionalUsage	(conditionalUsage)
	, m_conditionalType		(conditionalType)
{
}

ShaderCompilerTextureCase::~ShaderCompilerTextureCase (void)
{
	ShaderCompilerTextureCase::deinit();
}

void ShaderCompilerTextureCase::deinit (void)
{
	for (vector<glu::Texture2D*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
		delete *i;
	m_textures.clear();
}

void ShaderCompilerTextureCase::init (void)
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	// Setup texture.

	DE_ASSERT(m_textures.empty());

	m_textures.reserve(m_numLookups);

	for (int i = 0; i < m_numLookups; i++)
	{
		glu::Texture2D*			tex		= new glu::Texture2D(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, TEXTURE_WIDTH, TEXTURE_HEIGHT);
		tcu::TextureFormatInfo	fmtInfo	= tcu::getTextureFormatInfo(tex->getRefTexture().getFormat());

		tex->getRefTexture().allocLevel(0);
		tcu::fillWithComponentGradients(tex->getRefTexture().getLevel(0), fmtInfo.valueMin, fmtInfo.valueMax);

		gl.activeTexture(GL_TEXTURE0 + i);
		gl.bindTexture(GL_TEXTURE_2D, tex->getGLTexture());
		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		tex->upload();

		m_textures.push_back(tex);
	}

	ShaderCompilerCase::init();
}

ShaderCompilerCase::ProgramContext ShaderCompilerTextureCase::generateShaderData (int measurementNdx) const
{
	deUint32		specID		= getSpecializationID(measurementNdx);
	string			nameSpec	= getNameSpecialization(specID);
	ProgramContext	result;

	result.vertShaderSource		= specializeShaderSource(textureLookupVertexTemplate(m_conditionalUsage, m_conditionalType), specID, SHADER_VALIDITY_VALID);
	result.fragShaderSource		= specializeShaderSource(textureLookupFragmentTemplate(m_numLookups, m_conditionalUsage, m_conditionalType), specID, SHADER_VALIDITY_VALID);
	result.vertexAttributes		= textureLookupShaderAttributes(nameSpec, m_conditionalUsage, m_conditionalType);
	result.uniforms				= textureLookupShaderUniforms(nameSpec, m_numLookups, m_conditionalUsage, m_conditionalType);

	return result;
}

ShaderCompilerLoopCase::ShaderCompilerLoopCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, LoopType type, int numLoopIterations, int nestingDepth)
	: ShaderCompilerCase	(context, name, description, caseID, avoidCache, addWhitespaceAndComments)
	, m_numLoopIterations	(numLoopIterations)
	, m_nestingDepth		(nestingDepth)
	, m_isVertexCase		(isVertexCase)
	, m_type				(type)
{
}

ShaderCompilerLoopCase::~ShaderCompilerLoopCase (void)
{
}

ShaderCompilerCase::ProgramContext ShaderCompilerLoopCase::generateShaderData (int measurementNdx) const
{
	deUint32		specID		= getSpecializationID(measurementNdx);
	string			nameSpec	= getNameSpecialization(specID);
	ProgramContext	result;

	result.vertShaderSource		= specializeShaderSource(loopVertexTemplate(m_type, m_isVertexCase, m_numLoopIterations, m_nestingDepth), specID, SHADER_VALIDITY_VALID);
	result.fragShaderSource		= specializeShaderSource(loopFragmentTemplate(m_type, m_isVertexCase, m_numLoopIterations, m_nestingDepth), specID, SHADER_VALIDITY_VALID);

	result.vertexAttributes		= loopShaderAttributes(nameSpec, m_type, m_numLoopIterations);
	result.uniforms				= loopShaderUniforms(nameSpec, m_type, m_numLoopIterations);

	return result;
}

ShaderCompilerOperCase::ShaderCompilerOperCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, const char* oper, int numOperations)
	: ShaderCompilerCase	(context, name, description, caseID, avoidCache, addWhitespaceAndComments)
	, m_oper				(oper)
	, m_numOperations		(numOperations)
	, m_isVertexCase		(isVertexCase)
{
}

ShaderCompilerOperCase::~ShaderCompilerOperCase (void)
{
}

ShaderCompilerCase::ProgramContext ShaderCompilerOperCase::generateShaderData (int measurementNdx) const
{
	deUint32		specID		= getSpecializationID(measurementNdx);
	string			nameSpec	= getNameSpecialization(specID);
	ProgramContext	result;

	if (m_isVertexCase)
	{
		result.vertShaderSource = specializeShaderSource(binaryOpVertexTemplate(m_numOperations, m_oper.c_str()), specID, SHADER_VALIDITY_VALID);
		result.fragShaderSource = specializeShaderSource(singleVaryingFragmentTemplate(), specID, SHADER_VALIDITY_VALID);
	}
	else
	{
		result.vertShaderSource = specializeShaderSource(singleVaryingVertexTemplate(), specID, SHADER_VALIDITY_VALID);
		result.fragShaderSource = specializeShaderSource(binaryOpFragmentTemplate(m_numOperations, m_oper.c_str()), specID, SHADER_VALIDITY_VALID);
	}

	result.vertexAttributes = singleValueShaderAttributes(nameSpec);

	result.uniforms.clear(); // No uniforms used.

	return result;
}

ShaderCompilerMandelbrotCase::ShaderCompilerMandelbrotCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, int numFractalIterations)
	: ShaderCompilerCase		(context, name, description, caseID, avoidCache, addWhitespaceAndComments)
	, m_numFractalIterations	(numFractalIterations)
{
}

ShaderCompilerMandelbrotCase::~ShaderCompilerMandelbrotCase (void)
{
}

ShaderCompilerCase::ProgramContext ShaderCompilerMandelbrotCase::generateShaderData (int measurementNdx) const
{
	deUint32		specID		= getSpecializationID(measurementNdx);
	string			nameSpec	= getNameSpecialization(specID);
	ProgramContext	result;

	result.vertShaderSource = specializeShaderSource(mandelbrotVertexTemplate(), specID, SHADER_VALIDITY_VALID);
	result.fragShaderSource = specializeShaderSource(mandelbrotFragmentTemplate(m_numFractalIterations), specID, SHADER_VALIDITY_VALID);

	result.vertexAttributes = mandelbrotShaderAttributes(nameSpec);
	result.uniforms = mandelbrotShaderUniforms(nameSpec);

	return result;
}

InvalidShaderCompilerCase::InvalidShaderCompilerCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType)
	: TestCase						(context, tcu::NODETYPE_PERFORMANCE, name, description)
	, m_invalidityType				(invalidityType)
	, m_startHash					((deUint32)(deUint64Hash(deGetTime()) ^ deUint64Hash(deGetMicroseconds()) ^ deInt32Hash(caseID)))
{
	int cmdLineIterCount = context.getTestContext().getCommandLine().getTestIterationCount();
	m_minimumMeasurementCount = cmdLineIterCount > 0 ? cmdLineIterCount : DEFAULT_MINIMUM_MEASUREMENT_COUNT;
	m_maximumMeasurementCount = 3*m_minimumMeasurementCount;
}

InvalidShaderCompilerCase::~InvalidShaderCompilerCase (void)
{
}

deUint32 InvalidShaderCompilerCase::getSpecializationID (int measurementNdx) const
{
	return m_startHash ^ (deUint32)deInt32Hash((deInt32)measurementNdx);
}

InvalidShaderCompilerCase::Shaders InvalidShaderCompilerCase::createShaders (void) const
{
	const glw::Functions&	gl = m_context.getRenderContext().getFunctions();
	Shaders					result;

	result.vertShader = gl.createShader(GL_VERTEX_SHADER);
	result.fragShader = gl.createShader(GL_FRAGMENT_SHADER);

	return result;
}

void InvalidShaderCompilerCase::setShaderSources (const Shaders& shaders, const ProgramContext& progCtx) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
	const char* vertShaderSourceCStr = progCtx.vertShaderSource.c_str();
	const char* fragShaderSourceCStr = progCtx.fragShaderSource.c_str();
	gl.shaderSource(shaders.vertShader, 1, &vertShaderSourceCStr, DE_NULL);
	gl.shaderSource(shaders.fragShader, 1, &fragShaderSourceCStr, DE_NULL);
}

bool InvalidShaderCompilerCase::compileShader (deUint32 shader) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
	GLint status;
	gl.compileShader(shader);
	gl.getShaderiv(shader, GL_COMPILE_STATUS, &status);
	return status != 0;
}

void InvalidShaderCompilerCase::logProgramData (const BuildInfo& buildInfo, const ProgramContext& progCtx) const
{
	m_testCtx.getLog() << TestLog::ShaderProgram(false, "(No linking done)")
					   << TestLog::Shader(QP_SHADER_TYPE_VERTEX,	progCtx.vertShaderSource, buildInfo.vertCompileSuccess, buildInfo.logs.vert)
					   << TestLog::Shader(QP_SHADER_TYPE_FRAGMENT,	progCtx.fragShaderSource, buildInfo.fragCompileSuccess, buildInfo.logs.frag)
					   << TestLog::EndShaderProgram;
}

InvalidShaderCompilerCase::Logs InvalidShaderCompilerCase::getLogs (const Shaders& shaders) const
{
	const glw::Functions&	gl = m_context.getRenderContext().getFunctions();
	Logs					result;

	result.vert = getShaderInfoLog(gl, shaders.vertShader);
	result.frag = getShaderInfoLog(gl, shaders.fragShader);

	return result;
}

void InvalidShaderCompilerCase::cleanup (const Shaders& shaders) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	gl.deleteShader(shaders.vertShader);
	gl.deleteShader(shaders.fragShader);
}

bool InvalidShaderCompilerCase::goodEnoughMeasurements (const vector<Measurement>& measurements) const
{
	if ((int)measurements.size() < m_minimumMeasurementCount)
		return false;
	else
	{
		if ((int)measurements.size() >= m_maximumMeasurementCount)
			return true;
		else
		{
			vector<deInt64> totalTimes;
			for (int i = 0; i < (int)measurements.size(); i++)
				totalTimes.push_back(measurements[i].totalTime());
			return vectorFloatRelativeMedianAbsoluteDeviation(vectorLowestPercentage(totalTimes, 0.5f)) < RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD;
		}
	}
}

InvalidShaderCompilerCase::IterateResult InvalidShaderCompilerCase::iterate (void)
{
	ShaderValidity shaderValidity = m_invalidityType == INVALIDITY_INVALID_CHAR		? SHADER_VALIDITY_INVALID_CHAR
								  : m_invalidityType == INVALIDITY_SEMANTIC_ERROR	? SHADER_VALIDITY_SEMANTIC_ERROR
								  : SHADER_VALIDITY_LAST;

	DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST);

	// Before actual measurements, compile a dummy shader to avoid possible initial slowdowns in the actual test.
	{
		deUint32		specID = getSpecializationID(0);
		ProgramContext	progCtx;
		progCtx.vertShaderSource = specializeShaderSource(singleVaryingVertexTemplate(), specID, shaderValidity);
		progCtx.fragShaderSource = specializeShaderSource(singleVaryingFragmentTemplate(), specID, shaderValidity);

		Shaders shaders = createShaders();
		setShaderSources(shaders, progCtx);

		BuildInfo buildInfo;
		buildInfo.vertCompileSuccess = compileShader(shaders.vertShader);
		buildInfo.fragCompileSuccess = compileShader(shaders.fragShader);
		if (buildInfo.vertCompileSuccess || buildInfo.fragCompileSuccess)
		{
			buildInfo.logs = getLogs(shaders);
			logProgramData(buildInfo, progCtx);
			cleanup(shaders);
			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compilation of a shader erroneously succeeded");
			return STOP;
		}
		cleanup(shaders);
	}

	vector<Measurement>		measurements;
	// \note These are logged after measurements are done.
	ProgramContext			latestProgramContext;
	BuildInfo				latestBuildInfo;

	if (WARMUP_CPU_AT_BEGINNING_OF_CASE)
		tcu::warmupCPU();

	// Actual test measurements.
	while (!goodEnoughMeasurements(measurements))
	{
		// Create shader and compile. Measure time.

		// \note Shader sources are generated and GL shader objects are created before any time measurements.
		ProgramContext	progCtx		= generateShaderSources((int)measurements.size());
		Shaders			shaders		= createShaders();
		BuildInfo		buildInfo;

		if (WARMUP_CPU_BEFORE_EACH_MEASUREMENT)
			tcu::warmupCPU();

		// \note Do NOT do anything too hefty between the first and last deGetMicroseconds() here (other than the gl calls); it would disturb the measurement.

		deUint64 startTime = deGetMicroseconds();

		setShaderSources(shaders, progCtx);
		deUint64 shaderSourceSetEndTime = deGetMicroseconds();

		buildInfo.vertCompileSuccess = compileShader(shaders.vertShader);
		deUint64 vertexShaderCompileEndTime = deGetMicroseconds();

		buildInfo.fragCompileSuccess = compileShader(shaders.fragShader);
		deUint64 fragmentShaderCompileEndTime = deGetMicroseconds();

		buildInfo.logs = getLogs(shaders);

		// Both shader compilations should have failed.
		if (buildInfo.vertCompileSuccess || buildInfo.fragCompileSuccess)
		{
			logProgramData(buildInfo, progCtx);
			cleanup(shaders);
			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compilation of a shader erroneously succeeded");
			return STOP;
		}

		// De-initializations (delete shaders).

		cleanup(shaders);

		// Output measurement log later (after last measurement).

		measurements.push_back(Measurement((deInt64)(shaderSourceSetEndTime			- startTime),
										   (deInt64)(vertexShaderCompileEndTime		- shaderSourceSetEndTime),
										   (deInt64)(fragmentShaderCompileEndTime	- vertexShaderCompileEndTime)));

		latestBuildInfo			= buildInfo;
		latestProgramContext	= progCtx;

		m_testCtx.touchWatchdog(); // \note Measurements may take a while in a bad case.
	}

	// End of test case, log information about measurements.
	{
		TestLog& log = m_testCtx.getLog();

		vector<deInt64> sourceSetTimes;
		vector<deInt64> vertexCompileTimes;
		vector<deInt64> fragmentCompileTimes;
		vector<deInt64> totalTimes;

		log << TestLog::Section("IterationMeasurements", "Iteration measurements of compilation times");

		for (int ndx = 0; ndx < (int)measurements.size(); ndx++)
		{
			sourceSetTimes.push_back		(measurements[ndx].sourceSetTime);
			vertexCompileTimes.push_back	(measurements[ndx].vertexCompileTime);
			fragmentCompileTimes.push_back	(measurements[ndx].fragmentCompileTime);
			totalTimes.push_back			(measurements[ndx].totalTime());

			// Log this measurement.
			log << TestLog::Float("Measurement" + de::toString(ndx) + "Time",
								  "Measurement " + de::toString(ndx) + " time",
								  "ms", QP_KEY_TAG_TIME, measurements[ndx].totalTime()/1000.0f);
		}

		// Log some statistics.

		for (int entireRangeOrLowestHalf = 0; entireRangeOrLowestHalf < 2; entireRangeOrLowestHalf++)
		{
			bool				isEntireRange	= entireRangeOrLowestHalf == 0;
			string				statNamePrefix	= isEntireRange ? "" : "LowestHalf";
			vector<deInt64>		rangeTimes		= isEntireRange ? totalTimes : vectorLowestPercentage(totalTimes, 0.5f);

			log << TestLog::Message << "\nStatistics computed from "
									<< (isEntireRange ? "all" : "only the lowest 50%")
									<< " of the above measurements:"
									<< TestLog::EndMessage;

#define LOG_TIME_STAT(NAME, DESC, FUNC)			log << TestLog::Float(statNamePrefix + "TotalTime" + (NAME), (DESC) + string(" of total time"), "ms",	QP_KEY_TAG_TIME, (FUNC)(rangeTimes)/1000.0f)
#define LOG_RELATIVE_STAT(NAME, DESC, FUNC)		log << TestLog::Float(statNamePrefix + "TotalTime" + (NAME), (DESC) + string(" of total time"), "",		QP_KEY_TAG_NONE, (FUNC)(rangeTimes))

			LOG_TIME_STAT		("Median",							"Median",								vectorFloatMedian);
			LOG_TIME_STAT		("Average",							"Average",								vectorFloatAverage);
			LOG_TIME_STAT		("Minimum",							"Minimum",								vectorFloatMinimum);
			LOG_TIME_STAT		("Maximum",							"Maximum",								vectorFloatMaximum);
			LOG_TIME_STAT		("MedianAbsoluteDeviation",			"Median absolute deviation",			vectorFloatMedianAbsoluteDeviation);
			LOG_RELATIVE_STAT	("RelativeMedianAbsoluteDeviation",	"Relative median absolute deviation",	vectorFloatRelativeMedianAbsoluteDeviation);
			LOG_TIME_STAT		("StandardDeviation",				"Standard deviation",					vectorFloatStandardDeviation);
			LOG_RELATIVE_STAT	("RelativeStandardDeviation",		"Relative standard deviation",			vectorFloatRelativeStandardDeviation);
			LOG_TIME_STAT		("MaxMinusMin",						"Max-min",								vectorFloatMaximumMinusMinimum);
			LOG_RELATIVE_STAT	("RelativeMaxMinusMin",				"Relative max-min",						vectorFloatRelativeMaximumMinusMinimum);

#undef LOG_TIME_STAT
#undef LOG_RELATIVE_STAT

			if (!isEntireRange && vectorFloatRelativeMedianAbsoluteDeviation(rangeTimes) > RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD)
				log << TestLog::Message << "\nWARNING: couldn't achieve relative median absolute deviation under threshold value " << RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD << TestLog::EndMessage;
		}

		log << TestLog::EndSection; // End section IterationMeasurements

		for (int medianOrAverage = 0; medianOrAverage < 2; medianOrAverage++)
		{
			typedef float (*VecFunc)(const vector<deInt64>&);

			bool	isMedian						= medianOrAverage == 0;
			string	singular						= isMedian ? "Median" : "Average";
			string	plural							= singular + "s";
			VecFunc func							= isMedian ? (VecFunc) vectorFloatMedian<deInt64> : (VecFunc) vectorFloatAverage<deInt64>;

			log << TestLog::Section(plural + "PerPhase", plural + " per phase");

			for (int entireRangeOrLowestHalf = 0; entireRangeOrLowestHalf < 2; entireRangeOrLowestHalf++)
			{
				bool	isEntireRange	= entireRangeOrLowestHalf == 0;
				string	statNamePrefix	= isEntireRange ? "" : "LowestHalf";
				float	rangeSizeRatio	= isEntireRange ? 1.0f : 0.5f;

#define LOG_TIME(NAME, DESC, DATA) log << TestLog::Float(statNamePrefix + (NAME) + singular, singular + " of " + (DESC), "ms", QP_KEY_TAG_TIME, func(vectorLowestPercentage((DATA), rangeSizeRatio))/1000.0f);

				log << TestLog::Message << (isEntireRange ? "For all measurements:" : "\nFor only the lowest 50% of the measurements:") << TestLog::EndMessage;
				LOG_TIME("ShaderSourceSetTime",			"shader source set time",			sourceSetTimes);
				LOG_TIME("VertexShaderCompileTime",		"vertex shader compile time",		vertexCompileTimes);
				LOG_TIME("FragmentShaderCompileTime",	"fragment shader compile time",		fragmentCompileTimes);

#undef LOG_TIME
			}

			log << TestLog::EndSection;
		}

		// Set result.

		{
			log << TestLog::Message << "Note: test result is the first quartile (i.e. median of the lowest half of measurements) of total times" << TestLog::EndMessage;
			float result = vectorFloatFirstQuartile(totalTimes) / 1000.0f;
			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString(result, 2).c_str());
		}

		// Log shaders.

		log << TestLog::Message << "Note: the following shaders are the ones from the last iteration; variables' names and some constant expressions differ between iterations." << TestLog::EndMessage;

		logProgramData(latestBuildInfo, latestProgramContext);

		return STOP;
	}
}

InvalidShaderCompilerLightCase::InvalidShaderCompilerLightCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool isVertexCase, int numLights, LightType lightType)
	: InvalidShaderCompilerCase	(context, name, description, caseID, invalidityType)
	, m_isVertexCase			(isVertexCase)
	, m_numLights				(numLights)
	, m_lightType				(lightType)
{
}

InvalidShaderCompilerLightCase::~InvalidShaderCompilerLightCase (void)
{
}

InvalidShaderCompilerCase::ProgramContext InvalidShaderCompilerLightCase::generateShaderSources (int measurementNdx) const
{
	deUint32		specID			= getSpecializationID(measurementNdx);
	ProgramContext	result;
	ShaderValidity	shaderValidity	= m_invalidityType == INVALIDITY_INVALID_CHAR	? SHADER_VALIDITY_INVALID_CHAR
									: m_invalidityType == INVALIDITY_SEMANTIC_ERROR	? SHADER_VALIDITY_SEMANTIC_ERROR
									: SHADER_VALIDITY_LAST;

	DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST);

	result.vertShaderSource = specializeShaderSource(lightVertexTemplate(m_numLights, m_isVertexCase, m_lightType), specID, shaderValidity);
	result.fragShaderSource = specializeShaderSource(lightFragmentTemplate(m_numLights, m_isVertexCase, m_lightType), specID, shaderValidity);

	return result;
}

InvalidShaderCompilerTextureCase::InvalidShaderCompilerTextureCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType)
	: InvalidShaderCompilerCase	(context, name, description, caseID, invalidityType)
	, m_numLookups				(numLookups)
	, m_conditionalUsage		(conditionalUsage)
	, m_conditionalType			(conditionalType)
{
}

InvalidShaderCompilerTextureCase::~InvalidShaderCompilerTextureCase (void)
{
}

InvalidShaderCompilerCase::ProgramContext InvalidShaderCompilerTextureCase::generateShaderSources (int measurementNdx) const
{
	deUint32		specID			= getSpecializationID(measurementNdx);
	ProgramContext	result;
	ShaderValidity	shaderValidity	= m_invalidityType == INVALIDITY_INVALID_CHAR	? SHADER_VALIDITY_INVALID_CHAR
									: m_invalidityType == INVALIDITY_SEMANTIC_ERROR	? SHADER_VALIDITY_SEMANTIC_ERROR
									: SHADER_VALIDITY_LAST;

	DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST);

	result.vertShaderSource = specializeShaderSource(textureLookupVertexTemplate(m_conditionalUsage, m_conditionalType), specID, shaderValidity);
	result.fragShaderSource = specializeShaderSource(textureLookupFragmentTemplate(m_numLookups, m_conditionalUsage, m_conditionalType), specID, shaderValidity);

	return result;
}

InvalidShaderCompilerLoopCase::InvalidShaderCompilerLoopCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool isVertexCase, LoopType type, int numLoopIterations, int nestingDepth)
	: InvalidShaderCompilerCase	(context, name, description, caseID, invalidityType)
	, m_isVertexCase			(isVertexCase)
	, m_numLoopIterations		(numLoopIterations)
	, m_nestingDepth			(nestingDepth)
	, m_type					(type)
{
}

InvalidShaderCompilerLoopCase::~InvalidShaderCompilerLoopCase (void)
{
}

InvalidShaderCompilerCase::ProgramContext InvalidShaderCompilerLoopCase::generateShaderSources (int measurementNdx) const
{
	deUint32		specID			= getSpecializationID(measurementNdx);
	ProgramContext	result;
	ShaderValidity	shaderValidity	= m_invalidityType == INVALIDITY_INVALID_CHAR	? SHADER_VALIDITY_INVALID_CHAR
									: m_invalidityType == INVALIDITY_SEMANTIC_ERROR	? SHADER_VALIDITY_SEMANTIC_ERROR
									: SHADER_VALIDITY_LAST;

	DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST);

	result.vertShaderSource = specializeShaderSource(loopVertexTemplate(m_type, m_isVertexCase, m_numLoopIterations, m_nestingDepth), specID, shaderValidity);
	result.fragShaderSource = specializeShaderSource(loopFragmentTemplate(m_type, m_isVertexCase, m_numLoopIterations, m_nestingDepth), specID, shaderValidity);

	return result;
}

InvalidShaderCompilerOperCase::InvalidShaderCompilerOperCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool isVertexCase, const char* oper, int numOperations)
	: InvalidShaderCompilerCase	(context, name, description, caseID, invalidityType)
	, m_isVertexCase			(isVertexCase)
	, m_oper					(oper)
	, m_numOperations			(numOperations)
{
}

InvalidShaderCompilerOperCase::~InvalidShaderCompilerOperCase (void)
{
}

InvalidShaderCompilerCase::ProgramContext InvalidShaderCompilerOperCase::generateShaderSources (int measurementNdx) const
{
	deUint32		specID			= getSpecializationID(measurementNdx);
	ProgramContext	result;
	ShaderValidity	shaderValidity	= m_invalidityType == INVALIDITY_INVALID_CHAR	? SHADER_VALIDITY_INVALID_CHAR
									: m_invalidityType == INVALIDITY_SEMANTIC_ERROR	? SHADER_VALIDITY_SEMANTIC_ERROR
									: SHADER_VALIDITY_LAST;

	DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST);

	if (m_isVertexCase)
	{
		result.vertShaderSource = specializeShaderSource(binaryOpVertexTemplate(m_numOperations, m_oper.c_str()), specID, shaderValidity);
		result.fragShaderSource = specializeShaderSource(singleVaryingFragmentTemplate(), specID, shaderValidity);
	}
	else
	{
		result.vertShaderSource = specializeShaderSource(singleVaryingVertexTemplate(), specID, shaderValidity);
		result.fragShaderSource = specializeShaderSource(binaryOpFragmentTemplate(m_numOperations, m_oper.c_str()), specID, shaderValidity);
	}

	return result;
}

InvalidShaderCompilerMandelbrotCase::InvalidShaderCompilerMandelbrotCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, int numFractalIterations)
	: InvalidShaderCompilerCase	(context, name, description, caseID, invalidityType)
	, m_numFractalIterations	(numFractalIterations)
{
}

InvalidShaderCompilerMandelbrotCase::~InvalidShaderCompilerMandelbrotCase (void)
{
}

InvalidShaderCompilerCase::ProgramContext InvalidShaderCompilerMandelbrotCase::generateShaderSources (int measurementNdx) const
{
	deUint32		specID			= getSpecializationID(measurementNdx);
	ProgramContext	result;
	ShaderValidity	shaderValidity	= m_invalidityType == INVALIDITY_INVALID_CHAR	? SHADER_VALIDITY_INVALID_CHAR
									: m_invalidityType == INVALIDITY_SEMANTIC_ERROR	? SHADER_VALIDITY_SEMANTIC_ERROR
									: SHADER_VALIDITY_LAST;

	DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST);

	result.vertShaderSource = specializeShaderSource(mandelbrotVertexTemplate(), specID, shaderValidity);
	result.fragShaderSource = specializeShaderSource(mandelbrotFragmentTemplate(m_numFractalIterations), specID, shaderValidity);

	return result;
}

void addShaderCompilationPerformanceCases (TestCaseGroup& parentGroup)
{
	Context&	context		= parentGroup.getContext();
	int			caseID		= 0; // Increment this after adding each case. Used for avoiding cache hits between cases.

	TestCaseGroup* validGroup			= new TestCaseGroup(context, "valid_shader",	"Valid Shader Compiler Cases");
	TestCaseGroup* invalidGroup			= new TestCaseGroup(context, "invalid_shader",	"Invalid Shader Compiler Cases");
	TestCaseGroup* cacheGroup			= new TestCaseGroup(context, "cache",			"Allow shader caching");
	parentGroup.addChild(validGroup);
	parentGroup.addChild(invalidGroup);
	parentGroup.addChild(cacheGroup);

	TestCaseGroup* invalidCharGroup		= new TestCaseGroup(context, "invalid_char",	"Invalid Character Shader Compiler Cases");
	TestCaseGroup* semanticErrorGroup	= new TestCaseGroup(context, "semantic_error",	"Semantic Error Shader Compiler Cases");
	invalidGroup->addChild(invalidCharGroup);
	invalidGroup->addChild(semanticErrorGroup);

	// Lighting shader compilation cases.

	{
		static const int lightCounts[] = { 1, 2, 4, 8 };

		TestCaseGroup* validLightingGroup			= new TestCaseGroup(context, "lighting", "Shader Compiler Lighting Cases");
		TestCaseGroup* invalidCharLightingGroup		= new TestCaseGroup(context, "lighting", "Invalid Character Shader Compiler Lighting Cases");
		TestCaseGroup* semanticErrorLightingGroup	= new TestCaseGroup(context, "lighting", "Semantic Error Shader Compiler Lighting Cases");
		TestCaseGroup* cacheLightingGroup			= new TestCaseGroup(context, "lighting", "Shader Compiler Lighting Cache Cases");
		validGroup->addChild(validLightingGroup);
		invalidCharGroup->addChild(invalidCharLightingGroup);
		semanticErrorGroup->addChild(semanticErrorLightingGroup);
		cacheGroup->addChild(cacheLightingGroup);

		for (int lightType = 0; lightType < (int)LIGHT_LAST; lightType++)
		{
			const char* lightTypeName = lightType == (int)LIGHT_DIRECTIONAL	? "directional"
									  : lightType == (int)LIGHT_POINT		? "point"
									  : DE_NULL;

			DE_ASSERT(lightTypeName != DE_NULL);

			for (int isFrag = 0; isFrag <= 1; isFrag++)
			{
				bool		isVertex	= isFrag == 0;
				const char*	vertFragStr	= isVertex ? "vertex" : "fragment";

				for (int lightCountNdx = 0; lightCountNdx < DE_LENGTH_OF_ARRAY(lightCounts); lightCountNdx++)
				{
					int numLights = lightCounts[lightCountNdx];

					string caseName = string("") + lightTypeName + "_" + de::toString(numLights) + "_lights_" + vertFragStr;

					// Valid shader case, no-cache and cache versions.

					validLightingGroup->addChild(new ShaderCompilerLightCase(context, caseName.c_str(), "", caseID++, true  /* avoid cache */, false, isVertex, numLights, (LightType)lightType));
					cacheLightingGroup->addChild(new ShaderCompilerLightCase(context, caseName.c_str(), "", caseID++, false /* allow cache */, false, isVertex, numLights, (LightType)lightType));

					// Invalid shader cases.

					for (int invalidityType = 0; invalidityType < (int)InvalidShaderCompilerCase::INVALIDITY_LAST; invalidityType++)
					{
						TestCaseGroup* curInvalidGroup	= invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_INVALID_CHAR		? invalidCharLightingGroup
														: invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_SEMANTIC_ERROR	? semanticErrorLightingGroup
														: DE_NULL;

						DE_ASSERT(curInvalidGroup != DE_NULL);

						curInvalidGroup->addChild(new InvalidShaderCompilerLightCase(context, caseName.c_str(), "", caseID++, (InvalidShaderCompilerCase::InvalidityType)invalidityType, isVertex, numLights, (LightType)lightType));
					}
				}
			}
		}
	}

	// Texture lookup shader compilation cases.

	{
		static const int texLookupCounts[] = { 1, 2, 4, 8 };

		TestCaseGroup* validTexGroup			= new TestCaseGroup(context, "texture", "Shader Compiler Texture Lookup Cases");
		TestCaseGroup* invalidCharTexGroup		= new TestCaseGroup(context, "texture", "Invalid Character Shader Compiler Texture Lookup Cases");
		TestCaseGroup* semanticErrorTexGroup	= new TestCaseGroup(context, "texture", "Semantic Error Shader Compiler Texture Lookup Cases");
		TestCaseGroup* cacheTexGroup			= new TestCaseGroup(context, "texture", "Shader Compiler Texture Lookup Cache Cases");
		validGroup->addChild(validTexGroup);
		invalidCharGroup->addChild(invalidCharTexGroup);
		semanticErrorGroup->addChild(semanticErrorTexGroup);
		cacheGroup->addChild(cacheTexGroup);

		for (int conditionalUsage = 0; conditionalUsage < (int)CONDITIONAL_USAGE_LAST; conditionalUsage++)
		{
			const char* conditionalUsageName = conditionalUsage == (int)CONDITIONAL_USAGE_NONE			? "no_conditionals"
											 : conditionalUsage == (int)CONDITIONAL_USAGE_FIRST_HALF	? "first_half"
											 : conditionalUsage == (int)CONDITIONAL_USAGE_EVERY_OTHER	? "every_other"
											 : DE_NULL;

			DE_ASSERT(conditionalUsageName != DE_NULL);

			int lastConditionalType = conditionalUsage == (int)CONDITIONAL_USAGE_NONE ? 1 : (int)CONDITIONAL_TYPE_LAST;

			for (int conditionalType = 0; conditionalType < lastConditionalType; conditionalType++)
			{
				const char* conditionalTypeName = conditionalType == (int)CONDITIONAL_TYPE_STATIC	? "static_conditionals"
												: conditionalType == (int)CONDITIONAL_TYPE_UNIFORM	? "uniform_conditionals"
												: conditionalType == (int)CONDITIONAL_TYPE_DYNAMIC	? "dynamic_conditionals"
												: DE_NULL;

				DE_ASSERT(conditionalTypeName != DE_NULL);

				for (int lookupCountNdx = 0; lookupCountNdx < DE_LENGTH_OF_ARRAY(texLookupCounts); lookupCountNdx++)
				{
					int numLookups = texLookupCounts[lookupCountNdx];

					string caseName = de::toString(numLookups) + "_lookups_" + conditionalUsageName + (conditionalUsage == (int)CONDITIONAL_USAGE_NONE ? "" : string("_") + conditionalTypeName);

					// Valid shader case, no-cache and cache versions.

					validTexGroup->addChild(new ShaderCompilerTextureCase(context, caseName.c_str(), "", caseID++, true  /* avoid cache */, false, numLookups, (ConditionalUsage)conditionalUsage, (ConditionalType)conditionalType));
					cacheTexGroup->addChild(new ShaderCompilerTextureCase(context, caseName.c_str(), "", caseID++, false /* allow cache */, false, numLookups, (ConditionalUsage)conditionalUsage, (ConditionalType)conditionalType));

					// Invalid shader cases.

					for (int invalidityType = 0; invalidityType < (int)InvalidShaderCompilerCase::INVALIDITY_LAST; invalidityType++)
					{
						TestCaseGroup* curInvalidGroup	= invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_INVALID_CHAR		? invalidCharTexGroup
														: invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_SEMANTIC_ERROR	? semanticErrorTexGroup
														: DE_NULL;

						DE_ASSERT(curInvalidGroup != DE_NULL);

						curInvalidGroup->addChild(new InvalidShaderCompilerTextureCase(context, caseName.c_str(), "", caseID++, (InvalidShaderCompilerCase::InvalidityType)invalidityType, numLookups, (ConditionalUsage)conditionalUsage, (ConditionalType)conditionalType));
					}
				}
			}
		}
	}

	// Loop shader compilation cases.

	{
		static const int loopIterCounts[]		= { 10, 100, 1000 };
		static const int maxLoopNestingDepth	= 3;
		static const int maxTotalLoopIterations	= 2000; // If <loop iteration count> ** <loop nesting depth> (where ** is exponentiation) exceeds this, don't generate the case.

		TestCaseGroup* validLoopGroup			= new TestCaseGroup(context, "loop", "Shader Compiler Loop Cases");
		TestCaseGroup* invalidCharLoopGroup		= new TestCaseGroup(context, "loop", "Invalid Character Shader Compiler Loop Cases");
		TestCaseGroup* semanticErrorLoopGroup	= new TestCaseGroup(context, "loop", "Semantic Error Shader Compiler Loop Cases");
		TestCaseGroup* cacheLoopGroup			= new TestCaseGroup(context, "loop", "Shader Compiler Loop Cache Cases");
		validGroup->addChild(validLoopGroup);
		invalidCharGroup->addChild(invalidCharLoopGroup);
		semanticErrorGroup->addChild(semanticErrorLoopGroup);
		cacheGroup->addChild(cacheLoopGroup);

		for (int loopType = 0; loopType < (int)LOOP_LAST; loopType++)
		{
			const char* loopTypeName = loopType == (int)LOOP_TYPE_STATIC	? "static"
									 : loopType == (int)LOOP_TYPE_UNIFORM	? "uniform"
									 : loopType == (int)LOOP_TYPE_DYNAMIC	? "dynamic"
									 : DE_NULL;

			DE_ASSERT(loopTypeName != DE_NULL);

			TestCaseGroup* validLoopTypeGroup			= new TestCaseGroup(context, loopTypeName, "");
			TestCaseGroup* invalidCharLoopTypeGroup		= new TestCaseGroup(context, loopTypeName, "");
			TestCaseGroup* semanticErrorLoopTypeGroup	= new TestCaseGroup(context, loopTypeName, "");
			TestCaseGroup* cacheLoopTypeGroup			= new TestCaseGroup(context, loopTypeName, "");
			validLoopGroup->addChild(validLoopTypeGroup);
			invalidCharLoopGroup->addChild(invalidCharLoopTypeGroup);
			semanticErrorLoopGroup->addChild(semanticErrorLoopTypeGroup);
			cacheLoopGroup->addChild(cacheLoopTypeGroup);

			for (int isFrag = 0; isFrag <= 1; isFrag++)
			{
				bool		isVertex	= isFrag == 0;
				const char*	vertFragStr	= isVertex ? "vertex" : "fragment";

				// \note Non-static loop cases with different iteration counts have identical shaders, so only make one of each.
				int loopIterCountMaxNdx = loopType != (int)LOOP_TYPE_STATIC ? 1 : DE_LENGTH_OF_ARRAY(loopIterCounts);

				for (int nestingDepth = 1; nestingDepth <= maxLoopNestingDepth; nestingDepth++)
				{
					for (int loopIterCountNdx = 0; loopIterCountNdx < loopIterCountMaxNdx; loopIterCountNdx++)
					{
						int numIterations = loopIterCounts[loopIterCountNdx];

						if (deFloatPow((float)numIterations, (float)nestingDepth) > (float)maxTotalLoopIterations)
							continue; // Don't generate too heavy tasks.

						string validCaseName = de::toString(numIterations) + "_iterations_" + de::toString(nestingDepth) + "_levels_" + vertFragStr;

						// Valid shader case, no-cache and cache versions.

						validLoopTypeGroup->addChild(new ShaderCompilerLoopCase(context, validCaseName.c_str(), "", caseID++, true  /* avoid cache */, false, isVertex, (LoopType)loopType, numIterations, nestingDepth));
						cacheLoopTypeGroup->addChild(new ShaderCompilerLoopCase(context, validCaseName.c_str(), "", caseID++, false /* allow cache */, false, isVertex, (LoopType)loopType, numIterations, nestingDepth));

						// Invalid shader cases.

						for (int invalidityType = 0; invalidityType < (int)InvalidShaderCompilerCase::INVALIDITY_LAST; invalidityType++)
						{
							TestCaseGroup* curInvalidGroup	= invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_INVALID_CHAR		? invalidCharLoopTypeGroup
															: invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_SEMANTIC_ERROR	? semanticErrorLoopTypeGroup
															: DE_NULL;

							DE_ASSERT(curInvalidGroup != DE_NULL);

							string invalidCaseName = de::toString(nestingDepth) + "_levels_" + vertFragStr;

							if (loopType == (int)LOOP_TYPE_STATIC)
								invalidCaseName = de::toString(numIterations) + "_iterations_" + invalidCaseName; // \note For invalid, non-static loop cases the iteration count means nothing (since no uniforms or attributes are set).

							curInvalidGroup->addChild(new InvalidShaderCompilerLoopCase(context, invalidCaseName.c_str(), "", caseID++, (InvalidShaderCompilerCase::InvalidityType)invalidityType, isVertex, (LoopType)loopType, numIterations, nestingDepth));
						}
					}
				}
			}
		}
	}

	// Multiplication shader compilation cases.

	{
		static const int multiplicationCounts[] = { 10, 100, 1000 };

		TestCaseGroup* validMulGroup			= new TestCaseGroup(context, "multiplication", "Shader Compiler Multiplication Cases");
		TestCaseGroup* invalidCharMulGroup		= new TestCaseGroup(context, "multiplication", "Invalid Character Shader Compiler Multiplication Cases");
		TestCaseGroup* semanticErrorMulGroup	= new TestCaseGroup(context, "multiplication", "Semantic Error Shader Compiler Multiplication Cases");
		TestCaseGroup* cacheMulGroup			= new TestCaseGroup(context, "multiplication", "Shader Compiler Multiplication Cache Cases");
		validGroup->addChild(validMulGroup);
		invalidCharGroup->addChild(invalidCharMulGroup);
		semanticErrorGroup->addChild(semanticErrorMulGroup);
		cacheGroup->addChild(cacheMulGroup);

		for (int isFrag = 0; isFrag <= 1; isFrag++)
		{
			bool		isVertex	= isFrag == 0;
			const char*	vertFragStr	= isVertex ? "vertex" : "fragment";

			for (int operCountNdx = 0; operCountNdx < DE_LENGTH_OF_ARRAY(multiplicationCounts); operCountNdx++)
			{
				int numOpers = multiplicationCounts[operCountNdx];

				string caseName = de::toString(numOpers) + "_operations_" + vertFragStr;

				// Valid shader case, no-cache and cache versions.

				validMulGroup->addChild(new ShaderCompilerOperCase(context, caseName.c_str(), "", caseID++, true  /* avoid cache */, false, isVertex, "*", numOpers));
				cacheMulGroup->addChild(new ShaderCompilerOperCase(context, caseName.c_str(), "", caseID++, false /* allow cache */, false, isVertex, "*", numOpers));

				// Invalid shader cases.

				for (int invalidityType = 0; invalidityType < (int)InvalidShaderCompilerCase::INVALIDITY_LAST; invalidityType++)
				{
					TestCaseGroup* curInvalidGroup	= invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_INVALID_CHAR		? invalidCharMulGroup
													: invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_SEMANTIC_ERROR	? semanticErrorMulGroup
													: DE_NULL;

					DE_ASSERT(curInvalidGroup != DE_NULL);

					curInvalidGroup->addChild(new InvalidShaderCompilerOperCase(context, caseName.c_str(), "", caseID++, (InvalidShaderCompilerCase::InvalidityType)invalidityType, isVertex, "*", numOpers));
				}
			}
		}
	}

	// Mandelbrot shader compilation cases.

	{
		static const int mandelbrotIterationCounts[] = { 32, 64, 128 };

		TestCaseGroup* validMandelbrotGroup			= new TestCaseGroup(context, "mandelbrot", "Shader Compiler Mandelbrot Fractal Cases");
		TestCaseGroup* invalidCharMandelbrotGroup	= new TestCaseGroup(context, "mandelbrot", "Invalid Character Shader Compiler Mandelbrot Fractal Cases");
		TestCaseGroup* semanticErrorMandelbrotGroup	= new TestCaseGroup(context, "mandelbrot", "Semantic Error Shader Compiler Mandelbrot Fractal Cases");
		TestCaseGroup* cacheMandelbrotGroup			= new TestCaseGroup(context, "mandelbrot", "Shader Compiler Mandelbrot Fractal Cache Cases");
		validGroup->addChild(validMandelbrotGroup);
		invalidCharGroup->addChild(invalidCharMandelbrotGroup);
		semanticErrorGroup->addChild(semanticErrorMandelbrotGroup);
		cacheGroup->addChild(cacheMandelbrotGroup);

		for (int iterCountNdx = 0; iterCountNdx < DE_LENGTH_OF_ARRAY(mandelbrotIterationCounts); iterCountNdx++)
		{
			int		numFractalIterations	= mandelbrotIterationCounts[iterCountNdx];
			string	caseName				= de::toString(numFractalIterations) + "_iterations";

			// Valid shader case, no-cache and cache versions.

			validMandelbrotGroup->addChild(new ShaderCompilerMandelbrotCase(context, caseName.c_str(), "", caseID++, true  /* avoid cache */, false, numFractalIterations));
			cacheMandelbrotGroup->addChild(new ShaderCompilerMandelbrotCase(context, caseName.c_str(), "", caseID++, false /* allow cache */, false, numFractalIterations));

			// Invalid shader cases.

			for (int invalidityType = 0; invalidityType < (int)InvalidShaderCompilerCase::INVALIDITY_LAST; invalidityType++)
			{
				TestCaseGroup* curInvalidGroup	= invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_INVALID_CHAR		? invalidCharMandelbrotGroup
												: invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_SEMANTIC_ERROR	? semanticErrorMandelbrotGroup
												: DE_NULL;

				DE_ASSERT(curInvalidGroup != DE_NULL);

				curInvalidGroup->addChild(new InvalidShaderCompilerMandelbrotCase(context, caseName.c_str(), "", caseID++, (InvalidShaderCompilerCase::InvalidityType)invalidityType, numFractalIterations));
			}
		}
	}

	// Cases testing cache behaviour when whitespace and comments are added.

	{
		TestCaseGroup* whitespaceCommentCacheGroup = new TestCaseGroup(context, "cache_whitespace_comment", "Cases testing the effect of whitespace and comments on caching");
		parentGroup.addChild(whitespaceCommentCacheGroup);

		// \note Add just a small subset of the cases that were added above for the main performance tests.

		// Cases with both vertex and fragment variants.
		for (int isFrag = 0; isFrag <= 1; isFrag++)
		{
			bool	isVertex		= isFrag == 0;
			string	vtxFragSuffix	= isVertex ? "_vertex" : "_fragment";
			string	dirLightName	= "directional_2_lights" + vtxFragSuffix;
			string	loopName		= "static_loop_100_iterations" + vtxFragSuffix;
			string	multCase		= "multiplication_100_operations" + vtxFragSuffix;

			whitespaceCommentCacheGroup->addChild(new ShaderCompilerLightCase(context, dirLightName.c_str(), "", caseID++, false, true, isVertex, 2, LIGHT_DIRECTIONAL));
			whitespaceCommentCacheGroup->addChild(new ShaderCompilerLoopCase(context, loopName.c_str(), "", caseID++, false, true, isVertex, LOOP_TYPE_STATIC, 100, 1));
			whitespaceCommentCacheGroup->addChild(new ShaderCompilerOperCase(context, multCase.c_str(), "", caseID++, false, true, isVertex, "*", 100));
		}

		// Cases that don't have vertex and fragment variants.
		whitespaceCommentCacheGroup->addChild(new ShaderCompilerTextureCase(context, "texture_4_lookups", "", caseID++, false, true, 4, CONDITIONAL_USAGE_NONE, CONDITIONAL_TYPE_STATIC));
		whitespaceCommentCacheGroup->addChild(new ShaderCompilerMandelbrotCase(context, "mandelbrot_32_operations", "", caseID++, false, true, 32));
	}
}

} // Performance
} // gles3
} // deqp