C++程序  |  1953行  |  65.68 KB

/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL (ES) Module
 * -----------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Uniform block case.
 *//*--------------------------------------------------------------------*/

#include "glsUniformBlockCase.hpp"
#include "gluRenderContext.hpp"
#include "gluShaderProgram.hpp"
#include "gluPixelTransfer.hpp"
#include "gluContextInfo.hpp"
#include "gluRenderContext.hpp"
#include "gluDrawUtil.hpp"
#include "glwFunctions.hpp"
#include "glwEnums.hpp"
#include "tcuTestLog.hpp"
#include "tcuSurface.hpp"
#include "tcuRenderTarget.hpp"
#include "deRandom.hpp"
#include "deStringUtil.hpp"
#include "deMemory.h"
#include "deString.h"

#include <algorithm>
#include <map>

using tcu::TestLog;
using std::string;
using std::vector;
using std::map;

namespace deqp
{
namespace gls
{
namespace ub
{

static bool isSupportedGLSLVersion (glu::GLSLVersion version)
{
	return version >= (glslVersionIsES(version) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330);
}

struct PrecisionFlagsFmt
{
	deUint32 flags;
	PrecisionFlagsFmt (deUint32 flags_) : flags(flags_) {}
};

std::ostream& operator<< (std::ostream& str, const PrecisionFlagsFmt& fmt)
{
	// Precision.
	DE_ASSERT(dePop32(fmt.flags & (PRECISION_LOW|PRECISION_MEDIUM|PRECISION_HIGH)) <= 1);
	str << (fmt.flags & PRECISION_LOW		? "lowp"	:
			fmt.flags & PRECISION_MEDIUM	? "mediump"	:
			fmt.flags & PRECISION_HIGH		? "highp"	: "");
	return str;
}

struct LayoutFlagsFmt
{
	deUint32 flags;
	LayoutFlagsFmt (deUint32 flags_) : flags(flags_) {}
};

std::ostream& operator<< (std::ostream& str, const LayoutFlagsFmt& fmt)
{
	static const struct
	{
		deUint32	bit;
		const char*	token;
	} bitDesc[] =
	{
		{ LAYOUT_SHARED,		"shared"		},
		{ LAYOUT_PACKED,		"packed"		},
		{ LAYOUT_STD140,		"std140"		},
		{ LAYOUT_ROW_MAJOR,		"row_major"		},
		{ LAYOUT_COLUMN_MAJOR,	"column_major"	}
	};

	deUint32 remBits = fmt.flags;
	for (int descNdx = 0; descNdx < DE_LENGTH_OF_ARRAY(bitDesc); descNdx++)
	{
		if (remBits & bitDesc[descNdx].bit)
		{
			if (remBits != fmt.flags)
				str << ", ";
			str << bitDesc[descNdx].token;
			remBits &= ~bitDesc[descNdx].bit;
		}
	}
	DE_ASSERT(remBits == 0);
	return str;
}

// VarType implementation.

VarType::VarType (void)
	: m_type	(TYPE_LAST)
	, m_flags	(0)
{
}

VarType::VarType (const VarType& other)
	: m_type	(TYPE_LAST)
	, m_flags	(0)
{
	*this = other;
}

VarType::VarType (glu::DataType basicType, deUint32 flags)
	: m_type	(TYPE_BASIC)
	, m_flags	(flags)
{
	m_data.basicType = basicType;
}

VarType::VarType (const VarType& elementType, int arraySize)
	: m_type	(TYPE_ARRAY)
	, m_flags	(0)
{
	m_data.array.size			= arraySize;
	m_data.array.elementType	= new VarType(elementType);
}

VarType::VarType (const StructType* structPtr)
	: m_type	(TYPE_STRUCT)
	, m_flags	(0)
{
	m_data.structPtr = structPtr;
}

VarType::~VarType (void)
{
	if (m_type == TYPE_ARRAY)
		delete m_data.array.elementType;
}

VarType& VarType::operator= (const VarType& other)
{
	if (this == &other)
		return *this; // Self-assignment.

	if (m_type == TYPE_ARRAY)
		delete m_data.array.elementType;

	m_type	= other.m_type;
	m_flags	= other.m_flags;
	m_data	= Data();

	if (m_type == TYPE_ARRAY)
	{
		m_data.array.elementType	= new VarType(*other.m_data.array.elementType);
		m_data.array.size			= other.m_data.array.size;
	}
	else
		m_data = other.m_data;

	return *this;
}

// StructType implementation.

void StructType::addMember (const char* name, const VarType& type, deUint32 flags)
{
	m_members.push_back(StructMember(name, type, flags));
}

// Uniform implementation.

Uniform::Uniform (const char* name, const VarType& type, deUint32 flags)
	: m_name	(name)
	, m_type	(type)
	, m_flags	(flags)
{
}

// UniformBlock implementation.

UniformBlock::UniformBlock (const char* blockName)
	: m_blockName	(blockName)
	, m_arraySize	(0)
	, m_flags		(0)
{
}

struct BlockLayoutEntry
{
	BlockLayoutEntry (void)
		: size(0)
	{
	}

	std::string			name;
	int					size;
	std::vector<int>	activeUniformIndices;
};

std::ostream& operator<< (std::ostream& stream, const BlockLayoutEntry& entry)
{
	stream << entry.name << " { name = " << entry.name
		   << ", size = " << entry.size
		   << ", activeUniformIndices = [";

	for (vector<int>::const_iterator i = entry.activeUniformIndices.begin(); i != entry.activeUniformIndices.end(); i++)
	{
		if (i != entry.activeUniformIndices.begin())
			stream << ", ";
		stream << *i;
	}

	stream << "] }";
	return stream;
}

struct UniformLayoutEntry
{
	UniformLayoutEntry (void)
		: type			(glu::TYPE_LAST)
		, size			(0)
		, blockNdx		(-1)
		, offset		(-1)
		, arrayStride	(-1)
		, matrixStride	(-1)
		, isRowMajor	(false)
	{
	}

	std::string			name;
	glu::DataType		type;
	int					size;
	int					blockNdx;
	int					offset;
	int					arrayStride;
	int					matrixStride;
	bool				isRowMajor;
};

std::ostream& operator<< (std::ostream& stream, const UniformLayoutEntry& entry)
{
	stream << entry.name << " { type = " << glu::getDataTypeName(entry.type)
		   << ", size = " << entry.size
		   << ", blockNdx = " << entry.blockNdx
		   << ", offset = " << entry.offset
		   << ", arrayStride = " << entry.arrayStride
		   << ", matrixStride = " << entry.matrixStride
		   << ", isRowMajor = " << (entry.isRowMajor ? "true" : "false")
		   << " }";
	return stream;
}

class UniformLayout
{
public:
	std::vector<BlockLayoutEntry>		blocks;
	std::vector<UniformLayoutEntry>		uniforms;

	int									getUniformIndex			(const char* name) const;
	int									getBlockIndex			(const char* name) const;
};

// \todo [2012-01-24 pyry] Speed up lookups using hash.

int UniformLayout::getUniformIndex (const char* name) const
{
	for (int ndx = 0; ndx < (int)uniforms.size(); ndx++)
	{
		if (uniforms[ndx].name == name)
			return ndx;
	}
	return -1;
}

int UniformLayout::getBlockIndex (const char* name) const
{
	for (int ndx = 0; ndx < (int)blocks.size(); ndx++)
	{
		if (blocks[ndx].name == name)
			return ndx;
	}
	return -1;
}

// ShaderInterface implementation.

ShaderInterface::ShaderInterface (void)
{
}

ShaderInterface::~ShaderInterface (void)
{
	for (std::vector<StructType*>::iterator i = m_structs.begin(); i != m_structs.end(); i++)
		delete *i;

	for (std::vector<UniformBlock*>::iterator i = m_uniformBlocks.begin(); i != m_uniformBlocks.end(); i++)
		delete *i;
}

StructType& ShaderInterface::allocStruct (const char* name)
{
	m_structs.reserve(m_structs.size()+1);
	m_structs.push_back(new StructType(name));
	return *m_structs.back();
}

struct StructNameEquals
{
	std::string name;

	StructNameEquals (const char* name_) : name(name_) {}

	bool operator() (const StructType* type) const
	{
		return type->getTypeName() && name == type->getTypeName();
	}
};

const StructType* ShaderInterface::findStruct (const char* name) const
{
	std::vector<StructType*>::const_iterator pos = std::find_if(m_structs.begin(), m_structs.end(), StructNameEquals(name));
	return pos != m_structs.end() ? *pos : DE_NULL;
}

void ShaderInterface::getNamedStructs (std::vector<const StructType*>& structs) const
{
	for (std::vector<StructType*>::const_iterator i = m_structs.begin(); i != m_structs.end(); i++)
	{
		if ((*i)->getTypeName() != DE_NULL)
			structs.push_back(*i);
	}
}

UniformBlock& ShaderInterface::allocBlock (const char* name)
{
	m_uniformBlocks.reserve(m_uniformBlocks.size()+1);
	m_uniformBlocks.push_back(new UniformBlock(name));
	return *m_uniformBlocks.back();
}

namespace // Utilities
{

// Layout computation.

int getDataTypeByteSize (glu::DataType type)
{
	return glu::getDataTypeScalarSize(type)*(int)sizeof(deUint32);
}

int getDataTypeByteAlignment (glu::DataType type)
{
	switch (type)
	{
		case glu::TYPE_FLOAT:
		case glu::TYPE_INT:
		case glu::TYPE_UINT:
		case glu::TYPE_BOOL:		return 1*(int)sizeof(deUint32);

		case glu::TYPE_FLOAT_VEC2:
		case glu::TYPE_INT_VEC2:
		case glu::TYPE_UINT_VEC2:
		case glu::TYPE_BOOL_VEC2:	return 2*(int)sizeof(deUint32);

		case glu::TYPE_FLOAT_VEC3:
		case glu::TYPE_INT_VEC3:
		case glu::TYPE_UINT_VEC3:
		case glu::TYPE_BOOL_VEC3:	// Fall-through to vec4

		case glu::TYPE_FLOAT_VEC4:
		case glu::TYPE_INT_VEC4:
		case glu::TYPE_UINT_VEC4:
		case glu::TYPE_BOOL_VEC4:	return 4*(int)sizeof(deUint32);

		default:
			DE_ASSERT(false);
			return 0;
	}
}

int getDataTypeArrayStride (glu::DataType type)
{
	DE_ASSERT(!glu::isDataTypeMatrix(type));

	const int baseStride	= getDataTypeByteSize(type);
	const int vec4Alignment	= (int)sizeof(deUint32)*4;

	DE_ASSERT(baseStride <= vec4Alignment);
	return de::max(baseStride, vec4Alignment); // Really? See rule 4.
}

static inline int deRoundUp32 (int a, int b)
{
	int d = a/b;
	return d*b == a ? a : (d+1)*b;
}

int computeStd140BaseAlignment (const VarType& type)
{
	const int vec4Alignment = (int)sizeof(deUint32)*4;

	if (type.isBasicType())
	{
		glu::DataType basicType = type.getBasicType();

		if (glu::isDataTypeMatrix(basicType))
		{
			bool	isRowMajor	= !!(type.getFlags() & LAYOUT_ROW_MAJOR);
			int		vecSize		= isRowMajor ? glu::getDataTypeMatrixNumColumns(basicType)
											 : glu::getDataTypeMatrixNumRows(basicType);

			return getDataTypeArrayStride(glu::getDataTypeFloatVec(vecSize));
		}
		else
			return getDataTypeByteAlignment(basicType);
	}
	else if (type.isArrayType())
	{
		int elemAlignment = computeStd140BaseAlignment(type.getElementType());

		// Round up to alignment of vec4
		return deRoundUp32(elemAlignment, vec4Alignment);
	}
	else
	{
		DE_ASSERT(type.isStructType());

		int maxBaseAlignment = 0;

		for (StructType::ConstIterator memberIter = type.getStruct().begin(); memberIter != type.getStruct().end(); memberIter++)
			maxBaseAlignment = de::max(maxBaseAlignment, computeStd140BaseAlignment(memberIter->getType()));

		return deRoundUp32(maxBaseAlignment, vec4Alignment);
	}
}

inline deUint32 mergeLayoutFlags (deUint32 prevFlags, deUint32 newFlags)
{
	const deUint32	packingMask		= LAYOUT_PACKED|LAYOUT_SHARED|LAYOUT_STD140;
	const deUint32	matrixMask		= LAYOUT_ROW_MAJOR|LAYOUT_COLUMN_MAJOR;

	deUint32 mergedFlags = 0;

	mergedFlags |= ((newFlags & packingMask)	? newFlags : prevFlags) & packingMask;
	mergedFlags |= ((newFlags & matrixMask)		? newFlags : prevFlags) & matrixMask;

	return mergedFlags;
}

void computeStd140Layout (UniformLayout& layout, int& curOffset, int curBlockNdx, const std::string& curPrefix, const VarType& type, deUint32 layoutFlags)
{
	int baseAlignment = computeStd140BaseAlignment(type);

	curOffset = deAlign32(curOffset, baseAlignment);

	if (type.isBasicType())
	{
		glu::DataType		basicType	= type.getBasicType();
		UniformLayoutEntry	entry;

		entry.name			= curPrefix;
		entry.type			= basicType;
		entry.size			= 1;
		entry.arrayStride	= 0;
		entry.matrixStride	= 0;
		entry.blockNdx		= curBlockNdx;

		if (glu::isDataTypeMatrix(basicType))
		{
			// Array of vectors as specified in rules 5 & 7.
			bool	isRowMajor	= !!(layoutFlags & LAYOUT_ROW_MAJOR);
			int		vecSize		= isRowMajor ? glu::getDataTypeMatrixNumColumns(basicType)
											 : glu::getDataTypeMatrixNumRows(basicType);
			int		numVecs		= isRowMajor ? glu::getDataTypeMatrixNumRows(basicType)
											 : glu::getDataTypeMatrixNumColumns(basicType);
			int		stride		= getDataTypeArrayStride(glu::getDataTypeFloatVec(vecSize));

			entry.offset		= curOffset;
			entry.matrixStride	= stride;
			entry.isRowMajor	= isRowMajor;

			curOffset += numVecs*stride;
		}
		else
		{
			// Scalar or vector.
			entry.offset = curOffset;

			curOffset += getDataTypeByteSize(basicType);
		}

		layout.uniforms.push_back(entry);
	}
	else if (type.isArrayType())
	{
		const VarType&	elemType	= type.getElementType();

		if (elemType.isBasicType() && !glu::isDataTypeMatrix(elemType.getBasicType()))
		{
			// Array of scalars or vectors.
			glu::DataType		elemBasicType	= elemType.getBasicType();
			UniformLayoutEntry	entry;
			int					stride			= getDataTypeArrayStride(elemBasicType);

			entry.name			= curPrefix + "[0]"; // Array uniforms are always postfixed with [0]
			entry.type			= elemBasicType;
			entry.blockNdx		= curBlockNdx;
			entry.offset		= curOffset;
			entry.size			= type.getArraySize();
			entry.arrayStride	= stride;
			entry.matrixStride	= 0;

			curOffset += stride*type.getArraySize();

			layout.uniforms.push_back(entry);
		}
		else if (elemType.isBasicType() && glu::isDataTypeMatrix(elemType.getBasicType()))
		{
			// Array of matrices.
			glu::DataType		elemBasicType	= elemType.getBasicType();
			bool				isRowMajor		= !!(layoutFlags & LAYOUT_ROW_MAJOR);
			int					vecSize			= isRowMajor ? glu::getDataTypeMatrixNumColumns(elemBasicType)
															 : glu::getDataTypeMatrixNumRows(elemBasicType);
			int					numVecs			= isRowMajor ? glu::getDataTypeMatrixNumRows(elemBasicType)
															 : glu::getDataTypeMatrixNumColumns(elemBasicType);
			int					stride			= getDataTypeArrayStride(glu::getDataTypeFloatVec(vecSize));
			UniformLayoutEntry	entry;

			entry.name			= curPrefix + "[0]"; // Array uniforms are always postfixed with [0]
			entry.type			= elemBasicType;
			entry.blockNdx		= curBlockNdx;
			entry.offset		= curOffset;
			entry.size			= type.getArraySize();
			entry.arrayStride	= stride*numVecs;
			entry.matrixStride	= stride;
			entry.isRowMajor	= isRowMajor;

			curOffset += numVecs*type.getArraySize()*stride;

			layout.uniforms.push_back(entry);
		}
		else
		{
			DE_ASSERT(elemType.isStructType() || elemType.isArrayType());

			for (int elemNdx = 0; elemNdx < type.getArraySize(); elemNdx++)
				computeStd140Layout(layout, curOffset, curBlockNdx, curPrefix + "[" + de::toString(elemNdx) + "]", type.getElementType(), layoutFlags);
		}
	}
	else
	{
		DE_ASSERT(type.isStructType());

		for (StructType::ConstIterator memberIter = type.getStruct().begin(); memberIter != type.getStruct().end(); memberIter++)
			computeStd140Layout(layout, curOffset, curBlockNdx, curPrefix + "." + memberIter->getName(), memberIter->getType(), layoutFlags);

		curOffset = deAlign32(curOffset, baseAlignment);
	}
}

void computeStd140Layout (UniformLayout& layout, const ShaderInterface& interface)
{
	// \todo [2012-01-23 pyry] Uniforms in default block.

	int numUniformBlocks = interface.getNumUniformBlocks();

	for (int blockNdx = 0; blockNdx < numUniformBlocks; blockNdx++)
	{
		const UniformBlock&	block			= interface.getUniformBlock(blockNdx);
		bool				hasInstanceName	= block.getInstanceName() != DE_NULL;
		std::string			blockPrefix		= hasInstanceName ? (std::string(block.getBlockName()) + ".") : std::string("");
		int					curOffset		= 0;
		int					activeBlockNdx	= (int)layout.blocks.size();
		int					firstUniformNdx	= (int)layout.uniforms.size();

		for (UniformBlock::ConstIterator uniformIter = block.begin(); uniformIter != block.end(); uniformIter++)
		{
			const Uniform& uniform = *uniformIter;
			computeStd140Layout(layout, curOffset, activeBlockNdx, blockPrefix + uniform.getName(), uniform.getType(), mergeLayoutFlags(block.getFlags(), uniform.getFlags()));
		}

		int	uniformIndicesEnd	= (int)layout.uniforms.size();
		int	blockSize			= curOffset;
		int	numInstances		= block.isArray() ? block.getArraySize() : 1;

		// Create block layout entries for each instance.
		for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
		{
			// Allocate entry for instance.
			layout.blocks.push_back(BlockLayoutEntry());
			BlockLayoutEntry& blockEntry = layout.blocks.back();

			blockEntry.name = block.getBlockName();
			blockEntry.size = blockSize;

			// Compute active uniform set for block.
			for (int uniformNdx = firstUniformNdx; uniformNdx < uniformIndicesEnd; uniformNdx++)
				blockEntry.activeUniformIndices.push_back(uniformNdx);

			if (block.isArray())
				blockEntry.name += "[" + de::toString(instanceNdx) + "]";
		}
	}
}

// Value generator.

void generateValue (const UniformLayoutEntry& entry, void* basePtr, de::Random& rnd)
{
	glu::DataType	scalarType		= glu::getDataTypeScalarType(entry.type);
	int				scalarSize		= glu::getDataTypeScalarSize(entry.type);
	bool			isMatrix		= glu::isDataTypeMatrix(entry.type);
	int				numVecs			= isMatrix ? (entry.isRowMajor ? glu::getDataTypeMatrixNumRows(entry.type) : glu::getDataTypeMatrixNumColumns(entry.type)) : 1;
	int				vecSize			= scalarSize / numVecs;
	bool			isArray			= entry.size > 1;
	const int		compSize		= sizeof(deUint32);

	DE_ASSERT(scalarSize%numVecs == 0);

	for (int elemNdx = 0; elemNdx < entry.size; elemNdx++)
	{
		deUint8* elemPtr = (deUint8*)basePtr + entry.offset + (isArray ? elemNdx*entry.arrayStride : 0);

		for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
		{
			deUint8* vecPtr = elemPtr + (isMatrix ? vecNdx*entry.matrixStride : 0);

			for (int compNdx = 0; compNdx < vecSize; compNdx++)
			{
				deUint8* compPtr = vecPtr + compSize*compNdx;

				switch (scalarType)
				{
					case glu::TYPE_FLOAT:	*((float*)compPtr)		= (float)rnd.getInt(-9, 9);						break;
					case glu::TYPE_INT:		*((int*)compPtr)		= rnd.getInt(-9, 9);							break;
					case glu::TYPE_UINT:	*((deUint32*)compPtr)	= (deUint32)rnd.getInt(0, 9);					break;
					// \note Random bit pattern is used for true values. Spec states that all non-zero values are
					//       interpreted as true but some implementations fail this.
					case glu::TYPE_BOOL:	*((deUint32*)compPtr)	= rnd.getBool() ? rnd.getUint32()|1u : 0u;		break;
					default:
						DE_ASSERT(false);
				}
			}
		}
	}
}

void generateValues (const UniformLayout& layout, const std::map<int, void*>& blockPointers, deUint32 seed)
{
	de::Random	rnd			(seed);
	int			numBlocks	= (int)layout.blocks.size();

	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
	{
		void*	basePtr		= blockPointers.find(blockNdx)->second;
		int		numEntries	= (int)layout.blocks[blockNdx].activeUniformIndices.size();

		for (int entryNdx = 0; entryNdx < numEntries; entryNdx++)
		{
			const UniformLayoutEntry& entry = layout.uniforms[layout.blocks[blockNdx].activeUniformIndices[entryNdx]];
			generateValue(entry, basePtr, rnd);
		}
	}
}

// Shader generator.

const char* getCompareFuncForType (glu::DataType type)
{
	switch (type)
	{
		case glu::TYPE_FLOAT:			return "mediump float compare_float    (highp float a, highp float b)  { return abs(a - b) < 0.05 ? 1.0 : 0.0; }\n";
		case glu::TYPE_FLOAT_VEC2:		return "mediump float compare_vec2     (highp vec2 a, highp vec2 b)    { return compare_float(a.x, b.x)*compare_float(a.y, b.y); }\n";
		case glu::TYPE_FLOAT_VEC3:		return "mediump float compare_vec3     (highp vec3 a, highp vec3 b)    { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z); }\n";
		case glu::TYPE_FLOAT_VEC4:		return "mediump float compare_vec4     (highp vec4 a, highp vec4 b)    { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z)*compare_float(a.w, b.w); }\n";
		case glu::TYPE_FLOAT_MAT2:		return "mediump float compare_mat2     (highp mat2 a, highp mat2 b)    { return compare_vec2(a[0], b[0])*compare_vec2(a[1], b[1]); }\n";
		case glu::TYPE_FLOAT_MAT2X3:	return "mediump float compare_mat2x3   (highp mat2x3 a, highp mat2x3 b){ return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1]); }\n";
		case glu::TYPE_FLOAT_MAT2X4:	return "mediump float compare_mat2x4   (highp mat2x4 a, highp mat2x4 b){ return compare_vec4(a[0], b[0])*compare_vec4(a[1], b[1]); }\n";
		case glu::TYPE_FLOAT_MAT3X2:	return "mediump float compare_mat3x2   (highp mat3x2 a, highp mat3x2 b){ return compare_vec2(a[0], b[0])*compare_vec2(a[1], b[1])*compare_vec2(a[2], b[2]); }\n";
		case glu::TYPE_FLOAT_MAT3:		return "mediump float compare_mat3     (highp mat3 a, highp mat3 b)    { return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1])*compare_vec3(a[2], b[2]); }\n";
		case glu::TYPE_FLOAT_MAT3X4:	return "mediump float compare_mat3x4   (highp mat3x4 a, highp mat3x4 b){ return compare_vec4(a[0], b[0])*compare_vec4(a[1], b[1])*compare_vec4(a[2], b[2]); }\n";
		case glu::TYPE_FLOAT_MAT4X2:	return "mediump float compare_mat4x2   (highp mat4x2 a, highp mat4x2 b){ return compare_vec2(a[0], b[0])*compare_vec2(a[1], b[1])*compare_vec2(a[2], b[2])*compare_vec2(a[3], b[3]); }\n";
		case glu::TYPE_FLOAT_MAT4X3:	return "mediump float compare_mat4x3   (highp mat4x3 a, highp mat4x3 b){ return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1])*compare_vec3(a[2], b[2])*compare_vec3(a[3], b[3]); }\n";
		case glu::TYPE_FLOAT_MAT4:		return "mediump float compare_mat4     (highp mat4 a, highp mat4 b)    { return compare_vec4(a[0], b[0])*compare_vec4(a[1], b[1])*compare_vec4(a[2], b[2])*compare_vec4(a[3], b[3]); }\n";
		case glu::TYPE_INT:				return "mediump float compare_int      (highp int a, highp int b)      { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_INT_VEC2:		return "mediump float compare_ivec2    (highp ivec2 a, highp ivec2 b)  { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_INT_VEC3:		return "mediump float compare_ivec3    (highp ivec3 a, highp ivec3 b)  { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_INT_VEC4:		return "mediump float compare_ivec4    (highp ivec4 a, highp ivec4 b)  { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_UINT:			return "mediump float compare_uint     (highp uint a, highp uint b)    { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_UINT_VEC2:		return "mediump float compare_uvec2    (highp uvec2 a, highp uvec2 b)  { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_UINT_VEC3:		return "mediump float compare_uvec3    (highp uvec3 a, highp uvec3 b)  { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_UINT_VEC4:		return "mediump float compare_uvec4    (highp uvec4 a, highp uvec4 b)  { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_BOOL:			return "mediump float compare_bool     (bool a, bool b)                { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_BOOL_VEC2:		return "mediump float compare_bvec2    (bvec2 a, bvec2 b)              { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_BOOL_VEC3:		return "mediump float compare_bvec3    (bvec3 a, bvec3 b)              { return a == b ? 1.0 : 0.0; }\n";
		case glu::TYPE_BOOL_VEC4:		return "mediump float compare_bvec4    (bvec4 a, bvec4 b)              { return a == b ? 1.0 : 0.0; }\n";
		default:
			DE_ASSERT(false);
			return DE_NULL;
	}
}

void getCompareDependencies (std::set<glu::DataType>& compareFuncs, glu::DataType basicType)
{
	switch (basicType)
	{
		case glu::TYPE_FLOAT_VEC2:
		case glu::TYPE_FLOAT_VEC3:
		case glu::TYPE_FLOAT_VEC4:
			compareFuncs.insert(glu::TYPE_FLOAT);
			compareFuncs.insert(basicType);
			break;

		case glu::TYPE_FLOAT_MAT2:
		case glu::TYPE_FLOAT_MAT2X3:
		case glu::TYPE_FLOAT_MAT2X4:
		case glu::TYPE_FLOAT_MAT3X2:
		case glu::TYPE_FLOAT_MAT3:
		case glu::TYPE_FLOAT_MAT3X4:
		case glu::TYPE_FLOAT_MAT4X2:
		case glu::TYPE_FLOAT_MAT4X3:
		case glu::TYPE_FLOAT_MAT4:
			compareFuncs.insert(glu::TYPE_FLOAT);
			compareFuncs.insert(glu::getDataTypeFloatVec(glu::getDataTypeMatrixNumRows(basicType)));
			compareFuncs.insert(basicType);
			break;

		default:
			compareFuncs.insert(basicType);
			break;
	}
}

void collectUniqueBasicTypes (std::set<glu::DataType>& basicTypes, const VarType& type)
{
	if (type.isStructType())
	{
		for (StructType::ConstIterator iter = type.getStruct().begin(); iter != type.getStruct().end(); ++iter)
			collectUniqueBasicTypes(basicTypes, iter->getType());
	}
	else if (type.isArrayType())
		collectUniqueBasicTypes(basicTypes, type.getElementType());
	else
	{
		DE_ASSERT(type.isBasicType());
		basicTypes.insert(type.getBasicType());
	}
}

void collectUniqueBasicTypes (std::set<glu::DataType>& basicTypes, const UniformBlock& uniformBlock)
{
	for (UniformBlock::ConstIterator iter = uniformBlock.begin(); iter != uniformBlock.end(); ++iter)
		collectUniqueBasicTypes(basicTypes, iter->getType());
}

void collectUniqueBasicTypes (std::set<glu::DataType>& basicTypes, const ShaderInterface& interface)
{
	for (int ndx = 0; ndx < interface.getNumUniformBlocks(); ++ndx)
		collectUniqueBasicTypes(basicTypes, interface.getUniformBlock(ndx));
}

void generateCompareFuncs (std::ostream& str, const ShaderInterface& interface)
{
	std::set<glu::DataType> types;
	std::set<glu::DataType> compareFuncs;

	// Collect unique basic types
	collectUniqueBasicTypes(types, interface);

	// Set of compare functions required
	for (std::set<glu::DataType>::const_iterator iter = types.begin(); iter != types.end(); ++iter)
	{
		getCompareDependencies(compareFuncs, *iter);
	}

	for (int type = 0; type < glu::TYPE_LAST; ++type)
	{
		if (compareFuncs.find(glu::DataType(type)) != compareFuncs.end())
			str << getCompareFuncForType(glu::DataType(type));
	}
}

struct Indent
{
	int level;
	Indent (int level_) : level(level_) {}
};

std::ostream& operator<< (std::ostream& str, const Indent& indent)
{
	for (int i = 0; i < indent.level; i++)
		str << "\t";
	return str;
}

void		generateDeclaration			(std::ostringstream& src, const VarType& type, const char* name, int indentLevel, deUint32 unusedHints);
void		generateDeclaration			(std::ostringstream& src, const Uniform& uniform, int indentLevel);
void		generateDeclaration			(std::ostringstream& src, const StructType& structType, int indentLevel);

void		generateLocalDeclaration	(std::ostringstream& src, const StructType& structType, int indentLevel);
void		generateFullDeclaration		(std::ostringstream& src, const StructType& structType, int indentLevel);

void generateDeclaration (std::ostringstream& src, const StructType& structType, int indentLevel)
{
	DE_ASSERT(structType.getTypeName() != DE_NULL);
	generateFullDeclaration(src, structType, indentLevel);
	src << ";\n";
}

void generateFullDeclaration (std::ostringstream& src, const StructType& structType, int indentLevel)
{
	src << "struct";
	if (structType.getTypeName())
		src << " " << structType.getTypeName();
	src << "\n" << Indent(indentLevel) << "{\n";

	for (StructType::ConstIterator memberIter = structType.begin(); memberIter != structType.end(); memberIter++)
	{
		src << Indent(indentLevel+1);
		generateDeclaration(src, memberIter->getType(), memberIter->getName(), indentLevel+1, memberIter->getFlags() & UNUSED_BOTH);
	}

	src << Indent(indentLevel) << "}";
}

void generateLocalDeclaration (std::ostringstream& src, const StructType& structType, int indentLevel)
{
	if (structType.getTypeName() == DE_NULL)
		generateFullDeclaration(src, structType, indentLevel);
	else
		src << structType.getTypeName();
}

void generateDeclaration (std::ostringstream& src, const VarType& type, const char* name, int indentLevel, deUint32 unusedHints)
{
	deUint32 flags = type.getFlags();

	if ((flags & LAYOUT_MASK) != 0)
		src << "layout(" << LayoutFlagsFmt(flags & LAYOUT_MASK) << ") ";

	if ((flags & PRECISION_MASK) != 0)
		src << PrecisionFlagsFmt(flags & PRECISION_MASK) << " ";

	if (type.isBasicType())
		src << glu::getDataTypeName(type.getBasicType()) << " " << name;
	else if (type.isArrayType())
	{
		std::vector<int>	arraySizes;
		const VarType*		curType		= &type;
		while (curType->isArrayType())
		{
			arraySizes.push_back(curType->getArraySize());
			curType = &curType->getElementType();
		}

		if (curType->isBasicType())
		{
			if ((curType->getFlags() & PRECISION_MASK) != 0)
				src << PrecisionFlagsFmt(curType->getFlags() & PRECISION_MASK) << " ";
			src << glu::getDataTypeName(curType->getBasicType());
		}
		else
		{
			DE_ASSERT(curType->isStructType());
			generateLocalDeclaration(src, curType->getStruct(), indentLevel+1);
		}

		src << " " << name;

		for (std::vector<int>::const_iterator sizeIter = arraySizes.begin(); sizeIter != arraySizes.end(); sizeIter++)
			src << "[" << *sizeIter << "]";
	}
	else
	{
		generateLocalDeclaration(src, type.getStruct(), indentLevel+1);
		src << " " << name;
	}

	src << ";";

	// Print out unused hints.
	if (unusedHints != 0)
		src << " // unused in " << (unusedHints == UNUSED_BOTH		? "both shaders"	:
									unusedHints == UNUSED_VERTEX	? "vertex shader"	:
									unusedHints == UNUSED_FRAGMENT	? "fragment shader" : "???");

	src << "\n";
}

void generateDeclaration (std::ostringstream& src, const Uniform& uniform, int indentLevel)
{
	if ((uniform.getFlags() & LAYOUT_MASK) != 0)
		src << "layout(" << LayoutFlagsFmt(uniform.getFlags() & LAYOUT_MASK) << ") ";

	generateDeclaration(src, uniform.getType(), uniform.getName(), indentLevel, uniform.getFlags() & UNUSED_BOTH);
}

void generateDeclaration (std::ostringstream& src, const UniformBlock& block)
{
	if ((block.getFlags() & LAYOUT_MASK) != 0)
		src << "layout(" << LayoutFlagsFmt(block.getFlags() & LAYOUT_MASK) << ") ";

	src << "uniform " << block.getBlockName();
	src << "\n{\n";

	for (UniformBlock::ConstIterator uniformIter = block.begin(); uniformIter != block.end(); uniformIter++)
	{
		src << Indent(1);
		generateDeclaration(src, *uniformIter, 1 /* indent level */);
	}

	src << "}";

	if (block.getInstanceName() != DE_NULL)
	{
		src << " " << block.getInstanceName();
		if (block.isArray())
			src << "[" << block.getArraySize() << "]";
	}
	else
		DE_ASSERT(!block.isArray());

	src << ";\n";
}

void generateValueSrc (std::ostringstream& src, const UniformLayoutEntry& entry, const void* basePtr, int elementNdx)
{
	glu::DataType	scalarType		= glu::getDataTypeScalarType(entry.type);
	int				scalarSize		= glu::getDataTypeScalarSize(entry.type);
	bool			isArray			= entry.size > 1;
	const deUint8*	elemPtr			= (const deUint8*)basePtr + entry.offset + (isArray ? elementNdx*entry.arrayStride : 0);
	const int		compSize		= sizeof(deUint32);

	if (scalarSize > 1)
		src << glu::getDataTypeName(entry.type) << "(";

	if (glu::isDataTypeMatrix(entry.type))
	{
		int	numRows	= glu::getDataTypeMatrixNumRows(entry.type);
		int	numCols	= glu::getDataTypeMatrixNumColumns(entry.type);

		DE_ASSERT(scalarType == glu::TYPE_FLOAT);

		// Constructed in column-wise order.
		for (int colNdx = 0; colNdx < numCols; colNdx++)
		{
			for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
			{
				const deUint8*	compPtr	= elemPtr + (entry.isRowMajor ? rowNdx*entry.matrixStride + colNdx*compSize
																	  : colNdx*entry.matrixStride + rowNdx*compSize);

				if (colNdx > 0 || rowNdx > 0)
					src << ", ";

				src << de::floatToString(*((const float*)compPtr), 1);
			}
		}
	}
	else
	{
		for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
		{
			const deUint8* compPtr = elemPtr + scalarNdx*compSize;

			if (scalarNdx > 0)
				src << ", ";

			switch (scalarType)
			{
				case glu::TYPE_FLOAT:	src << de::floatToString(*((const float*)compPtr), 1);			break;
				case glu::TYPE_INT:		src << *((const int*)compPtr);									break;
				case glu::TYPE_UINT:	src << *((const deUint32*)compPtr) << "u";						break;
				case glu::TYPE_BOOL:	src << (*((const deUint32*)compPtr) != 0u ? "true" : "false");	break;
				default:
					DE_ASSERT(false);
			}
		}
	}

	if (scalarSize > 1)
		src << ")";
}

void generateCompareSrc (std::ostringstream& src, const char* resultVar, const VarType& type, const char* srcName, const char* apiName, const UniformLayout& layout, const void* basePtr, deUint32 unusedMask)
{
	if (type.isBasicType() || (type.isArrayType() && type.getElementType().isBasicType()))
	{
		// Basic type or array of basic types.
		bool						isArray			= type.isArrayType();
		glu::DataType				elementType		= isArray ? type.getElementType().getBasicType() : type.getBasicType();
		const char*					typeName		= glu::getDataTypeName(elementType);
		std::string					fullApiName		= string(apiName) + (isArray ? "[0]" : ""); // Arrays are always postfixed with [0]
		int							uniformNdx		= layout.getUniformIndex(fullApiName.c_str());
		const UniformLayoutEntry&	entry			= layout.uniforms[uniformNdx];

		if (isArray)
		{
			for (int elemNdx = 0; elemNdx < type.getArraySize(); elemNdx++)
			{
				src << "\tresult *= compare_" << typeName << "(" << srcName << "[" << elemNdx << "], ";
				generateValueSrc(src, entry, basePtr, elemNdx);
				src << ");\n";
			}
		}
		else
		{
			src << "\tresult *= compare_" << typeName << "(" << srcName << ", ";
			generateValueSrc(src, entry, basePtr, 0);
			src << ");\n";
		}
	}
	else if (type.isArrayType())
	{
		const VarType& elementType = type.getElementType();

		for (int elementNdx = 0; elementNdx < type.getArraySize(); elementNdx++)
		{
			std::string op = string("[") + de::toString(elementNdx) + "]";
			generateCompareSrc(src, resultVar, elementType, (string(srcName) + op).c_str(), (string(apiName) + op).c_str(), layout, basePtr, unusedMask);
		}
	}
	else
	{
		DE_ASSERT(type.isStructType());

		for (StructType::ConstIterator memberIter = type.getStruct().begin(); memberIter != type.getStruct().end(); memberIter++)
		{
			if (memberIter->getFlags() & unusedMask)
				continue; // Skip member.

			string op = string(".") + memberIter->getName();
			generateCompareSrc(src, resultVar, memberIter->getType(), (string(srcName) + op).c_str(), (string(apiName) + op).c_str(), layout, basePtr, unusedMask);
		}
	}
}

void generateCompareSrc (std::ostringstream& src, const char* resultVar, const ShaderInterface& interface, const UniformLayout& layout, const std::map<int, void*>& blockPointers, bool isVertex)
{
	deUint32 unusedMask = isVertex ? UNUSED_VERTEX : UNUSED_FRAGMENT;

	for (int blockNdx = 0; blockNdx < interface.getNumUniformBlocks(); blockNdx++)
	{
		const UniformBlock& block = interface.getUniformBlock(blockNdx);

		if ((block.getFlags() & (isVertex ? DECLARE_VERTEX : DECLARE_FRAGMENT)) == 0)
			continue; // Skip.

		bool			hasInstanceName	= block.getInstanceName() != DE_NULL;
		bool			isArray			= block.isArray();
		int				numInstances	= isArray ? block.getArraySize() : 1;
		std::string		apiPrefix		= hasInstanceName ? string(block.getBlockName()) + "." : string("");

		DE_ASSERT(!isArray || hasInstanceName);

		for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
		{
			std::string		instancePostfix		= isArray ? string("[") + de::toString(instanceNdx) + "]" : string("");
			std::string		blockInstanceName	= block.getBlockName() + instancePostfix;
			std::string		srcPrefix			= hasInstanceName ? string(block.getInstanceName()) + instancePostfix + "." : string("");
			int				activeBlockNdx		= layout.getBlockIndex(blockInstanceName.c_str());
			void*			basePtr				= blockPointers.find(activeBlockNdx)->second;

			for (UniformBlock::ConstIterator uniformIter = block.begin(); uniformIter != block.end(); uniformIter++)
			{
				const Uniform& uniform = *uniformIter;

				if (uniform.getFlags() & unusedMask)
					continue; // Don't read from that uniform.

				generateCompareSrc(src, resultVar, uniform.getType(), (srcPrefix + uniform.getName()).c_str(), (apiPrefix + uniform.getName()).c_str(), layout, basePtr, unusedMask);
			}
		}
	}
}

void generateVertexShader (std::ostringstream& src, glu::GLSLVersion glslVersion, const ShaderInterface& interface, const UniformLayout& layout, const std::map<int, void*>& blockPointers)
{
	DE_ASSERT(isSupportedGLSLVersion(glslVersion));

	src << glu::getGLSLVersionDeclaration(glslVersion) << "\n";
	src << "in highp vec4 a_position;\n";
	src << "out mediump float v_vtxResult;\n";
	src << "\n";

	std::vector<const StructType*> namedStructs;
	interface.getNamedStructs(namedStructs);
	for (std::vector<const StructType*>::const_iterator structIter = namedStructs.begin(); structIter != namedStructs.end(); structIter++)
		generateDeclaration(src, **structIter, 0);

	for (int blockNdx = 0; blockNdx < interface.getNumUniformBlocks(); blockNdx++)
	{
		const UniformBlock& block = interface.getUniformBlock(blockNdx);
		if (block.getFlags() & DECLARE_VERTEX)
			generateDeclaration(src, block);
	}

	// Comparison utilities.
	src << "\n";
	generateCompareFuncs(src, interface);

	src << "\n"
		   "void main (void)\n"
		   "{\n"
		   "	gl_Position = a_position;\n"
		   "	mediump float result = 1.0;\n";

	// Value compare.
	generateCompareSrc(src, "result", interface, layout, blockPointers, true);

	src << "	v_vtxResult = result;\n"
		   "}\n";
}

void generateFragmentShader (std::ostringstream& src, glu::GLSLVersion glslVersion, const ShaderInterface& interface, const UniformLayout& layout, const std::map<int, void*>& blockPointers)
{
	DE_ASSERT(isSupportedGLSLVersion(glslVersion));

	src << glu::getGLSLVersionDeclaration(glslVersion) << "\n";
	src << "in mediump float v_vtxResult;\n";
	src << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
	src << "\n";

	std::vector<const StructType*> namedStructs;
	interface.getNamedStructs(namedStructs);
	for (std::vector<const StructType*>::const_iterator structIter = namedStructs.begin(); structIter != namedStructs.end(); structIter++)
		generateDeclaration(src, **structIter, 0);

	for (int blockNdx = 0; blockNdx < interface.getNumUniformBlocks(); blockNdx++)
	{
		const UniformBlock& block = interface.getUniformBlock(blockNdx);
		if (block.getFlags() & DECLARE_FRAGMENT)
			generateDeclaration(src, block);
	}

	// Comparison utilities.
	src << "\n";
	generateCompareFuncs(src, interface);

	src << "\n"
		   "void main (void)\n"
		   "{\n"
		   "	mediump float result = 1.0;\n";

	// Value compare.
	generateCompareSrc(src, "result", interface, layout, blockPointers, false);

	src << "	dEQP_FragColor = vec4(1.0, v_vtxResult, result, 1.0);\n"
		   "}\n";
}

void getGLUniformLayout (const glw::Functions& gl, UniformLayout& layout, deUint32 program)
{
	int		numActiveUniforms	= 0;
	int		numActiveBlocks		= 0;

	gl.getProgramiv(program, GL_ACTIVE_UNIFORMS,		&numActiveUniforms);
	gl.getProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS,	&numActiveBlocks);

	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to get number of uniforms and uniform blocks");

	// Block entries.
	layout.blocks.resize(numActiveBlocks);
	for (int blockNdx = 0; blockNdx < numActiveBlocks; blockNdx++)
	{
		BlockLayoutEntry&	entry				= layout.blocks[blockNdx];
		int					size;
		int					nameLen;
		int					numBlockUniforms;

		gl.getActiveUniformBlockiv(program, (deUint32)blockNdx, GL_UNIFORM_BLOCK_DATA_SIZE,			&size);
		gl.getActiveUniformBlockiv(program, (deUint32)blockNdx, GL_UNIFORM_BLOCK_NAME_LENGTH,		&nameLen);
		gl.getActiveUniformBlockiv(program, (deUint32)blockNdx, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS,	&numBlockUniforms);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform block query failed");

		// \note Some implementations incorrectly return 0 as name length even though the length should include null terminator.
		std::vector<char> nameBuf(nameLen > 0 ? nameLen : 1);
		gl.getActiveUniformBlockName(program, (deUint32)blockNdx, (glw::GLsizei)nameBuf.size(), DE_NULL, &nameBuf[0]);

		entry.name	= std::string(&nameBuf[0]);
		entry.size	= size;
		entry.activeUniformIndices.resize(numBlockUniforms);

		if (numBlockUniforms > 0)
			gl.getActiveUniformBlockiv(program, (deUint32)blockNdx, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &entry.activeUniformIndices[0]);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform block query failed");
	}

	if (numActiveUniforms > 0)
	{
		// Uniform entries.
		std::vector<deUint32> uniformIndices(numActiveUniforms);
		for (int i = 0; i < numActiveUniforms; i++)
			uniformIndices[i] = (deUint32)i;

		std::vector<int>		types			(numActiveUniforms);
		std::vector<int>		sizes			(numActiveUniforms);
		std::vector<int>		nameLengths		(numActiveUniforms);
		std::vector<int>		blockIndices	(numActiveUniforms);
		std::vector<int>		offsets			(numActiveUniforms);
		std::vector<int>		arrayStrides	(numActiveUniforms);
		std::vector<int>		matrixStrides	(numActiveUniforms);
		std::vector<int>		rowMajorFlags	(numActiveUniforms);

		// Execute queries.
		gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_TYPE,			&types[0]);
		gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_SIZE,			&sizes[0]);
		gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_NAME_LENGTH,	&nameLengths[0]);
		gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_BLOCK_INDEX,	&blockIndices[0]);
		gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_OFFSET,			&offsets[0]);
		gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_ARRAY_STRIDE,	&arrayStrides[0]);
		gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_MATRIX_STRIDE,	&matrixStrides[0]);
		gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_IS_ROW_MAJOR,	&rowMajorFlags[0]);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Active uniform query failed");

		// Translate to LayoutEntries
		layout.uniforms.resize(numActiveUniforms);
		for (int uniformNdx = 0; uniformNdx < numActiveUniforms; uniformNdx++)
		{
			UniformLayoutEntry&	entry		= layout.uniforms[uniformNdx];
			std::vector<char>	nameBuf		(nameLengths[uniformNdx]);
			glw::GLsizei		nameLen		= 0;
			int					size		= 0;
			deUint32			type		= GL_NONE;

			gl.getActiveUniform(program, (deUint32)uniformNdx, (glw::GLsizei)nameBuf.size(), &nameLen, &size, &type, &nameBuf[0]);

			GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform name query failed");

			// \note glGetActiveUniform() returns length without \0 and glGetActiveUniformsiv() with \0
			if (nameLen+1	!= nameLengths[uniformNdx]	||
				size		!= sizes[uniformNdx]		||
				type		!= (deUint32)types[uniformNdx])
				TCU_FAIL("Values returned by glGetActiveUniform() don't match with values queried with glGetActiveUniformsiv().");

			entry.name			= std::string(&nameBuf[0]);
			entry.type			= glu::getDataTypeFromGLType(types[uniformNdx]);
			entry.size			= sizes[uniformNdx];
			entry.blockNdx		= blockIndices[uniformNdx];
			entry.offset		= offsets[uniformNdx];
			entry.arrayStride	= arrayStrides[uniformNdx];
			entry.matrixStride	= matrixStrides[uniformNdx];
			entry.isRowMajor	= rowMajorFlags[uniformNdx] != GL_FALSE;
		}
	}
}

void copyUniformData (const UniformLayoutEntry& dstEntry, void* dstBlockPtr, const UniformLayoutEntry& srcEntry, const void* srcBlockPtr)
{
	deUint8*					dstBasePtr	= (deUint8*)dstBlockPtr + dstEntry.offset;
	const deUint8*				srcBasePtr	= (const deUint8*)srcBlockPtr + srcEntry.offset;

	DE_ASSERT(dstEntry.size <= srcEntry.size);
	DE_ASSERT(dstEntry.type == srcEntry.type);

	int							scalarSize	= glu::getDataTypeScalarSize(dstEntry.type);
	bool						isMatrix	= glu::isDataTypeMatrix(dstEntry.type);
	const int					compSize	= sizeof(deUint32);

	for (int elementNdx = 0; elementNdx < dstEntry.size; elementNdx++)
	{
		deUint8*		dstElemPtr	= dstBasePtr + elementNdx*dstEntry.arrayStride;
		const deUint8*	srcElemPtr	= srcBasePtr + elementNdx*srcEntry.arrayStride;

		if (isMatrix)
		{
			int	numRows	= glu::getDataTypeMatrixNumRows(dstEntry.type);
			int	numCols	= glu::getDataTypeMatrixNumColumns(dstEntry.type);

			for (int colNdx = 0; colNdx < numCols; colNdx++)
			{
				for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
				{
					deUint8*		dstCompPtr	= dstElemPtr + (dstEntry.isRowMajor ? rowNdx*dstEntry.matrixStride + colNdx*compSize
																					: colNdx*dstEntry.matrixStride + rowNdx*compSize);
					const deUint8*	srcCompPtr	= srcElemPtr + (srcEntry.isRowMajor ? rowNdx*srcEntry.matrixStride + colNdx*compSize
																					: colNdx*srcEntry.matrixStride + rowNdx*compSize);
					deMemcpy(dstCompPtr, srcCompPtr, compSize);
				}
			}
		}
		else
			deMemcpy(dstElemPtr, srcElemPtr, scalarSize*compSize);
	}
}

void copyUniformData (const UniformLayout& dstLayout, const std::map<int, void*>& dstBlockPointers, const UniformLayout& srcLayout, const std::map<int, void*>& srcBlockPointers)
{
	// \note Src layout is used as reference in case of activeUniforms happens to be incorrect in dstLayout blocks.
	int numBlocks = (int)srcLayout.blocks.size();

	for (int srcBlockNdx = 0; srcBlockNdx < numBlocks; srcBlockNdx++)
	{
		const BlockLayoutEntry&		srcBlock	= srcLayout.blocks[srcBlockNdx];
		const void*					srcBlockPtr	= srcBlockPointers.find(srcBlockNdx)->second;
		int							dstBlockNdx	= dstLayout.getBlockIndex(srcBlock.name.c_str());
		void*						dstBlockPtr	= dstBlockNdx >= 0 ? dstBlockPointers.find(dstBlockNdx)->second : DE_NULL;

		if (dstBlockNdx < 0)
			continue;

		for (vector<int>::const_iterator srcUniformNdxIter = srcBlock.activeUniformIndices.begin(); srcUniformNdxIter != srcBlock.activeUniformIndices.end(); srcUniformNdxIter++)
		{
			const UniformLayoutEntry&	srcEntry		= srcLayout.uniforms[*srcUniformNdxIter];
			int							dstUniformNdx	= dstLayout.getUniformIndex(srcEntry.name.c_str());

			if (dstUniformNdx < 0)
				continue;

			copyUniformData(dstLayout.uniforms[dstUniformNdx], dstBlockPtr, srcEntry, srcBlockPtr);
		}
	}
}

} // anonymous (utilities)

class UniformBufferManager
{
public:
								UniformBufferManager	(const glu::RenderContext& renderCtx);
								~UniformBufferManager	(void);

	deUint32					allocBuffer				(void);

private:
								UniformBufferManager	(const UniformBufferManager& other);
	UniformBufferManager&		operator=				(const UniformBufferManager& other);

	const glu::RenderContext&	m_renderCtx;
	std::vector<deUint32>		m_buffers;
};

UniformBufferManager::UniformBufferManager (const glu::RenderContext& renderCtx)
	: m_renderCtx(renderCtx)
{
}

UniformBufferManager::~UniformBufferManager (void)
{
	if (!m_buffers.empty())
		m_renderCtx.getFunctions().deleteBuffers((glw::GLsizei)m_buffers.size(), &m_buffers[0]);
}

deUint32 UniformBufferManager::allocBuffer (void)
{
	deUint32 buf = 0;

	m_buffers.reserve(m_buffers.size()+1);
	m_renderCtx.getFunctions().genBuffers(1, &buf);
	GLU_EXPECT_NO_ERROR(m_renderCtx.getFunctions().getError(), "Failed to allocate uniform buffer");
	m_buffers.push_back(buf);

	return buf;
}

} // ub

using namespace ub;

// UniformBlockCase.

UniformBlockCase::UniformBlockCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, glu::GLSLVersion glslVersion, BufferMode bufferMode)
	: TestCase		(testCtx, name, description)
	, m_renderCtx	(renderCtx)
	, m_glslVersion	(glslVersion)
	, m_bufferMode	(bufferMode)
{
	TCU_CHECK_INTERNAL(isSupportedGLSLVersion(glslVersion));
}

UniformBlockCase::~UniformBlockCase (void)
{
}

UniformBlockCase::IterateResult UniformBlockCase::iterate (void)
{
	TestLog&				log				= m_testCtx.getLog();
	const glw::Functions&	gl				= m_renderCtx.getFunctions();
	UniformLayout			refLayout;		//!< std140 layout.
	vector<deUint8>			data;			//!< Data.
	map<int, void*>			blockPointers;	//!< Reference block pointers.

	// Initialize result to pass.
	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");

	// Compute reference layout.
	computeStd140Layout(refLayout, m_interface);

	// Assign storage for reference values.
	{
		int totalSize = 0;
		for (vector<BlockLayoutEntry>::const_iterator blockIter = refLayout.blocks.begin(); blockIter != refLayout.blocks.end(); blockIter++)
			totalSize += blockIter->size;
		data.resize(totalSize);

		// Pointers for each block.
		int curOffset = 0;
		for (int blockNdx = 0; blockNdx < (int)refLayout.blocks.size(); blockNdx++)
		{
			blockPointers[blockNdx] = &data[0] + curOffset;
			curOffset += refLayout.blocks[blockNdx].size;
		}
	}

	// Generate values.
	generateValues(refLayout, blockPointers, 1 /* seed */);

	// Generate shaders and build program.
	std::ostringstream vtxSrc;
	std::ostringstream fragSrc;

	generateVertexShader(vtxSrc, m_glslVersion, m_interface, refLayout, blockPointers);
	generateFragmentShader(fragSrc, m_glslVersion, m_interface, refLayout, blockPointers);

	glu::ShaderProgram program(m_renderCtx, glu::makeVtxFragSources(vtxSrc.str(), fragSrc.str()));
	log << program;

	if (!program.isOk())
	{
		// Compile failed.
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
		return STOP;
	}

	// Query layout from GL.
	UniformLayout glLayout;
	getGLUniformLayout(gl, glLayout, program.getProgram());

	// Print layout to log.
	log << TestLog::Section("ActiveUniformBlocks", "Active Uniform Blocks");
	for (int blockNdx = 0; blockNdx < (int)glLayout.blocks.size(); blockNdx++)
		log << TestLog::Message << blockNdx << ": " << glLayout.blocks[blockNdx] << TestLog::EndMessage;
	log << TestLog::EndSection;

	log << TestLog::Section("ActiveUniforms", "Active Uniforms");
	for (int uniformNdx = 0; uniformNdx < (int)glLayout.uniforms.size(); uniformNdx++)
		log << TestLog::Message << uniformNdx << ": " << glLayout.uniforms[uniformNdx] << TestLog::EndMessage;
	log << TestLog::EndSection;

	// Check that we can even try rendering with given layout.
	if (!checkLayoutIndices(glLayout) || !checkLayoutBounds(glLayout) || !compareTypes(refLayout, glLayout))
	{
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid layout");
		return STOP; // It is not safe to use the given layout.
	}

	// Verify all std140 blocks.
	if (!compareStd140Blocks(refLayout, glLayout))
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid std140 layout");

	// Verify all shared blocks - all uniforms should be active, and certain properties match.
	if (!compareSharedBlocks(refLayout, glLayout))
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid shared layout");

	// Check consistency with index queries
	if (!checkIndexQueries(program.getProgram(), glLayout))
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Inconsintent block index query results");

	// Use program.
	gl.useProgram(program.getProgram());

	// Assign binding points to all active uniform blocks.
	for (int blockNdx = 0; blockNdx < (int)glLayout.blocks.size(); blockNdx++)
	{
		deUint32 binding = (deUint32)blockNdx; // \todo [2012-01-25 pyry] Randomize order?
		gl.uniformBlockBinding(program.getProgram(), (deUint32)blockNdx, binding);
	}

	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set uniform block bindings");

	// Allocate buffers, write data and bind to targets.
	UniformBufferManager bufferManager(m_renderCtx);
	if (m_bufferMode == BUFFERMODE_PER_BLOCK)
	{
		int							numBlocks			= (int)glLayout.blocks.size();
		vector<vector<deUint8> >	glData				(numBlocks);
		map<int, void*>				glBlockPointers;

		for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
		{
			glData[blockNdx].resize(glLayout.blocks[blockNdx].size);
			glBlockPointers[blockNdx] = &glData[blockNdx][0];
		}

		copyUniformData(glLayout, glBlockPointers, refLayout, blockPointers);

		for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
		{
			deUint32	buffer	= bufferManager.allocBuffer();
			deUint32	binding	= (deUint32)blockNdx;

			gl.bindBuffer(GL_UNIFORM_BUFFER, buffer);
			gl.bufferData(GL_UNIFORM_BUFFER, (glw::GLsizeiptr)glData[blockNdx].size(), &glData[blockNdx][0], GL_STATIC_DRAW);
			GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to upload uniform buffer data");

			gl.bindBufferBase(GL_UNIFORM_BUFFER, binding, buffer);
			GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase(GL_UNIFORM_BUFFER) failed");
		}
	}
	else
	{
		DE_ASSERT(m_bufferMode == BUFFERMODE_SINGLE);

		int				totalSize			= 0;
		int				curOffset			= 0;
		int				numBlocks			= (int)glLayout.blocks.size();
		int				bindingAlignment	= 0;
		map<int, int>	glBlockOffsets;

		gl.getIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &bindingAlignment);

		// Compute total size and offsets.
		curOffset = 0;
		for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
		{
			if (bindingAlignment > 0)
				curOffset = deRoundUp32(curOffset, bindingAlignment);
			glBlockOffsets[blockNdx] = curOffset;
			curOffset += glLayout.blocks[blockNdx].size;
		}
		totalSize = curOffset;

		// Assign block pointers.
		vector<deUint8>	glData(totalSize);
		map<int, void*>	glBlockPointers;

		for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
			glBlockPointers[blockNdx] = &glData[glBlockOffsets[blockNdx]];

		// Copy to gl format.
		copyUniformData(glLayout, glBlockPointers, refLayout, blockPointers);

		// Allocate buffer and upload data.
		deUint32 buffer = bufferManager.allocBuffer();
		gl.bindBuffer(GL_UNIFORM_BUFFER, buffer);
		if (!glData.empty())
			gl.bufferData(GL_UNIFORM_BUFFER, (glw::GLsizeiptr)glData.size(), &glData[0], GL_STATIC_DRAW);

		GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to upload uniform buffer data");

		// Bind ranges to binding points.
		for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
		{
			deUint32 binding = (deUint32)blockNdx;
			gl.bindBufferRange(GL_UNIFORM_BUFFER, binding, buffer, (glw::GLintptr)glBlockOffsets[blockNdx], (glw::GLsizeiptr)glLayout.blocks[blockNdx].size);
			GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferRange(GL_UNIFORM_BUFFER) failed");
		}
	}

	bool renderOk = render(program.getProgram());
	if (!renderOk)
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image compare failed");

	return STOP;
}

bool UniformBlockCase::compareStd140Blocks (const UniformLayout& refLayout, const UniformLayout& cmpLayout) const
{
	TestLog&	log			= m_testCtx.getLog();
	bool		isOk		= true;
	int			numBlocks	= m_interface.getNumUniformBlocks();

	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
	{
		const UniformBlock&		block			= m_interface.getUniformBlock(blockNdx);
		bool					isArray			= block.isArray();
		std::string				instanceName	= string(block.getBlockName()) + (isArray ? "[0]" : "");
		int						refBlockNdx		= refLayout.getBlockIndex(instanceName.c_str());
		int						cmpBlockNdx		= cmpLayout.getBlockIndex(instanceName.c_str());
		bool					isUsed			= (block.getFlags() & (DECLARE_VERTEX|DECLARE_FRAGMENT)) != 0;

		if ((block.getFlags() & LAYOUT_STD140) == 0)
			continue; // Not std140 layout.

		DE_ASSERT(refBlockNdx >= 0);

		if (cmpBlockNdx < 0)
		{
			// Not found, should it?
			if (isUsed)
			{
				log << TestLog::Message << "Error: Uniform block '" << instanceName << "' not found" << TestLog::EndMessage;
				isOk = false;
			}

			continue; // Skip block.
		}

		const BlockLayoutEntry&		refBlockLayout	= refLayout.blocks[refBlockNdx];
		const BlockLayoutEntry&		cmpBlockLayout	= cmpLayout.blocks[cmpBlockNdx];

		// \todo [2012-01-24 pyry] Verify that activeUniformIndices is correct.
		// \todo [2012-01-24 pyry] Verify all instances.
		if (refBlockLayout.activeUniformIndices.size() != cmpBlockLayout.activeUniformIndices.size())
		{
			log << TestLog::Message << "Error: Number of active uniforms differ in block '" << instanceName
				<< "' (expected " << refBlockLayout.activeUniformIndices.size()
				<< ", got " << cmpBlockLayout.activeUniformIndices.size()
				<< ")" << TestLog::EndMessage;
			isOk = false;
		}

		for (vector<int>::const_iterator ndxIter = refBlockLayout.activeUniformIndices.begin(); ndxIter != refBlockLayout.activeUniformIndices.end(); ndxIter++)
		{
			const UniformLayoutEntry&	refEntry	= refLayout.uniforms[*ndxIter];
			int							cmpEntryNdx	= cmpLayout.getUniformIndex(refEntry.name.c_str());

			if (cmpEntryNdx < 0)
			{
				log << TestLog::Message << "Error: Uniform '" << refEntry.name << "' not found" << TestLog::EndMessage;
				isOk = false;
				continue;
			}

			const UniformLayoutEntry&	cmpEntry	= cmpLayout.uniforms[cmpEntryNdx];

			if (refEntry.type			!= cmpEntry.type			||
				refEntry.size			!= cmpEntry.size			||
				refEntry.offset			!= cmpEntry.offset			||
				refEntry.arrayStride	!= cmpEntry.arrayStride		||
				refEntry.matrixStride	!= cmpEntry.matrixStride	||
				refEntry.isRowMajor		!= cmpEntry.isRowMajor)
			{
				log << TestLog::Message << "Error: Layout mismatch in '" << refEntry.name << "':\n"
					<< "  expected: type = " << glu::getDataTypeName(refEntry.type) << ", size = " << refEntry.size << ", offset = " << refEntry.offset << ", array stride = "<< refEntry.arrayStride << ", matrix stride = " << refEntry.matrixStride << ", row major = " << (refEntry.isRowMajor ? "true" : "false") << "\n"
					<< "  got: type = " << glu::getDataTypeName(cmpEntry.type) << ", size = " << cmpEntry.size << ", offset = " << cmpEntry.offset << ", array stride = "<< cmpEntry.arrayStride << ", matrix stride = " << cmpEntry.matrixStride << ", row major = " << (cmpEntry.isRowMajor ? "true" : "false")
					<< TestLog::EndMessage;
				isOk = false;
			}
		}
	}

	return isOk;
}

bool UniformBlockCase::compareSharedBlocks (const UniformLayout& refLayout, const UniformLayout& cmpLayout) const
{
	TestLog&	log			= m_testCtx.getLog();
	bool		isOk		= true;
	int			numBlocks	= m_interface.getNumUniformBlocks();

	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
	{
		const UniformBlock&		block			= m_interface.getUniformBlock(blockNdx);
		bool					isArray			= block.isArray();
		std::string				instanceName	= string(block.getBlockName()) + (isArray ? "[0]" : "");
		int						refBlockNdx		= refLayout.getBlockIndex(instanceName.c_str());
		int						cmpBlockNdx		= cmpLayout.getBlockIndex(instanceName.c_str());
		bool					isUsed			= (block.getFlags() & (DECLARE_VERTEX|DECLARE_FRAGMENT)) != 0;

		if ((block.getFlags() & LAYOUT_SHARED) == 0)
			continue; // Not shared layout.

		DE_ASSERT(refBlockNdx >= 0);

		if (cmpBlockNdx < 0)
		{
			// Not found, should it?
			if (isUsed)
			{
				log << TestLog::Message << "Error: Uniform block '" << instanceName << "' not found" << TestLog::EndMessage;
				isOk = false;
			}

			continue; // Skip block.
		}

		const BlockLayoutEntry&		refBlockLayout	= refLayout.blocks[refBlockNdx];
		const BlockLayoutEntry&		cmpBlockLayout	= cmpLayout.blocks[cmpBlockNdx];

		if (refBlockLayout.activeUniformIndices.size() != cmpBlockLayout.activeUniformIndices.size())
		{
			log << TestLog::Message << "Error: Number of active uniforms differ in block '" << instanceName
				<< "' (expected " << refBlockLayout.activeUniformIndices.size()
				<< ", got " << cmpBlockLayout.activeUniformIndices.size()
				<< ")" << TestLog::EndMessage;
			isOk = false;
		}

		for (vector<int>::const_iterator ndxIter = refBlockLayout.activeUniformIndices.begin(); ndxIter != refBlockLayout.activeUniformIndices.end(); ndxIter++)
		{
			const UniformLayoutEntry&	refEntry	= refLayout.uniforms[*ndxIter];
			int							cmpEntryNdx	= cmpLayout.getUniformIndex(refEntry.name.c_str());

			if (cmpEntryNdx < 0)
			{
				log << TestLog::Message << "Error: Uniform '" << refEntry.name << "' not found" << TestLog::EndMessage;
				isOk = false;
				continue;
			}

			const UniformLayoutEntry&	cmpEntry	= cmpLayout.uniforms[cmpEntryNdx];

			if (refEntry.type		!= cmpEntry.type	||
				refEntry.size		!= cmpEntry.size	||
				refEntry.isRowMajor	!= cmpEntry.isRowMajor)
			{
				log << TestLog::Message << "Error: Layout mismatch in '" << refEntry.name << "':\n"
					<< "  expected: type = " << glu::getDataTypeName(refEntry.type) << ", size = " << refEntry.size << ", row major = " << (refEntry.isRowMajor ? "true" : "false") << "\n"
					<< "  got: type = " << glu::getDataTypeName(cmpEntry.type) << ", size = " << cmpEntry.size << ", row major = " << (cmpEntry.isRowMajor ? "true" : "false")
					<< TestLog::EndMessage;
				isOk = false;
			}
		}
	}

	return isOk;
}

bool UniformBlockCase::compareTypes (const UniformLayout& refLayout, const UniformLayout& cmpLayout) const
{
	TestLog&	log			= m_testCtx.getLog();
	bool		isOk		= true;
	int			numBlocks	= m_interface.getNumUniformBlocks();

	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
	{
		const UniformBlock&		block			= m_interface.getUniformBlock(blockNdx);
		bool					isArray			= block.isArray();
		int						numInstances	= isArray ? block.getArraySize() : 1;

		for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
		{
			std::ostringstream instanceName;

			instanceName << block.getBlockName();
			if (isArray)
				instanceName << "[" << instanceNdx << "]";

			int cmpBlockNdx = cmpLayout.getBlockIndex(instanceName.str().c_str());

			if (cmpBlockNdx < 0)
				continue;

			const BlockLayoutEntry& cmpBlockLayout = cmpLayout.blocks[cmpBlockNdx];

			for (vector<int>::const_iterator ndxIter = cmpBlockLayout.activeUniformIndices.begin(); ndxIter != cmpBlockLayout.activeUniformIndices.end(); ndxIter++)
			{
				const UniformLayoutEntry&	cmpEntry	= cmpLayout.uniforms[*ndxIter];
				int							refEntryNdx	= refLayout.getUniformIndex(cmpEntry.name.c_str());

				if (refEntryNdx < 0)
				{
					log << TestLog::Message << "Error: Uniform '" << cmpEntry.name << "' not found in reference layout" << TestLog::EndMessage;
					isOk = false;
					continue;
				}

				const UniformLayoutEntry&	refEntry	= refLayout.uniforms[refEntryNdx];

				// \todo [2012-11-26 pyry] Should we check other properties as well?
				if (refEntry.type != cmpEntry.type)
				{
					log << TestLog::Message << "Error: Uniform type mismatch in '" << refEntry.name << "':\n"
						<< "  expected: " << glu::getDataTypeName(refEntry.type) << "\n"
						<< "  got: " << glu::getDataTypeName(cmpEntry.type)
						<< TestLog::EndMessage;
					isOk = false;
				}
			}
		}
	}

	return isOk;
}

bool UniformBlockCase::checkLayoutIndices (const UniformLayout& layout) const
{
	TestLog&	log			= m_testCtx.getLog();
	int			numUniforms	= (int)layout.uniforms.size();
	int			numBlocks	= (int)layout.blocks.size();
	bool		isOk		= true;

	// Check uniform block indices.
	for (int uniformNdx = 0; uniformNdx < numUniforms; uniformNdx++)
	{
		const UniformLayoutEntry& uniform = layout.uniforms[uniformNdx];

		if (uniform.blockNdx < 0 || !deInBounds32(uniform.blockNdx, 0, numBlocks))
		{
			log << TestLog::Message << "Error: Invalid block index in uniform '" << uniform.name << "'" << TestLog::EndMessage;
			isOk = false;
		}
	}

	// Check active uniforms.
	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
	{
		const BlockLayoutEntry& block = layout.blocks[blockNdx];

		for (vector<int>::const_iterator uniformIter = block.activeUniformIndices.begin(); uniformIter != block.activeUniformIndices.end(); uniformIter++)
		{
			if (!deInBounds32(*uniformIter, 0, numUniforms))
			{
				log << TestLog::Message << "Error: Invalid active uniform index " << *uniformIter << " in block '" << block.name << "'" << TestLog::EndMessage;
				isOk = false;
			}
		}
	}

	return isOk;
}

bool UniformBlockCase::checkLayoutBounds (const UniformLayout& layout) const
{
	TestLog&	log			= m_testCtx.getLog();
	int			numUniforms	= (int)layout.uniforms.size();
	bool		isOk		= true;

	for (int uniformNdx = 0; uniformNdx < numUniforms; uniformNdx++)
	{
		const UniformLayoutEntry& uniform = layout.uniforms[uniformNdx];

		if (uniform.blockNdx < 0)
			continue;

		const BlockLayoutEntry&		block			= layout.blocks[uniform.blockNdx];
		bool						isMatrix		= glu::isDataTypeMatrix(uniform.type);
		int							numVecs			= isMatrix ? (uniform.isRowMajor ? glu::getDataTypeMatrixNumRows(uniform.type) : glu::getDataTypeMatrixNumColumns(uniform.type)) : 1;
		int							numComps		= isMatrix ? (uniform.isRowMajor ? glu::getDataTypeMatrixNumColumns(uniform.type) : glu::getDataTypeMatrixNumRows(uniform.type)) : glu::getDataTypeScalarSize(uniform.type);
		int							numElements		= uniform.size;
		const int					compSize		= sizeof(deUint32);
		int							vecSize			= numComps*compSize;

		int							minOffset		= 0;
		int							maxOffset		= 0;

		// For negative strides.
		minOffset	= de::min(minOffset, (numVecs-1)*uniform.matrixStride);
		minOffset	= de::min(minOffset, (numElements-1)*uniform.arrayStride);
		minOffset	= de::min(minOffset, (numElements-1)*uniform.arrayStride + (numVecs-1)*uniform.matrixStride);

		maxOffset	= de::max(maxOffset, vecSize);
		maxOffset	= de::max(maxOffset, (numVecs-1)*uniform.matrixStride + vecSize);
		maxOffset	= de::max(maxOffset, (numElements-1)*uniform.arrayStride + vecSize);
		maxOffset	= de::max(maxOffset, (numElements-1)*uniform.arrayStride + (numVecs-1)*uniform.matrixStride + vecSize);

		if (uniform.offset+minOffset < 0 || uniform.offset+maxOffset > block.size)
		{
			log << TestLog::Message << "Error: Uniform '" << uniform.name << "' out of block bounds" << TestLog::EndMessage;
			isOk = false;
		}
	}

	return isOk;
}

bool UniformBlockCase::checkIndexQueries (deUint32 program, const UniformLayout& layout) const
{
	tcu::TestLog&				log			= m_testCtx.getLog();
	const glw::Functions&		gl			= m_renderCtx.getFunctions();
	bool						allOk		= true;

	// \note Spec mandates that uniform blocks are assigned consecutive locations from 0
	//		 to ACTIVE_UNIFORM_BLOCKS. BlockLayoutEntries are stored in that order in UniformLayout.
	for (int blockNdx = 0; blockNdx < (int)layout.blocks.size(); blockNdx++)
	{
		const BlockLayoutEntry&		block		= layout.blocks[blockNdx];
		const int					queriedNdx	= gl.getUniformBlockIndex(program, block.name.c_str());

		if (queriedNdx != blockNdx)
		{
			log << TestLog::Message << "ERROR: glGetUniformBlockIndex(" << block.name << ") returned " << queriedNdx << ", expected " << blockNdx << "!" << TestLog::EndMessage;
			allOk = false;
		}

		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformBlockIndex()");
	}

	return allOk;
}

bool UniformBlockCase::render (deUint32 program) const
{
	tcu::TestLog&				log				= m_testCtx.getLog();
	const glw::Functions&		gl				= m_renderCtx.getFunctions();
	de::Random					rnd				(deStringHash(getName()));
	const tcu::RenderTarget&	renderTarget	= m_renderCtx.getRenderTarget();
	const int					viewportW		= de::min(renderTarget.getWidth(),	128);
	const int					viewportH		= de::min(renderTarget.getHeight(),	128);
	const int					viewportX		= rnd.getInt(0, renderTarget.getWidth()		- viewportW);
	const int					viewportY		= rnd.getInt(0, renderTarget.getHeight()	- viewportH);

	gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
	gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);

	// Draw
	{
		const float position[] =
		{
			-1.0f, -1.0f, 0.0f, 1.0f,
			-1.0f, +1.0f, 0.0f, 1.0f,
			+1.0f, -1.0f, 0.0f, 1.0f,
			+1.0f, +1.0f, 0.0f, 1.0f
		};
		const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 };

		gl.viewport(viewportX, viewportY, viewportW, viewportH);

		glu::VertexArrayBinding posArray = glu::va::Float("a_position", 4, 4, 0, &position[0]);
		glu::draw(m_renderCtx, program, 1, &posArray,
				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
	}

	// Verify that all pixels are white.
	{
		tcu::Surface	pixels			(viewportW, viewportH);
		int				numFailedPixels = 0;

		glu::readPixels(m_renderCtx, viewportX, viewportY, pixels.getAccess());
		GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels failed");

		for (int y = 0; y < pixels.getHeight(); y++)
		{
			for (int x = 0; x < pixels.getWidth(); x++)
			{
				if (pixels.getPixel(x, y) != tcu::RGBA::white())
					numFailedPixels += 1;
			}
		}

		if (numFailedPixels > 0)
		{
			log << TestLog::Image("Image", "Rendered image", pixels);
			log << TestLog::Message << "Image comparison failed, got " << numFailedPixels << " non-white pixels" << TestLog::EndMessage;
		}

		return numFailedPixels == 0;
	}
}

} // gls
} // deqp