text: /*-------------------------------------------------------------------------
   * drawElements Quality Program OpenGL ES 3.1 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 Program interface query tests.
   *//*--------------------------------------------------------------------*/
  
  #include "es31fProgramInterfaceQueryTests.hpp"
  #include "es31fProgramInterfaceQueryTestCase.hpp"
  #include "es31fProgramInterfaceDefinition.hpp"
  #include "es31fProgramInterfaceDefinitionUtil.hpp"
  #include "tcuTestLog.hpp"
  #include "tcuStringTemplate.hpp"
  #include "gluShaderProgram.hpp"
  #include "gluVarTypeUtil.hpp"
  #include "gluStrUtil.hpp"
  #include "gluContextInfo.hpp"
  #include "glwFunctions.hpp"
  #include "glwEnums.hpp"
  #include "deRandom.hpp"
  #include "deString.h"
  #include "deStringUtil.hpp"
  #include "deSharedPtr.hpp"
  #include "deUniquePtr.hpp"
  #include "deSTLUtil.hpp"
  #include "deArrayUtil.hpp"
  
  #include <set>
  #include <map>
  
  namespace deqp
  {
  namespace gles31
  {
  namespace Functional
  {
  namespace
  {
  
  static int getTypeSize (glu::DataType type)
  {
  	if (type == glu::TYPE_FLOAT)
  		return 4;
  	else if (type == glu::TYPE_INT || type == glu::TYPE_UINT)
  		return 4;
  	else if (type == glu::TYPE_BOOL)
  		return 4; // uint
  
  	DE_ASSERT(false);
  	return 0;
  }
  
  static int getVarTypeSize (const glu::VarType& type)
  {
  	if (type.isBasicType())
  		return glu::getDataTypeScalarSize(type.getBasicType()) * getTypeSize(glu::getDataTypeScalarType(type.getBasicType()));
  	else if (type.isStructType())
  	{
  		int size = 0;
  		for (int ndx = 0; ndx < type.getStructPtr()->getNumMembers(); ++ndx)
  			size += getVarTypeSize(type.getStructPtr()->getMember(ndx).getType());
  		return size;
  	}
  	else if (type.isArrayType())
  	{
  		if (type.getArraySize() == glu::VarType::UNSIZED_ARRAY)
  			return getVarTypeSize(type.getElementType());
  		else
  			return type.getArraySize() * getVarTypeSize(type.getElementType());
  	}
  	else
  	{
  		DE_ASSERT(false);
  		return 0;
  	}
  }
  
  static std::string convertGLTypeNameToTestName (const char* glName)
  {
  	// vectors and matrices are fine as is
  	{
  		if (deStringBeginsWith(glName, "vec")  == DE_TRUE ||
  			deStringBeginsWith(glName, "ivec") == DE_TRUE ||
  			deStringBeginsWith(glName, "uvec") == DE_TRUE ||
  			deStringBeginsWith(glName, "bvec") == DE_TRUE ||
  			deStringBeginsWith(glName, "mat")  == DE_TRUE)
  			return std::string(glName);
  	}
  
  	// convert camel case to use underscore
  	{
  		std::ostringstream	buf;
  		std::istringstream	name					(glName);
  		bool				mergeNextToken			= false;
  		bool				previousTokenWasDigit	= false;
  
  		while (!name.eof())
  		{
  			std::ostringstream token;
  
  			while (name.peek() != EOF)
  			{
  				if ((de::isDigit((char)name.peek()) || de::isUpper((char)name.peek())) && token.tellp())
  					break;
  
  				token << de::toLower((char)name.get());
  			}
  
  			if (buf.str().empty() || mergeNextToken)
  				buf << token.str();
  			else
  				buf << '_' << token.str();
  
  			// Single char causes next char to be merged (don't split initialisms or acronyms) unless it is 'D' after a number (split to ..._2d_acronym_aa
  			mergeNextToken = false;
  			if (token.tellp() == (std::streamoff)1)
  			{
  				if (!previousTokenWasDigit || token.str()[0] != 'd')
  					mergeNextToken = true;
  
  				previousTokenWasDigit = de::isDigit(token.str()[0]);
  			}
  			else
  				previousTokenWasDigit = false;
  		}
  
  		return buf.str();
  	}
  }
  
  static glw::GLenum getProgramInterfaceGLEnum (ProgramInterface interface)
  {
  	static const glw::GLenum s_enums[] =
  	{
  		GL_UNIFORM,						// PROGRAMINTERFACE_UNIFORM
  		GL_UNIFORM_BLOCK,				// PROGRAMINTERFACE_UNIFORM_BLOCK
  		GL_ATOMIC_COUNTER_BUFFER,		// PROGRAMINTERFACE_ATOMIC_COUNTER_BUFFER
  		GL_PROGRAM_INPUT,				// PROGRAMINTERFACE_PROGRAM_INPUT
  		GL_PROGRAM_OUTPUT,				// PROGRAMINTERFACE_PROGRAM_OUTPUT
  		GL_TRANSFORM_FEEDBACK_VARYING,	// PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING
  		GL_BUFFER_VARIABLE,				// PROGRAMINTERFACE_BUFFER_VARIABLE
  		GL_SHADER_STORAGE_BLOCK,		// PROGRAMINTERFACE_SHADER_STORAGE_BLOCK
  	};
  
  	return de::getSizedArrayElement<PROGRAMINTERFACE_LAST>(s_enums, interface);
  }
  
  static glu::ShaderType getShaderMaskFirstStage (deUint32 mask)
  {
  	if (mask & (1u << glu::SHADERTYPE_COMPUTE))
  		return glu::SHADERTYPE_COMPUTE;
  
  	if (mask & (1u << glu::SHADERTYPE_VERTEX))
  		return glu::SHADERTYPE_VERTEX;
  
  	if (mask & (1u << glu::SHADERTYPE_TESSELLATION_CONTROL))
  		return glu::SHADERTYPE_TESSELLATION_CONTROL;
  
  	if (mask & (1u << glu::SHADERTYPE_TESSELLATION_EVALUATION))
  		return glu::SHADERTYPE_TESSELLATION_EVALUATION;
  
  	if (mask & (1u << glu::SHADERTYPE_GEOMETRY))
  		return glu::SHADERTYPE_GEOMETRY;
  
  	if (mask & (1u << glu::SHADERTYPE_FRAGMENT))
  		return glu::SHADERTYPE_FRAGMENT;
  
  	DE_ASSERT(false);
  	return glu::SHADERTYPE_LAST;
  }
  
  static glu::ShaderType getShaderMaskLastStage (deUint32 mask)
  {
  	if (mask & (1u << glu::SHADERTYPE_FRAGMENT))
  		return glu::SHADERTYPE_FRAGMENT;
  
  	if (mask & (1u << glu::SHADERTYPE_GEOMETRY))
  		return glu::SHADERTYPE_GEOMETRY;
  
  	if (mask & (1u << glu::SHADERTYPE_TESSELLATION_EVALUATION))
  		return glu::SHADERTYPE_TESSELLATION_EVALUATION;
  
  	if (mask & (1u << glu::SHADERTYPE_TESSELLATION_CONTROL))
  		return glu::SHADERTYPE_TESSELLATION_CONTROL;
  
  	if (mask & (1u << glu::SHADERTYPE_VERTEX))
  		return glu::SHADERTYPE_VERTEX;
  
  	if (mask & (1u << glu::SHADERTYPE_COMPUTE))
  		return glu::SHADERTYPE_COMPUTE;
  
  	DE_ASSERT(false);
  	return glu::SHADERTYPE_LAST;
  }
  
  static bool checkSupport(Context& ctx)
  {
  	auto ctxType = ctx.getRenderContext().getType();
  	return contextSupports(ctxType, glu::ApiType::es(3, 2)) ||
  		   contextSupports(ctxType, glu::ApiType::core(4, 5));
  }
  
  static std::string specializeShader(Context& context, const char* code)
  {
  	const glu::GLSLVersion				glslVersion			= glu::getContextTypeGLSLVersion(context.getRenderContext().getType());
  	std::map<std::string, std::string>	specializationMap;
  
  	specializationMap["GLSL_VERSION_DECL"] = glu::getGLSLVersionDeclaration(glslVersion);
  
  	return tcu::StringTemplate(code).specialize(specializationMap);
  }
  
  namespace ResourceDefinition
  {
  
  class Node
  {
  public:
  	enum NodeType
  	{
  		TYPE_PROGRAM = 0,
  		TYPE_SHADER,
  		TYPE_DEFAULT_BLOCK,
  		TYPE_VARIABLE,
  		TYPE_INTERFACE_BLOCK,
  		TYPE_ARRAY_ELEMENT,
  		TYPE_STRUCT_MEMBER,
  		TYPE_STORAGE_QUALIFIER,
  		TYPE_LAYOUT_QUALIFIER,
  		TYPE_SHADER_SET,
  		TYPE_INTERPOLATION_QUALIFIER,
  		TYPE_TRANSFORM_FEEDBACK_TARGET,
  
  		TYPE_LAST
  	};
  
  	typedef de::SharedPtr<const Node> SharedPtr;
  
  							Node				(NodeType type, const SharedPtr& enclosingNode) : m_type(type), m_enclosingNode(enclosingNode) { DE_ASSERT(type < TYPE_LAST); }
  	virtual					~Node				(void) { }
  
  	inline const Node*		getEnclosingNode	(void) const					{ return m_enclosingNode.get();	}
  	inline NodeType			getType				(void) const					{ return m_type;				}
  
  private:
  	const NodeType			m_type;
  	const SharedPtr			m_enclosingNode;
  };
  
  class Program : public Node
  {
  public:
  	Program (bool separable = false)
  		: Node			(TYPE_PROGRAM, SharedPtr())
  		, m_separable	(separable)
  	{
  	}
  
  	const bool m_separable;
  };
  
  class Shader : public Node
  {
  public:
  	Shader (const SharedPtr& enclosingNode, glu::ShaderType type, glu::GLSLVersion version)
  		: Node		(TYPE_SHADER, enclosingNode)
  		, m_type	(type)
  		, m_version	(version)
  	{
  		DE_ASSERT(enclosingNode->getType() == TYPE_PROGRAM);
  		DE_ASSERT(type < glu::SHADERTYPE_LAST);
  	}
  
  	const glu::ShaderType	m_type;
  	const glu::GLSLVersion	m_version;
  };
  
  class DefaultBlock : public Node
  {
  public:
  	DefaultBlock (const SharedPtr& enclosing)
  		: Node(TYPE_DEFAULT_BLOCK, enclosing)
  	{
  		// enclosed by the shader
  		DE_ASSERT(enclosing->getType() == TYPE_SHADER		||
  				  enclosing->getType() == TYPE_SHADER_SET);
  	}
  };
  
  class StorageQualifier : public Node
  {
  public:
  	StorageQualifier (const SharedPtr& enclosing, glu::Storage storage)
  		: Node		(TYPE_STORAGE_QUALIFIER, enclosing)
  		, m_storage	(storage)
  	{
  		// not a part of any block
  		DE_ASSERT(enclosing->getType() == TYPE_DEFAULT_BLOCK);
  	}
  
  	const glu::Storage	m_storage;
  };
  
  class Variable : public Node
  {
  public:
  	Variable (const SharedPtr& enclosing, glu::DataType dataType)
  		: Node			(TYPE_VARIABLE, enclosing)
  		, m_dataType	(dataType)
  	{
  		DE_ASSERT(enclosing->getType() == TYPE_STORAGE_QUALIFIER		||
  				  enclosing->getType() == TYPE_LAYOUT_QUALIFIER			||
  				  enclosing->getType() == TYPE_INTERPOLATION_QUALIFIER	||
  				  enclosing->getType() == TYPE_INTERFACE_BLOCK			||
  				  enclosing->getType() == TYPE_ARRAY_ELEMENT			||
  				  enclosing->getType() == TYPE_STRUCT_MEMBER			||
  				  enclosing->getType() == TYPE_TRANSFORM_FEEDBACK_TARGET);
  	}
  
  	const glu::DataType	m_dataType;
  };
  
  class InterfaceBlock : public Node
  {
  public:
  	InterfaceBlock (const SharedPtr& enclosing, bool named)
  		: Node		(TYPE_INTERFACE_BLOCK, enclosing)
  		, m_named	(named)
  	{
  		// Must be storage qualified
  		const Node* storageNode = enclosing.get();
  		while (storageNode->getType() == TYPE_ARRAY_ELEMENT ||
  			   storageNode->getType() == TYPE_LAYOUT_QUALIFIER)
  		{
  			storageNode = storageNode->getEnclosingNode();
  		}
  
  		DE_ASSERT(storageNode->getType() == TYPE_STORAGE_QUALIFIER);
  		DE_UNREF(storageNode);
  	}
  
  	const bool	m_named;
  };
  
  class ArrayElement : public Node
  {
  public:
  	ArrayElement (const SharedPtr& enclosing, int arraySize = DEFAULT_SIZE)
  		: Node			(TYPE_ARRAY_ELEMENT, enclosing)
  		, m_arraySize	(arraySize)
  	{
  		DE_ASSERT(enclosing->getType() == TYPE_STORAGE_QUALIFIER		||
  				  enclosing->getType() == TYPE_LAYOUT_QUALIFIER			||
  				  enclosing->getType() == TYPE_INTERPOLATION_QUALIFIER	||
  				  enclosing->getType() == TYPE_INTERFACE_BLOCK			||
  				  enclosing->getType() == TYPE_ARRAY_ELEMENT			||
  				  enclosing->getType() == TYPE_STRUCT_MEMBER			||
  				  enclosing->getType() == TYPE_TRANSFORM_FEEDBACK_TARGET);
  	}
  
  	const int m_arraySize;
  
  	enum
  	{
  		DEFAULT_SIZE	= -1,
  		UNSIZED_ARRAY	= -2,
  	};
  };
  
  class StructMember : public Node
  {
  public:
  	StructMember (const SharedPtr& enclosing)
  		: Node(TYPE_STRUCT_MEMBER, enclosing)
  	{
  		DE_ASSERT(enclosing->getType() == TYPE_STORAGE_QUALIFIER		||
  				  enclosing->getType() == TYPE_LAYOUT_QUALIFIER			||
  				  enclosing->getType() == TYPE_INTERPOLATION_QUALIFIER	||
  				  enclosing->getType() == TYPE_INTERFACE_BLOCK			||
  				  enclosing->getType() == TYPE_ARRAY_ELEMENT			||
  				  enclosing->getType() == TYPE_STRUCT_MEMBER			||
  				  enclosing->getType() == TYPE_TRANSFORM_FEEDBACK_TARGET);
  	}
  };
  
  class LayoutQualifier : public Node
  {
  public:
  	LayoutQualifier (const SharedPtr& enclosing, const glu::Layout& layout)
  		: Node		(TYPE_LAYOUT_QUALIFIER, enclosing)
  		, m_layout	(layout)
  	{
  		DE_ASSERT(enclosing->getType() == TYPE_STORAGE_QUALIFIER		||
  				  enclosing->getType() == TYPE_LAYOUT_QUALIFIER			||
  				  enclosing->getType() == TYPE_INTERPOLATION_QUALIFIER	||
  				  enclosing->getType() == TYPE_DEFAULT_BLOCK			||
  				  enclosing->getType() == TYPE_INTERFACE_BLOCK);
  	}
  
  	const glu::Layout m_layout;
  };
  
  class InterpolationQualifier : public Node
  {
  public:
  	InterpolationQualifier (const SharedPtr& enclosing, const glu::Interpolation& interpolation)
  		: Node				(TYPE_INTERPOLATION_QUALIFIER, enclosing)
  		, m_interpolation	(interpolation)
  	{
  		DE_ASSERT(enclosing->getType() == TYPE_STORAGE_QUALIFIER		||
  				  enclosing->getType() == TYPE_LAYOUT_QUALIFIER			||
  				  enclosing->getType() == TYPE_INTERPOLATION_QUALIFIER	||
  				  enclosing->getType() == TYPE_DEFAULT_BLOCK			||
  				  enclosing->getType() == TYPE_INTERFACE_BLOCK);
  	}
  
  	const glu::Interpolation m_interpolation;
  };
  
  class ShaderSet : public Node
  {
  public:
  				ShaderSet			(const SharedPtr& enclosing, glu::GLSLVersion version);
  				ShaderSet			(const SharedPtr& enclosing, glu::GLSLVersion version, deUint32 stagesPresentBits, deUint32 stagesReferencingBits);
  
  	void		setStage			(glu::ShaderType type, bool referencing);
  	bool		isStagePresent		(glu::ShaderType stage) const;
  	bool		isStageReferencing	(glu::ShaderType stage) const;
  
  	deUint32	getReferencingMask	(void) const;
  
  	const glu::GLSLVersion	m_version;
  private:
  	bool		m_stagePresent[glu::SHADERTYPE_LAST];
  	bool		m_stageReferencing[glu::SHADERTYPE_LAST];
  };
  
  ShaderSet::ShaderSet (const SharedPtr& enclosing, glu::GLSLVersion version)
  	: Node		(TYPE_SHADER_SET, enclosing)
  	, m_version	(version)
  {
  	DE_ASSERT(enclosing->getType() == TYPE_PROGRAM);
  
  	deMemset(m_stagePresent, 0, sizeof(m_stagePresent));
  	deMemset(m_stageReferencing, 0, sizeof(m_stageReferencing));
  }
  
  ShaderSet::ShaderSet (const SharedPtr&	enclosing,
  					  glu::GLSLVersion	version,
  					  deUint32			stagesPresentBits,
  					  deUint32			stagesReferencingBits)
  	: Node		(TYPE_SHADER_SET, enclosing)
  	, m_version	(version)
  {
  	for (deUint32 stageNdx = 0; stageNdx < glu::SHADERTYPE_LAST; ++stageNdx)
  	{
  		const deUint32	stageMask			= (1u << stageNdx);
  		const bool		stagePresent		= (stagesPresentBits & stageMask) != 0;
  		const bool		stageReferencing	= (stagesReferencingBits & stageMask) != 0;
  
  		DE_ASSERT(stagePresent || !stageReferencing);
  
  		m_stagePresent[stageNdx]		= stagePresent;
  		m_stageReferencing[stageNdx]	= stageReferencing;
  	}
  }
  
  void ShaderSet::setStage (glu::ShaderType type, bool referencing)
  {
  	DE_ASSERT(type < glu::SHADERTYPE_LAST);
  	m_stagePresent[type] = true;
  	m_stageReferencing[type] = referencing;
  }
  
  bool ShaderSet::isStagePresent (glu::ShaderType stage) const
  {
  	DE_ASSERT(stage < glu::SHADERTYPE_LAST);
  	return m_stagePresent[stage];
  }
  
  bool ShaderSet::isStageReferencing (glu::ShaderType stage) const
  {
  	DE_ASSERT(stage < glu::SHADERTYPE_LAST);
  	return m_stageReferencing[stage];
  }
  
  deUint32 ShaderSet::getReferencingMask (void) const
  {
  	deUint32 mask = 0;
  	for (deUint32 stage = 0; stage < glu::SHADERTYPE_LAST; ++stage)
  	{
  		if (m_stageReferencing[stage])
  			mask |= (1u << stage);
  	}
  	return mask;
  }
  
  class TransformFeedbackTarget : public Node
  {
  public:
  	TransformFeedbackTarget (const SharedPtr& enclosing, const char* builtinVarName = DE_NULL)
  		: Node				(TYPE_TRANSFORM_FEEDBACK_TARGET, enclosing)
  		, m_builtinVarName	(builtinVarName)
  	{
  	}
  
  	const char* const m_builtinVarName;
  };
  
  } // ResourceDefinition
  
  static glu::Precision getDataTypeDefaultPrecision (const glu::DataType& type)
  {
  	if (glu::isDataTypeBoolOrBVec(type))
  		return glu::PRECISION_LAST;
  	else if (glu::isDataTypeScalarOrVector(type) || glu::isDataTypeMatrix(type))
  		return glu::PRECISION_HIGHP;
  	else if (glu::isDataTypeSampler(type))
  		return glu::PRECISION_HIGHP;
  	else if (glu::isDataTypeImage(type))
  		return glu::PRECISION_HIGHP;
  	else if (type == glu::TYPE_UINT_ATOMIC_COUNTER)
  		return glu::PRECISION_HIGHP;
  
  	DE_ASSERT(false);
  	return glu::PRECISION_LAST;
  }
  
  static de::MovePtr<ProgramInterfaceDefinition::Program>	generateProgramDefinitionFromResource (const ResourceDefinition::Node* resource)
  {
  	de::MovePtr<ProgramInterfaceDefinition::Program>	program	(new ProgramInterfaceDefinition::Program());
  	const ResourceDefinition::Node*						head	= resource;
  
  	if (head->getType() == ResourceDefinition::Node::TYPE_VARIABLE)
  	{
  		DE_ASSERT(dynamic_cast<const ResourceDefinition::Variable*>(resource));
  
  		enum BindingType
  		{
  			BINDING_VARIABLE,
  			BINDING_INTERFACE_BLOCK,
  			BINDING_DEFAULT_BLOCK
  		};
  
  		int											structNdx				= 0;
  		int											autoAssignArraySize		= 0;
  		const glu::DataType							basicType				= static_cast<const ResourceDefinition::Variable*>(resource)->m_dataType;
  		BindingType									boundObject				= BINDING_VARIABLE;
  		glu::VariableDeclaration					variable				(glu::VarType(basicType, getDataTypeDefaultPrecision(basicType)), "target");
  		glu::InterfaceBlock							interfaceBlock;
  		ProgramInterfaceDefinition::DefaultBlock	defaultBlock;
  		std::vector<std::string>					feedbackTargetVaryingPath;
  		bool										feedbackTargetSet		= false;
  
  		// image specific
  		if (glu::isDataTypeImage(basicType))
  		{
  			variable.memoryAccessQualifierBits |= glu::MEMORYACCESSQUALIFIER_READONLY_BIT;
  			variable.layout.binding = 1;
  
  			if (basicType >= glu::TYPE_IMAGE_2D && basicType <= glu::TYPE_IMAGE_3D)
  				variable.layout.format = glu::FORMATLAYOUT_RGBA8;
  			else if (basicType >= glu::TYPE_INT_IMAGE_2D && basicType <= glu::TYPE_INT_IMAGE_3D)
  				variable.layout.format = glu::FORMATLAYOUT_RGBA8I;
  			else if (basicType >= glu::TYPE_UINT_IMAGE_2D && basicType <= glu::TYPE_UINT_IMAGE_3D)
  				variable.layout.format = glu::FORMATLAYOUT_RGBA8UI;
  			else
  				DE_ASSERT(false);
  		}
  
  		// atomic counter specific
  		if (basicType == glu::TYPE_UINT_ATOMIC_COUNTER)
  			variable.layout.binding = 1;
  
  		for (head = head->getEnclosingNode(); head; head = head->getEnclosingNode())
  		{
  			if (head->getType() == ResourceDefinition::Node::TYPE_STORAGE_QUALIFIER)
  			{
  				const ResourceDefinition::StorageQualifier* qualifier = static_cast<const ResourceDefinition::StorageQualifier*>(head);
  
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::StorageQualifier*>(head));
  
  				if (boundObject == BINDING_VARIABLE)
  				{
  					DE_ASSERT(variable.storage == glu::STORAGE_LAST);
  					variable.storage = qualifier->m_storage;
  				}
  				else if (boundObject == BINDING_INTERFACE_BLOCK)
  				{
  					DE_ASSERT(interfaceBlock.storage == glu::STORAGE_LAST);
  					interfaceBlock.storage = qualifier->m_storage;
  				}
  				else
  					DE_ASSERT(false);
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_LAYOUT_QUALIFIER)
  			{
  				const ResourceDefinition::LayoutQualifier*	qualifier		= static_cast<const ResourceDefinition::LayoutQualifier*>(head);
  				glu::Layout*								targetLayout	= DE_NULL;
  
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::LayoutQualifier*>(head));
  
  				if (boundObject == BINDING_VARIABLE)
  					targetLayout = &variable.layout;
  				else if (boundObject == BINDING_INTERFACE_BLOCK)
  					targetLayout = &interfaceBlock.layout;
  				else
  					DE_ASSERT(false);
  
  				if (qualifier->m_layout.location != -1)
  					targetLayout->location = qualifier->m_layout.location;
  
  				if (qualifier->m_layout.binding != -1)
  					targetLayout->binding = qualifier->m_layout.binding;
  
  				if (qualifier->m_layout.offset != -1)
  					targetLayout->offset = qualifier->m_layout.offset;
  
  				if (qualifier->m_layout.format != glu::FORMATLAYOUT_LAST)
  					targetLayout->format = qualifier->m_layout.format;
  
  				if (qualifier->m_layout.matrixOrder != glu::MATRIXORDER_LAST)
  					targetLayout->matrixOrder = qualifier->m_layout.matrixOrder;
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_INTERPOLATION_QUALIFIER)
  			{
  				const ResourceDefinition::InterpolationQualifier* qualifier = static_cast<const ResourceDefinition::InterpolationQualifier*>(head);
  
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::InterpolationQualifier*>(head));
  
  				if (boundObject == BINDING_VARIABLE)
  					variable.interpolation = qualifier->m_interpolation;
  				else
  					DE_ASSERT(false);
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_ARRAY_ELEMENT)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::ArrayElement*>(head));
  
  				const ResourceDefinition::ArrayElement*	arrayElement = static_cast<const ResourceDefinition::ArrayElement*>(head);
  				int										arraySize;
  
  				// Vary array size per level
  				if (arrayElement->m_arraySize == ResourceDefinition::ArrayElement::DEFAULT_SIZE)
  				{
  					if (--autoAssignArraySize <= 1)
  						autoAssignArraySize = 3;
  
  					arraySize = autoAssignArraySize;
  				}
  				else if (arrayElement->m_arraySize == ResourceDefinition::ArrayElement::UNSIZED_ARRAY)
  					arraySize = glu::VarType::UNSIZED_ARRAY;
  				else
  					arraySize = arrayElement->m_arraySize;
  
  				if (boundObject == BINDING_VARIABLE)
  					variable.varType = glu::VarType(variable.varType, arraySize);
  				else if (boundObject == BINDING_INTERFACE_BLOCK)
  					interfaceBlock.dimensions.push_back(arraySize);
  				else
  					DE_ASSERT(false);
  
  				if (feedbackTargetSet)
  					feedbackTargetVaryingPath.back().append("[0]");
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_STRUCT_MEMBER)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::StructMember*>(head));
  				DE_ASSERT(boundObject == BINDING_VARIABLE);
  
  				// Struct members cannot contain any qualifiers except precision
  				DE_ASSERT(variable.interpolation == glu::INTERPOLATION_LAST);
  				DE_ASSERT(variable.layout == glu::Layout());
  				DE_ASSERT(variable.memoryAccessQualifierBits == 0);
  				DE_ASSERT(variable.storage == glu::STORAGE_LAST);
  
  				{
  					glu::StructType* structPtr = new glu::StructType(("StructType" + de::toString(structNdx++)).c_str());
  					structPtr->addMember(variable.name.c_str(), variable.varType);
  
  					variable = glu::VariableDeclaration(glu::VarType(structPtr), "target");
  				}
  
  				if (feedbackTargetSet)
  					feedbackTargetVaryingPath.push_back("target");
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::InterfaceBlock*>(head));
  				DE_ASSERT(boundObject == BINDING_VARIABLE);
  
  				const bool named = static_cast<const ResourceDefinition::InterfaceBlock*>(head)->m_named;
  
  				boundObject = BINDING_INTERFACE_BLOCK;
  
  				interfaceBlock.interfaceName = "TargetInterface";
  				interfaceBlock.instanceName = (named) ? ("targetInstance") : ("");
  				interfaceBlock.variables.push_back(variable);
  
  				if (feedbackTargetSet && !interfaceBlock.instanceName.empty())
  					feedbackTargetVaryingPath.push_back(interfaceBlock.interfaceName);
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::DefaultBlock*>(head));
  				DE_ASSERT(boundObject == BINDING_VARIABLE || boundObject == BINDING_INTERFACE_BLOCK);
  
  				if (boundObject == BINDING_VARIABLE)
  					defaultBlock.variables.push_back(variable);
  				else if (boundObject == BINDING_INTERFACE_BLOCK)
  					defaultBlock.interfaceBlocks.push_back(interfaceBlock);
  				else
  					DE_ASSERT(false);
  
  				boundObject = BINDING_DEFAULT_BLOCK;
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_SHADER)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::Shader*>(head));
  
  				const ResourceDefinition::Shader*	shaderDef	= static_cast<const ResourceDefinition::Shader*>(head);
  				ProgramInterfaceDefinition::Shader* shader		= program->addShader(shaderDef->m_type, shaderDef->m_version);
  
  				shader->getDefaultBlock() = defaultBlock;
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_SHADER_SET)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::ShaderSet*>(head));
  
  				const ResourceDefinition::ShaderSet* shaderDef = static_cast<const ResourceDefinition::ShaderSet*>(head);
  
  				for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; ++shaderType)
  				{
  					if (shaderDef->isStagePresent((glu::ShaderType)shaderType))
  					{
  						ProgramInterfaceDefinition::Shader* shader = program->addShader((glu::ShaderType)shaderType, shaderDef->m_version);
  
  						if (shaderDef->isStageReferencing((glu::ShaderType)shaderType))
  							shader->getDefaultBlock() = defaultBlock;
  					}
  				}
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_PROGRAM)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::Program*>(head));
  
  				const ResourceDefinition::Program* programDef = static_cast<const ResourceDefinition::Program*>(head);
  
  				program->setSeparable(programDef->m_separable);
  
  				DE_ASSERT(feedbackTargetSet == !feedbackTargetVaryingPath.empty());
  				if (!feedbackTargetVaryingPath.empty())
  				{
  					std::ostringstream buf;
  
  					for (std::vector<std::string>::reverse_iterator it = feedbackTargetVaryingPath.rbegin(); it != feedbackTargetVaryingPath.rend(); ++it)
  					{
  						if (it != feedbackTargetVaryingPath.rbegin())
  							buf << ".";
  						buf << *it;
  					}
  
  					program->addTransformFeedbackVarying(buf.str());
  					program->setTransformFeedbackMode(GL_INTERLEAVED_ATTRIBS);
  				}
  				break;
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_TRANSFORM_FEEDBACK_TARGET)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::TransformFeedbackTarget*>(head));
  
  				const ResourceDefinition::TransformFeedbackTarget* feedbackTarget = static_cast<const ResourceDefinition::TransformFeedbackTarget*>(head);
  
  				DE_ASSERT(feedbackTarget->m_builtinVarName == DE_NULL);
  				DE_UNREF(feedbackTarget);
  
  				feedbackTargetSet = true;
  				feedbackTargetVaryingPath.push_back(variable.name);
  			}
  			else
  			{
  				DE_ASSERT(DE_FALSE);
  				break;
  			}
  		}
  	}
  	else if (head->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK ||
  			 head->getType() == ResourceDefinition::Node::TYPE_TRANSFORM_FEEDBACK_TARGET)
  	{
  		const char* feedbackTargetVaryingName = DE_NULL;
  
  		// empty default block
  
  		for (; head; head = head->getEnclosingNode())
  		{
  			if (head->getType() == ResourceDefinition::Node::TYPE_SHADER)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::Shader*>(head));
  
  				const ResourceDefinition::Shader* shaderDef = static_cast<const ResourceDefinition::Shader*>(head);
  
  				program->addShader(shaderDef->m_type, shaderDef->m_version);
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_SHADER_SET)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::ShaderSet*>(head));
  
  				const ResourceDefinition::ShaderSet* shaderDef = static_cast<const ResourceDefinition::ShaderSet*>(head);
  
  				for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; ++shaderType)
  					if (shaderDef->isStagePresent((glu::ShaderType)shaderType))
  						program->addShader((glu::ShaderType)shaderType, shaderDef->m_version);
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_PROGRAM)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::Program*>(head));
  
  				const ResourceDefinition::Program* programDef = static_cast<const ResourceDefinition::Program*>(head);
  
  				program->setSeparable(programDef->m_separable);
  				if (feedbackTargetVaryingName)
  				{
  					program->addTransformFeedbackVarying(std::string(feedbackTargetVaryingName));
  					program->setTransformFeedbackMode(GL_INTERLEAVED_ATTRIBS);
  				}
  				break;
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_TRANSFORM_FEEDBACK_TARGET)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::TransformFeedbackTarget*>(head));
  
  				const ResourceDefinition::TransformFeedbackTarget* feedbackTarget = static_cast<const ResourceDefinition::TransformFeedbackTarget*>(head);
  
  				DE_ASSERT(feedbackTarget->m_builtinVarName != DE_NULL);
  
  				feedbackTargetVaryingName = feedbackTarget->m_builtinVarName;
  			}
  			else if (head->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK)
  			{
  			}
  			else
  			{
  				DE_ASSERT(DE_FALSE);
  				break;
  			}
  		}
  	}
  
  	if (program->hasStage(glu::SHADERTYPE_GEOMETRY))
  		program->setGeometryNumOutputVertices(1);
  	if (program->hasStage(glu::SHADERTYPE_TESSELLATION_CONTROL) || program->hasStage(glu::SHADERTYPE_TESSELLATION_EVALUATION))
  		program->setTessellationNumOutputPatchVertices(1);
  
  	return program;
  }
  
  static void checkAndLogProgram (const glu::ShaderProgram& program, const ProgramInterfaceDefinition::Program* programDefinition, const glw::Functions& gl, tcu::TestLog& log)
  {
  	const tcu::ScopedLogSection section(log, "Program", "Program");
  
  	log << program;
  	if (!program.isOk())
  	{
  		log << tcu::TestLog::Message << "Program build failed, checking if program exceeded implementation limits" << tcu::TestLog::EndMessage;
  		checkProgramResourceUsage(programDefinition, gl, log);
  
  		// within limits
  		throw tcu::TestError("could not build program");
  	}
  }
  
  // Resource list query case
  
  class ResourceListTestCase : public TestCase
  {
  public:
  												ResourceListTestCase		(Context& context, const ResourceDefinition::Node::SharedPtr& targetResource, ProgramInterface interface, const char* name = DE_NULL);
  												~ResourceListTestCase		(void);
  
  protected:
  	void										init						(void);
  	void										deinit						(void);
  	IterateResult								iterate						(void);
  
  	void										queryResourceList			(std::vector<std::string>& dst, glw::GLuint program);
  	bool										verifyResourceList			(const std::vector<std::string>& resourceList, const std::vector<std::string>& expectedResources);
  	bool										verifyResourceIndexQuery	(const std::vector<std::string>& resourceList, const std::vector<std::string>& referenceResources, glw::GLuint program);
  	bool										verifyMaxNameLength			(const std::vector<std::string>& referenceResourceList, glw::GLuint program);
  
  	static std::string							genTestCaseName				(ProgramInterface interface, const ResourceDefinition::Node*);
  	static bool									isArrayedInterface			(ProgramInterface interface, deUint32 stageBits);
  
  	const ProgramInterface						m_programInterface;
  	ResourceDefinition::Node::SharedPtr			m_targetResource;
  	ProgramInterfaceDefinition::Program*		m_programDefinition;
  };
  
  ResourceListTestCase::ResourceListTestCase (Context& context, const ResourceDefinition::Node::SharedPtr& targetResource, ProgramInterface interface, const char* name)
  	: TestCase				(context, (name == DE_NULL) ? (genTestCaseName(interface, targetResource.get()).c_str()) : (name), "")
  	, m_programInterface	(interface)
  	, m_targetResource		(targetResource)
  	, m_programDefinition	(DE_NULL)
  {
  	// GL_ATOMIC_COUNTER_BUFFER: no resource names
  	DE_ASSERT(m_programInterface != PROGRAMINTERFACE_ATOMIC_COUNTER_BUFFER);
  }
  
  ResourceListTestCase::~ResourceListTestCase (void)
  {
  	deinit();
  }
  
  void ResourceListTestCase::init (void)
  {
  	m_programDefinition	= generateProgramDefinitionFromResource(m_targetResource.get()).release();
  	const bool supportsES32orGL45 = checkSupport(m_context);
  
  	if ((m_programDefinition->hasStage(glu::SHADERTYPE_TESSELLATION_CONTROL) || m_programDefinition->hasStage(glu::SHADERTYPE_TESSELLATION_EVALUATION)) &&
  		!supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
  	{
  		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
  	}
  	if (m_programDefinition->hasStage(glu::SHADERTYPE_GEOMETRY) &&
  		!supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
  	{
  		throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension");
  	}
  	if (programContainsIOBlocks(m_programDefinition) &&
  		!supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_io_blocks"))
  	{
  		throw tcu::NotSupportedError("Test requires GL_EXT_shader_io_blocks extension");
  	}
  }
  
  void ResourceListTestCase::deinit (void)
  {
  	m_targetResource.clear();
  
  	delete m_programDefinition;
  	m_programDefinition = DE_NULL;
  }
  
  ResourceListTestCase::IterateResult ResourceListTestCase::iterate (void)
  {
  	const glu::ShaderProgram program(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_programDefinition));
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  	checkAndLogProgram(program, m_programDefinition, m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
  
  	// Check resource list
  	{
  		const tcu::ScopedLogSection	section				(m_testCtx.getLog(), "ResourceList", "Resource list");
  		std::vector<std::string>	resourceList;
  		std::vector<std::string>	expectedResources;
  
  		queryResourceList(resourceList, program.getProgram());
  		expectedResources = getProgramInterfaceResourceList(m_programDefinition, m_programInterface);
  
  		// verify the list and the expected list match
  
  		if (!verifyResourceList(resourceList, expectedResources))
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid resource list");
  
  		// verify GetProgramResourceIndex() matches the indices of the list
  
  		if (!verifyResourceIndexQuery(resourceList, expectedResources, program.getProgram()))
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "GetProgramResourceIndex returned unexpected values");
  
  		// Verify MAX_NAME_LENGTH
  		if (!verifyMaxNameLength(resourceList, program.getProgram()))
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "MAX_NAME_LENGTH invalid");
  	}
  
  	return STOP;
  }
  
  void ResourceListTestCase::queryResourceList (std::vector<std::string>& dst, glw::GLuint program)
  {
  	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
  	const glw::GLenum		programInterface	= getProgramInterfaceGLEnum(m_programInterface);
  	glw::GLint				numActiveResources	= 0;
  	glw::GLint				maxNameLength		= 0;
  	std::vector<char>		buffer;
  
  	m_testCtx.getLog() << tcu::TestLog::Message << "Querying " << glu::getProgramInterfaceName(programInterface) << " interface:" << tcu::TestLog::EndMessage;
  
  	gl.getProgramInterfaceiv(program, programInterface, GL_ACTIVE_RESOURCES, &numActiveResources);
  	gl.getProgramInterfaceiv(program, programInterface, GL_MAX_NAME_LENGTH, &maxNameLength);
  	GLU_EXPECT_NO_ERROR(gl.getError(), "query interface");
  
  	m_testCtx.getLog()	<< tcu::TestLog::Message
  						<< "\tGL_ACTIVE_RESOURCES = " << numActiveResources << "\n"
  						<< "\tGL_MAX_NAME_LENGTH = " << maxNameLength
  						<< tcu::TestLog::EndMessage;
  
  	m_testCtx.getLog() << tcu::TestLog::Message << "Querying all active resources" << tcu::TestLog::EndMessage;
  
  	buffer.resize(maxNameLength+1, '\0');
  
  	for (int resourceNdx = 0; resourceNdx < numActiveResources; ++resourceNdx)
  	{
  		glw::GLint written = 0;
  
  		gl.getProgramResourceName(program, programInterface, resourceNdx, maxNameLength, &written, &buffer[0]);
  		GLU_EXPECT_NO_ERROR(gl.getError(), "query resource name");
  
  		dst.push_back(std::string(&buffer[0], written));
  	}
  }
  
  bool ResourceListTestCase::verifyResourceList (const std::vector<std::string>& resourceList, const std::vector<std::string>& expectedResources)
  {
  	bool error = false;
  
  	// Log and compare resource lists
  
  	m_testCtx.getLog() << tcu::TestLog::Message << "GL returned resources:" << tcu::TestLog::EndMessage;
  
  	for (int ndx = 0; ndx < (int)resourceList.size(); ++ndx)
  	{
  		// dummyZero is a uniform that may be added by
  		// generateProgramInterfaceProgramSources.  Omit it here to avoid
  		// confusion about the output.
  		if (resourceList[ndx] != getDummyZeroUniformName())
  			m_testCtx.getLog() << tcu::TestLog::Message << "\t" << ndx << ": " << resourceList[ndx] << tcu::TestLog::EndMessage;
  	}
  
  	m_testCtx.getLog() << tcu::TestLog::Message << "Expected list of resources:" << tcu::TestLog::EndMessage;
  
  	for (int ndx = 0; ndx < (int)expectedResources.size(); ++ndx)
  		m_testCtx.getLog() << tcu::TestLog::Message << "\t" << ndx << ": " << expectedResources[ndx] << tcu::TestLog::EndMessage;
  
  	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying resource list contents." << tcu::TestLog::EndMessage;
  
  	for (int ndx = 0; ndx < (int)expectedResources.size(); ++ndx)
  	{
  		if (!de::contains(resourceList.begin(), resourceList.end(), expectedResources[ndx]))
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, resource list did not contain active resource " << expectedResources[ndx] << tcu::TestLog::EndMessage;
  			error = true;
  		}
  	}
  
  	for (int ndx = 0; ndx < (int)resourceList.size(); ++ndx)
  	{
  		if (!de::contains(expectedResources.begin(), expectedResources.end(), resourceList[ndx]))
  		{
  			// Ignore all builtin variables or the variable dummyZero,
  			// mismatch causes errors otherwise.  dummyZero is a uniform that
  			// may be added by generateProgramInterfaceProgramSources.
  			if (deStringBeginsWith(resourceList[ndx].c_str(), "gl_") == DE_FALSE &&
  				resourceList[ndx] != getDummyZeroUniformName())
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, resource list contains unexpected resource name " << resourceList[ndx] << tcu::TestLog::EndMessage;
  				error = true;
  			}
  			else
  				m_testCtx.getLog() << tcu::TestLog::Message << "Note, resource list contains unknown built-in " << resourceList[ndx] << ". This variable is ignored." << tcu::TestLog::EndMessage;
  		}
  	}
  
  	return !error;
  }
  
  bool ResourceListTestCase::verifyResourceIndexQuery (const std::vector<std::string>& resourceList, const std::vector<std::string>& referenceResources, glw::GLuint program)
  {
  	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
  	const glw::GLenum		programInterface	= getProgramInterfaceGLEnum(m_programInterface);
  	bool					error				= false;
  
  	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying GetProgramResourceIndex returns correct indices for resource names." << tcu::TestLog::EndMessage;
  
  	for (int ndx = 0; ndx < (int)referenceResources.size(); ++ndx)
  	{
  		const glw::GLuint index = gl.getProgramResourceIndex(program, programInterface, referenceResources[ndx].c_str());
  		GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index");
  
  		if (index == GL_INVALID_INDEX)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, for active resource \"" << referenceResources[ndx] << "\" got index GL_INVALID_INDEX." << tcu::TestLog::EndMessage;
  			error = true;
  		}
  		else if ((int)index >= (int)resourceList.size())
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, for active resource \"" << referenceResources[ndx] << "\" got index " << index << " (larger or equal to GL_ACTIVE_RESOURCES)." << tcu::TestLog::EndMessage;
  			error = true;
  		}
  		else if (resourceList[index] != referenceResources[ndx])
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, for active resource \"" << referenceResources[ndx] << "\" got index (index = " << index << ") of another resource (" << resourceList[index] << ")." << tcu::TestLog::EndMessage;
  			error = true;
  		}
  	}
  
  	// Query for "name" should match "name[0]" except for XFB
  
  	if (m_programInterface != PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING)
  	{
  		for (int ndx = 0; ndx < (int)referenceResources.size(); ++ndx)
  		{
  			if (de::endsWith(referenceResources[ndx], "[0]"))
  			{
  				const std::string	queryString	= referenceResources[ndx].substr(0, referenceResources[ndx].length()-3);
  				const glw::GLuint	index		= gl.getProgramResourceIndex(program, programInterface, queryString.c_str());
  				GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index");
  
  				if (index == GL_INVALID_INDEX)
  				{
  					m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for \"" << queryString << "\" resulted in index GL_INVALID_INDEX." << tcu::TestLog::EndMessage;
  					error = true;
  				}
  				else if ((int)index >= (int)resourceList.size())
  				{
  					m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for \"" << queryString << "\" resulted in index " << index << " (larger or equal to GL_ACTIVE_RESOURCES)." << tcu::TestLog::EndMessage;
  					error = true;
  				}
  				else if (resourceList[index] != queryString + "[0]")
  				{
  					m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for \"" << queryString << "\" got index (index = " << index << ") of another resource (\"" << resourceList[index] << "\")." << tcu::TestLog::EndMessage;
  					error = true;
  				}
  			}
  		}
  	}
  
  	return !error;
  }
  
  bool ResourceListTestCase::verifyMaxNameLength (const std::vector<std::string>& resourceList, glw::GLuint program)
  {
  	const glw::Functions&	gl						= m_context.getRenderContext().getFunctions();
  	const glw::GLenum		programInterface		= getProgramInterfaceGLEnum(m_programInterface);
  	glw::GLint				maxNameLength			= 0;
  	glw::GLint				expectedMaxNameLength	= 0;
  
  	gl.getProgramInterfaceiv(program, programInterface, GL_MAX_NAME_LENGTH, &maxNameLength);
  	GLU_EXPECT_NO_ERROR(gl.getError(), "query interface");
  
  	for (int ndx = 0; ndx < (int)resourceList.size(); ++ndx)
  		expectedMaxNameLength = de::max(expectedMaxNameLength, (int)resourceList[ndx].size() + 1);
  
  	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying MAX_NAME_LENGTH, expecting " << expectedMaxNameLength << " (i.e. consistent with the queried resource list)" << tcu::TestLog::EndMessage;
  
  	if (expectedMaxNameLength != maxNameLength)
  	{
  		m_testCtx.getLog() << tcu::TestLog::Message << "Error, got " << maxNameLength << tcu::TestLog::EndMessage;
  		return false;
  	}
  
  	return true;
  }
  
  std::string ResourceListTestCase::genTestCaseName (ProgramInterface interface, const ResourceDefinition::Node* root)
  {
  	bool				isImplicitlySizedArray	= false;
  	bool				hasVariable				= false;
  	bool				accumulateName			= true;
  	std::string			buf						= "var";
  	std::string			prefix;
  
  	for (const ResourceDefinition::Node* node = root; node; node = node->getEnclosingNode())
  	{
  		switch (node->getType())
  		{
  			case ResourceDefinition::Node::TYPE_VARIABLE:
  			{
  				hasVariable = true;
  				break;
  			}
  
  			case ResourceDefinition::Node::TYPE_STRUCT_MEMBER:
  			{
  				if (accumulateName)
  					buf += "_struct";
  				break;
  			}
  
  			case ResourceDefinition::Node::TYPE_ARRAY_ELEMENT:
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::ArrayElement*>(node));
  				const ResourceDefinition::ArrayElement* arrayElement = static_cast<const ResourceDefinition::ArrayElement*>(node);
  
  				isImplicitlySizedArray = (arrayElement->m_arraySize == ResourceDefinition::ArrayElement::UNSIZED_ARRAY);
  
  				if (accumulateName)
  					buf += "_array";
  				break;
  			}
  
  			case ResourceDefinition::Node::TYPE_STORAGE_QUALIFIER:
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::StorageQualifier*>(node));
  				const ResourceDefinition::StorageQualifier* storageDef = static_cast<const ResourceDefinition::StorageQualifier*>(node);
  
  				if (storageDef->m_storage == glu::STORAGE_PATCH_IN ||
  					storageDef->m_storage == glu::STORAGE_PATCH_OUT)
  				{
  					if (accumulateName)
  						prefix += "patch_";
  				}
  				break;
  			}
  
  			case ResourceDefinition::Node::TYPE_SHADER:
  			case ResourceDefinition::Node::TYPE_SHADER_SET:
  			{
  				bool arrayedInterface;
  
  				if (node->getType() == ResourceDefinition::Node::TYPE_SHADER)
  				{
  					DE_ASSERT(dynamic_cast<const ResourceDefinition::Shader*>(node));
  					const ResourceDefinition::Shader* shaderDef = static_cast<const ResourceDefinition::Shader*>(node);
  
  					arrayedInterface = isArrayedInterface(interface, (1u << shaderDef->m_type));
  				}
  				else
  				{
  					DE_ASSERT(node->getType() == ResourceDefinition::Node::TYPE_SHADER_SET);
  					DE_ASSERT(dynamic_cast<const ResourceDefinition::ShaderSet*>(node));
  					const ResourceDefinition::ShaderSet* shaderDef = static_cast<const ResourceDefinition::ShaderSet*>(node);
  
  					arrayedInterface = isArrayedInterface(interface, shaderDef->getReferencingMask());
  				}
  
  				if (arrayedInterface && isImplicitlySizedArray)
  				{
  					// omit implicit arrayness from name, i.e. remove trailing "_array"
  					DE_ASSERT(de::endsWith(buf, "_array"));
  					buf = buf.substr(0, buf.length() - 6);
  				}
  
  				break;
  			}
  
  			case ResourceDefinition::Node::TYPE_INTERFACE_BLOCK:
  			{
  				accumulateName = false;
  				break;
  			}
  
  			default:
  				break;
  		}
  	}
  
  	if (!hasVariable)
  		return prefix + "empty";
  	else
  		return prefix + buf;
  }
  
  bool ResourceListTestCase::isArrayedInterface (ProgramInterface interface, deUint32 stageBits)
  {
  	if (interface == PROGRAMINTERFACE_PROGRAM_INPUT)
  	{
  		const glu::ShaderType firstStage = getShaderMaskFirstStage(stageBits);
  		return	firstStage == glu::SHADERTYPE_TESSELLATION_CONTROL		||
  				firstStage == glu::SHADERTYPE_TESSELLATION_EVALUATION	||
  				firstStage == glu::SHADERTYPE_GEOMETRY;
  	}
  	else if (interface == PROGRAMINTERFACE_PROGRAM_OUTPUT)
  	{
  		const glu::ShaderType lastStage = getShaderMaskLastStage(stageBits);
  		return	lastStage == glu::SHADERTYPE_TESSELLATION_CONTROL;
  	}
  	return false;
  }
  
  // Resouce property query case
  
  class ResourceTestCase : public ProgramInterfaceQueryTestCase
  {
  public:
  															ResourceTestCase			(Context& context, const ResourceDefinition::Node::SharedPtr& targetResource, const ProgramResourceQueryTestTarget& queryTarget, const char* name = DE_NULL);
  															~ResourceTestCase			(void);
  
  private:
  	void													init						(void);
  	void													deinit						(void);
  	const ProgramInterfaceDefinition::Program*				getProgramDefinition		(void) const;
  	std::vector<std::string>								getQueryTargetResources		(void) const;
  
  	static std::string										genTestCaseName				(const ResourceDefinition::Node*);
  	static std::string										genMultilineDescription		(const ResourceDefinition::Node*);
  
  	ResourceDefinition::Node::SharedPtr						m_targetResource;
  	ProgramInterfaceDefinition::Program*					m_program;
  	std::vector<std::string>								m_targetResources;
  };
  
  ResourceTestCase::ResourceTestCase (Context& context, const ResourceDefinition::Node::SharedPtr& targetResource, const ProgramResourceQueryTestTarget& queryTarget, const char* name)
  	: ProgramInterfaceQueryTestCase	(context, (name == DE_NULL) ? (genTestCaseName(targetResource.get()).c_str()) : (name), "", queryTarget)
  	, m_targetResource				(targetResource)
  	, m_program						(DE_NULL)
  {
  }
  
  ResourceTestCase::~ResourceTestCase (void)
  {
  	deinit();
  }
  
  void ResourceTestCase::init (void)
  {
  	m_testCtx.getLog()
  		<< tcu::TestLog::Message
  		<< genMultilineDescription(m_targetResource.get())
  		<< tcu::TestLog::EndMessage;
  
  	// Program
  	{
  		// Generate interface with target resource
  		m_program = generateProgramDefinitionFromResource(m_targetResource.get()).release();
  		m_targetResources = getProgramInterfaceResourceList(m_program, getTargetInterface());
  	}
  }
  
  void ResourceTestCase::deinit (void)
  {
  	m_targetResource.clear();
  
  	delete m_program;
  	m_program = DE_NULL;
  
  	m_targetResources = std::vector<std::string>();
  }
  
  const ProgramInterfaceDefinition::Program* ResourceTestCase::getProgramDefinition (void) const
  {
  	return m_program;
  }
  
  std::vector<std::string> ResourceTestCase::getQueryTargetResources (void) const
  {
  	return m_targetResources;
  }
  
  std::string ResourceTestCase::genTestCaseName (const ResourceDefinition::Node* resource)
  {
  	if (resource->getType() == ResourceDefinition::Node::TYPE_VARIABLE)
  	{
  		DE_ASSERT(dynamic_cast<const ResourceDefinition::Variable*>(resource));
  
  		const ResourceDefinition::Variable* variable = static_cast<const ResourceDefinition::Variable*>(resource);
  
  		return convertGLTypeNameToTestName(glu::getDataTypeName(variable->m_dataType));
  	}
  
  	DE_ASSERT(false);
  	return "";
  }
  
  std::string ResourceTestCase::genMultilineDescription (const ResourceDefinition::Node* resource)
  {
  	if (resource->getType() == ResourceDefinition::Node::TYPE_VARIABLE)
  	{
  		DE_ASSERT(dynamic_cast<const ResourceDefinition::Variable*>(resource));
  
  		const ResourceDefinition::Variable*	varDef				= static_cast<const ResourceDefinition::Variable*>(resource);
  		std::ostringstream					buf;
  		std::ostringstream					structureDescriptor;
  		std::string							uniformType;
  
  		for (const ResourceDefinition::Node* node = resource; node; node = node->getEnclosingNode())
  		{
  			if (node->getType() == ResourceDefinition::Node::TYPE_STORAGE_QUALIFIER)
  			{
  				DE_ASSERT(dynamic_cast<const ResourceDefinition::StorageQualifier*>(node));
  
  				const ResourceDefinition::StorageQualifier*	storageDef = static_cast<const ResourceDefinition::StorageQualifier*>(node);
  
  				uniformType = std::string(" ") + glu::getStorageName(storageDef->m_storage);
  				structureDescriptor << "\n\tdeclared as \"" << glu::getStorageName(storageDef->m_storage) << "\"";
  			}
  
  			if (node->getType() == ResourceDefinition::Node::TYPE_ARRAY_ELEMENT)
  				structureDescriptor << "\n\tarray";
  
  			if (node->getType() == ResourceDefinition::Node::TYPE_STRUCT_MEMBER)
  				structureDescriptor << "\n\tin a struct";
  
  			if (node->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK)
  				structureDescriptor << "\n\tin the default block";
  
  			if (node->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK)
  				structureDescriptor << "\n\tin an interface block";
  		}
  
  		buf	<< "Querying properties of " << glu::getDataTypeName(varDef->m_dataType) << uniformType << " variable.\n"
  			<< "Variable is:\n"
  			<< "\t" << glu::getDataTypeName(varDef->m_dataType)
  			<< structureDescriptor.str();
  
  		return buf.str();
  	}
  	else if (resource->getType() == ResourceDefinition::Node::TYPE_TRANSFORM_FEEDBACK_TARGET)
  	{
  		DE_ASSERT(dynamic_cast<const ResourceDefinition::TransformFeedbackTarget*>(resource));
  
  		const ResourceDefinition::TransformFeedbackTarget* xfbDef = static_cast<const ResourceDefinition::TransformFeedbackTarget*>(resource);
  
  		DE_ASSERT(xfbDef->m_builtinVarName);
  
  		return std::string("Querying properties of a builtin variable ") + xfbDef->m_builtinVarName;
  	}
  
  	DE_ASSERT(false);
  	return DE_NULL;
  }
  
  class ResourceNameBufferLimitCase : public TestCase
  {
  public:
  					ResourceNameBufferLimitCase		(Context& context, const char* name, const char* description);
  					~ResourceNameBufferLimitCase	(void);
  
  private:
  	IterateResult	iterate							(void);
  };
  
  ResourceNameBufferLimitCase::ResourceNameBufferLimitCase (Context& context, const char* name, const char* description)
  	: TestCase(context, name, description)
  {
  }
  
  ResourceNameBufferLimitCase::~ResourceNameBufferLimitCase (void)
  {
  }
  
  ResourceNameBufferLimitCase::IterateResult ResourceNameBufferLimitCase::iterate (void)
  {
  	static const char* const computeSource =	"${GLSL_VERSION_DECL}\n"
  												"layout(local_size_x = 1) in;\n"
  												"uniform highp int u_uniformWithALongName;\n"
  												"writeonly buffer OutputBufferBlock { highp int b_output_int; };\n"
  												"void main ()\n"
  												"{\n"
  												"	b_output_int = u_uniformWithALongName;\n"
  												"}\n";
  
  	const glw::Functions&		gl				= m_context.getRenderContext().getFunctions();
  	const glu::ShaderProgram	program			(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(specializeShader(m_context, computeSource)));
  	glw::GLuint					uniformIndex;
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  
  	// Log program
  	{
  		const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program", "Program");
  
  		m_testCtx.getLog() << program;
  		if (!program.isOk())
  			throw tcu::TestError("could not build program");
  	}
  
  	uniformIndex = gl.getProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_uniformWithALongName");
  	GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index");
  
  	if (uniformIndex == GL_INVALID_INDEX)
  		throw tcu::TestError("Uniform u_uniformWithALongName resource index was GL_INVALID_INDEX");
  
  	// Query with different sized buffers, len("u_uniformWithALongName") == 22
  
  	{
  		static const struct
  		{
  			const char*	description;
  			int			querySize;
  			bool		returnLength;
  		} querySizes[] =
  		{
  			{ "Query to larger buffer",										24,		true	},
  			{ "Query to buffer the same size",								23,		true	},
  			{ "Query to one byte too small buffer",							22,		true	},
  			{ "Query to one byte buffer",									1,		true	},
  			{ "Query to zero sized buffer",									0,		true	},
  			{ "Query to one byte too small buffer, null length argument",	22,		false	},
  			{ "Query to one byte buffer, null length argument",				1,		false	},
  			{ "Query to zero sized buffer, null length argument",			0,		false	},
  		};
  
  		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(querySizes); ++ndx)
  		{
  			const tcu::ScopedLogSection			section				(m_testCtx.getLog(), "Query", querySizes[ndx].description);
  			const int							uniformNameLen		= 22;
  			const int							expectedWriteLen	= (querySizes[ndx].querySize != 0) ? (de::min(uniformNameLen, (querySizes[ndx].querySize - 1))) : (0);
  			char								buffer				[26];
  			glw::GLsizei						written				= -1;
  
  			// One byte for guard
  			DE_ASSERT((int)sizeof(buffer) > querySizes[ndx].querySize);
  
  			deMemset(buffer, 'x', sizeof(buffer));
  
  			if (querySizes[ndx].querySize)
  				m_testCtx.getLog()
  					<< tcu::TestLog::Message
  					<< "Querying uniform name to a buffer of size " << querySizes[ndx].querySize
  					<< ", expecting query to write " << expectedWriteLen << " bytes followed by a null terminator"
  					<< tcu::TestLog::EndMessage;
  			else
  				m_testCtx.getLog()
  					<< tcu::TestLog::Message
  					<< "Querying uniform name to a buffer of size " << querySizes[ndx].querySize
  					<< ", expecting query to write 0 bytes"
  					<< tcu::TestLog::EndMessage;
  
  			gl.getProgramResourceName(program.getProgram(), GL_UNIFORM, uniformIndex, querySizes[ndx].querySize, (querySizes[ndx].returnLength) ? (&written) : (DE_NULL), buffer);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query resource name");
  
  			if (querySizes[ndx].returnLength && written != expectedWriteLen)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, expected write length of " << expectedWriteLen << ", got " << written << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected write lenght");
  			}
  			else if (querySizes[ndx].querySize != 0 && buffer[expectedWriteLen] != 0)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, expected null terminator at " << expectedWriteLen << ", got dec=" << (int)buffer[expectedWriteLen] << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Missing null terminator");
  			}
  			else if (querySizes[ndx].querySize != 0 && buffer[expectedWriteLen+1] != 'x')
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, guard at index " << (expectedWriteLen+1) << " was modified, got dec=" << (int)buffer[expectedWriteLen+1] << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrote over buffer size");
  			}
  			else if (querySizes[ndx].querySize == 0 && buffer[0] != 'x')
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, buffer size was 0 but buffer contents were modified. At index 0 got dec=" << (int)buffer[0] << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer contents were modified");
  			}
  		}
  	}
  
  	return STOP;
  }
  
  class ResourceQueryBufferLimitCase : public TestCase
  {
  public:
  					ResourceQueryBufferLimitCase	(Context& context, const char* name, const char* description);
  					~ResourceQueryBufferLimitCase	(void);
  
  private:
  	IterateResult	iterate							(void);
  };
  
  ResourceQueryBufferLimitCase::ResourceQueryBufferLimitCase (Context& context, const char* name, const char* description)
  	: TestCase(context, name, description)
  {
  }
  
  ResourceQueryBufferLimitCase::~ResourceQueryBufferLimitCase (void)
  {
  }
  
  ResourceQueryBufferLimitCase::IterateResult ResourceQueryBufferLimitCase::iterate (void)
  {
  	static const char* const computeSource =	"${GLSL_VERSION_DECL}\n"
  												"layout(local_size_x = 1) in;\n"
  												"uniform highp int u_uniform;\n"
  												"writeonly buffer OutputBufferBlock { highp int b_output_int; };\n"
  												"void main ()\n"
  												"{\n"
  												"	b_output_int = u_uniform;\n"
  												"}\n";
  
  	const glw::Functions&		gl				= m_context.getRenderContext().getFunctions();
  	const glu::ShaderProgram	program			(m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(specializeShader(m_context, computeSource)));
  	glw::GLuint					uniformIndex;
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  
  	// Log program
  	{
  		const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program", "Program");
  
  		m_testCtx.getLog() << program;
  		if (!program.isOk())
  			throw tcu::TestError("could not build program");
  	}
  
  	uniformIndex = gl.getProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_uniform");
  	GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index");
  
  	if (uniformIndex == GL_INVALID_INDEX)
  		throw tcu::TestError("Uniform u_uniform resource index was GL_INVALID_INDEX");
  
  	// Query uniform properties
  
  	{
  		static const struct
  		{
  			const char*	description;
  			int			numProps;
  			int			bufferSize;
  			bool		returnLength;
  		} querySizes[] =
  		{
  			{ "Query to a larger buffer",							2, 3, true	},
  			{ "Query to too small a buffer",						3, 2, true	},
  			{ "Query to zero sized buffer",							3, 0, true	},
  			{ "Query to a larger buffer, null length argument",		2, 3, false	},
  			{ "Query to too small a buffer, null length argument",	3, 2, false	},
  			{ "Query to zero sized buffer, null length argument",	3, 0, false	},
  		};
  
  		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(querySizes); ++ndx)
  		{
  			const tcu::ScopedLogSection		section				(m_testCtx.getLog(), "QueryToLarger", querySizes[ndx].description);
  			const glw::GLenum				props[]				= { GL_LOCATION, GL_LOCATION, GL_LOCATION };
  			const int						expectedWriteLen	= de::min(querySizes[ndx].bufferSize, querySizes[ndx].numProps);
  			int								params[]			= { 255, 255, 255, 255 };
  			glw::GLsizei					written				= -1;
  
  			DE_ASSERT(querySizes[ndx].numProps <= DE_LENGTH_OF_ARRAY(props));
  			DE_ASSERT(querySizes[ndx].bufferSize < DE_LENGTH_OF_ARRAY(params)); // leave at least one element for overflow detection
  
  			m_testCtx.getLog()
  				<< tcu::TestLog::Message
  				<< "Querying " << querySizes[ndx].numProps << " uniform prop(s) to a buffer with size " << querySizes[ndx].bufferSize << ". Expecting query to return " << expectedWriteLen << " prop(s)"
  				<< tcu::TestLog::EndMessage;
  
  			gl.getProgramResourceiv(program.getProgram(), GL_UNIFORM, uniformIndex, querySizes[ndx].numProps, props, querySizes[ndx].bufferSize, (querySizes[ndx].returnLength) ? (&written) : (DE_NULL), params);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query program resources");
  
  			if (querySizes[ndx].returnLength && written != expectedWriteLen)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, expected write length of " << expectedWriteLen << ", got " << written << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected write lenght");
  			}
  			else if (params[expectedWriteLen] != 255)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, guard at index " << (expectedWriteLen) << " was modified. Was 255 before call, got dec=" << params[expectedWriteLen] << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrote over buffer size");
  			}
  		}
  	}
  
  	return STOP;
  }
  
  class InterfaceBlockBaseCase : public TestCase
  {
  public:
  	enum CaseType
  	{
  		CASE_NAMED_BLOCK = 0,
  		CASE_UNNAMED_BLOCK,
  		CASE_BLOCK_ARRAY,
  
  		CASE_LAST
  	};
  
  											InterfaceBlockBaseCase		(Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType);
  											~InterfaceBlockBaseCase		(void);
  
  private:
  	void									init						(void);
  	void									deinit						(void);
  
  protected:
  	const glu::Storage						m_storage;
  	const CaseType							m_caseType;
  	ProgramInterfaceDefinition::Program*	m_program;
  };
  
  InterfaceBlockBaseCase::InterfaceBlockBaseCase (Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType)
  	: TestCase		(context, name, description)
  	, m_storage		(storage)
  	, m_caseType	(caseType)
  	, m_program		(DE_NULL)
  {
  	DE_ASSERT(storage == glu::STORAGE_UNIFORM || storage == glu::STORAGE_BUFFER);
  }
  
  InterfaceBlockBaseCase::~InterfaceBlockBaseCase (void)
  {
  	deinit();
  }
  
  void InterfaceBlockBaseCase::init (void)
  {
  	const glu::GLSLVersion				glslVersion	= glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
  	ProgramInterfaceDefinition::Shader*	shader;
  
  	m_program = new ProgramInterfaceDefinition::Program();
  	shader = m_program->addShader(glu::SHADERTYPE_COMPUTE, glslVersion);
  
  	// PrecedingInterface
  	{
  		glu::InterfaceBlock precedingInterfaceBlock;
  
  		precedingInterfaceBlock.interfaceName	= "PrecedingInterface";
  		precedingInterfaceBlock.layout.binding	= 0;
  		precedingInterfaceBlock.storage			= m_storage;
  		precedingInterfaceBlock.instanceName	= "precedingInstance";
  
  		precedingInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), "precedingMember"));
  
  		// Unsized array type
  		if (m_storage == glu::STORAGE_BUFFER)
  			precedingInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), glu::VarType::UNSIZED_ARRAY), "precedingMemberUnsizedArray"));
  		else
  			precedingInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), 2), "precedingMemberArray"));
  
  		shader->getDefaultBlock().interfaceBlocks.push_back(precedingInterfaceBlock);
  	}
  
  	// TargetInterface
  	{
  		glu::InterfaceBlock targetInterfaceBlock;
  
  		targetInterfaceBlock.interfaceName	= "TargetInterface";
  		targetInterfaceBlock.layout.binding	= 1;
  		targetInterfaceBlock.storage		= m_storage;
  
  		if (m_caseType == CASE_UNNAMED_BLOCK)
  			targetInterfaceBlock.instanceName = "";
  		else
  			targetInterfaceBlock.instanceName = "targetInstance";
  
  		if (m_caseType == CASE_BLOCK_ARRAY)
  			targetInterfaceBlock.dimensions.push_back(2);
  
  		// Basic type
  		{
  			targetInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), "blockMemberBasic"));
  		}
  
  		// Array type
  		{
  			targetInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), 3), "blockMemberArray"));
  		}
  
  		// Struct type
  		{
  			glu::StructType* structPtr = new glu::StructType("StructType");
  			structPtr->addMember("structMemberBasic", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
  			structPtr->addMember("structMemberArray", glu::VarType(glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), 2));
  
  			targetInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(structPtr), 2), "blockMemberStruct"));
  		}
  
  		// Unsized array type
  		if (m_storage == glu::STORAGE_BUFFER)
  			targetInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), glu::VarType::UNSIZED_ARRAY), "blockMemberUnsizedArray"));
  
  		shader->getDefaultBlock().interfaceBlocks.push_back(targetInterfaceBlock);
  	}
  
  	// TrailingInterface
  	{
  		glu::InterfaceBlock trailingInterfaceBlock;
  
  		trailingInterfaceBlock.interfaceName	= "TrailingInterface";
  		trailingInterfaceBlock.layout.binding	= 3;
  		trailingInterfaceBlock.storage			= m_storage;
  		trailingInterfaceBlock.instanceName		= "trailingInstance";
  		trailingInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), "trailingMember"));
  
  		shader->getDefaultBlock().interfaceBlocks.push_back(trailingInterfaceBlock);
  	}
  
  	DE_ASSERT(m_program->isValid());
  }
  
  void InterfaceBlockBaseCase::deinit (void)
  {
  	delete m_program;
  	m_program = DE_NULL;
  }
  
  class InterfaceBlockActiveVariablesTestCase : public InterfaceBlockBaseCase
  {
  public:
  											InterfaceBlockActiveVariablesTestCase	(Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType);
  
  private:
  	IterateResult							iterate									(void);
  };
  
  InterfaceBlockActiveVariablesTestCase::InterfaceBlockActiveVariablesTestCase (Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType)
  	: InterfaceBlockBaseCase(context, name, description, storage, caseType)
  {
  }
  
  InterfaceBlockActiveVariablesTestCase::IterateResult InterfaceBlockActiveVariablesTestCase::iterate (void)
  {
  	const ProgramInterface			programInterface				= (m_storage == glu::STORAGE_UNIFORM) ? (PROGRAMINTERFACE_UNIFORM_BLOCK) :
  																	  (m_storage == glu::STORAGE_BUFFER) ? (PROGRAMINTERFACE_SHADER_STORAGE_BLOCK) :
  																	  (PROGRAMINTERFACE_LAST);
  	const glw::GLenum				programGLInterfaceValue			= getProgramInterfaceGLEnum(programInterface);
  	const glw::GLenum				programMemberInterfaceValue		= (m_storage == glu::STORAGE_UNIFORM) ? (GL_UNIFORM) :
  																	  (m_storage == glu::STORAGE_BUFFER) ? (GL_BUFFER_VARIABLE) :
  																	  (0);
  	const std::vector<std::string>	blockNames						= getProgramInterfaceResourceList(m_program, programInterface);
  	glu::ShaderProgram				program							(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program));
  	int								expectedMaxNumActiveVariables	= 0;
  
  	DE_ASSERT(programInterface != PROGRAMINTERFACE_LAST);
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  	checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
  
  	// Verify all blocks
  
  	for (int blockNdx = 0; blockNdx < (int)blockNames.size(); ++blockNdx)
  	{
  		const tcu::ScopedLogSection section				(m_testCtx.getLog(), "Block", "Block \"" + blockNames[blockNdx] + "\"");
  		const glw::Functions&		gl					= m_context.getRenderContext().getFunctions();
  		const glw::GLuint			resourceNdx			= gl.getProgramResourceIndex(program.getProgram(), programGLInterfaceValue, blockNames[blockNdx].c_str());
  		glw::GLint					numActiveResources;
  		std::vector<std::string>	activeResourceNames;
  
  		GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index");
  
  		if (resourceNdx == GL_INVALID_INDEX)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, getProgramResourceIndex returned GL_INVALID_INDEX for \"" << blockNames[blockNdx] << "\"" << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Resource not found");
  			continue;
  		}
  
  		// query block information
  
  		{
  			const glw::GLenum	props[]			= { GL_NUM_ACTIVE_VARIABLES };
  			glw::GLint			retBuffer[2]	= { -1, -1 };
  			glw::GLint			written			= -1;
  
  			gl.getProgramResourceiv(program.getProgram(), programGLInterfaceValue, resourceNdx, DE_LENGTH_OF_ARRAY(props), props, 1, &written, retBuffer);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query GL_NUM_ACTIVE_VARIABLES");
  
  			numActiveResources = retBuffer[0];
  			expectedMaxNumActiveVariables = de::max(expectedMaxNumActiveVariables, numActiveResources);
  			m_testCtx.getLog() << tcu::TestLog::Message << "NUM_ACTIVE_VARIABLES = " << numActiveResources << tcu::TestLog::EndMessage;
  
  			if (written == -1 || retBuffer[0] == -1)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, Query for NUM_ACTIVE_VARIABLES did not return a value" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query for NUM_ACTIVE_VARIABLES failed");
  				continue;
  			}
  			else if (retBuffer[1] != -1)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, Query for NUM_ACTIVE_VARIABLES returned too many values" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query for NUM_ACTIVE_VARIABLES returned too many values");
  				continue;
  			}
  			else if (retBuffer[0] < 0)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, NUM_ACTIVE_VARIABLES < 0" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "NUM_ACTIVE_VARIABLES < 0");
  				continue;
  			}
  		}
  
  		// query block variable information
  
  		{
  			const glw::GLenum			props[]					= { GL_ACTIVE_VARIABLES };
  			std::vector<glw::GLint>		activeVariableIndices	(numActiveResources + 1, -1);	// Allocate one extra trailing to detect wrong write lengths
  			glw::GLint					written					= -1;
  
  			gl.getProgramResourceiv(program.getProgram(), programGLInterfaceValue, resourceNdx, DE_LENGTH_OF_ARRAY(props), props, (glw::GLsizei)activeVariableIndices.size(), &written, &activeVariableIndices[0]);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query GL_ACTIVE_VARIABLES");
  
  			if (written == -1)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, Query for GL_ACTIVE_VARIABLES did not return any values" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query for GL_ACTIVE_VARIABLES failed");
  				continue;
  			}
  			else if (written != numActiveResources)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, Query for GL_ACTIVE_VARIABLES did not return NUM_ACTIVE_VARIABLES values" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query for GL_ACTIVE_VARIABLES returned invalid number of values");
  				continue;
  			}
  			else if (activeVariableIndices.back() != -1)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, GL_ACTIVE_VARIABLES query return buffer trailing guard value was modified, getProgramResourceiv returned more than NUM_ACTIVE_VARIABLES values" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query for GL_ACTIVE_VARIABLES returned too many values");
  				continue;
  			}
  
  			// log indices
  			{
  				tcu::MessageBuilder builder(&m_testCtx.getLog());
  
  				builder << "Active variable indices: {";
  				for (int varNdx = 0; varNdx < numActiveResources; ++varNdx)
  				{
  					if (varNdx)
  						builder << ", ";
  					builder << activeVariableIndices[varNdx];
  				}
  				builder << "}" << tcu::TestLog::EndMessage;
  			}
  
  			// collect names
  
  			activeResourceNames.resize(numActiveResources);
  
  			for (int varNdx = 0; varNdx < numActiveResources; ++varNdx)
  			{
  				const glw::GLenum	nameProp	= GL_NAME_LENGTH;
  				glw::GLint			nameLength	= -1;
  				std::vector<char>	nameBuffer;
  
  				written = -1;
  				gl.getProgramResourceiv(program.getProgram(), programMemberInterfaceValue, activeVariableIndices[varNdx], 1, &nameProp, 1, &written, &nameLength);
  				GLU_EXPECT_NO_ERROR(gl.getError(), "query GL_NAME_LENGTH");
  
  				if (nameLength <= 0 || written <= 0)
  				{
  					m_testCtx.getLog() << tcu::TestLog::Message << "Error, GL_NAME_LENGTH query failed" << tcu::TestLog::EndMessage;
  					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "GL_NAME_LENGTH query failed");
  					continue;
  				}
  
  				nameBuffer.resize(nameLength + 2, 'X'); // allocate more than required
  				written = -1;
  				gl.getProgramResourceName(program.getProgram(), programMemberInterfaceValue, activeVariableIndices[varNdx], nameLength+1, &written, &nameBuffer[0]);
  				GLU_EXPECT_NO_ERROR(gl.getError(), "getProgramResourceName");
  
  				if (written <= 0)
  				{
  					m_testCtx.getLog() << tcu::TestLog::Message << "Error, name query failed, no data written" << tcu::TestLog::EndMessage;
  					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "name query failed");
  					continue;
  				}
  				else if (written > nameLength)
  				{
  					m_testCtx.getLog() << tcu::TestLog::Message << "Error, name query failed, query returned too much data" << tcu::TestLog::EndMessage;
  					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "name query failed");
  					continue;
  				}
  
  				activeResourceNames[varNdx] = std::string(&nameBuffer[0], written);
  			}
  
  			// log collected names
  			{
  				tcu::MessageBuilder builder(&m_testCtx.getLog());
  
  				builder << "Active variables:\n";
  				for (int varNdx = 0; varNdx < numActiveResources; ++varNdx)
  					builder << "\t" << activeResourceNames[varNdx] << "\n";
  				builder << tcu::TestLog::EndMessage;
  			}
  		}
  
  		// verify names
  		{
  			glu::InterfaceBlock*		block		= DE_NULL;
  			const std::string			blockName	= glu::parseVariableName(blockNames[blockNdx].c_str());
  			std::vector<std::string>	referenceList;
  
  			for (int interfaceNdx = 0; interfaceNdx < (int)m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks.size(); ++interfaceNdx)
  			{
  				if (m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks[interfaceNdx].interfaceName == blockName)
  				{
  					block = &m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks[interfaceNdx];
  					break;
  				}
  			}
  
  			if (!block)
  				throw tcu::InternalError("could not find block referenced in the reference resource list");
  
  			// generate reference list
  
  			referenceList = getProgramInterfaceBlockMemberResourceList(*block);
  			{
  				tcu::MessageBuilder builder(&m_testCtx.getLog());
  
  				builder << "Expected variable names:\n";
  				for (int varNdx = 0; varNdx < (int)referenceList.size(); ++varNdx)
  					builder << "\t" << referenceList[varNdx] << "\n";
  				builder << tcu::TestLog::EndMessage;
  			}
  
  			// compare lists
  			{
  				bool listsIdentical = true;
  
  				for (int ndx = 0; ndx < (int)referenceList.size(); ++ndx)
  				{
  					if (!de::contains(activeResourceNames.begin(), activeResourceNames.end(), referenceList[ndx]))
  					{
  						m_testCtx.getLog() << tcu::TestLog::Message << "Error, variable name list did not contain active variable " << referenceList[ndx] << tcu::TestLog::EndMessage;
  						listsIdentical = false;
  					}
  				}
  
  				for (int ndx = 0; ndx < (int)activeResourceNames.size(); ++ndx)
  				{
  					if (!de::contains(referenceList.begin(), referenceList.end(), activeResourceNames[ndx]))
  					{
  						m_testCtx.getLog() << tcu::TestLog::Message << "Error, variable name list contains unexpected resource \"" << activeResourceNames[ndx] << "\"" << tcu::TestLog::EndMessage;
  						listsIdentical = false;
  					}
  				}
  
  				if (listsIdentical)
  					m_testCtx.getLog() << tcu::TestLog::Message << "Lists identical" << tcu::TestLog::EndMessage;
  				else
  				{
  					m_testCtx.getLog() << tcu::TestLog::Message << "Error, invalid active variable list" << tcu::TestLog::EndMessage;
  					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid active variable list");
  					continue;
  				}
  			}
  		}
  	}
  
  	// Max num active variables
  	{
  		const tcu::ScopedLogSection	section					(m_testCtx.getLog(), "MaxNumActiveVariables", "MAX_NUM_ACTIVE_VARIABLES");
  		const glw::Functions&		gl						= m_context.getRenderContext().getFunctions();
  		glw::GLint					maxNumActiveVariables	= -1;
  
  		gl.getProgramInterfaceiv(program.getProgram(), programGLInterfaceValue, GL_MAX_NUM_ACTIVE_VARIABLES, &maxNumActiveVariables);
  		GLU_EXPECT_NO_ERROR(gl.getError(), "query MAX_NUM_ACTIVE_VARIABLES");
  
  		m_testCtx.getLog() << tcu::TestLog::Message << "MAX_NUM_ACTIVE_VARIABLES = " << maxNumActiveVariables << tcu::TestLog::EndMessage;
  
  		if (expectedMaxNumActiveVariables != maxNumActiveVariables)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected MAX_NUM_ACTIVE_VARIABLES" << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "unexpected MAX_NUM_ACTIVE_VARIABLES");
  		}
  		else
  			m_testCtx.getLog() << tcu::TestLog::Message << "MAX_NUM_ACTIVE_VARIABLES valid" << tcu::TestLog::EndMessage;
  	}
  
  	return STOP;
  }
  
  class InterfaceBlockDataSizeTestCase : public InterfaceBlockBaseCase
  {
  public:
  											InterfaceBlockDataSizeTestCase	(Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType);
  
  private:
  	IterateResult							iterate							(void);
  	int										getBlockMinDataSize				(const std::string& blockName) const;
  	int										getBlockMinDataSize				(const glu::InterfaceBlock& block) const;
  };
  
  InterfaceBlockDataSizeTestCase::InterfaceBlockDataSizeTestCase (Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType)
  	: InterfaceBlockBaseCase(context, name, description, storage, caseType)
  {
  }
  
  InterfaceBlockDataSizeTestCase::IterateResult InterfaceBlockDataSizeTestCase::iterate (void)
  {
  	const ProgramInterface			programInterface		= (m_storage == glu::STORAGE_UNIFORM) ? (PROGRAMINTERFACE_UNIFORM_BLOCK) :
  															  (m_storage == glu::STORAGE_BUFFER) ? (PROGRAMINTERFACE_SHADER_STORAGE_BLOCK) :
  															  (PROGRAMINTERFACE_LAST);
  	const glw::GLenum				programGLInterfaceValue	= getProgramInterfaceGLEnum(programInterface);
  	const std::vector<std::string>	blockNames				= getProgramInterfaceResourceList(m_program, programInterface);
  	glu::ShaderProgram				program					(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program));
  
  	DE_ASSERT(programInterface != PROGRAMINTERFACE_LAST);
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  	checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
  
  	// Verify all blocks
  	for (int blockNdx = 0; blockNdx < (int)blockNames.size(); ++blockNdx)
  	{
  		const tcu::ScopedLogSection section				(m_testCtx.getLog(), "Block", "Block \"" + blockNames[blockNdx] + "\"");
  		const glw::Functions&		gl					= m_context.getRenderContext().getFunctions();
  		const glw::GLuint			resourceNdx			= gl.getProgramResourceIndex(program.getProgram(), programGLInterfaceValue, blockNames[blockNdx].c_str());
  		const int					expectedMinDataSize	= getBlockMinDataSize(blockNames[blockNdx]);
  		glw::GLint					queryDataSize		= -1;
  
  		GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index");
  
  		if (resourceNdx == GL_INVALID_INDEX)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, getProgramResourceIndex returned GL_INVALID_INDEX for \"" << blockNames[blockNdx] << "\"" << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Resource not found");
  			continue;
  		}
  
  		// query
  		{
  			const glw::GLenum prop = GL_BUFFER_DATA_SIZE;
  
  			gl.getProgramResourceiv(program.getProgram(), programGLInterfaceValue, resourceNdx, 1, &prop, 1, DE_NULL, &queryDataSize);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query resource BUFFER_DATA_SIZE");
  		}
  
  		m_testCtx.getLog()
  			<< tcu::TestLog::Message
  			<< "BUFFER_DATA_SIZE = " << queryDataSize << "\n"
  			<< "Buffer data size with tight packing: " << expectedMinDataSize
  			<< tcu::TestLog::EndMessage;
  
  		if (queryDataSize < expectedMinDataSize)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, buffer size was less than minimum buffer data size" << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer data size invalid");
  			continue;
  		}
  		else
  			m_testCtx.getLog() << tcu::TestLog::Message << "Buffer size valid" << tcu::TestLog::EndMessage;
  	}
  
  	return STOP;
  }
  
  int InterfaceBlockDataSizeTestCase::getBlockMinDataSize (const std::string& blockFullName) const
  {
  	const std::string blockName = glu::parseVariableName(blockFullName.c_str());
  
  	for (int interfaceNdx = 0; interfaceNdx < (int)m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks.size(); ++interfaceNdx)
  	{
  		if (m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks[interfaceNdx].interfaceName == blockName &&
  			m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks[interfaceNdx].storage == m_storage)
  			return getBlockMinDataSize(m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks[interfaceNdx]);
  	}
  
  	DE_ASSERT(false);
  	return -1;
  }
  
  class AtomicCounterCase : public TestCase
  {
  public:
  											AtomicCounterCase			(Context& context, const char* name, const char* description);
  											~AtomicCounterCase			(void);
  
  private:
  	void									init						(void);
  	void									deinit						(void);
  
  protected:
  	int										getNumAtomicCounterBuffers	(void) const;
  	int										getMaxNumActiveVariables	(void) const;
  	int										getBufferVariableCount		(int binding) const;
  	int										getBufferMinimumDataSize	(int binding) const;
  
  	ProgramInterfaceDefinition::Program*	m_program;
  };
  
  AtomicCounterCase::AtomicCounterCase (Context& context, const char* name, const char* description)
  	: TestCase	(context, name, description)
  	, m_program	(DE_NULL)
  {
  }
  
  AtomicCounterCase::~AtomicCounterCase (void)
  {
  	deinit();
  }
  
  void AtomicCounterCase::init (void)
  {
  	ProgramInterfaceDefinition::Shader* shader;
  	glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
  
  	m_program = new ProgramInterfaceDefinition::Program();
  	shader = m_program->addShader(glu::SHADERTYPE_COMPUTE, glslVersion);
  
  	{
  		glu::VariableDeclaration decl(glu::VarType(glu::TYPE_UINT_ATOMIC_COUNTER, glu::PRECISION_LAST), "binding1_counter1", glu::STORAGE_UNIFORM);
  		decl.layout.binding = 1;
  		shader->getDefaultBlock().variables.push_back(decl);
  	}
  	{
  		glu::VariableDeclaration decl(glu::VarType(glu::TYPE_UINT_ATOMIC_COUNTER, glu::PRECISION_LAST), "binding1_counter2", glu::STORAGE_UNIFORM);
  		decl.layout.binding = 1;
  		decl.layout.offset = 8;
  
  		shader->getDefaultBlock().variables.push_back(decl);
  	}
  	{
  		glu::VariableDeclaration decl(glu::VarType(glu::TYPE_UINT_ATOMIC_COUNTER, glu::PRECISION_LAST), "binding2_counter1", glu::STORAGE_UNIFORM);
  		decl.layout.binding = 2;
  		shader->getDefaultBlock().variables.push_back(decl);
  	}
  
  	DE_ASSERT(m_program->isValid());
  }
  
  void AtomicCounterCase::deinit (void)
  {
  	delete m_program;
  	m_program = DE_NULL;
  }
  
  int AtomicCounterCase::getNumAtomicCounterBuffers (void) const
  {
  	std::set<int> buffers;
  
  	for (int ndx = 0; ndx < (int)m_program->getShaders()[0]->getDefaultBlock().variables.size(); ++ndx)
  	{
  		if (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.isBasicType() &&
  			glu::isDataTypeAtomicCounter(m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.getBasicType()))
  		{
  			buffers.insert(m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.binding);
  		}
  	}
  
  	return (int)buffers.size();
  }
  
  int AtomicCounterCase::getMaxNumActiveVariables (void) const
  {
  	int					maxVars			= 0;
  	std::map<int,int>	numBufferVars;
  
  	for (int ndx = 0; ndx < (int)m_program->getShaders()[0]->getDefaultBlock().variables.size(); ++ndx)
  	{
  		if (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.isBasicType() &&
  			glu::isDataTypeAtomicCounter(m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.getBasicType()))
  		{
  			const int binding = m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.binding;
  
  			if (numBufferVars.find(binding) == numBufferVars.end())
  				numBufferVars[binding] = 1;
  			else
  				++numBufferVars[binding];
  		}
  	}
  
  	for (std::map<int,int>::const_iterator it = numBufferVars.begin(); it != numBufferVars.end(); ++it)
  		maxVars = de::max(maxVars, it->second);
  
  	return maxVars;
  }
  
  int AtomicCounterCase::getBufferVariableCount (int binding) const
  {
  	int numVars = 0;
  
  	for (int ndx = 0; ndx < (int)m_program->getShaders()[0]->getDefaultBlock().variables.size(); ++ndx)
  	{
  		if (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.isBasicType() &&
  			glu::isDataTypeAtomicCounter(m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.getBasicType()) &&
  			m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.binding == binding)
  			++numVars;
  	}
  
  	return numVars;
  }
  
  int AtomicCounterCase::getBufferMinimumDataSize (int binding) const
  {
  	int minSize			= -1;
  	int currentOffset	= 0;
  
  	for (int ndx = 0; ndx < (int)m_program->getShaders()[0]->getDefaultBlock().variables.size(); ++ndx)
  	{
  		if (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.isBasicType() &&
  			glu::isDataTypeAtomicCounter(m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.getBasicType()) &&
  			m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.binding == binding)
  		{
  			const int thisOffset = (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.offset != -1) ? (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.offset) : (currentOffset);
  			currentOffset = thisOffset + 4;
  
  			minSize = de::max(minSize, thisOffset + 4);
  		}
  	}
  
  	return minSize;
  }
  
  class AtomicCounterResourceListCase : public AtomicCounterCase
  {
  public:
  						AtomicCounterResourceListCase	(Context& context, const char* name, const char* description);
  
  private:
  	IterateResult		iterate							(void);
  };
  
  AtomicCounterResourceListCase::AtomicCounterResourceListCase (Context& context, const char* name, const char* description)
  	: AtomicCounterCase(context, name, description)
  {
  }
  
  AtomicCounterResourceListCase::IterateResult AtomicCounterResourceListCase::iterate (void)
  {
  	const glu::ShaderProgram program(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program));
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  	checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
  
  	{
  		const tcu::ScopedLogSection		section						(m_testCtx.getLog(), "ActiveResources", "ACTIVE_RESOURCES");
  		const glw::Functions&			gl							= m_context.getRenderContext().getFunctions();
  		glw::GLint						numActiveResources			= -1;
  		const int						numExpectedActiveResources	= 2; // 2 buffer bindings
  
  		m_testCtx.getLog() << tcu::TestLog::Message << "Verifying ACTIVE_RESOURCES, expecting " << numExpectedActiveResources << tcu::TestLog::EndMessage;
  
  		gl.getProgramInterfaceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, GL_ACTIVE_RESOURCES, &numActiveResources);
  		GLU_EXPECT_NO_ERROR(gl.getError(), "query GL_ACTIVE_RESOURCES");
  
  		m_testCtx.getLog() << tcu::TestLog::Message << "ACTIVE_RESOURCES = " << numActiveResources << tcu::TestLog::EndMessage;
  
  		if (numActiveResources != numExpectedActiveResources)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected ACTIVE_RESOURCES" << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected ACTIVE_RESOURCES");
  		}
  		else
  			m_testCtx.getLog() << tcu::TestLog::Message << "ACTIVE_RESOURCES valid" << tcu::TestLog::EndMessage;
  	}
  
  	return STOP;
  }
  
  class AtomicCounterActiveVariablesCase : public AtomicCounterCase
  {
  public:
  					AtomicCounterActiveVariablesCase	(Context& context, const char* name, const char* description);
  
  private:
  	IterateResult	iterate								(void);
  };
  
  AtomicCounterActiveVariablesCase::AtomicCounterActiveVariablesCase (Context& context, const char* name, const char* description)
  	: AtomicCounterCase(context, name, description)
  {
  }
  
  AtomicCounterActiveVariablesCase::IterateResult AtomicCounterActiveVariablesCase::iterate (void)
  {
  	const glw::Functions&		gl								= m_context.getRenderContext().getFunctions();
  	const glu::ShaderProgram	program							(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program));
  	const int					numAtomicBuffers				= getNumAtomicCounterBuffers();
  	const int					expectedMaxNumActiveVariables	= getMaxNumActiveVariables();
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  	checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
  
  	// check active variables
  	{
  		const tcu::ScopedLogSection	section						(m_testCtx.getLog(), "Interface", "ATOMIC_COUNTER_BUFFER interface");
  		glw::GLint					queryActiveResources		= -1;
  		glw::GLint					queryMaxNumActiveVariables	= -1;
  
  		gl.getProgramInterfaceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, GL_ACTIVE_RESOURCES, &queryActiveResources);
  		gl.getProgramInterfaceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, GL_MAX_NUM_ACTIVE_VARIABLES, &queryMaxNumActiveVariables);
  		GLU_EXPECT_NO_ERROR(gl.getError(), "query interface");
  
  		m_testCtx.getLog()
  			<< tcu::TestLog::Message
  			<< "GL_ACTIVE_RESOURCES = " << queryActiveResources << "\n"
  			<< "GL_MAX_NUM_ACTIVE_VARIABLES = " << queryMaxNumActiveVariables << "\n"
  			<< tcu::TestLog::EndMessage;
  
  		if (queryActiveResources != numAtomicBuffers)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected GL_ACTIVE_RESOURCES, expected " << numAtomicBuffers << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected GL_ACTIVE_RESOURCES");
  		}
  
  		if (queryMaxNumActiveVariables != expectedMaxNumActiveVariables)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected GL_MAX_NUM_ACTIVE_VARIABLES, expected " << expectedMaxNumActiveVariables << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected GL_MAX_NUM_ACTIVE_VARIABLES");
  		}
  	}
  
  	// Check each buffer
  	for (int bufferNdx = 0; bufferNdx < numAtomicBuffers; ++bufferNdx)
  	{
  		const tcu::ScopedLogSection	section				(m_testCtx.getLog(), "Resource", "Resource index " + de::toString(bufferNdx));
  		std::vector<glw::GLint>		activeVariables;
  		std::vector<std::string>	memberNames;
  
  		// Find active variables
  		{
  			const glw::GLenum	numActiveVariablesProp	= GL_NUM_ACTIVE_VARIABLES;
  			const glw::GLenum	activeVariablesProp		= GL_ACTIVE_VARIABLES;
  			glw::GLint			numActiveVariables		= -2;
  			glw::GLint			written					= -1;
  
  			gl.getProgramResourceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, bufferNdx, 1, &numActiveVariablesProp, 1, &written, &numActiveVariables);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query num active variables");
  
  			if (numActiveVariables <= 0)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected NUM_ACTIVE_VARIABLES: " << numActiveVariables  << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected NUM_ACTIVE_VARIABLES");
  				continue;
  			}
  
  			if (written <= 0)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for NUM_ACTIVE_VARIABLES returned no values" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "NUM_ACTIVE_VARIABLES query failed");
  				continue;
  			}
  
  			m_testCtx.getLog() << tcu::TestLog::Message << "GL_NUM_ACTIVE_VARIABLES = " << numActiveVariables << tcu::TestLog::EndMessage;
  
  			written = -1;
  			activeVariables.resize(numActiveVariables + 1, -2);
  
  			gl.getProgramResourceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, bufferNdx, 1, &activeVariablesProp, numActiveVariables, &written, &activeVariables[0]);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query active variables");
  
  			if (written != numActiveVariables)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, unexpected number of ACTIVE_VARIABLES, NUM_ACTIVE_VARIABLES = " << numActiveVariables << ", query returned " << written << " values" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected ACTIVE_VARIABLES");
  				continue;
  			}
  
  			if (activeVariables.back() != -2)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for ACTIVE_VARIABLES wrote over target buffer bounds" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "ACTIVE_VARIABLES query failed");
  				continue;
  			}
  
  			activeVariables.pop_back();
  		}
  
  		// log indices
  		{
  			tcu::MessageBuilder builder(&m_testCtx.getLog());
  
  			builder << "Active variable indices: {";
  			for (int varNdx = 0; varNdx < (int)activeVariables.size(); ++varNdx)
  			{
  				if (varNdx)
  					builder << ", ";
  				builder << activeVariables[varNdx];
  			}
  			builder << "}" << tcu::TestLog::EndMessage;
  		}
  
  		// collect member names
  		for (int ndx = 0; ndx < (int)activeVariables.size(); ++ndx)
  		{
  			const glw::GLenum	nameLengthProp	= GL_NAME_LENGTH;
  			glw::GLint			nameLength		= -1;
  			glw::GLint			written			= -1;
  			std::vector<char>	nameBuf;
  
  			gl.getProgramResourceiv(program.getProgram(), GL_UNIFORM, activeVariables[ndx], 1, &nameLengthProp, 1, &written, &nameLength);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query buffer variable name length");
  
  			if (written <= 0 || nameLength == -1)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for GL_NAME_LENGTH returned no values" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "GL_NAME_LENGTH query failed");
  				continue;
  			}
  
  			nameBuf.resize(nameLength + 2, 'X'); // +2 to tolerate potential off-by-ones in some implementations, name queries will check these cases better
  			written = -1;
  
  			gl.getProgramResourceName(program.getProgram(), GL_UNIFORM, activeVariables[ndx], (int)nameBuf.size(), &written, &nameBuf[0]);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query buffer variable name");
  
  			if (written <= 0)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for resource name returned no name" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Name query failed");
  				continue;
  			}
  
  			memberNames.push_back(std::string(&nameBuf[0], written));
  		}
  
  		// log names
  		{
  			tcu::MessageBuilder builder(&m_testCtx.getLog());
  
  			builder << "Active variables:\n";
  			for (int varNdx = 0; varNdx < (int)memberNames.size(); ++varNdx)
  			{
  				builder << "\t" << memberNames[varNdx] << "\n";
  			}
  			builder << tcu::TestLog::EndMessage;
  		}
  
  		// check names are all in the same buffer
  		{
  			bool bindingsValid = true;
  
  			m_testCtx.getLog() << tcu::TestLog::Message << "Verifying names" << tcu::TestLog::EndMessage;
  
  			for (int nameNdx = 0; nameNdx < (int)memberNames.size(); ++nameNdx)
  			{
  				int prevBinding = -1;
  
  				for (int varNdx = 0; varNdx < (int)m_program->getShaders()[0]->getDefaultBlock().variables.size(); ++varNdx)
  				{
  					if (m_program->getShaders()[0]->getDefaultBlock().variables[varNdx].name == memberNames[nameNdx])
  					{
  						const int varBinding = m_program->getShaders()[0]->getDefaultBlock().variables[varNdx].layout.binding;
  
  						if (prevBinding == -1 || prevBinding == varBinding)
  							prevBinding = varBinding;
  						else
  							bindingsValid = false;
  					}
  				}
  
  				if (prevBinding == -1)
  				{
  					m_testCtx.getLog() << tcu::TestLog::Message << "Error, could not find variable with name \"" << memberNames[nameNdx] << "\"" << tcu::TestLog::EndMessage;
  					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Variable name invalid");
  				}
  				else if (getBufferVariableCount(prevBinding) != (int)memberNames.size())
  				{
  					m_testCtx.getLog()
  						<< tcu::TestLog::Message
  						<< "Error, unexpected variable count for binding " << prevBinding
  						<< ". Expected " << getBufferVariableCount(prevBinding) << ", got " << (int)memberNames.size()
  						<< tcu::TestLog::EndMessage;
  					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Variable names invalid");
  				}
  			}
  
  			if (!bindingsValid)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, all resource do not share the same buffer" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Active variables invalid");
  				continue;
  			}
  		}
  	}
  
  	return STOP;
  }
  
  class AtomicCounterBufferBindingCase : public AtomicCounterCase
  {
  public:
  					AtomicCounterBufferBindingCase		(Context& context, const char* name, const char* description);
  
  private:
  	IterateResult	iterate								(void);
  };
  
  AtomicCounterBufferBindingCase::AtomicCounterBufferBindingCase (Context& context, const char* name, const char* description)
  	: AtomicCounterCase(context, name, description)
  {
  }
  
  AtomicCounterBufferBindingCase::IterateResult AtomicCounterBufferBindingCase::iterate (void)
  {
  	const glw::Functions&		gl								= m_context.getRenderContext().getFunctions();
  	const glu::ShaderProgram	program							(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program));
  	const int					numAtomicBuffers				= getNumAtomicCounterBuffers();
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  	checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
  
  	// check every buffer
  	for (int bufferNdx = 0; bufferNdx < numAtomicBuffers; ++bufferNdx)
  	{
  		const tcu::ScopedLogSection	section				(m_testCtx.getLog(), "Resource", "Resource index " + de::toString(bufferNdx));
  		const glw::GLenum			bufferBindingProp	= GL_BUFFER_BINDING;
  		glw::GLint					bufferBinding		= -1;
  		glw::GLint					written				= -1;
  
  		gl.getProgramResourceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, bufferNdx, 1, &bufferBindingProp, 1, &written, &bufferBinding);
  		GLU_EXPECT_NO_ERROR(gl.getError(), "query buffer binding");
  
  		if (written <= 0)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for BUFFER_BINDING returned no values." << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "BUFFER_BINDING query failed");
  		}
  
  		m_testCtx.getLog() << tcu::TestLog::Message << "GL_BUFFER_BINDING = " << bufferBinding << tcu::TestLog::EndMessage;
  
  		// no such buffer binding?
  		if (getBufferVariableCount(bufferBinding) == 0)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, got buffer with BUFFER_BINDING = " << bufferBinding << ", but such buffer does not exist." << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected BUFFER_BINDING");
  		}
  	}
  
  	return STOP;
  }
  
  class AtomicCounterBufferDataSizeCase : public AtomicCounterCase
  {
  public:
  					AtomicCounterBufferDataSizeCase		(Context& context, const char* name, const char* description);
  
  private:
  	IterateResult	iterate								(void);
  };
  
  AtomicCounterBufferDataSizeCase::AtomicCounterBufferDataSizeCase (Context& context, const char* name, const char* description)
  	: AtomicCounterCase(context, name, description)
  {
  }
  
  AtomicCounterBufferDataSizeCase::IterateResult AtomicCounterBufferDataSizeCase::iterate (void)
  {
  	const glw::Functions&		gl								= m_context.getRenderContext().getFunctions();
  	const glu::ShaderProgram	program							(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program));
  	const int					numAtomicBuffers				= getNumAtomicCounterBuffers();
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  	checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
  
  	// check every buffer
  	for (int bufferNdx = 0; bufferNdx < numAtomicBuffers; ++bufferNdx)
  	{
  		const tcu::ScopedLogSection	section				(m_testCtx.getLog(), "Resource", "Resource index " + de::toString(bufferNdx));
  		const glw::GLenum			props[]				= { GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE };
  		glw::GLint					values[]			= { -1, -1 };
  		glw::GLint					written				= -1;
  		int							bufferMinDataSize;
  
  		gl.getProgramResourceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, bufferNdx, DE_LENGTH_OF_ARRAY(props), props, DE_LENGTH_OF_ARRAY(values), &written, values);
  		GLU_EXPECT_NO_ERROR(gl.getError(), "query buffer binding");
  
  		if (written != 2)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for (BUFFER_BINDING, BUFFER_DATA_SIZE) returned " << written << " value(s)." << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "property query failed");
  			continue;
  		}
  
  		m_testCtx.getLog()
  			<< tcu::TestLog::Message
  			<< "GL_BUFFER_BINDING = " << values[0] << "\n"
  			<< "GL_BUFFER_DATA_SIZE = " << values[1]
  			<< tcu::TestLog::EndMessage;
  
  		bufferMinDataSize = getBufferMinimumDataSize(values[0]);
  		m_testCtx.getLog() << tcu::TestLog::Message << "Verifying data size, expected greater than or equal to " << bufferMinDataSize << tcu::TestLog::EndMessage;
  
  		// no such buffer binding?
  		if (bufferMinDataSize == -1)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, got buffer with BUFFER_BINDING = " << values[0] << ", but such buffer does not exist." << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected BUFFER_BINDING");
  		}
  		else if (values[1] < bufferMinDataSize)
  		{
  			m_testCtx.getLog() << tcu::TestLog::Message << "Error, got buffer with BUFFER_DATA_SIZE = " << values[1] << ", expected greater than or equal to " << bufferMinDataSize << tcu::TestLog::EndMessage;
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected BUFFER_BINDING");
  		}
  		else
  			m_testCtx.getLog() << tcu::TestLog::Message << "Data size valid" << tcu::TestLog::EndMessage;
  	}
  
  	return STOP;
  }
  
  class AtomicCounterReferencedByCase : public TestCase
  {
  public:
  											AtomicCounterReferencedByCase	(Context&		context,
  																			 const char*	name,
  																			 const char*	description,
  																			 bool			separable,
  																			 deUint32		presentStagesMask,
  																			 deUint32		activeStagesMask);
  											~AtomicCounterReferencedByCase	(void);
  
  private:
  	void									init							(void);
  	void									deinit							(void);
  	IterateResult							iterate							(void);
  
  	const bool								m_separable;
  	const deUint32							m_presentStagesMask;
  	const deUint32							m_activeStagesMask;
  	ProgramInterfaceDefinition::Program*	m_program;
  };
  
  AtomicCounterReferencedByCase::AtomicCounterReferencedByCase (Context&		context,
  															  const char*	name,
  															  const char*	description,
  															  bool			separable,
  															  deUint32		presentStagesMask,
  															  deUint32		activeStagesMask)
  	: TestCase				(context, name, description)
  	, m_separable			(separable)
  	, m_presentStagesMask	(presentStagesMask)
  	, m_activeStagesMask	(activeStagesMask)
  	, m_program				(DE_NULL)
  {
  	DE_ASSERT((activeStagesMask & presentStagesMask) == activeStagesMask);
  }
  
  AtomicCounterReferencedByCase::~AtomicCounterReferencedByCase (void)
  {
  	deinit();
  }
  
  void AtomicCounterReferencedByCase::init (void)
  {
  	const deUint32				geometryMask		= (1 << glu::SHADERTYPE_GEOMETRY);
  	const deUint32				tessellationMask	= (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) | (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION);
  	glu::VariableDeclaration	atomicVar			(glu::VarType(glu::TYPE_UINT_ATOMIC_COUNTER, glu::PRECISION_LAST), "targetCounter", glu::STORAGE_UNIFORM);
  	const glu::GLSLVersion		glslVersion			= glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
  	const bool					supportsES32orGL45	= checkSupport(m_context);
  
  	if ((m_presentStagesMask & tessellationMask) != 0 && !supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
  		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
  	if ((m_presentStagesMask & geometryMask) != 0 && !supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
  		throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension");
  
  	atomicVar.layout.binding = 1;
  
  	m_program = new ProgramInterfaceDefinition::Program();
  	m_program->setSeparable(m_separable);
  
  	for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; ++shaderType)
  	{
  		if (m_activeStagesMask & (1 << shaderType))
  			m_program->addShader((glu::ShaderType)shaderType, glslVersion)->getDefaultBlock().variables.push_back(atomicVar);
  		else if (m_presentStagesMask & (1 << shaderType))
  			m_program->addShader((glu::ShaderType)shaderType, glslVersion);
  	}
  
  	if (m_program->hasStage(glu::SHADERTYPE_GEOMETRY))
  		m_program->setGeometryNumOutputVertices(1);
  	if (m_program->hasStage(glu::SHADERTYPE_TESSELLATION_CONTROL) || m_program->hasStage(glu::SHADERTYPE_TESSELLATION_EVALUATION))
  		m_program->setTessellationNumOutputPatchVertices(1);
  
  	DE_ASSERT(m_program->isValid());
  }
  
  void AtomicCounterReferencedByCase::deinit (void)
  {
  	delete m_program;
  	m_program = DE_NULL;
  }
  
  AtomicCounterReferencedByCase::IterateResult AtomicCounterReferencedByCase::iterate (void)
  {
  	const glu::RenderContext& rc = m_context.getRenderContext();
  	const bool isES32orGL45 = glu::contextSupports(rc.getType(), glu::ApiType::es(3, 2)) ||
  							  glu::contextSupports(rc.getType(), glu::ApiType::core(4, 5));
  
  	const struct
  	{
  		glw::GLenum		propName;
  		glu::ShaderType	shaderType;
  		const char*		extension;
  	} targetProps[] =
  	{
  		{ GL_REFERENCED_BY_VERTEX_SHADER,			glu::SHADERTYPE_VERTEX,						DE_NULL												},
  		{ GL_REFERENCED_BY_FRAGMENT_SHADER,			glu::SHADERTYPE_FRAGMENT,					DE_NULL												},
  		{ GL_REFERENCED_BY_COMPUTE_SHADER,			glu::SHADERTYPE_COMPUTE,					DE_NULL												},
  		{ GL_REFERENCED_BY_TESS_CONTROL_SHADER,		glu::SHADERTYPE_TESSELLATION_CONTROL,		(isES32orGL45 ? DE_NULL : "GL_EXT_tessellation_shader")	},
  		{ GL_REFERENCED_BY_TESS_EVALUATION_SHADER,	glu::SHADERTYPE_TESSELLATION_EVALUATION,	(isES32orGL45 ? DE_NULL : "GL_EXT_tessellation_shader")	},
  		{ GL_REFERENCED_BY_GEOMETRY_SHADER,			glu::SHADERTYPE_GEOMETRY,					(isES32orGL45 ? DE_NULL : "GL_EXT_geometry_shader")		},
  	};
  
  	const glw::Functions&		gl			= rc.getFunctions();
  	const glu::ShaderProgram	program		(rc, generateProgramInterfaceProgramSources(m_program));
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  	checkAndLogProgram(program, m_program, rc.getFunctions(), m_testCtx.getLog());
  
  	// check props
  	for (int propNdx = 0; propNdx < DE_LENGTH_OF_ARRAY(targetProps); ++propNdx)
  	{
  		if (targetProps[propNdx].extension == DE_NULL || m_context.getContextInfo().isExtensionSupported(targetProps[propNdx].extension))
  		{
  			const glw::GLenum	prop		= targetProps[propNdx].propName;
  			const glw::GLint	expected	= ((m_activeStagesMask & (1 << targetProps[propNdx].shaderType)) != 0) ? (GL_TRUE) : (GL_FALSE);
  			glw::GLint			value		= -1;
  			glw::GLint			written		= -1;
  
  			m_testCtx.getLog() << tcu::TestLog::Message << "Verifying " << glu::getProgramResourcePropertyName(prop) << ", expecting " << glu::getBooleanName(expected) << tcu::TestLog::EndMessage;
  
  			gl.getProgramResourceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, 0, 1, &prop, 1, &written, &value);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query buffer binding");
  
  			if (written != 1)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for referenced_by_* returned invalid number of values." << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "property query failed");
  				continue;
  			}
  
  			m_testCtx.getLog() << tcu::TestLog::Message << glu::getProgramResourcePropertyName(prop) << " = " << glu::getBooleanStr(value) << tcu::TestLog::EndMessage;
  
  			if (value != expected)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected value" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "unexpected property value");
  				continue;
  			}
  		}
  	}
  
  	return STOP;
  }
  
  class ProgramInputOutputReferencedByCase : public TestCase
  {
  public:
  	enum CaseType
  	{
  		CASE_VERTEX_FRAGMENT = 0,
  		CASE_VERTEX_GEO_FRAGMENT,
  		CASE_VERTEX_TESS_FRAGMENT,
  		CASE_VERTEX_TESS_GEO_FRAGMENT,
  
  		CASE_SEPARABLE_VERTEX,
  		CASE_SEPARABLE_FRAGMENT,
  		CASE_SEPARABLE_GEOMETRY,
  		CASE_SEPARABLE_TESS_CTRL,
  		CASE_SEPARABLE_TESS_EVAL,
  
  		CASE_LAST
  	};
  											ProgramInputOutputReferencedByCase	(Context& context, const char* name, const char* description, glu::Storage targetStorage, CaseType caseType);
  											~ProgramInputOutputReferencedByCase	(void);
  
  private:
  	void									init								(void);
  	void									deinit								(void);
  	IterateResult							iterate								(void);
  
  	const CaseType							m_caseType;
  	const glu::Storage						m_targetStorage;
  	ProgramInterfaceDefinition::Program*	m_program;
  };
  
  ProgramInputOutputReferencedByCase::ProgramInputOutputReferencedByCase (Context& context, const char* name, const char* description, glu::Storage targetStorage, CaseType caseType)
  	: TestCase				(context, name, description)
  	, m_caseType			(caseType)
  	, m_targetStorage		(targetStorage)
  	, m_program				(DE_NULL)
  {
  	DE_ASSERT(caseType < CASE_LAST);
  }
  
  ProgramInputOutputReferencedByCase::~ProgramInputOutputReferencedByCase (void)
  {
  	deinit();
  }
  
  void ProgramInputOutputReferencedByCase::init (void)
  {
  	const bool hasTessellationShader =	(m_caseType == CASE_VERTEX_TESS_FRAGMENT)		||
  										(m_caseType == CASE_VERTEX_TESS_GEO_FRAGMENT)	||
  										(m_caseType == CASE_SEPARABLE_TESS_CTRL)		||
  										(m_caseType == CASE_SEPARABLE_TESS_EVAL);
  	const bool hasGeometryShader =		(m_caseType == CASE_VERTEX_GEO_FRAGMENT)		||
  										(m_caseType == CASE_VERTEX_TESS_GEO_FRAGMENT)	||
  										(m_caseType == CASE_SEPARABLE_GEOMETRY);
  	const bool supportsES32orGL45 =		checkSupport(m_context);
  
  	if (hasTessellationShader && !supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
  		throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
  	if (hasGeometryShader && !supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
  		throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension");
  
  	glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
  	m_program = new ProgramInterfaceDefinition::Program();
  
  	if (m_caseType == CASE_SEPARABLE_VERTEX		||
  		m_caseType == CASE_SEPARABLE_FRAGMENT	||
  		m_caseType == CASE_SEPARABLE_GEOMETRY	||
  		m_caseType == CASE_SEPARABLE_TESS_CTRL	||
  		m_caseType == CASE_SEPARABLE_TESS_EVAL)
  	{
  		const bool						isInputCase			= (m_targetStorage == glu::STORAGE_IN || m_targetStorage == glu::STORAGE_PATCH_IN);
  		const bool						perPatchStorage		= (m_targetStorage == glu::STORAGE_PATCH_IN || m_targetStorage == glu::STORAGE_PATCH_OUT);
  		const char*						varName				= (isInputCase) ? ("shaderInput") : ("shaderOutput");
  		const glu::VariableDeclaration	targetDecl			(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), varName, m_targetStorage);
  		const glu::ShaderType			shaderType			= (m_caseType == CASE_SEPARABLE_VERTEX)		? (glu::SHADERTYPE_VERTEX)
  															: (m_caseType == CASE_SEPARABLE_FRAGMENT)	? (glu::SHADERTYPE_FRAGMENT)
  															: (m_caseType == CASE_SEPARABLE_GEOMETRY)	? (glu::SHADERTYPE_GEOMETRY)
  															: (m_caseType == CASE_SEPARABLE_TESS_CTRL)	? (glu::SHADERTYPE_TESSELLATION_CONTROL)
  															: (m_caseType == CASE_SEPARABLE_TESS_EVAL)	? (glu::SHADERTYPE_TESSELLATION_EVALUATION)
  															:											  (glu::SHADERTYPE_LAST);
  		const bool						arrayedInterface	= (isInputCase) ? ((shaderType == glu::SHADERTYPE_GEOMETRY)					||
  																			   (shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL)		||
  																			   (shaderType == glu::SHADERTYPE_TESSELLATION_EVALUATION))
  																			: (shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL);
  
  		m_program->setSeparable(true);
  
  		if (arrayedInterface && !perPatchStorage)
  		{
  			const glu::VariableDeclaration targetDeclArr(glu::VarType(targetDecl.varType, glu::VarType::UNSIZED_ARRAY), varName, m_targetStorage);
  			m_program->addShader(shaderType, glslVersion)->getDefaultBlock().variables.push_back(targetDeclArr);
  		}
  		else
  		{
  			m_program->addShader(shaderType, glslVersion)->getDefaultBlock().variables.push_back(targetDecl);
  		}
  	}
  	else if (m_caseType == CASE_VERTEX_FRAGMENT			||
  			 m_caseType == CASE_VERTEX_GEO_FRAGMENT		||
  			 m_caseType == CASE_VERTEX_TESS_FRAGMENT	||
  			 m_caseType == CASE_VERTEX_TESS_GEO_FRAGMENT)
  	{
  		ProgramInterfaceDefinition::Shader*	vertex		= m_program->addShader(glu::SHADERTYPE_VERTEX, glslVersion);
  		ProgramInterfaceDefinition::Shader*	fragment	= m_program->addShader(glu::SHADERTYPE_FRAGMENT, glslVersion);
  
  		m_program->setSeparable(false);
  
  		vertex->getDefaultBlock().variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP),
  																			   "shaderInput",
  																			   glu::STORAGE_IN));
  		vertex->getDefaultBlock().variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP),
  																			   "shaderOutput",
  																			   glu::STORAGE_OUT,
  																			   glu::INTERPOLATION_LAST,
  																			   glu::Layout(1)));
  
  		fragment->getDefaultBlock().variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP),
  																				 "shaderOutput",
  																				 glu::STORAGE_OUT,
  																				 glu::INTERPOLATION_LAST,
  																				 glu::Layout(0)));
  		fragment->getDefaultBlock().variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP),
  																				 "shaderInput",
  																				 glu::STORAGE_IN,
  																				 glu::INTERPOLATION_LAST,
  																				 glu::Layout(1)));
  
  		if (m_caseType == CASE_VERTEX_TESS_FRAGMENT || m_caseType == CASE_VERTEX_TESS_GEO_FRAGMENT)
  		{
  			ProgramInterfaceDefinition::Shader* tessCtrl = m_program->addShader(glu::SHADERTYPE_TESSELLATION_CONTROL, glslVersion);
  			ProgramInterfaceDefinition::Shader* tessEval = m_program->addShader(glu::SHADERTYPE_TESSELLATION_EVALUATION, glslVersion);
  
  			tessCtrl->getDefaultBlock().variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), glu::VarType::UNSIZED_ARRAY),
  																					 "shaderInput",
  																					 glu::STORAGE_IN,
  																					 glu::INTERPOLATION_LAST,
  																					 glu::Layout(1)));
  			tessCtrl->getDefaultBlock().variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), glu::VarType::UNSIZED_ARRAY),
  																					 "shaderOutput",
  																					 glu::STORAGE_OUT,
  																					 glu::INTERPOLATION_LAST,
  																					 glu::Layout(1)));
  
  			tessEval->getDefaultBlock().variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), glu::VarType::UNSIZED_ARRAY),
  																					 "shaderInput",
  																					 glu::STORAGE_IN,
  																					 glu::INTERPOLATION_LAST,
  																					 glu::Layout(1)));
  			tessEval->getDefaultBlock().variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP),
  																					 "shaderOutput",
  																					 glu::STORAGE_OUT,
  																					 glu::INTERPOLATION_LAST,
  																					 glu::Layout(1)));
  		}
  
  		if (m_caseType == CASE_VERTEX_GEO_FRAGMENT || m_caseType == CASE_VERTEX_TESS_GEO_FRAGMENT)
  		{
  			ProgramInterfaceDefinition::Shader* geometry = m_program->addShader(glu::SHADERTYPE_GEOMETRY, glslVersion);
  
  			geometry->getDefaultBlock().variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), glu::VarType::UNSIZED_ARRAY),
  																					 "shaderInput",
  																					 glu::STORAGE_IN,
  																					 glu::INTERPOLATION_LAST,
  																					 glu::Layout(1)));
  			geometry->getDefaultBlock().variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP),
  																					 "shaderOutput",
  																					 glu::STORAGE_OUT,
  																					 glu::INTERPOLATION_LAST,
  																					 glu::Layout(1)));
  		}
  	}
  	else
  		DE_ASSERT(false);
  
  	if (m_program->hasStage(glu::SHADERTYPE_GEOMETRY))
  		m_program->setGeometryNumOutputVertices(1);
  	if (m_program->hasStage(glu::SHADERTYPE_TESSELLATION_CONTROL) || m_program->hasStage(glu::SHADERTYPE_TESSELLATION_EVALUATION))
  		m_program->setTessellationNumOutputPatchVertices(1);
  
  	DE_ASSERT(m_program->isValid());
  }
  
  void ProgramInputOutputReferencedByCase::deinit (void)
  {
  	delete m_program;
  	m_program = DE_NULL;
  }
  
  ProgramInputOutputReferencedByCase::IterateResult ProgramInputOutputReferencedByCase::iterate (void)
  {
  	static const struct
  	{
  		glw::GLenum		propName;
  		glu::ShaderType	shaderType;
  		const char*		extension;
  	} targetProps[] =
  	{
  		{ GL_REFERENCED_BY_VERTEX_SHADER,			glu::SHADERTYPE_VERTEX,						DE_NULL							},
  		{ GL_REFERENCED_BY_FRAGMENT_SHADER,			glu::SHADERTYPE_FRAGMENT,					DE_NULL							},
  		{ GL_REFERENCED_BY_COMPUTE_SHADER,			glu::SHADERTYPE_COMPUTE,					DE_NULL							},
  		{ GL_REFERENCED_BY_TESS_CONTROL_SHADER,		glu::SHADERTYPE_TESSELLATION_CONTROL,		"GL_EXT_tessellation_shader"	},
  		{ GL_REFERENCED_BY_TESS_EVALUATION_SHADER,	glu::SHADERTYPE_TESSELLATION_EVALUATION,	"GL_EXT_tessellation_shader"	},
  		{ GL_REFERENCED_BY_GEOMETRY_SHADER,			glu::SHADERTYPE_GEOMETRY,					"GL_EXT_geometry_shader"		},
  	};
  
  	const bool					isInputCase						= (m_targetStorage == glu::STORAGE_IN || m_targetStorage == glu::STORAGE_PATCH_IN);
  	const glw::Functions&		gl								= m_context.getRenderContext().getFunctions();
  	const glu::ShaderProgram	program							(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program));
  	const std::string			targetResourceName				= (isInputCase) ? ("shaderInput") : ("shaderOutput");
  	const glw::GLenum			programGLInterface				= (isInputCase) ? (GL_PROGRAM_INPUT) : (GL_PROGRAM_OUTPUT);
  	glw::GLuint					resourceIndex;
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  	checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
  
  	// find target resource index
  
  	resourceIndex = gl.getProgramResourceIndex(program.getProgram(), programGLInterface, targetResourceName.c_str());
  	GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index");
  
  	if (resourceIndex == GL_INVALID_INDEX)
  	{
  		m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for resource \"" << targetResourceName << "\" index returned invalid index." << tcu::TestLog::EndMessage;
  		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "could not find target resource");
  		return STOP;
  	}
  
  	// check props
  	for (int propNdx = 0; propNdx < DE_LENGTH_OF_ARRAY(targetProps); ++propNdx)
  	{
  		if (targetProps[propNdx].extension == DE_NULL || m_context.getContextInfo().isExtensionSupported(targetProps[propNdx].extension))
  		{
  			const glw::GLenum	prop			= targetProps[propNdx].propName;
  			const bool			expected		= (isInputCase) ? (targetProps[propNdx].shaderType == m_program->getFirstStage()) : (targetProps[propNdx].shaderType == m_program->getLastStage());
  			glw::GLint			value			= -1;
  			glw::GLint			written			= -1;
  
  			m_testCtx.getLog() << tcu::TestLog::Message << "Verifying " << glu::getProgramResourcePropertyName(prop) << ", expecting " << ((expected) ? ("TRUE") : ("FALSE")) << tcu::TestLog::EndMessage;
  
  			gl.getProgramResourceiv(program.getProgram(), programGLInterface, resourceIndex, 1, &prop, 1, &written, &value);
  			GLU_EXPECT_NO_ERROR(gl.getError(), "query buffer binding");
  
  			if (written != 1)
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for referenced_by_* returned invalid number of values." << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "property query failed");
  				continue;
  			}
  
  			m_testCtx.getLog() << tcu::TestLog::Message << glu::getProgramResourcePropertyName(prop) << " = " << glu::getBooleanStr(value) << tcu::TestLog::EndMessage;
  
  			if (value != ((expected) ? (GL_TRUE) : (GL_FALSE)))
  			{
  				m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected value" << tcu::TestLog::EndMessage;
  				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "unexpected property value");
  				continue;
  			}
  		}
  	}
  
  	return STOP;
  }
  
  class FeedbackResourceListTestCase : public ResourceListTestCase
  {
  public:
  											FeedbackResourceListTestCase	(Context& context, const ResourceDefinition::Node::SharedPtr& resource, const char* name);
  											~FeedbackResourceListTestCase	(void);
  
  private:
  	IterateResult							iterate							(void);
  };
  
  FeedbackResourceListTestCase::FeedbackResourceListTestCase (Context& context, const ResourceDefinition::Node::SharedPtr& resource, const char* name)
  	: ResourceListTestCase(context, resource, PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING, name)
  {
  }
  
  FeedbackResourceListTestCase::~FeedbackResourceListTestCase (void)
  {
  	deinit();
  }
  
  FeedbackResourceListTestCase::IterateResult FeedbackResourceListTestCase::iterate (void)
  {
  	const glu::ShaderProgram program(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_programDefinition));
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  
  	// Feedback varyings
  	{
  		tcu::MessageBuilder builder(&m_testCtx.getLog());
  		builder << "Transform feedback varyings: {";
  		for (int ndx = 0; ndx < (int)m_programDefinition->getTransformFeedbackVaryings().size(); ++ndx)
  		{
  			if (ndx)
  				builder << ", ";
  			builder << "\"" << m_programDefinition->getTransformFeedbackVaryings()[ndx] << "\"";
  		}
  		builder << "}" << tcu::TestLog::EndMessage;
  	}
  
  	checkAndLogProgram(program, m_programDefinition, m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
  
  	// Check resource list
  	{
  		const tcu::ScopedLogSection	section				(m_testCtx.getLog(), "ResourceList", "Resource list");
  		std::vector<std::string>	resourceList;
  		std::vector<std::string>	expectedResources;
  
  		queryResourceList(resourceList, program.getProgram());
  		expectedResources = getProgramInterfaceResourceList(m_programDefinition, m_programInterface);
  
  		// verify the list and the expected list match
  
  		if (!verifyResourceList(resourceList, expectedResources))
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid resource list");
  
  		// verify GetProgramResourceIndex() matches the indices of the list
  
  		if (!verifyResourceIndexQuery(resourceList, expectedResources, program.getProgram()))
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "GetProgramResourceIndex returned unexpected values");
  
  		// Verify MAX_NAME_LENGTH
  		if (!verifyMaxNameLength(resourceList, program.getProgram()))
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "MAX_NAME_LENGTH invalid");
  	}
  
  	return STOP;
  }
  
  int InterfaceBlockDataSizeTestCase::getBlockMinDataSize (const glu::InterfaceBlock& block) const
  {
  	int dataSize = 0;
  
  	for (int ndx = 0; ndx < (int)block.variables.size(); ++ndx)
  		dataSize += getVarTypeSize(block.variables[ndx].varType);
  
  	return dataSize;
  }
  
  static bool isDataTypeLayoutQualified (glu::DataType type)
  {
  	return glu::isDataTypeImage(type) || glu::isDataTypeAtomicCounter(type);
  }
  
  static void generateVariableCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const ProgramResourceQueryTestTarget& queryTarget, int expandLevel = 3, bool createTestGroup = true)
  {
  	static const struct
  	{
  		int				level;
  		glu::DataType	dataType;
  	} variableTypes[] =
  	{
  		{ 0,	glu::TYPE_FLOAT			},
  		{ 1,	glu::TYPE_INT			},
  		{ 1,	glu::TYPE_UINT			},
  		{ 1,	glu::TYPE_BOOL			},
  
  		{ 3,	glu::TYPE_FLOAT_VEC2	},
  		{ 1,	glu::TYPE_FLOAT_VEC3	},
  		{ 1,	glu::TYPE_FLOAT_VEC4	},
  
  		{ 3,	glu::TYPE_INT_VEC2		},
  		{ 2,	glu::TYPE_INT_VEC3		},
  		{ 3,	glu::TYPE_INT_VEC4		},
  
  		{ 3,	glu::TYPE_UINT_VEC2		},
  		{ 2,	glu::TYPE_UINT_VEC3		},
  		{ 3,	glu::TYPE_UINT_VEC4		},
  
  		{ 3,	glu::TYPE_BOOL_VEC2		},
  		{ 2,	glu::TYPE_BOOL_VEC3		},
  		{ 3,	glu::TYPE_BOOL_VEC4		},
  
  		{ 2,	glu::TYPE_FLOAT_MAT2	},
  		{ 3,	glu::TYPE_FLOAT_MAT2X3	},
  		{ 3,	glu::TYPE_FLOAT_MAT2X4	},
  		{ 2,	glu::TYPE_FLOAT_MAT3X2	},
  		{ 2,	glu::TYPE_FLOAT_MAT3	},
  		{ 3,	glu::TYPE_FLOAT_MAT3X4	},
  		{ 2,	glu::TYPE_FLOAT_MAT4X2	},
  		{ 3,	glu::TYPE_FLOAT_MAT4X3	},
  		{ 2,	glu::TYPE_FLOAT_MAT4	},
  	};
  
  	tcu::TestCaseGroup* group;
  
  	if (createTestGroup)
  	{
  		group = new tcu::TestCaseGroup(context.getTestContext(), "basic_type", "Basic variable");
  		targetGroup->addChild(group);
  	}
  	else
  		group = targetGroup;
  
  	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(variableTypes); ++ndx)
  	{
  		if (variableTypes[ndx].level <= expandLevel)
  		{
  			const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, variableTypes[ndx].dataType));
  			group->addChild(new ResourceTestCase(context, variable, queryTarget));
  		}
  	}
  }
  
  static void generateOpaqueTypeCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const ProgramResourceQueryTestTarget& queryTarget, int expandLevel = 3, bool createTestGroup = true)
  {
  	static const struct
  	{
  		int				level;
  		glu::DataType	dataType;
  	} variableTypes[] =
  	{
  		{ 0,	glu::TYPE_SAMPLER_2D					},
  		{ 2,	glu::TYPE_SAMPLER_CUBE					},
  		{ 1,	glu::TYPE_SAMPLER_2D_ARRAY				},
  		{ 1,	glu::TYPE_SAMPLER_3D					},
  		{ 2,	glu::TYPE_SAMPLER_2D_SHADOW				},
  		{ 3,	glu::TYPE_SAMPLER_CUBE_SHADOW			},
  		{ 3,	glu::TYPE_SAMPLER_2D_ARRAY_SHADOW		},
  		{ 1,	glu::TYPE_INT_SAMPLER_2D				},
  		{ 3,	glu::TYPE_INT_SAMPLER_CUBE				},
  		{ 3,	glu::TYPE_INT_SAMPLER_2D_ARRAY			},
  		{ 3,	glu::TYPE_INT_SAMPLER_3D				},
  		{ 2,	glu::TYPE_UINT_SAMPLER_2D				},
  		{ 3,	glu::TYPE_UINT_SAMPLER_CUBE				},
  		{ 3,	glu::TYPE_UINT_SAMPLER_2D_ARRAY			},
  		{ 3,	glu::TYPE_UINT_SAMPLER_3D				},
  		{ 2,	glu::TYPE_SAMPLER_2D_MULTISAMPLE		},
  		{ 2,	glu::TYPE_INT_SAMPLER_2D_MULTISAMPLE	},
  		{ 3,	glu::TYPE_UINT_SAMPLER_2D_MULTISAMPLE	},
  		{ 1,	glu::TYPE_IMAGE_2D						},
  		{ 3,	glu::TYPE_IMAGE_CUBE					},
  		{ 3,	glu::TYPE_IMAGE_2D_ARRAY				},
  		{ 3,	glu::TYPE_IMAGE_3D						},
  		{ 3,	glu::TYPE_INT_IMAGE_2D					},
  		{ 3,	glu::TYPE_INT_IMAGE_CUBE				},
  		{ 1,	glu::TYPE_INT_IMAGE_2D_ARRAY			},
  		{ 3,	glu::TYPE_INT_IMAGE_3D					},
  		{ 2,	glu::TYPE_UINT_IMAGE_2D					},
  		{ 3,	glu::TYPE_UINT_IMAGE_CUBE				},
  		{ 3,	glu::TYPE_UINT_IMAGE_2D_ARRAY			},
  		{ 3,	glu::TYPE_UINT_IMAGE_3D					},
  		{ 1,	glu::TYPE_UINT_ATOMIC_COUNTER			},
  	};
  
  	bool isStructMember = false;
  
  	// Requirements
  	for (const ResourceDefinition::Node* node = parentStructure.get(); node; node = node->getEnclosingNode())
  	{
  		// Don't insert inside a interface block
  		if (node->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK)
  			return;
  
  		isStructMember |= (node->getType() == ResourceDefinition::Node::TYPE_STRUCT_MEMBER);
  	}
  
  	// Add cases
  	{
  		tcu::TestCaseGroup* group;
  
  		if (createTestGroup)
  		{
  			group = new tcu::TestCaseGroup(context.getTestContext(), "opaque_type", "Opaque types");
  			targetGroup->addChild(group);
  		}
  		else
  			group = targetGroup;
  
  		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(variableTypes); ++ndx)
  		{
  			if (variableTypes[ndx].level > expandLevel)
  				continue;
  
  			// Layout qualifiers are not allowed on struct members
  			if (isDataTypeLayoutQualified(variableTypes[ndx].dataType) && isStructMember)
  				continue;
  
  			{
  				const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, variableTypes[ndx].dataType));
  				group->addChild(new ResourceTestCase(context, variable, queryTarget));
  			}
  		}
  	}
  }
  
  static void generateCompoundVariableCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const ProgramResourceQueryTestTarget& queryTarget, int expandLevel = 3);
  
  static void generateVariableArrayCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const ProgramResourceQueryTestTarget& queryTarget, int expandLevel = 3)
  {
  	if (expandLevel > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(parentStructure));
  		tcu::TestCaseGroup* const					blockGroup		= new tcu::TestCaseGroup(context.getTestContext(), "array", "Arrays");
  
  		targetGroup->addChild(blockGroup);
  
  		// Arrays of basic variables
  		generateVariableCases(context, arrayElement, blockGroup, queryTarget, expandLevel, expandLevel != 1);
  
  		// Arrays of opaque types
  		generateOpaqueTypeCases(context, arrayElement, blockGroup, queryTarget, expandLevel, expandLevel != 1);
  
  		// Arrays of arrays
  		generateVariableArrayCases(context, arrayElement, blockGroup, queryTarget, expandLevel-1);
  
  		// Arrays of structs
  		generateCompoundVariableCases(context, arrayElement, blockGroup, queryTarget, expandLevel-1);
  	}
  }
  
  static void generateCompoundVariableCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const ProgramResourceQueryTestTarget& queryTarget, int expandLevel)
  {
  	if (expandLevel > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr	structMember	(new ResourceDefinition::StructMember(parentStructure));
  		tcu::TestCaseGroup* const					blockGroup		= new tcu::TestCaseGroup(context.getTestContext(), "struct", "Structs");
  
  		targetGroup->addChild(blockGroup);
  
  		// Struct containing basic variable
  		generateVariableCases(context, structMember, blockGroup, queryTarget, expandLevel, expandLevel != 1);
  
  		// Struct containing opaque types
  		generateOpaqueTypeCases(context, structMember, blockGroup, queryTarget, expandLevel, expandLevel != 1);
  
  		// Struct containing arrays
  		generateVariableArrayCases(context, structMember, blockGroup, queryTarget, expandLevel-1);
  
  		// Struct containing struct
  		generateCompoundVariableCases(context, structMember, blockGroup, queryTarget, expandLevel-1);
  	}
  }
  
  // Resource list cases
  
  enum BlockFlags
  {
  	BLOCKFLAG_DEFAULT	= 0x01,
  	BLOCKFLAG_NAMED		= 0x02,
  	BLOCKFLAG_UNNAMED	= 0x04,
  	BLOCKFLAG_ARRAY		= 0x08,
  
  	BLOCKFLAG_ALL		= 0x0F
  };
  
  static void generateUniformCaseBlocks (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, deUint32 blockFlags, void (*blockContentGenerator)(Context&, const ResourceDefinition::Node::SharedPtr&, tcu::TestCaseGroup* const))
  {
  	const ResourceDefinition::Node::SharedPtr defaultBlock	(new ResourceDefinition::DefaultBlock(parentStructure));
  	const ResourceDefinition::Node::SharedPtr uniform		(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_UNIFORM));
  
  	// .default_block
  	if (blockFlags & BLOCKFLAG_DEFAULT)
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "default_block", "Default block");
  		targetGroup->addChild(blockGroup);
  
  		blockContentGenerator(context, uniform, blockGroup);
  	}
  
  	// .named_block
  	if (blockFlags & BLOCKFLAG_NAMED)
  	{
  		const ResourceDefinition::Node::SharedPtr block(new ResourceDefinition::InterfaceBlock(uniform, true));
  
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "named_block", "Named uniform block");
  		targetGroup->addChild(blockGroup);
  
  		blockContentGenerator(context, block, blockGroup);
  	}
  
  	// .unnamed_block
  	if (blockFlags & BLOCKFLAG_UNNAMED)
  	{
  		const ResourceDefinition::Node::SharedPtr block(new ResourceDefinition::InterfaceBlock(uniform, false));
  
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "unnamed_block", "Unnamed uniform block");
  		targetGroup->addChild(blockGroup);
  
  		blockContentGenerator(context, block, blockGroup);
  	}
  
  	// .block_array
  	if (blockFlags & BLOCKFLAG_ARRAY)
  	{
  		const ResourceDefinition::Node::SharedPtr arrayElement	(new ResourceDefinition::ArrayElement(uniform));
  		const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "block_array", "Uniform block array");
  		targetGroup->addChild(blockGroup);
  
  		blockContentGenerator(context, block, blockGroup);
  	}
  }
  
  static void generateBufferBackedResourceListBlockContentCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, ProgramInterface interface, int depth)
  {
  	// variable
  	{
  		const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, glu::TYPE_FLOAT_VEC4));
  		targetGroup->addChild(new ResourceListTestCase(context, variable, interface));
  	}
  
  	// struct
  	if (depth > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr structMember(new ResourceDefinition::StructMember(parentStructure));
  		generateBufferBackedResourceListBlockContentCases(context, structMember, targetGroup, interface, depth - 1);
  	}
  
  	// array
  	if (depth > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr arrayElement(new ResourceDefinition::ArrayElement(parentStructure));
  		generateBufferBackedResourceListBlockContentCases(context, arrayElement, targetGroup, interface, depth - 1);
  	}
  }
  
  static void generateBufferBackedVariableAggregateTypeCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, ProgramInterface interface, ProgramResourcePropFlags targetProp, glu::DataType dataType, const std::string& nameSuffix, int depth)
  {
  	// variable
  	{
  		const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, dataType));
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(interface, targetProp), ("var" + nameSuffix).c_str()));
  	}
  
  	// struct
  	if (depth > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr structMember(new ResourceDefinition::StructMember(parentStructure));
  		generateBufferBackedVariableAggregateTypeCases(context, structMember, targetGroup, interface, targetProp, dataType, "_struct" + nameSuffix, depth - 1);
  	}
  
  	// array
  	if (depth > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr arrayElement(new ResourceDefinition::ArrayElement(parentStructure));
  		generateBufferBackedVariableAggregateTypeCases(context, arrayElement, targetGroup, interface, targetProp, dataType, "_array" + nameSuffix, depth - 1);
  	}
  }
  
  static void generateUniformResourceListBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup)
  {
  	generateBufferBackedResourceListBlockContentCases(context, parentStructure, targetGroup, PROGRAMINTERFACE_UNIFORM, 4);
  }
  
  static void generateUniformBlockArraySizeContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup)
  {
  	const ProgramResourceQueryTestTarget	queryTarget			(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_ARRAY_SIZE);
  	const bool								isInterfaceBlock	= (parentStructure->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK);
  	const bool								namedNonArrayBlock	= isInterfaceBlock																					&&
  																  static_cast<const ResourceDefinition::InterfaceBlock*>(parentStructure.get())->m_named			&&
  																  parentStructure->getEnclosingNode()->getType() != ResourceDefinition::Node::TYPE_ARRAY_ELEMENT;
  
  	if (!isInterfaceBlock || namedNonArrayBlock)
  	{
  		// .types
  		{
  			tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "types", "Types");
  			targetGroup->addChild(blockGroup);
  
  			generateVariableCases(context, parentStructure, blockGroup, queryTarget, 2, false);
  			generateOpaqueTypeCases(context, parentStructure, blockGroup, queryTarget, 2, false);
  		}
  
  		// aggregates
  		{
  			tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "aggregates", "Aggregate types");
  			targetGroup->addChild(blockGroup);
  
  			generateBufferBackedVariableAggregateTypeCases(context, parentStructure, blockGroup, queryTarget.interface, PROGRAMRESOURCEPROP_ARRAY_SIZE, glu::TYPE_FLOAT, "", 3);
  		}
  	}
  	else
  	{
  		// aggregates
  		generateBufferBackedVariableAggregateTypeCases(context, parentStructure, targetGroup, queryTarget.interface, PROGRAMRESOURCEPROP_ARRAY_SIZE, glu::TYPE_FLOAT, "", 2);
  	}
  }
  
  static void generateBufferBackedArrayStrideTypeAggregateSubCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const std::string& namePrefix, ProgramInterface interface, glu::DataType type, int expandLevel)
  {
  	// case
  	{
  		const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, type));
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(interface, PROGRAMRESOURCEPROP_ARRAY_STRIDE), namePrefix.c_str()));
  	}
  
  	if (expandLevel > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr	structMember	(new ResourceDefinition::StructMember(parentStructure));
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(parentStructure));
  
  		// _struct
  		generateBufferBackedArrayStrideTypeAggregateSubCases(context, structMember, targetGroup, namePrefix + "_struct", interface, type, expandLevel - 1);
  
  		// _array
  		generateBufferBackedArrayStrideTypeAggregateSubCases(context, arrayElement, targetGroup, namePrefix + "_array", interface, type, expandLevel - 1);
  	}
  }
  
  static void generateBufferBackedArrayStrideTypeAggregateCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, ProgramInterface interface, glu::DataType type, int expandLevel, bool includeBaseCase)
  {
  	const ResourceDefinition::Node::SharedPtr	structMember	(new ResourceDefinition::StructMember(parentStructure));
  	const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(parentStructure));
  	const std::string							namePrefix		= glu::getDataTypeName(type);
  
  	if (expandLevel == 0 || includeBaseCase)
  	{
  		const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, type));
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(interface, PROGRAMRESOURCEPROP_ARRAY_STRIDE), namePrefix.c_str()));
  	}
  	if (expandLevel >= 1)
  	{
  		// _struct
  		if (!glu::isDataTypeAtomicCounter(type))
  			generateBufferBackedArrayStrideTypeAggregateSubCases(context, structMember, targetGroup, namePrefix + "_struct", interface, type, expandLevel - 1);
  
  		// _array
  		generateBufferBackedArrayStrideTypeAggregateSubCases(context, arrayElement, targetGroup, namePrefix + "_array", interface, type, expandLevel - 1);
  	}
  }
  
  static void generateUniformBlockArrayStrideContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup)
  {
  	const ProgramResourceQueryTestTarget	queryTarget			(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_ARRAY_STRIDE);
  	const bool								isInterfaceBlock	= (parentStructure->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK);
  	const bool								namedNonArrayBlock	= isInterfaceBlock																					&&
  																  static_cast<const ResourceDefinition::InterfaceBlock*>(parentStructure.get())->m_named			&&
  																  parentStructure->getEnclosingNode()->getType() != ResourceDefinition::Node::TYPE_ARRAY_ELEMENT;
  
  	if (!isInterfaceBlock || namedNonArrayBlock)
  	{
  		// .types
  		{
  			tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "types", "Types");
  			targetGroup->addChild(blockGroup);
  
  			generateVariableCases(context, parentStructure, blockGroup, queryTarget, 2, false);
  			generateOpaqueTypeCases(context, parentStructure, blockGroup, queryTarget, 2, false);
  		}
  
  		// .aggregates
  		{
  			tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "aggregates", "Aggregate types");
  			targetGroup->addChild(blockGroup);
  
  			// .sampler_2d_*
  			if (!isInterfaceBlock)
  				generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, blockGroup, queryTarget.interface, glu::TYPE_SAMPLER_2D, 1, false);
  
  			// .atomic_counter_*
  			if (!isInterfaceBlock)
  			{
  				const ResourceDefinition::Node::SharedPtr layout(new ResourceDefinition::LayoutQualifier(parentStructure, glu::Layout(-1, 0)));
  				generateBufferBackedArrayStrideTypeAggregateCases(context, layout, blockGroup, queryTarget.interface, glu::TYPE_UINT_ATOMIC_COUNTER, 1, false);
  			}
  
  			// .float_*
  			generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, blockGroup, queryTarget.interface, glu::TYPE_FLOAT, 2, false);
  
  			// .bool_*
  			generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, blockGroup, queryTarget.interface, glu::TYPE_BOOL, 1, false);
  
  			// .bvec3_*
  			generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, blockGroup, queryTarget.interface, glu::TYPE_BOOL_VEC3, 2, false);
  
  			// .vec3_*
  			generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, blockGroup, queryTarget.interface, glu::TYPE_FLOAT_VEC3, 2, false);
  
  			// .ivec2_*
  			generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, blockGroup, queryTarget.interface, glu::TYPE_INT_VEC3, 2, false);
  		}
  	}
  	else
  	{
  		generateVariableCases(context, parentStructure, targetGroup, queryTarget, 1);
  		generateVariableArrayCases(context, parentStructure, targetGroup, queryTarget, 1);
  		generateCompoundVariableCases(context, parentStructure, targetGroup, queryTarget, 1);
  	}
  }
  
  static void generateUniformBlockLocationContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup)
  {
  	const ProgramResourceQueryTestTarget	queryTarget			(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_LOCATION);
  	const bool								isInterfaceBlock	= (parentStructure->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK);
  
  	if (!isInterfaceBlock)
  	{
  		generateVariableCases(context, parentStructure, targetGroup, queryTarget, 3);
  		generateOpaqueTypeCases(context, parentStructure, targetGroup, queryTarget, 3);
  		generateVariableArrayCases(context, parentStructure, targetGroup, queryTarget, 2);
  		generateCompoundVariableCases(context, parentStructure, targetGroup, queryTarget, 2);
  	}
  	else
  		generateVariableCases(context, parentStructure, targetGroup, queryTarget, 1, false);
  }
  
  static void generateUniformBlockBlockIndexContents (Context& context, tcu::TestCaseGroup* const targetGroup, glu::GLSLVersion glslVersion)
  {
  	const ResourceDefinition::Node::SharedPtr	program			(new ResourceDefinition::Program());
  	const ResourceDefinition::Node::SharedPtr	shader			(new ResourceDefinition::Shader(program, glu::SHADERTYPE_COMPUTE, glslVersion));
  	const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(shader));
  	const ResourceDefinition::Node::SharedPtr	uniform			(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_UNIFORM));
  	const ResourceDefinition::Node::SharedPtr	binding			(new ResourceDefinition::LayoutQualifier(uniform, glu::Layout(-1, 0)));
  
  	// .default_block
  	{
  		const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(uniform, glu::TYPE_FLOAT_VEC4));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_BLOCK_INDEX), "default_block"));
  	}
  
  	// .named_block
  	{
  		const ResourceDefinition::Node::SharedPtr	buffer		(new ResourceDefinition::InterfaceBlock(binding, true));
  		const ResourceDefinition::Node::SharedPtr	variable	(new ResourceDefinition::Variable(buffer, glu::TYPE_FLOAT_VEC4));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_BLOCK_INDEX), "named_block"));
  	}
  
  	// .unnamed_block
  	{
  		const ResourceDefinition::Node::SharedPtr	buffer		(new ResourceDefinition::InterfaceBlock(binding, false));
  		const ResourceDefinition::Node::SharedPtr	variable	(new ResourceDefinition::Variable(buffer, glu::TYPE_FLOAT_VEC4));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_BLOCK_INDEX), "unnamed_block"));
  	}
  
  	// .block_array
  	{
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(binding));
  		const ResourceDefinition::Node::SharedPtr	buffer			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  		const ResourceDefinition::Node::SharedPtr	variable		(new ResourceDefinition::Variable(buffer, glu::TYPE_FLOAT_VEC4));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_BLOCK_INDEX), "block_array"));
  	}
  }
  
  static void generateUniformBlockAtomicCounterBufferIndexContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup)
  {
  	const ProgramResourceQueryTestTarget	queryTarget			(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_ATOMIC_COUNTER_BUFFER_INDEX);
  	const bool								isInterfaceBlock	= (parentStructure->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK);
  
  	if (!isInterfaceBlock)
  	{
  		generateVariableCases(context, parentStructure, targetGroup, queryTarget, 3);
  		generateOpaqueTypeCases(context, parentStructure, targetGroup, queryTarget, 3);
  
  		// .array
  		{
  			const ResourceDefinition::Node::SharedPtr	arrayElement		(new ResourceDefinition::ArrayElement(parentStructure));
  			const ResourceDefinition::Node::SharedPtr	arrayArrayElement	(new ResourceDefinition::ArrayElement(arrayElement));
  			const ResourceDefinition::Node::SharedPtr	variable			(new ResourceDefinition::Variable(arrayElement, glu::TYPE_UINT_ATOMIC_COUNTER));
  			const ResourceDefinition::Node::SharedPtr	elementvariable		(new ResourceDefinition::Variable(arrayArrayElement, glu::TYPE_UINT_ATOMIC_COUNTER));
  			tcu::TestCaseGroup* const					blockGroup			= new tcu::TestCaseGroup(context.getTestContext(), "array", "Arrays");
  
  			targetGroup->addChild(blockGroup);
  
  			blockGroup->addChild(new ResourceTestCase(context, variable, queryTarget, "var_array"));
  			blockGroup->addChild(new ResourceTestCase(context, elementvariable, queryTarget, "var_array_array"));
  		}
  	}
  	else
  		generateVariableCases(context, parentStructure, targetGroup, queryTarget, 1, false);
  }
  
  static void generateUniformBlockNameLengthContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup)
  {
  	const bool	isInterfaceBlock	= (parentStructure->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK);
  	const bool	namedNonArrayBlock	= isInterfaceBlock																					&&
  									  static_cast<const ResourceDefinition::InterfaceBlock*>(parentStructure.get())->m_named			&&
  									  parentStructure->getEnclosingNode()->getType() != ResourceDefinition::Node::TYPE_ARRAY_ELEMENT;
  
  	if (!isInterfaceBlock || namedNonArrayBlock)
  		generateBufferBackedVariableAggregateTypeCases(context, parentStructure, targetGroup, PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_NAME_LENGTH, glu::TYPE_FLOAT, "", 2);
  	else
  		generateBufferBackedVariableAggregateTypeCases(context, parentStructure, targetGroup, PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_NAME_LENGTH, glu::TYPE_FLOAT, "", 1);
  }
  
  static void generateUniformBlockTypeContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup)
  {
  	const ProgramResourceQueryTestTarget	queryTarget			(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_TYPE);
  	const bool								isInterfaceBlock	= (parentStructure->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK);
  	const bool								namedNonArrayBlock	= isInterfaceBlock																					&&
  																  static_cast<const ResourceDefinition::InterfaceBlock*>(parentStructure.get())->m_named			&&
  																  parentStructure->getEnclosingNode()->getType() != ResourceDefinition::Node::TYPE_ARRAY_ELEMENT;
  
  	if (!isInterfaceBlock || namedNonArrayBlock)
  	{
  		// .types
  		{
  			tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "types", "Types");
  			targetGroup->addChild(blockGroup);
  
  			generateVariableCases(context, parentStructure, blockGroup, queryTarget, 3, false);
  			generateOpaqueTypeCases(context, parentStructure, blockGroup, queryTarget, 3, false);
  		}
  
  		generateVariableArrayCases(context, parentStructure, targetGroup, queryTarget, 1);
  		generateCompoundVariableCases(context, parentStructure, targetGroup, queryTarget, 1);
  
  	}
  	else
  	{
  		generateVariableCases(context, parentStructure, targetGroup, queryTarget, 1);
  		generateVariableArrayCases(context, parentStructure, targetGroup, queryTarget, 1);
  		generateCompoundVariableCases(context, parentStructure, targetGroup, queryTarget, 1);
  	}
  }
  
  static void generateUniformBlockOffsetContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup)
  {
  	const ProgramResourceQueryTestTarget	queryTarget			(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_OFFSET);
  	const bool								isInterfaceBlock	= (parentStructure->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK);
  	const bool								namedNonArrayBlock	= isInterfaceBlock																					&&
  																  static_cast<const ResourceDefinition::InterfaceBlock*>(parentStructure.get())->m_named			&&
  																  parentStructure->getEnclosingNode()->getType() != ResourceDefinition::Node::TYPE_ARRAY_ELEMENT;
  
  	if (!isInterfaceBlock)
  	{
  		// .types
  		{
  			tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "types", "Types");
  			targetGroup->addChild(blockGroup);
  
  			generateVariableCases(context, parentStructure, blockGroup, queryTarget, 3, false);
  			generateOpaqueTypeCases(context, parentStructure, blockGroup, queryTarget, 3, false);
  		}
  
  		// .aggregates
  		{
  			tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "aggregates", "Aggregate types");
  			targetGroup->addChild(blockGroup);
  
  			// .atomic_uint_struct
  			// .atomic_uint_array
  			{
  				const ResourceDefinition::Node::SharedPtr offset			(new ResourceDefinition::LayoutQualifier(parentStructure, glu::Layout(-1, -1, 4)));
  				const ResourceDefinition::Node::SharedPtr arrayElement		(new ResourceDefinition::ArrayElement(offset));
  				const ResourceDefinition::Node::SharedPtr elementVariable	(new ResourceDefinition::Variable(arrayElement, glu::TYPE_UINT_ATOMIC_COUNTER));
  
  				blockGroup->addChild(new ResourceTestCase(context, elementVariable, queryTarget, "atomic_uint_array"));
  			}
  
  			// .float_array
  			// .float_struct
  			{
  				const ResourceDefinition::Node::SharedPtr structMember		(new ResourceDefinition::StructMember(parentStructure));
  				const ResourceDefinition::Node::SharedPtr arrayElement		(new ResourceDefinition::ArrayElement(parentStructure));
  				const ResourceDefinition::Node::SharedPtr memberVariable	(new ResourceDefinition::Variable(structMember, glu::TYPE_FLOAT));
  				const ResourceDefinition::Node::SharedPtr elementVariable	(new ResourceDefinition::Variable(arrayElement, glu::TYPE_FLOAT));
  
  				blockGroup->addChild(new ResourceTestCase(context, memberVariable, queryTarget, "float_struct"));
  				blockGroup->addChild(new ResourceTestCase(context, elementVariable, queryTarget, "float_array"));
  			}
  		}
  	}
  	else if (namedNonArrayBlock)
  	{
  		// .types
  		{
  			tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "types", "Types");
  			targetGroup->addChild(blockGroup);
  
  			generateVariableCases(context, parentStructure, blockGroup, queryTarget, 3, false);
  			generateOpaqueTypeCases(context, parentStructure, blockGroup, queryTarget, 3, false);
  		}
  
  		// .aggregates
  		{
  			tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "aggregates", "Aggregate types");
  			targetGroup->addChild(blockGroup);
  
  			// .float_array
  			// .float_struct
  			{
  				const ResourceDefinition::Node::SharedPtr structMember		(new ResourceDefinition::StructMember(parentStructure));
  				const ResourceDefinition::Node::SharedPtr arrayElement		(new ResourceDefinition::StructMember(parentStructure));
  				const ResourceDefinition::Node::SharedPtr memberVariable	(new ResourceDefinition::Variable(structMember, glu::TYPE_FLOAT));
  				const ResourceDefinition::Node::SharedPtr elementVariable	(new ResourceDefinition::Variable(arrayElement, glu::TYPE_FLOAT));
  
  				blockGroup->addChild(new ResourceTestCase(context, memberVariable, queryTarget, "float_struct"));
  				blockGroup->addChild(new ResourceTestCase(context, elementVariable, queryTarget, "float_array"));
  			}
  		}
  	}
  	else
  	{
  		generateVariableCases(context, parentStructure, targetGroup, queryTarget, 1);
  		generateVariableArrayCases(context, parentStructure, targetGroup, queryTarget, 1);
  		generateCompoundVariableCases(context, parentStructure, targetGroup, queryTarget, 1);
  	}
  }
  
  static void generateMatrixVariableCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const ProgramResourceQueryTestTarget& queryTarget, bool createTestGroup = true, int expandLevel = 2)
  {
  	static const struct
  	{
  		int				priority;
  		glu::DataType	type;
  	} variableTypes[] =
  	{
  		{ 0,	glu::TYPE_FLOAT_MAT2	},
  		{ 1,	glu::TYPE_FLOAT_MAT2X3	},
  		{ 2,	glu::TYPE_FLOAT_MAT2X4	},
  		{ 2,	glu::TYPE_FLOAT_MAT3X2	},
  		{ 1,	glu::TYPE_FLOAT_MAT3	},
  		{ 0,	glu::TYPE_FLOAT_MAT3X4	},
  		{ 2,	glu::TYPE_FLOAT_MAT4X2	},
  		{ 1,	glu::TYPE_FLOAT_MAT4X3	},
  		{ 0,	glu::TYPE_FLOAT_MAT4	},
  	};
  
  	tcu::TestCaseGroup* group;
  
  	if (createTestGroup)
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "matrix", "Basic matrix type");
  		targetGroup->addChild(blockGroup);
  		group = blockGroup;
  	}
  	else
  		group = targetGroup;
  
  	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(variableTypes); ++ndx)
  	{
  		if (variableTypes[ndx].priority < expandLevel)
  		{
  			const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, variableTypes[ndx].type));
  			group->addChild(new ResourceTestCase(context, variable, queryTarget));
  		}
  	}
  }
  
  static void generateMatrixStructCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const ProgramResourceQueryTestTarget& queryTarget, int expandLevel);
  
  static void generateMatrixArrayCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const ProgramResourceQueryTestTarget& queryTarget, int expandLevel)
  {
  	if (expandLevel > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(parentStructure));
  		tcu::TestCaseGroup* const					blockGroup		= new tcu::TestCaseGroup(context.getTestContext(), "array", "Arrays");
  
  		targetGroup->addChild(blockGroup);
  
  		// Arrays of basic variables
  		generateMatrixVariableCases(context, arrayElement, blockGroup, queryTarget, expandLevel != 1, expandLevel);
  
  		// Arrays of arrays
  		generateMatrixArrayCases(context, arrayElement, blockGroup, queryTarget, expandLevel-1);
  
  		// Arrays of structs
  		generateMatrixStructCases(context, arrayElement, blockGroup, queryTarget, expandLevel-1);
  	}
  }
  
  static void generateMatrixStructCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const ProgramResourceQueryTestTarget& queryTarget, int expandLevel)
  {
  	if (expandLevel > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr	structMember	(new ResourceDefinition::StructMember(parentStructure));
  		tcu::TestCaseGroup* const					blockGroup		= new tcu::TestCaseGroup(context.getTestContext(), "struct", "Structs");
  
  		targetGroup->addChild(blockGroup);
  
  		// Struct containing basic variable
  		generateMatrixVariableCases(context, structMember, blockGroup, queryTarget, expandLevel != 1, expandLevel);
  
  		// Struct containing arrays
  		generateMatrixArrayCases(context, structMember, blockGroup, queryTarget, expandLevel-1);
  
  		// Struct containing struct
  		generateMatrixStructCases(context, structMember, blockGroup, queryTarget, expandLevel-1);
  	}
  }
  
  static void generateUniformMatrixOrderCaseBlockContentCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, bool extendedBasicTypeCases, bool opaqueCases)
  {
  	static const struct
  	{
  		const char*			name;
  		glu::MatrixOrder	order;
  	} qualifiers[] =
  	{
  		{ "no_qualifier",	glu::MATRIXORDER_LAST			},
  		{ "row_major",		glu::MATRIXORDER_ROW_MAJOR		},
  		{ "column_major",	glu::MATRIXORDER_COLUMN_MAJOR	},
  	};
  
  	const ProgramResourceQueryTestTarget queryTarget(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_MATRIX_ROW_MAJOR);
  
  	for (int qualifierNdx = 0; qualifierNdx < DE_LENGTH_OF_ARRAY(qualifiers); ++qualifierNdx)
  	{
  		// Add layout qualifiers only for block members
  		if (qualifiers[qualifierNdx].order == glu::MATRIXORDER_LAST || parentStructure->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK)
  		{
  			ResourceDefinition::Node::SharedPtr	subStructure	= parentStructure;
  			tcu::TestCaseGroup* const			qualifierGroup	= new tcu::TestCaseGroup(context.getTestContext(), qualifiers[qualifierNdx].name, "");
  
  			targetGroup->addChild(qualifierGroup);
  
  			if (qualifiers[qualifierNdx].order != glu::MATRIXORDER_LAST)
  			{
  				glu::Layout layout;
  				layout.matrixOrder = qualifiers[qualifierNdx].order;
  				subStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::LayoutQualifier(subStructure, layout));
  			}
  
  			if (extendedBasicTypeCases && qualifiers[qualifierNdx].order == glu::MATRIXORDER_LAST)
  			{
  				// .types
  				{
  					tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "types", "");
  					qualifierGroup->addChild(blockGroup);
  
  					generateVariableCases(context, subStructure, blockGroup, queryTarget, 1, false);
  					generateMatrixVariableCases(context, subStructure, blockGroup, queryTarget, false);
  					if (opaqueCases)
  						generateOpaqueTypeCases(context, subStructure, blockGroup, queryTarget, 2, false);
  				}
  
  				// .aggregates
  				{
  					tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "aggregates", "");
  					qualifierGroup->addChild(blockGroup);
  
  					generateBufferBackedVariableAggregateTypeCases(context, subStructure, blockGroup, queryTarget.interface, PROGRAMRESOURCEPROP_MATRIX_ROW_MAJOR, glu::TYPE_FLOAT_MAT3X2, "", 1);
  				}
  			}
  			else
  			{
  				generateBufferBackedVariableAggregateTypeCases(context, subStructure, qualifierGroup, queryTarget.interface, PROGRAMRESOURCEPROP_MATRIX_ROW_MAJOR, glu::TYPE_FLOAT_MAT3X2, "", 1);
  			}
  		}
  	}
  }
  
  static void generateUniformMatrixStrideCaseBlockContentCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, bool extendedBasicTypeCases, bool opaqueCases)
  {
  	static const struct
  	{
  		const char*			name;
  		glu::MatrixOrder	order;
  	} qualifiers[] =
  	{
  		{ "no_qualifier",	glu::MATRIXORDER_LAST			},
  		{ "row_major",		glu::MATRIXORDER_ROW_MAJOR		},
  		{ "column_major",	glu::MATRIXORDER_COLUMN_MAJOR	},
  	};
  
  	const ProgramResourceQueryTestTarget queryTarget(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_MATRIX_STRIDE);
  
  	for (int qualifierNdx = 0; qualifierNdx < DE_LENGTH_OF_ARRAY(qualifiers); ++qualifierNdx)
  	{
  		// Add layout qualifiers only for block members
  		if (qualifiers[qualifierNdx].order == glu::MATRIXORDER_LAST || parentStructure->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK)
  		{
  			ResourceDefinition::Node::SharedPtr	subStructure	= parentStructure;
  			tcu::TestCaseGroup* const			qualifierGroup	= new tcu::TestCaseGroup(context.getTestContext(), qualifiers[qualifierNdx].name, "");
  
  			targetGroup->addChild(qualifierGroup);
  
  			if (qualifiers[qualifierNdx].order != glu::MATRIXORDER_LAST)
  			{
  				glu::Layout layout;
  				layout.matrixOrder = qualifiers[qualifierNdx].order;
  				subStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::LayoutQualifier(subStructure, layout));
  			}
  
  			if (extendedBasicTypeCases)
  			{
  				// .types
  				// .matrix
  				if (qualifiers[qualifierNdx].order == glu::MATRIXORDER_LAST)
  				{
  					tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "types", "");
  					qualifierGroup->addChild(blockGroup);
  
  					generateVariableCases(context, subStructure, blockGroup, queryTarget, 1, false);
  					generateMatrixVariableCases(context, subStructure, blockGroup, queryTarget, false);
  					if (opaqueCases)
  						generateOpaqueTypeCases(context, subStructure, blockGroup, queryTarget, 2, false);
  				}
  				else
  					generateMatrixVariableCases(context, subStructure, qualifierGroup, queryTarget);
  
  				// .aggregates
  				{
  					tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "aggregates", "");
  					qualifierGroup->addChild(blockGroup);
  
  					generateBufferBackedVariableAggregateTypeCases(context, subStructure, blockGroup, queryTarget.interface, PROGRAMRESOURCEPROP_MATRIX_ROW_MAJOR, glu::TYPE_FLOAT_MAT3X2, "", 1);
  				}
  			}
  			else
  				generateBufferBackedVariableAggregateTypeCases(context, subStructure, qualifierGroup, queryTarget.interface, PROGRAMRESOURCEPROP_MATRIX_ROW_MAJOR, glu::TYPE_FLOAT_MAT3X2, "", 1);
  		}
  	}
  }
  
  static void generateUniformMatrixCaseBlocks (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, void (*blockContentGenerator)(Context&, const ResourceDefinition::Node::SharedPtr&, tcu::TestCaseGroup* const, bool, bool))
  {
  	static const struct
  	{
  		const char*			name;
  		const char*			description;
  		bool				block;
  		bool				namedBlock;
  		bool				extendedBasicTypeCases;
  		glu::MatrixOrder	order;
  	} children[] =
  	{
  		{ "default_block",				"Default block",			false,	true,	true,	glu::MATRIXORDER_LAST			},
  		{ "named_block",				"Named uniform block",		true,	true,	true,	glu::MATRIXORDER_LAST			},
  		{ "named_block_row_major",		"Named uniform block",		true,	true,	false,	glu::MATRIXORDER_ROW_MAJOR		},
  		{ "named_block_col_major",		"Named uniform block",		true,	true,	false,	glu::MATRIXORDER_COLUMN_MAJOR	},
  		{ "unnamed_block",				"Unnamed uniform block",	true,	false,	false,	glu::MATRIXORDER_LAST			},
  		{ "unnamed_block_row_major",	"Unnamed uniform block",	true,	false,	false,	glu::MATRIXORDER_ROW_MAJOR		},
  		{ "unnamed_block_col_major",	"Unnamed uniform block",	true,	false,	false,	glu::MATRIXORDER_COLUMN_MAJOR	},
  	};
  
  	const ResourceDefinition::Node::SharedPtr defaultBlock	(new ResourceDefinition::DefaultBlock(parentStructure));
  	const ResourceDefinition::Node::SharedPtr uniform		(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_UNIFORM));
  
  	for (int childNdx = 0; childNdx < (int)DE_LENGTH_OF_ARRAY(children); ++childNdx)
  	{
  		ResourceDefinition::Node::SharedPtr	subStructure	= uniform;
  		tcu::TestCaseGroup* const			blockGroup		= new tcu::TestCaseGroup(context.getTestContext(), children[childNdx].name, children[childNdx].description);
  		const bool							addOpaqueCases	= children[childNdx].extendedBasicTypeCases && !children[childNdx].block;
  
  		targetGroup->addChild(blockGroup);
  
  		if (children[childNdx].order != glu::MATRIXORDER_LAST)
  		{
  			glu::Layout layout;
  			layout.matrixOrder = children[childNdx].order;
  			subStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::LayoutQualifier(subStructure, layout));
  		}
  
  		if (children[childNdx].block)
  			subStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::InterfaceBlock(subStructure, children[childNdx].namedBlock));
  
  		blockContentGenerator(context, subStructure, blockGroup, children[childNdx].extendedBasicTypeCases, addOpaqueCases);
  	}
  }
  
  static void generateBufferReferencedByShaderInterfaceBlockCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, const ProgramResourceQueryTestTarget& queryTarget, bool extendedCases)
  {
  	const bool isDefaultBlock = (parentStructure->getType() != ResourceDefinition::Node::TYPE_INTERFACE_BLOCK);
  
  	// .float
  	// .float_array
  	// .float_struct
  	{
  		const ResourceDefinition::Node::SharedPtr	variable		(new ResourceDefinition::Variable(parentStructure, glu::TYPE_FLOAT));
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(parentStructure));
  		const ResourceDefinition::Node::SharedPtr	structMember	(new ResourceDefinition::StructMember(parentStructure));
  		const ResourceDefinition::Node::SharedPtr	variableArray	(new ResourceDefinition::Variable(arrayElement, glu::TYPE_FLOAT));
  		const ResourceDefinition::Node::SharedPtr	variableStruct	(new ResourceDefinition::Variable(structMember, glu::TYPE_FLOAT));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, queryTarget, "float"));
  		targetGroup->addChild(new ResourceTestCase(context, variableArray, queryTarget, "float_array"));
  		targetGroup->addChild(new ResourceTestCase(context, variableStruct, queryTarget, "float_struct"));
  	}
  
  	// .sampler
  	// .sampler_array
  	// .sampler_struct
  	if (isDefaultBlock)
  	{
  		const ResourceDefinition::Node::SharedPtr	layout			(new ResourceDefinition::LayoutQualifier(parentStructure, glu::Layout(-1, 0)));
  		const ResourceDefinition::Node::SharedPtr	variable		(new ResourceDefinition::Variable(layout, glu::TYPE_SAMPLER_2D));
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(layout));
  		const ResourceDefinition::Node::SharedPtr	structMember	(new ResourceDefinition::StructMember(parentStructure));
  		const ResourceDefinition::Node::SharedPtr	variableArray	(new ResourceDefinition::Variable(arrayElement, glu::TYPE_SAMPLER_2D));
  		const ResourceDefinition::Node::SharedPtr	variableStruct	(new ResourceDefinition::Variable(structMember, glu::TYPE_SAMPLER_2D));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, queryTarget, "sampler"));
  		targetGroup->addChild(new ResourceTestCase(context, variableArray, queryTarget, "sampler_array"));
  		targetGroup->addChild(new ResourceTestCase(context, variableStruct, queryTarget, "sampler_struct"));
  	}
  
  	// .atomic_uint
  	// .atomic_uint_array
  	if (isDefaultBlock)
  	{
  		const ResourceDefinition::Node::SharedPtr	layout			(new ResourceDefinition::LayoutQualifier(parentStructure, glu::Layout(-1, 0)));
  		const ResourceDefinition::Node::SharedPtr	variable		(new ResourceDefinition::Variable(layout, glu::TYPE_UINT_ATOMIC_COUNTER));
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(layout));
  		const ResourceDefinition::Node::SharedPtr	variableArray	(new ResourceDefinition::Variable(arrayElement, glu::TYPE_UINT_ATOMIC_COUNTER));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, queryTarget, "atomic_uint"));
  		targetGroup->addChild(new ResourceTestCase(context, variableArray, queryTarget, "atomic_uint_array"));
  	}
  
  	if (extendedCases)
  	{
  		// .float_array_struct
  		{
  			const ResourceDefinition::Node::SharedPtr	structMember		(new ResourceDefinition::StructMember(parentStructure));
  			const ResourceDefinition::Node::SharedPtr	arrayElement		(new ResourceDefinition::ArrayElement(structMember));
  			const ResourceDefinition::Node::SharedPtr	variableArrayStruct	(new ResourceDefinition::Variable(arrayElement, glu::TYPE_FLOAT));
  
  			targetGroup->addChild(new ResourceTestCase(context, variableArrayStruct, queryTarget, "float_array_struct"));
  		}
  
  		// .float_struct_array
  		{
  			const ResourceDefinition::Node::SharedPtr	arrayElement		(new ResourceDefinition::ArrayElement(parentStructure));
  			const ResourceDefinition::Node::SharedPtr	arrayStructMember	(new ResourceDefinition::StructMember(arrayElement));
  			const ResourceDefinition::Node::SharedPtr	variableArrayStruct	(new ResourceDefinition::Variable(arrayStructMember, glu::TYPE_FLOAT));
  
  			targetGroup->addChild(new ResourceTestCase(context, variableArrayStruct, queryTarget, "float_struct_array"));
  		}
  
  		// .float_array_array
  		{
  			const ResourceDefinition::Node::SharedPtr	arrayElement		(new ResourceDefinition::ArrayElement(parentStructure));
  			const ResourceDefinition::Node::SharedPtr	subArrayElement		(new ResourceDefinition::ArrayElement(arrayElement));
  			const ResourceDefinition::Node::SharedPtr	variableArrayStruct	(new ResourceDefinition::Variable(subArrayElement, glu::TYPE_FLOAT));
  
  			targetGroup->addChild(new ResourceTestCase(context, variableArrayStruct, queryTarget, "float_array_array"));
  		}
  
  		// .float_struct_struct
  		{
  			const ResourceDefinition::Node::SharedPtr	structMember		(new ResourceDefinition::StructMember(parentStructure));
  			const ResourceDefinition::Node::SharedPtr	subStructMember		(new ResourceDefinition::StructMember(structMember));
  			const ResourceDefinition::Node::SharedPtr	variableArrayStruct	(new ResourceDefinition::Variable(subStructMember, glu::TYPE_FLOAT));
  
  			targetGroup->addChild(new ResourceTestCase(context, variableArrayStruct, queryTarget, "float_struct_struct"));
  		}
  
  		if (queryTarget.interface == PROGRAMINTERFACE_BUFFER_VARIABLE)
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElement(new ResourceDefinition::ArrayElement(parentStructure, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  
  			// .float_unsized_array
  			{
  				const ResourceDefinition::Node::SharedPtr	variableArray	(new ResourceDefinition::Variable(arrayElement, glu::TYPE_FLOAT));
  
  				targetGroup->addChild(new ResourceTestCase(context, variableArray, queryTarget, "float_unsized_array"));
  			}
  
  			// .float_unsized_struct_array
  			{
  				const ResourceDefinition::Node::SharedPtr	structMember	(new ResourceDefinition::StructMember(arrayElement));
  				const ResourceDefinition::Node::SharedPtr	variableArray	(new ResourceDefinition::Variable(structMember, glu::TYPE_FLOAT));
  
  				targetGroup->addChild(new ResourceTestCase(context, variableArray, queryTarget, "float_unsized_struct_array"));
  			}
  		}
  	}
  }
  
  static void generateUniformReferencedByShaderSingleBlockContentCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, int expandLevel)
  {
  	DE_UNREF(expandLevel);
  
  	const ResourceDefinition::Node::SharedPtr	defaultBlock		(new ResourceDefinition::DefaultBlock(parentStructure));
  	const ResourceDefinition::Node::SharedPtr	uniform				(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_UNIFORM));
  	const ProgramResourceQueryTestTarget		queryTarget			(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_REFERENCED_BY_SHADER);
  	const bool									singleShaderCase	= parentStructure->getType() == ResourceDefinition::Node::TYPE_SHADER;
  
  	// .default_block
  	{
  		TestCaseGroup* const blockGroup = new TestCaseGroup(context, "default_block", "");
  		targetGroup->addChild(blockGroup);
  
  		generateBufferReferencedByShaderInterfaceBlockCases(context, uniform, blockGroup, queryTarget, singleShaderCase);
  	}
  
  	// .named_block
  	{
  		const ResourceDefinition::Node::SharedPtr	block		(new ResourceDefinition::InterfaceBlock(uniform, true));
  		TestCaseGroup* const						blockGroup	= new TestCaseGroup(context, "uniform_block", "");
  
  		targetGroup->addChild(blockGroup);
  
  		generateBufferReferencedByShaderInterfaceBlockCases(context, block, blockGroup, queryTarget, singleShaderCase);
  	}
  
  	// .unnamed_block
  	{
  		const ResourceDefinition::Node::SharedPtr	block		(new ResourceDefinition::InterfaceBlock(uniform, false));
  		TestCaseGroup* const						blockGroup	= new TestCaseGroup(context, "unnamed_block", "");
  
  		targetGroup->addChild(blockGroup);
  
  		generateBufferReferencedByShaderInterfaceBlockCases(context, block, blockGroup, queryTarget, false);
  	}
  
  	// .block_array
  	{
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(uniform));
  		const ResourceDefinition::Node::SharedPtr	block			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  		TestCaseGroup* const						blockGroup		= new TestCaseGroup(context, "block_array", "");
  
  		targetGroup->addChild(blockGroup);
  
  		generateBufferReferencedByShaderInterfaceBlockCases(context, block, blockGroup, queryTarget, false);
  	}
  }
  
  static void generateReferencedByShaderCaseBlocks (Context& context, tcu::TestCaseGroup* const targetGroup, glu::GLSLVersion glslVersion, void (*generateBlockContent)(Context&, const ResourceDefinition::Node::SharedPtr&, tcu::TestCaseGroup*, int expandLevel))
  {
  	static const struct
  	{
  		const char*		name;
  		glu::ShaderType	stage;
  		int				expandLevel;
  	} singleStageCases[] =
  	{
  		{ "compute",				glu::SHADERTYPE_COMPUTE,					3	},
  		{ "separable_vertex",		glu::SHADERTYPE_VERTEX,						2	},
  		{ "separable_fragment",		glu::SHADERTYPE_FRAGMENT,					2	},
  		{ "separable_tess_ctrl",	glu::SHADERTYPE_TESSELLATION_CONTROL,		2	},
  		{ "separable_tess_eval",	glu::SHADERTYPE_TESSELLATION_EVALUATION,	2	},
  		{ "separable_geometry",		glu::SHADERTYPE_GEOMETRY,					2	},
  	};
  	static const struct
  	{
  		const char*	name;
  		deUint32	flags;
  		int			expandLevel;
  		int			subExpandLevel;
  	} pipelines[] =
  	{
  		{
  			"vertex_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT),
  			3,
  			2,
  		},
  		{
  			"vertex_tess_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT) | (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) | (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION),
  			2,
  			2,
  		},
  		{
  			"vertex_geo_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT) | (1 << glu::SHADERTYPE_GEOMETRY),
  			2,
  			2,
  		},
  		{
  			"vertex_tess_geo_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT) | (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) | (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION) | (1 << glu::SHADERTYPE_GEOMETRY),
  			2,
  			1,
  		},
  	};
  
  	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(singleStageCases); ++ndx)
  	{
  		TestCaseGroup* const						blockGroup			= new TestCaseGroup(context, singleStageCases[ndx].name, "");
  		const bool									programSeparable	= (singleStageCases[ndx].stage != glu::SHADERTYPE_COMPUTE);
  		const ResourceDefinition::Node::SharedPtr	program				(new ResourceDefinition::Program(programSeparable));
  		const ResourceDefinition::Node::SharedPtr	stage				(new ResourceDefinition::Shader(program, singleStageCases[ndx].stage, glslVersion));
  
  		targetGroup->addChild(blockGroup);
  
  		generateBlockContent(context, stage, blockGroup, singleStageCases[ndx].expandLevel);
  	}
  
  	for (int pipelineNdx = 0; pipelineNdx < DE_LENGTH_OF_ARRAY(pipelines); ++pipelineNdx)
  	{
  		// whole pipeline
  		{
  			TestCaseGroup* const						blockGroup			= new TestCaseGroup(context, pipelines[pipelineNdx].name, "");
  			const ResourceDefinition::Node::SharedPtr	program				(new ResourceDefinition::Program());
  			ResourceDefinition::ShaderSet*				shaderSet			= new ResourceDefinition::ShaderSet(program,
  																												glslVersion,
  																												pipelines[pipelineNdx].flags,
  																												pipelines[pipelineNdx].flags);
  			targetGroup->addChild(blockGroup);
  
  			{
  				const ResourceDefinition::Node::SharedPtr shaders(shaderSet);
  				generateBlockContent(context, shaders, blockGroup, pipelines[pipelineNdx].expandLevel);
  			}
  		}
  
  		// only one stage
  		for (int selectedStageBit = 0; selectedStageBit < glu::SHADERTYPE_LAST; ++selectedStageBit)
  		{
  			if (pipelines[pipelineNdx].flags & (1 << selectedStageBit))
  			{
  				const ResourceDefinition::Node::SharedPtr	program		(new ResourceDefinition::Program());
  				ResourceDefinition::ShaderSet*				shaderSet	= new ResourceDefinition::ShaderSet(program,
  																											glslVersion,
  																											pipelines[pipelineNdx].flags,
  																											(1u << selectedStageBit));
  				const char*									stageName	= (selectedStageBit == glu::SHADERTYPE_VERTEX)					? ("vertex")
  																		: (selectedStageBit == glu::SHADERTYPE_FRAGMENT)				? ("fragment")
  																		: (selectedStageBit == glu::SHADERTYPE_GEOMETRY)				? ("geo")
  																		: (selectedStageBit == glu::SHADERTYPE_TESSELLATION_CONTROL)	? ("tess_ctrl")
  																		: (selectedStageBit == glu::SHADERTYPE_TESSELLATION_EVALUATION)	? ("tess_eval")
  																		: (DE_NULL);
  				const std::string							setName		= std::string() + pipelines[pipelineNdx].name + "_only_" + stageName;
  				TestCaseGroup* const						blockGroup	= new TestCaseGroup(context, setName.c_str(), "");
  				const ResourceDefinition::Node::SharedPtr	shaders		(shaderSet);
  
  				generateBlockContent(context, shaders, blockGroup, pipelines[pipelineNdx].subExpandLevel);
  				targetGroup->addChild(blockGroup);
  			}
  		}
  	}
  }
  
  static glu::DataType generateRandomDataType (de::Random& rnd, bool excludeOpaqueTypes)
  {
  	static const glu::DataType s_types[] =
  	{
  		glu::TYPE_FLOAT,
  		glu::TYPE_INT,
  		glu::TYPE_UINT,
  		glu::TYPE_BOOL,
  		glu::TYPE_FLOAT_VEC2,
  		glu::TYPE_FLOAT_VEC3,
  		glu::TYPE_FLOAT_VEC4,
  		glu::TYPE_INT_VEC2,
  		glu::TYPE_INT_VEC3,
  		glu::TYPE_INT_VEC4,
  		glu::TYPE_UINT_VEC2,
  		glu::TYPE_UINT_VEC3,
  		glu::TYPE_UINT_VEC4,
  		glu::TYPE_BOOL_VEC2,
  		glu::TYPE_BOOL_VEC3,
  		glu::TYPE_BOOL_VEC4,
  		glu::TYPE_FLOAT_MAT2,
  		glu::TYPE_FLOAT_MAT2X3,
  		glu::TYPE_FLOAT_MAT2X4,
  		glu::TYPE_FLOAT_MAT3X2,
  		glu::TYPE_FLOAT_MAT3,
  		glu::TYPE_FLOAT_MAT3X4,
  		glu::TYPE_FLOAT_MAT4X2,
  		glu::TYPE_FLOAT_MAT4X3,
  		glu::TYPE_FLOAT_MAT4,
  
  		glu::TYPE_SAMPLER_2D,
  		glu::TYPE_SAMPLER_CUBE,
  		glu::TYPE_SAMPLER_2D_ARRAY,
  		glu::TYPE_SAMPLER_3D,
  		glu::TYPE_SAMPLER_2D_SHADOW,
  		glu::TYPE_SAMPLER_CUBE_SHADOW,
  		glu::TYPE_SAMPLER_2D_ARRAY_SHADOW,
  		glu::TYPE_INT_SAMPLER_2D,
  		glu::TYPE_INT_SAMPLER_CUBE,
  		glu::TYPE_INT_SAMPLER_2D_ARRAY,
  		glu::TYPE_INT_SAMPLER_3D,
  		glu::TYPE_UINT_SAMPLER_2D,
  		glu::TYPE_UINT_SAMPLER_CUBE,
  		glu::TYPE_UINT_SAMPLER_2D_ARRAY,
  		glu::TYPE_UINT_SAMPLER_3D,
  		glu::TYPE_SAMPLER_2D_MULTISAMPLE,
  		glu::TYPE_INT_SAMPLER_2D_MULTISAMPLE,
  		glu::TYPE_UINT_SAMPLER_2D_MULTISAMPLE,
  		glu::TYPE_IMAGE_2D,
  		glu::TYPE_IMAGE_CUBE,
  		glu::TYPE_IMAGE_2D_ARRAY,
  		glu::TYPE_IMAGE_3D,
  		glu::TYPE_INT_IMAGE_2D,
  		glu::TYPE_INT_IMAGE_CUBE,
  		glu::TYPE_INT_IMAGE_2D_ARRAY,
  		glu::TYPE_INT_IMAGE_3D,
  		glu::TYPE_UINT_IMAGE_2D,
  		glu::TYPE_UINT_IMAGE_CUBE,
  		glu::TYPE_UINT_IMAGE_2D_ARRAY,
  		glu::TYPE_UINT_IMAGE_3D,
  		glu::TYPE_UINT_ATOMIC_COUNTER
  	};
  
  	for (;;)
  	{
  		const glu::DataType type = s_types[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_types)-1)];
  
  		if (!excludeOpaqueTypes					||
  			glu::isDataTypeScalarOrVector(type)	||
  			glu::isDataTypeMatrix(type))
  			return type;
  	}
  }
  
  static ResourceDefinition::Node::SharedPtr generateRandomVariableDefinition (de::Random&								rnd,
  																			 const ResourceDefinition::Node::SharedPtr&	parentStructure,
  																			 glu::DataType								baseType,
  																			 const glu::Layout&							layout,
  																			 bool										allowUnsized)
  {
  	const int							maxNesting			= 4;
  	ResourceDefinition::Node::SharedPtr	currentStructure	= parentStructure;
  	const bool							canBeInsideAStruct	= layout.binding == -1 && !isDataTypeLayoutQualified(baseType);
  
  	for (int nestNdx = 0; nestNdx < maxNesting; ++nestNdx)
  	{
  		if (allowUnsized && nestNdx == 0 && rnd.getFloat() < 0.2)
  			currentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::ArrayElement(currentStructure, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  		else if (rnd.getFloat() < 0.3 && canBeInsideAStruct)
  			currentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::StructMember(currentStructure));
  		else if (rnd.getFloat() < 0.3)
  			currentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::ArrayElement(currentStructure));
  		else
  			break;
  	}
  
  	return ResourceDefinition::Node::SharedPtr(new ResourceDefinition::Variable(currentStructure, baseType));
  }
  
  static ResourceDefinition::Node::SharedPtr generateRandomCoreShaderSet (de::Random& rnd, glu::GLSLVersion glslVersion)
  {
  	if (rnd.getFloat() < 0.5f)
  	{
  		// compute only
  		const ResourceDefinition::Node::SharedPtr program(new ResourceDefinition::Program());
  		return ResourceDefinition::Node::SharedPtr(new ResourceDefinition::Shader(program, glu::SHADERTYPE_COMPUTE, glslVersion));
  	}
  	else if (rnd.getFloat() < 0.5f)
  	{
  		// vertex and fragment
  		const ResourceDefinition::Node::SharedPtr	program		(new ResourceDefinition::Program());
  		ResourceDefinition::ShaderSet*				shaderSet	= new ResourceDefinition::ShaderSet(program, glslVersion);
  
  		if (rnd.getBool())
  		{
  			shaderSet->setStage(glu::SHADERTYPE_VERTEX, true);
  			shaderSet->setStage(glu::SHADERTYPE_FRAGMENT, rnd.getBool());
  		}
  		else
  		{
  			shaderSet->setStage(glu::SHADERTYPE_VERTEX, rnd.getBool());
  			shaderSet->setStage(glu::SHADERTYPE_FRAGMENT, true);
  		}
  
  		return ResourceDefinition::Node::SharedPtr(shaderSet);
  	}
  	else
  	{
  		// separate vertex or fragment
  		const ResourceDefinition::Node::SharedPtr	program		(new ResourceDefinition::Program(true));
  		const glu::ShaderType						shaderType	= (rnd.getBool()) ? (glu::SHADERTYPE_VERTEX) : (glu::SHADERTYPE_FRAGMENT);
  
  		return ResourceDefinition::Node::SharedPtr(new ResourceDefinition::Shader(program, shaderType, glslVersion));
  	}
  }
  
  static ResourceDefinition::Node::SharedPtr generateRandomExtShaderSet (de::Random& rnd, glu::GLSLVersion glslVersion)
  {
  	if (rnd.getFloat() < 0.5f)
  	{
  		// whole pipeline
  		const ResourceDefinition::Node::SharedPtr	program		(new ResourceDefinition::Program());
  		ResourceDefinition::ShaderSet*				shaderSet	= new ResourceDefinition::ShaderSet(program, glslVersion);
  
  		shaderSet->setStage(glu::SHADERTYPE_VERTEX, rnd.getBool());
  		shaderSet->setStage(glu::SHADERTYPE_FRAGMENT, rnd.getBool());
  
  		// tess shader are either both or neither present. Make cases interesting
  		// by forcing one extended shader to always have reference
  		if (rnd.getBool())
  		{
  			shaderSet->setStage(glu::SHADERTYPE_GEOMETRY, true);
  
  			if (rnd.getBool())
  			{
  				shaderSet->setStage(glu::SHADERTYPE_TESSELLATION_CONTROL, rnd.getBool());
  				shaderSet->setStage(glu::SHADERTYPE_TESSELLATION_EVALUATION, rnd.getBool());
  			}
  		}
  		else
  		{
  			shaderSet->setStage(glu::SHADERTYPE_GEOMETRY, rnd.getBool());
  
  			if (rnd.getBool())
  			{
  				shaderSet->setStage(glu::SHADERTYPE_TESSELLATION_CONTROL, true);
  				shaderSet->setStage(glu::SHADERTYPE_TESSELLATION_EVALUATION, rnd.getBool());
  			}
  			else
  			{
  				shaderSet->setStage(glu::SHADERTYPE_TESSELLATION_CONTROL, rnd.getBool());
  				shaderSet->setStage(glu::SHADERTYPE_TESSELLATION_EVALUATION, true);
  			}
  		}
  
  		return ResourceDefinition::Node::SharedPtr(shaderSet);
  	}
  	else
  	{
  		// separate
  		const ResourceDefinition::Node::SharedPtr	program		(new ResourceDefinition::Program(true));
  		const int									selector	= rnd.getInt(0, 2);
  		const glu::ShaderType						shaderType	= (selector == 0) ? (glu::SHADERTYPE_GEOMETRY)
  																: (selector == 1) ? (glu::SHADERTYPE_TESSELLATION_CONTROL)
  																: (selector == 2) ? (glu::SHADERTYPE_TESSELLATION_EVALUATION)
  																:					(glu::SHADERTYPE_LAST);
  
  		return ResourceDefinition::Node::SharedPtr(new ResourceDefinition::Shader(program, shaderType, glslVersion));
  	}
  }
  
  static ResourceDefinition::Node::SharedPtr generateRandomShaderSet (de::Random& rnd, glu::GLSLVersion glslVersion, bool onlyExtensionStages)
  {
  	if (!onlyExtensionStages)
  		return generateRandomCoreShaderSet(rnd, glslVersion);
  	else
  		return generateRandomExtShaderSet(rnd, glslVersion);
  }
  
  static glu::Layout generateRandomUniformBlockLayout (de::Random& rnd)
  {
  	glu::Layout layout;
  
  	if (rnd.getBool())
  		layout.binding = rnd.getInt(0, 5);
  
  	if (rnd.getBool())
  		layout.matrixOrder = (rnd.getBool()) ? (glu::MATRIXORDER_COLUMN_MAJOR) : (glu::MATRIXORDER_ROW_MAJOR);
  
  	return layout;
  }
  
  static glu::Layout generateRandomBufferBlockLayout (de::Random& rnd)
  {
  	return generateRandomUniformBlockLayout(rnd);
  }
  
  static glu::Layout generateRandomVariableLayout (de::Random& rnd, glu::DataType type, bool interfaceBlockMember)
  {
  	glu::Layout layout;
  
  	if ((glu::isDataTypeAtomicCounter(type) || glu::isDataTypeImage(type) || glu::isDataTypeSampler(type)) && rnd.getBool())
  		layout.binding = rnd.getInt(0, 5);
  
  	if (glu::isDataTypeAtomicCounter(type) && rnd.getBool())
  		layout.offset = rnd.getInt(0, 3) * 4;
  
  	if (glu::isDataTypeMatrix(type) && interfaceBlockMember && rnd.getBool())
  		layout.matrixOrder = (rnd.getBool()) ? (glu::MATRIXORDER_COLUMN_MAJOR) : (glu::MATRIXORDER_ROW_MAJOR);
  
  	return layout;
  }
  
  static void generateUniformRandomCase (Context& context, tcu::TestCaseGroup* const targetGroup, glu::GLSLVersion glslVersion, int index, bool onlyExtensionStages)
  {
  	de::Random									rnd					(index * 0x12345);
  	const ResourceDefinition::Node::SharedPtr	shader				= generateRandomShaderSet(rnd, glslVersion, onlyExtensionStages);
  	const bool									interfaceBlock		= rnd.getBool();
  	const glu::DataType							type				= generateRandomDataType(rnd, interfaceBlock);
  	const glu::Layout							layout				= generateRandomVariableLayout(rnd, type, interfaceBlock);
  	const ResourceDefinition::Node::SharedPtr	defaultBlock		(new ResourceDefinition::DefaultBlock(shader));
  	const ResourceDefinition::Node::SharedPtr	uniform				(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_UNIFORM));
  	ResourceDefinition::Node::SharedPtr			currentStructure	= uniform;
  
  	if (interfaceBlock)
  	{
  		const bool namedBlock = rnd.getBool();
  
  		currentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::LayoutQualifier(currentStructure, generateRandomUniformBlockLayout(rnd)));
  
  		if (namedBlock && rnd.getBool())
  			currentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::ArrayElement(currentStructure));
  
  		currentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::InterfaceBlock(currentStructure, namedBlock));
  	}
  
  	currentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::LayoutQualifier(currentStructure, layout));
  	currentStructure = generateRandomVariableDefinition(rnd, currentStructure, type, layout, false);
  
  	targetGroup->addChild(new ResourceTestCase(context, currentStructure, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_UNIFORM, PROGRAMRESOURCEPROP_UNIFORM_INTERFACE_MASK), de::toString(index).c_str()));
  }
  
  static void generateUniformCaseRandomCases (Context& context, tcu::TestCaseGroup* const targetGroup, glu::GLSLVersion glslVersion)
  {
  	const int numBasicCases		= 40;
  	const int numTessGeoCases	= 40;
  
  	for (int ndx = 0; ndx < numBasicCases; ++ndx)
  		generateUniformRandomCase(context, targetGroup, glslVersion, ndx, false);
  	for (int ndx = 0; ndx < numTessGeoCases; ++ndx)
  		generateUniformRandomCase(context, targetGroup, glslVersion, numBasicCases + ndx, true);
  }
  
  class UniformInterfaceTestGroup : public TestCaseGroup
  {
  public:
  			UniformInterfaceTestGroup	(Context& context);
  	void	init						(void);
  };
  
  UniformInterfaceTestGroup::UniformInterfaceTestGroup (Context& context)
  	: TestCaseGroup(context, "uniform", "Uniform interace")
  {
  }
  
  void UniformInterfaceTestGroup::init (void)
  {
  	glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
  	const ResourceDefinition::Node::SharedPtr	program			(new ResourceDefinition::Program());
  	const ResourceDefinition::Node::SharedPtr	computeShader	(new ResourceDefinition::Shader(program, glu::SHADERTYPE_COMPUTE, glslVersion));
  
  	// .resource_list
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "resource_list", "Resource list");
  		addChild(blockGroup);
  		generateUniformCaseBlocks(m_context, computeShader, blockGroup, BLOCKFLAG_ALL, generateUniformResourceListBlockContents);
  	}
  
  	// .array_size
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "array_size", "Query array size");
  		addChild(blockGroup);
  		generateUniformCaseBlocks(m_context, computeShader, blockGroup, BLOCKFLAG_ALL, generateUniformBlockArraySizeContents);
  	}
  
  	// .array_stride
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "array_stride", "Query array stride");
  		addChild(blockGroup);
  		generateUniformCaseBlocks(m_context, computeShader, blockGroup, BLOCKFLAG_ALL, generateUniformBlockArrayStrideContents);
  	}
  
  	// .atomic_counter_buffer_index
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "atomic_counter_buffer_index", "Query atomic counter buffer index");
  		addChild(blockGroup);
  		generateUniformCaseBlocks(m_context, computeShader, blockGroup, BLOCKFLAG_DEFAULT | BLOCKFLAG_NAMED, generateUniformBlockAtomicCounterBufferIndexContents);
  	}
  
  	// .block_index
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "block_index", "Query block index");
  		addChild(blockGroup);
  		generateUniformBlockBlockIndexContents(m_context, blockGroup, glslVersion);
  	}
  
  	// .location
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "location", "Query location");
  		addChild(blockGroup);
  		generateUniformCaseBlocks(m_context, computeShader, blockGroup, BLOCKFLAG_DEFAULT | BLOCKFLAG_NAMED | BLOCKFLAG_UNNAMED, generateUniformBlockLocationContents);
  	}
  
  	// .matrix_row_major
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "matrix_row_major", "Query matrix row_major");
  		addChild(blockGroup);
  		generateUniformMatrixCaseBlocks(m_context, computeShader, blockGroup, generateUniformMatrixOrderCaseBlockContentCases);
  	}
  
  	// .matrix_stride
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "matrix_stride", "Query matrix stride");
  		addChild(blockGroup);
  		generateUniformMatrixCaseBlocks(m_context, computeShader, blockGroup, generateUniformMatrixStrideCaseBlockContentCases);
  	}
  
  	// .name_length
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "name_length", "Query name length");
  		addChild(blockGroup);
  		generateUniformCaseBlocks(m_context, computeShader, blockGroup, BLOCKFLAG_ALL, generateUniformBlockNameLengthContents);
  	}
  
  	// .offset
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "offset", "Query offset");
  		addChild(blockGroup);
  		generateUniformCaseBlocks(m_context, computeShader, blockGroup, BLOCKFLAG_ALL, generateUniformBlockOffsetContents);
  	}
  
  	// .referenced_by_shader
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "referenced_by_shader", "Query referenced by shader");
  		addChild(blockGroup);
  		generateReferencedByShaderCaseBlocks(m_context, blockGroup, glslVersion, generateUniformReferencedByShaderSingleBlockContentCases);
  	}
  
  	// .type
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "type", "Query type");
  		addChild(blockGroup);
  		generateUniformCaseBlocks(m_context, computeShader, blockGroup, BLOCKFLAG_ALL, generateUniformBlockTypeContents);
  	}
  
  	// .random
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "random", "Random");
  		addChild(blockGroup);
  		generateUniformCaseRandomCases(m_context, blockGroup, glslVersion);
  	}
  }
  
  static void generateBufferBackedInterfaceResourceListCase (Context& context, const ResourceDefinition::Node::SharedPtr& targetResource, tcu::TestCaseGroup* const targetGroup, ProgramInterface interface, const char* blockName)
  {
  	targetGroup->addChild(new ResourceListTestCase(context, targetResource, interface, blockName));
  }
  
  static void generateBufferBackedInterfaceNameLengthCase (Context& context, const ResourceDefinition::Node::SharedPtr& targetResource, tcu::TestCaseGroup* const targetGroup, ProgramInterface interface, const char* blockName)
  {
  	targetGroup->addChild(new ResourceTestCase(context, targetResource, ProgramResourceQueryTestTarget(interface, PROGRAMRESOURCEPROP_NAME_LENGTH), blockName));
  }
  
  static void generateBufferBackedInterfaceResourceBasicBlockTypes (Context& context, tcu::TestCaseGroup* targetGroup, glu::GLSLVersion glslVersion, glu::Storage storage, void (*blockContentGenerator)(Context&, const ResourceDefinition::Node::SharedPtr&, tcu::TestCaseGroup* const, ProgramInterface interface, const char* blockName))
  {
  	const ResourceDefinition::Node::SharedPtr	program				(new ResourceDefinition::Program());
  	const ResourceDefinition::Node::SharedPtr	shader				(new ResourceDefinition::Shader(program, glu::SHADERTYPE_COMPUTE, glslVersion));
  	const ResourceDefinition::Node::SharedPtr	defaultBlock		(new ResourceDefinition::DefaultBlock(shader));
  	const ResourceDefinition::Node::SharedPtr	storageQualifier	(new ResourceDefinition::StorageQualifier(defaultBlock, storage));
  	const ResourceDefinition::Node::SharedPtr	binding				(new ResourceDefinition::LayoutQualifier(storageQualifier, glu::Layout(-1, 1)));
  	const ProgramInterface						programInterface	= (storage == glu::STORAGE_UNIFORM) ? (PROGRAMINTERFACE_UNIFORM_BLOCK) : (PROGRAMINTERFACE_SHADER_STORAGE_BLOCK);
  
  	// .named_block
  	{
  		const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(binding, true));
  		const ResourceDefinition::Node::SharedPtr dummyVariable	(new ResourceDefinition::Variable(block, glu::TYPE_BOOL_VEC3));
  
  		blockContentGenerator(context, dummyVariable, targetGroup, programInterface, "named_block");
  	}
  
  	// .unnamed_block
  	{
  		const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(binding, false));
  		const ResourceDefinition::Node::SharedPtr dummyVariable	(new ResourceDefinition::Variable(block, glu::TYPE_BOOL_VEC3));
  
  		blockContentGenerator(context, dummyVariable, targetGroup, programInterface, "unnamed_block");
  	}
  
  	// .block_array
  	{
  		const ResourceDefinition::Node::SharedPtr arrayElement	(new ResourceDefinition::ArrayElement(binding, 3));
  		const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  		const ResourceDefinition::Node::SharedPtr dummyVariable	(new ResourceDefinition::Variable(block, glu::TYPE_BOOL_VEC3));
  
  		blockContentGenerator(context, dummyVariable, targetGroup, programInterface, "block_array");
  	}
  
  	// .block_array_single_element
  	{
  		const ResourceDefinition::Node::SharedPtr arrayElement	(new ResourceDefinition::ArrayElement(binding, 1));
  		const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  		const ResourceDefinition::Node::SharedPtr dummyVariable	(new ResourceDefinition::Variable(block, glu::TYPE_BOOL_VEC3));
  
  		blockContentGenerator(context, dummyVariable, targetGroup, programInterface, "block_array_single_element");
  	}
  }
  
  static void generateBufferBackedInterfaceResourceBufferBindingCases (Context& context, tcu::TestCaseGroup* targetGroup, glu::GLSLVersion glslVersion, glu::Storage storage)
  {
  	const ResourceDefinition::Node::SharedPtr	program				(new ResourceDefinition::Program());
  	const ResourceDefinition::Node::SharedPtr	shader				(new ResourceDefinition::Shader(program, glu::SHADERTYPE_COMPUTE, glslVersion));
  	const ResourceDefinition::Node::SharedPtr	defaultBlock		(new ResourceDefinition::DefaultBlock(shader));
  	const ResourceDefinition::Node::SharedPtr	storageQualifier	(new ResourceDefinition::StorageQualifier(defaultBlock, storage));
  
  	for (int ndx = 0; ndx < 2; ++ndx)
  	{
  		const bool									explicitBinding		= (ndx == 1);
  		const int									bindingNdx			= (explicitBinding) ? (1) : (-1);
  		const std::string							nameSuffix			= (explicitBinding) ? ("_explicit_binding") : ("");
  		const ResourceDefinition::Node::SharedPtr	binding				(new ResourceDefinition::LayoutQualifier(storageQualifier, glu::Layout(-1, bindingNdx)));
  		const ProgramInterface						programInterface	= (storage == glu::STORAGE_UNIFORM) ? (PROGRAMINTERFACE_UNIFORM_BLOCK) : (PROGRAMINTERFACE_SHADER_STORAGE_BLOCK);
  
  		// .named_block*
  		{
  			const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(binding, true));
  			const ResourceDefinition::Node::SharedPtr dummyVariable	(new ResourceDefinition::Variable(block, glu::TYPE_BOOL_VEC3));
  
  			targetGroup->addChild(new ResourceTestCase(context, dummyVariable, ProgramResourceQueryTestTarget(programInterface, PROGRAMRESOURCEPROP_BUFFER_BINDING), ("named_block" + nameSuffix).c_str()));
  		}
  
  		// .unnamed_block*
  		{
  			const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(binding, false));
  			const ResourceDefinition::Node::SharedPtr dummyVariable	(new ResourceDefinition::Variable(block, glu::TYPE_BOOL_VEC3));
  
  			targetGroup->addChild(new ResourceTestCase(context, dummyVariable, ProgramResourceQueryTestTarget(programInterface, PROGRAMRESOURCEPROP_BUFFER_BINDING), ("unnamed_block" + nameSuffix).c_str()));
  		}
  
  		// .block_array*
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElement	(new ResourceDefinition::ArrayElement(binding, 3));
  			const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  			const ResourceDefinition::Node::SharedPtr dummyVariable	(new ResourceDefinition::Variable(block, glu::TYPE_BOOL_VEC3));
  
  			targetGroup->addChild(new ResourceTestCase(context, dummyVariable, ProgramResourceQueryTestTarget(programInterface, PROGRAMRESOURCEPROP_BUFFER_BINDING), ("block_array" + nameSuffix).c_str()));
  		}
  	}
  }
  
  template <glu::Storage Storage>
  static void generateBufferBlockReferencedByShaderSingleBlockContentCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, int expandLevel)
  {
  	const ProgramInterface						programInterface	= (Storage == glu::STORAGE_UNIFORM) ? (PROGRAMINTERFACE_UNIFORM_BLOCK) :
  																      (Storage == glu::STORAGE_BUFFER) ? (PROGRAMINTERFACE_SHADER_STORAGE_BLOCK) :
  																      (PROGRAMINTERFACE_LAST);
  	const ResourceDefinition::Node::SharedPtr	defaultBlock		(new ResourceDefinition::DefaultBlock(parentStructure));
  	const ResourceDefinition::Node::SharedPtr	storage				(new ResourceDefinition::StorageQualifier(defaultBlock, Storage));
  
  	DE_UNREF(expandLevel);
  
  	DE_ASSERT(programInterface != PROGRAMINTERFACE_LAST);
  
  	// .named_block
  	{
  		const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(storage, true));
  		const ResourceDefinition::Node::SharedPtr dummyVariable	(new ResourceDefinition::Variable(block, glu::TYPE_BOOL_VEC3));
  
  		targetGroup->addChild(new ResourceTestCase(context, dummyVariable, ProgramResourceQueryTestTarget(programInterface, PROGRAMRESOURCEPROP_REFERENCED_BY_SHADER), "named_block"));
  	}
  
  	// .unnamed_block
  	{
  		const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(storage, false));
  		const ResourceDefinition::Node::SharedPtr dummyVariable	(new ResourceDefinition::Variable(block, glu::TYPE_BOOL_VEC3));
  
  		targetGroup->addChild(new ResourceTestCase(context, dummyVariable, ProgramResourceQueryTestTarget(programInterface, PROGRAMRESOURCEPROP_REFERENCED_BY_SHADER), "unnamed_block"));
  	}
  
  	// .block_array
  	{
  		const ResourceDefinition::Node::SharedPtr arrayElement	(new ResourceDefinition::ArrayElement(storage, 3));
  		const ResourceDefinition::Node::SharedPtr block			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  		const ResourceDefinition::Node::SharedPtr dummyVariable	(new ResourceDefinition::Variable(block, glu::TYPE_BOOL_VEC3));
  
  		targetGroup->addChild(new ResourceTestCase(context, dummyVariable, ProgramResourceQueryTestTarget(programInterface, PROGRAMRESOURCEPROP_REFERENCED_BY_SHADER), "block_array"));
  	}
  }
  
  static void generateBufferBackedInterfaceResourceActiveVariablesCase (Context& context, tcu::TestCaseGroup* targetGroup, glu::Storage storage)
  {
  	targetGroup->addChild(new InterfaceBlockActiveVariablesTestCase(context, "named_block",		"Named block",		storage,	InterfaceBlockActiveVariablesTestCase::CASE_NAMED_BLOCK));
  	targetGroup->addChild(new InterfaceBlockActiveVariablesTestCase(context, "unnamed_block",	"Unnamed block",	storage,	InterfaceBlockActiveVariablesTestCase::CASE_UNNAMED_BLOCK));
  	targetGroup->addChild(new InterfaceBlockActiveVariablesTestCase(context, "block_array",		"Block array",		storage,	InterfaceBlockActiveVariablesTestCase::CASE_BLOCK_ARRAY));
  }
  
  static void generateBufferBackedInterfaceResourceBufferDataSizeCases (Context& context, tcu::TestCaseGroup* targetGroup, glu::Storage storage)
  {
  	targetGroup->addChild(new InterfaceBlockDataSizeTestCase(context, "named_block",	"Named block",		storage,	InterfaceBlockDataSizeTestCase::CASE_NAMED_BLOCK));
  	targetGroup->addChild(new InterfaceBlockDataSizeTestCase(context, "unnamed_block",	"Unnamed block",	storage,	InterfaceBlockDataSizeTestCase::CASE_UNNAMED_BLOCK));
  	targetGroup->addChild(new InterfaceBlockDataSizeTestCase(context, "block_array",	"Block array",		storage,	InterfaceBlockDataSizeTestCase::CASE_BLOCK_ARRAY));
  }
  
  class BufferBackedBlockInterfaceTestGroup : public TestCaseGroup
  {
  public:
  						BufferBackedBlockInterfaceTestGroup	(Context& context, glu::Storage interfaceBlockStorage);
  	void				init								(void);
  
  private:
  	static const char*	getGroupName						(glu::Storage storage);
  	static const char*	getGroupDescription					(glu::Storage storage);
  
  	const glu::Storage	m_storage;
  };
  
  BufferBackedBlockInterfaceTestGroup::BufferBackedBlockInterfaceTestGroup(Context& context, glu::Storage storage)
  	: TestCaseGroup	(context, getGroupName(storage), getGroupDescription(storage))
  	, m_storage		(storage)
  {
  	DE_ASSERT(storage == glu::STORAGE_BUFFER || storage == glu::STORAGE_UNIFORM);
  }
  
  void BufferBackedBlockInterfaceTestGroup::init (void)
  {
  	const glu::GLSLVersion	glslVersion	= glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
  
  	// .resource_list
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "resource_list", "Resource list");
  		addChild(blockGroup);
  		generateBufferBackedInterfaceResourceBasicBlockTypes(m_context, blockGroup, glslVersion, m_storage, generateBufferBackedInterfaceResourceListCase);
  	}
  
  	// .active_variables
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "active_variables", "Active variables");
  		addChild(blockGroup);
  		generateBufferBackedInterfaceResourceActiveVariablesCase(m_context, blockGroup, m_storage);
  	}
  
  	// .buffer_binding
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "buffer_binding", "Buffer binding");
  		addChild(blockGroup);
  		generateBufferBackedInterfaceResourceBufferBindingCases(m_context, blockGroup, glslVersion, m_storage);
  	}
  
  	// .buffer_data_size
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "buffer_data_size", "Buffer data size");
  		addChild(blockGroup);
  		generateBufferBackedInterfaceResourceBufferDataSizeCases(m_context, blockGroup, m_storage);
  	}
  
  	// .name_length
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "name_length", "Name length");
  		addChild(blockGroup);
  		generateBufferBackedInterfaceResourceBasicBlockTypes(m_context, blockGroup, glslVersion, m_storage, generateBufferBackedInterfaceNameLengthCase);
  	}
  
  	// .referenced_by
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "referenced_by", "Referenced by shader");
  		addChild(blockGroup);
  
  		if (m_storage == glu::STORAGE_UNIFORM)
  			generateReferencedByShaderCaseBlocks(m_context, blockGroup, glslVersion, generateBufferBlockReferencedByShaderSingleBlockContentCases<glu::STORAGE_UNIFORM>);
  		else if (m_storage == glu::STORAGE_BUFFER)
  			generateReferencedByShaderCaseBlocks(m_context, blockGroup, glslVersion, generateBufferBlockReferencedByShaderSingleBlockContentCases<glu::STORAGE_BUFFER>);
  		else
  			DE_ASSERT(false);
  	}
  }
  
  const char* BufferBackedBlockInterfaceTestGroup::getGroupName (glu::Storage storage)
  {
  	switch (storage)
  	{
  		case glu::STORAGE_UNIFORM:	return "uniform_block";
  		case glu::STORAGE_BUFFER:	return "shader_storage_block";
  		default:
  			DE_FATAL("invalid storage enum value");
  			return DE_NULL;
  	}
  }
  
  const char* BufferBackedBlockInterfaceTestGroup::getGroupDescription (glu::Storage storage)
  {
  	switch (storage)
  	{
  		case glu::STORAGE_UNIFORM:	return "Uniform block interface";
  		case glu::STORAGE_BUFFER:	return "Shader storage block interface";
  		default:
  			DE_FATAL("invalid storage enum value");
  			return DE_NULL;
  	}
  }
  
  class AtomicCounterTestGroup : public TestCaseGroup
  {
  public:
  			AtomicCounterTestGroup	(Context& context);
  	void	init					(void);
  };
  
  AtomicCounterTestGroup::AtomicCounterTestGroup (Context& context)
  	: TestCaseGroup(context, "atomic_counter_buffer", "Atomic counter buffer")
  {
  }
  
  void AtomicCounterTestGroup::init (void)
  {
  	static const struct
  	{
  		const char*	name;
  		deUint32	flags;
  	} pipelines[] =
  	{
  		{
  			"vertex_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT)
  		},
  		{
  			"vertex_tess_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT) | (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) | (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION)
  		},
  		{
  			"vertex_geo_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT) | (1 << glu::SHADERTYPE_GEOMETRY)
  		},
  		{
  			"vertex_tess_geo_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT) | (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) | (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION) | (1 << glu::SHADERTYPE_GEOMETRY),
  		},
  	};
  
  	// .resource_list
  	addChild(new AtomicCounterResourceListCase(m_context, "resource_list", "Resource list"));
  
  	// .active_variables
  	addChild(new AtomicCounterActiveVariablesCase(m_context, "active_variables", "Active variables"));
  
  	// .buffer_binding
  	addChild(new AtomicCounterBufferBindingCase(m_context, "buffer_binding", "Buffer binding"));
  
  	// .buffer_data_size
  	addChild(new AtomicCounterBufferDataSizeCase(m_context, "buffer_data_size", "Buffer binding"));
  
  	// .referenced_by
  	addChild(new AtomicCounterReferencedByCase(m_context, "referenced_by_compute",				"",	false,	(1 << glu::SHADERTYPE_COMPUTE),										(1 << glu::SHADERTYPE_COMPUTE)));
  	addChild(new AtomicCounterReferencedByCase(m_context, "referenced_by_separable_vertex",		"",	true,	(1 << glu::SHADERTYPE_VERTEX),										(1 << glu::SHADERTYPE_VERTEX)));
  	addChild(new AtomicCounterReferencedByCase(m_context, "referenced_by_separable_fragment",	"",	true,	(1 << glu::SHADERTYPE_FRAGMENT),									(1 << glu::SHADERTYPE_FRAGMENT)));
  	addChild(new AtomicCounterReferencedByCase(m_context, "referenced_by_separable_geometry",	"",	true,	(1 << glu::SHADERTYPE_GEOMETRY),									(1 << glu::SHADERTYPE_GEOMETRY)));
  	addChild(new AtomicCounterReferencedByCase(m_context, "referenced_by_separable_tess_ctrl",	"",	true,	(1 << glu::SHADERTYPE_TESSELLATION_CONTROL),						(1 << glu::SHADERTYPE_TESSELLATION_CONTROL)));
  	addChild(new AtomicCounterReferencedByCase(m_context, "referenced_by_separable_tess_eval",	"",	true,	(1 << glu::SHADERTYPE_TESSELLATION_EVALUATION),						(1 << glu::SHADERTYPE_TESSELLATION_EVALUATION)));
  
  	for (int pipelineNdx = 0; pipelineNdx < DE_LENGTH_OF_ARRAY(pipelines); ++pipelineNdx)
  	{
  		addChild(new AtomicCounterReferencedByCase(m_context, (std::string() + "referenced_by_" + pipelines[pipelineNdx].name).c_str(), "", false, pipelines[pipelineNdx].flags, pipelines[pipelineNdx].flags));
  
  		for (deUint32 stageNdx = 0; stageNdx < glu::SHADERTYPE_LAST; ++stageNdx)
  		{
  			const deUint32 currentBit = (1u << stageNdx);
  			if (currentBit > pipelines[pipelineNdx].flags)
  				break;
  			if (currentBit & pipelines[pipelineNdx].flags)
  			{
  				const char*			stageName	= (stageNdx == glu::SHADERTYPE_VERTEX)					? ("vertex")
  												: (stageNdx == glu::SHADERTYPE_FRAGMENT)				? ("fragment")
  												: (stageNdx == glu::SHADERTYPE_GEOMETRY)				? ("geo")
  												: (stageNdx == glu::SHADERTYPE_TESSELLATION_CONTROL)	? ("tess_ctrl")
  												: (stageNdx == glu::SHADERTYPE_TESSELLATION_EVALUATION)	? ("tess_eval")
  												: (DE_NULL);
  				const std::string	name		= std::string() + "referenced_by_" + pipelines[pipelineNdx].name + "_only_" + stageName;
  
  				addChild(new AtomicCounterReferencedByCase(m_context, name.c_str(), "", false, pipelines[pipelineNdx].flags, currentBit));
  			}
  		}
  	}
  }
  
  static void generateProgramInputOutputShaderCaseBlocks (Context& context, tcu::TestCaseGroup* targetGroup, glu::GLSLVersion glslVersion, bool withCompute, bool inputCase, bool isGL45, void (*blockContentGenerator)(Context&, const ResourceDefinition::Node::SharedPtr&, tcu::TestCaseGroup*, deUint32, bool))
  {
  	static const struct
  	{
  		const char*		name;
  		glu::ShaderType	stage;
  	} singleStageCases[] =
  	{
  		{ "separable_vertex",		glu::SHADERTYPE_VERTEX					},
  		{ "separable_fragment",		glu::SHADERTYPE_FRAGMENT				},
  		{ "separable_tess_ctrl",	glu::SHADERTYPE_TESSELLATION_CONTROL	},
  		{ "separable_tess_eval",	glu::SHADERTYPE_TESSELLATION_EVALUATION	},
  		{ "separable_geometry",		glu::SHADERTYPE_GEOMETRY				},
  	};
  
  	// .vertex_fragment
  	{
  		tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "vertex_fragment", "Vertex and fragment");
  		const ResourceDefinition::Node::SharedPtr	program			(new ResourceDefinition::Program(false));
  		ResourceDefinition::ShaderSet*				shaderSetPtr	= new ResourceDefinition::ShaderSet(program, glslVersion);
  		const ResourceDefinition::Node::SharedPtr	shaderSet		(shaderSetPtr);
  		const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(shaderSet));
  
  		shaderSetPtr->setStage(glu::SHADERTYPE_VERTEX, inputCase);
  		shaderSetPtr->setStage(glu::SHADERTYPE_FRAGMENT, !inputCase);
  
  		targetGroup->addChild(blockGroup);
  
  		blockContentGenerator(context, defaultBlock, blockGroup, (1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT), isGL45);
  	}
  
  	// .separable_*
  	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(singleStageCases); ++ndx)
  	{
  		TestCaseGroup* const						blockGroup			= new TestCaseGroup(context, singleStageCases[ndx].name, "");
  		const ResourceDefinition::Node::SharedPtr	program				(new ResourceDefinition::Program(true));
  		const ResourceDefinition::Node::SharedPtr	shader				(new ResourceDefinition::Shader(program, singleStageCases[ndx].stage, glslVersion));
  		const ResourceDefinition::Node::SharedPtr	defaultBlock		(new ResourceDefinition::DefaultBlock(shader));
  
  		targetGroup->addChild(blockGroup);
  		blockContentGenerator(context, defaultBlock, blockGroup, (1 << singleStageCases[ndx].stage), isGL45);
  	}
  
  	// .compute
  	if (withCompute)
  	{
  		tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "compute", "Compute");
  		const ResourceDefinition::Node::SharedPtr	program			(new ResourceDefinition::Program(true));
  		const ResourceDefinition::Node::SharedPtr	shader			(new ResourceDefinition::Shader(program, glu::SHADERTYPE_COMPUTE, glslVersion));
  		const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(shader));
  
  		targetGroup->addChild(blockGroup);
  
  		blockContentGenerator(context, defaultBlock, blockGroup, (1 << glu::SHADERTYPE_COMPUTE), isGL45);
  	}
  
  	// .interface_blocks
  	{
  		static const struct
  		{
  			const char*			inputName;
  			glu::ShaderType		inputStage;
  			glu::Storage		inputStorage;
  			const char*			outputName;
  			glu::ShaderType		outputStage;
  			glu::Storage		outputStorage;
  		} ioBlockTypes[] =
  		{
  			{
  				"in",
  				glu::SHADERTYPE_FRAGMENT,
  				glu::STORAGE_IN,
  				"out",
  				glu::SHADERTYPE_VERTEX,
  				glu::STORAGE_OUT,
  			},
  			{
  				"patch_in",
  				glu::SHADERTYPE_TESSELLATION_EVALUATION,
  				glu::STORAGE_PATCH_IN,
  				"patch_out",
  				glu::SHADERTYPE_TESSELLATION_CONTROL,
  				glu::STORAGE_PATCH_OUT,
  			},
  		};
  
  		tcu::TestCaseGroup* const ioBlocksGroup = new TestCaseGroup(context, "interface_blocks", "Interface blocks");
  		targetGroup->addChild(ioBlocksGroup);
  
  		// .in/out
  		// .sample in/out
  		// .patch in/out
  		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(ioBlockTypes); ++ndx)
  		{
  			const char* const							name			= (inputCase) ? (ioBlockTypes[ndx].inputName) : (ioBlockTypes[ndx].outputName);
  			const glu::ShaderType						shaderType		= (inputCase) ? (ioBlockTypes[ndx].inputStage) : (ioBlockTypes[ndx].outputStage);
  			const glu::Storage							storageType		= (inputCase) ? (ioBlockTypes[ndx].inputStorage) : (ioBlockTypes[ndx].outputStorage);
  			tcu::TestCaseGroup* const					ioBlockGroup	= new TestCaseGroup(context, name, "");
  			const ResourceDefinition::Node::SharedPtr	program			(new ResourceDefinition::Program(true));
  			const ResourceDefinition::Node::SharedPtr	shader			(new ResourceDefinition::Shader(program, shaderType, glslVersion));
  			const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(shader));
  			const ResourceDefinition::Node::SharedPtr	storage			(new ResourceDefinition::StorageQualifier(defaultBlock, storageType));
  
  			ioBlocksGroup->addChild(ioBlockGroup);
  
  			// .named_block
  			{
  				const ResourceDefinition::Node::SharedPtr	block		(new ResourceDefinition::InterfaceBlock(storage, true));
  				tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "named_block", "Named block");
  
  				ioBlockGroup->addChild(blockGroup);
  
  				blockContentGenerator(context, block, blockGroup, (1 << shaderType), isGL45);
  			}
  
  			// .named_block_explicit_location
  			{
  				const ResourceDefinition::Node::SharedPtr	layout		(new ResourceDefinition::LayoutQualifier(storage, glu::Layout(3)));
  				const ResourceDefinition::Node::SharedPtr	block		(new ResourceDefinition::InterfaceBlock(layout, true));
  				tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "named_block_explicit_location", "Named block with explicit location");
  
  				ioBlockGroup->addChild(blockGroup);
  
  				blockContentGenerator(context, block, blockGroup, (1 << shaderType), isGL45);
  			}
  
  			// .unnamed_block
  			if (!isGL45)
  			{
  				const ResourceDefinition::Node::SharedPtr	block		(new ResourceDefinition::InterfaceBlock(storage, false));
  				tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "unnamed_block", "Unnamed block");
  
  				ioBlockGroup->addChild(blockGroup);
  
  				blockContentGenerator(context, block, blockGroup, (1 << shaderType), isGL45);
  			}
  
  			// .block_array
  			{
  				const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(storage));
  				const ResourceDefinition::Node::SharedPtr	block			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  				tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "block_array", "Block array");
  
  				ioBlockGroup->addChild(blockGroup);
  
  				blockContentGenerator(context, block, blockGroup, (1 << shaderType), isGL45);
  			}
  		}
  	}
  }
  
  static void generateProgramInputBlockContents (Context&										context,
  											   const ResourceDefinition::Node::SharedPtr&	parentStructure,
  											   tcu::TestCaseGroup*							targetGroup,
  											   deUint32										presentShadersMask,
  											   bool											includeEmpty,
  											   void											(*genCase)(Context&										context,
  																									   const ResourceDefinition::Node::SharedPtr&	parentStructure,
  																									   tcu::TestCaseGroup*							targetGroup,
  																									   ProgramInterface								interface,
  																									   const char*									name))
  {
  	const bool									inDefaultBlock	= parentStructure->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK;
  	const ResourceDefinition::Node::SharedPtr	input			= (inDefaultBlock)
  																	? (ResourceDefinition::Node::SharedPtr(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_IN)))
  																	: (parentStructure);
  	const glu::ShaderType						firstStage		= getShaderMaskFirstStage(presentShadersMask);
  
  	// .empty
  	if (includeEmpty && inDefaultBlock)
  		genCase(context, parentStructure, targetGroup, PROGRAMINTERFACE_PROGRAM_INPUT, "empty");
  
  	if (firstStage == glu::SHADERTYPE_VERTEX)
  	{
  		// .var
  		const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(input, glu::TYPE_FLOAT_VEC4));
  		genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_INPUT, "var");
  	}
  	else if (firstStage == glu::SHADERTYPE_FRAGMENT || !inDefaultBlock)
  	{
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(input, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_INPUT, "var");
  		}
  		// .var_struct
  		{
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(input));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_INPUT, "var_struct");
  		}
  		// .var_array
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(input));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_INPUT, "var_array");
  		}
  	}
  	else if (firstStage == glu::SHADERTYPE_TESSELLATION_CONTROL ||
  			 firstStage == glu::SHADERTYPE_GEOMETRY)
  	{
  		// arrayed interface
  
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(input, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_INPUT, "var");
  		}
  		// extension forbids use arrays of structs
  		// extension forbids use arrays of arrays
  	}
  	else if (firstStage == glu::SHADERTYPE_TESSELLATION_EVALUATION)
  	{
  		// arrayed interface
  		const ResourceDefinition::Node::SharedPtr patchInput(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_PATCH_IN));
  
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(input, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_INPUT, "var");
  		}
  		// extension forbids use arrays of structs
  		// extension forbids use arrays of arrays
  
  		// .patch_var
  		{
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(patchInput, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_INPUT, "patch_var");
  		}
  		// .patch_var_struct
  		{
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(patchInput));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_INPUT, "patch_var_struct");
  		}
  		// .patch_var_array
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(patchInput));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_INPUT, "patch_var_array");
  		}
  	}
  	else if (firstStage == glu::SHADERTYPE_COMPUTE)
  	{
  		// nada
  	}
  	else
  		DE_ASSERT(false);
  }
  
  static void generateProgramOutputBlockContents (Context&										context,
  												const ResourceDefinition::Node::SharedPtr&		parentStructure,
  												tcu::TestCaseGroup*								targetGroup,
  												deUint32										presentShadersMask,
  												bool											includeEmpty,
  												void											(*genCase)(Context&										context,
  																										   const ResourceDefinition::Node::SharedPtr&	parentStructure,
  																										   tcu::TestCaseGroup*							targetGroup,
  																										   ProgramInterface								interface,
  																										   const char*									name))
  {
  	const bool									inDefaultBlock	= parentStructure->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK;
  	const ResourceDefinition::Node::SharedPtr	output			= (inDefaultBlock)
  																	? (ResourceDefinition::Node::SharedPtr(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_OUT)))
  																	: (parentStructure);
  	const glu::ShaderType						lastStage		= getShaderMaskLastStage(presentShadersMask);
  
  	// .empty
  	if (includeEmpty && inDefaultBlock)
  		genCase(context, parentStructure, targetGroup, PROGRAMINTERFACE_PROGRAM_OUTPUT, "empty");
  
  	if (lastStage == glu::SHADERTYPE_VERTEX						||
  		lastStage == glu::SHADERTYPE_GEOMETRY					||
  		lastStage == glu::SHADERTYPE_TESSELLATION_EVALUATION	||
  		!inDefaultBlock)
  	{
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(output, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_OUTPUT, "var");
  		}
  		// .var_struct
  		{
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(output));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_OUTPUT, "var_struct");
  		}
  		// .var_array
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(output));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_OUTPUT, "var_array");
  		}
  	}
  	else if (lastStage == glu::SHADERTYPE_FRAGMENT)
  	{
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(output, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_OUTPUT, "var");
  		}
  		// .var_array
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(output));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_OUTPUT, "var_array");
  		}
  	}
  	else if (lastStage == glu::SHADERTYPE_TESSELLATION_CONTROL)
  	{
  		// arrayed interface
  		const ResourceDefinition::Node::SharedPtr patchOutput(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_PATCH_OUT));
  
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(output, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_OUTPUT, "var");
  		}
  		// extension forbids use arrays of structs
  		// extension forbids use array of arrays
  
  		// .patch_var
  		{
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(patchOutput, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_OUTPUT, "patch_var");
  		}
  		// .patch_var_struct
  		{
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(patchOutput));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_OUTPUT, "patch_var_struct");
  		}
  		// .patch_var_array
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(patchOutput));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			genCase(context, variable, targetGroup, PROGRAMINTERFACE_PROGRAM_OUTPUT, "patch_var_array");
  		}
  	}
  	else if (lastStage == glu::SHADERTYPE_COMPUTE)
  	{
  		// nada
  	}
  	else
  		DE_ASSERT(false);
  }
  
  static void addProgramInputOutputResourceListCase (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, ProgramInterface programInterface, const char* name)
  {
  	ResourceListTestCase* const resourceListCase = new ResourceListTestCase(context, parentStructure, programInterface);
  
  	DE_ASSERT(deStringEqual(name, resourceListCase->getName()));
  	DE_UNREF(name);
  	targetGroup->addChild(resourceListCase);
  }
  
  static void generateProgramInputResourceListBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, deUint32 presentShadersMask, bool isGL45)
  {
  	DE_UNREF(isGL45);
  	generateProgramInputBlockContents(context, parentStructure, targetGroup, presentShadersMask, true, addProgramInputOutputResourceListCase);
  }
  
  static void generateProgramOutputResourceListBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, deUint32 presentShadersMask, bool isGL45)
  {
  	DE_UNREF(isGL45);
  	generateProgramOutputBlockContents(context, parentStructure, targetGroup, presentShadersMask, true, addProgramInputOutputResourceListCase);
  }
  
  template <ProgramResourcePropFlags TargetProp>
  static void addProgramInputOutputResourceTestCase (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, ProgramInterface programInterface, const char* name)
  {
  	ResourceTestCase* const resourceTestCase = new ResourceTestCase(context, parentStructure, ProgramResourceQueryTestTarget(programInterface, TargetProp), name);
  	targetGroup->addChild(resourceTestCase);
  }
  
  template <ProgramResourcePropFlags TargetProp>
  static void generateProgramInputBasicBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, deUint32 presentShadersMask, bool isGL45)
  {
  	DE_UNREF(isGL45);
  	generateProgramInputBlockContents(context, parentStructure, targetGroup, presentShadersMask, false, addProgramInputOutputResourceTestCase<TargetProp>);
  }
  
  template <ProgramResourcePropFlags TargetProp>
  static void generateProgramOutputBasicBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, deUint32 presentShadersMask, bool isGL45)
  {
  	DE_UNREF(isGL45);
  	generateProgramOutputBlockContents(context, parentStructure, targetGroup, presentShadersMask, false, addProgramInputOutputResourceTestCase<TargetProp>);
  }
  
  static void generateProgramInputLocationBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, deUint32 presentShadersMask, bool isGL45)
  {
  	DE_UNREF(isGL45);
  	const bool									inDefaultBlock	= parentStructure->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK;
  	const ResourceDefinition::Node::SharedPtr	input			= (inDefaultBlock)
  																	? (ResourceDefinition::Node::SharedPtr(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_IN)))
  																	: (parentStructure);
  	const glu::ShaderType						firstStage		= getShaderMaskFirstStage(presentShadersMask);
  
  	if (firstStage == glu::SHADERTYPE_VERTEX)
  	{
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(input, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var"));
  		}
  		// .var_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(input, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(layout, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var_explicit_location"));
  		}
  	}
  	else if (firstStage == glu::SHADERTYPE_FRAGMENT || !inDefaultBlock)
  	{
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(input, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var"));
  		}
  		// .var_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(input, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(layout, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var_explicit_location"));
  		}
  		// .var_struct
  		{
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(input));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var_struct"));
  		}
  		// .var_struct_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(input, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(layout));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var_struct_explicit_location"));
  		}
  		// .var_array
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(input));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var_array"));
  		}
  		// .var_array_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(input, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(layout));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var_array_explicit_location"));
  		}
  	}
  	else if (firstStage == glu::SHADERTYPE_TESSELLATION_CONTROL ||
  			 firstStage == glu::SHADERTYPE_GEOMETRY)
  	{
  		// arrayed interface
  
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(input, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var"));
  		}
  		// .var_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(input, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(layout, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var_explicit_location"));
  		}
  		// extension forbids use arrays of structs
  		// extension forbids use arrays of arrays
  	}
  	else if (firstStage == glu::SHADERTYPE_TESSELLATION_EVALUATION)
  	{
  		// arrayed interface
  		const ResourceDefinition::Node::SharedPtr patchInput(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_PATCH_IN));
  
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(input, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var"));
  		}
  		// .var_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(input, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(layout, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "var_explicit_location"));
  		}
  		// extension forbids use arrays of structs
  		// extension forbids use arrays of arrays
  
  		// .patch_var
  		{
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(patchInput, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var"));
  		}
  		// .patch_var_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(patchInput, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(layout, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var_explicit_location"));
  		}
  		// .patch_var_struct
  		{
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(patchInput));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var_struct"));
  		}
  		// .patch_var_struct_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(patchInput, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(layout));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var_struct_explicit_location"));
  		}
  		// .patch_var_array
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(patchInput));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var_array"));
  		}
  		// .patch_var_array_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(patchInput, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(layout));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_INPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var_array_explicit_location"));
  		}
  	}
  	else if (firstStage == glu::SHADERTYPE_COMPUTE)
  	{
  		// nada
  	}
  	else
  		DE_ASSERT(false);
  }
  
  static void generateProgramOutputLocationBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, deUint32 presentShadersMask, bool isGL45)
  {
  	const bool									inDefaultBlock	= parentStructure->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK;
  	const ResourceDefinition::Node::SharedPtr	output			= (inDefaultBlock)
  																	? (ResourceDefinition::Node::SharedPtr(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_OUT)))
  																	: (parentStructure);
  	const glu::ShaderType						lastStage		= getShaderMaskLastStage(presentShadersMask);
  
  	const bool									inBlockArray	= 0 == deStringEqual("block_array", targetGroup->getName());
  
  	if (lastStage == glu::SHADERTYPE_VERTEX						||
  		lastStage == glu::SHADERTYPE_GEOMETRY					||
  		lastStage == glu::SHADERTYPE_TESSELLATION_EVALUATION	||
  		!inDefaultBlock)
  	{
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(output, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var"));
  		}
  		// .var_explicit_location
  		if (!(isGL45 && inBlockArray))
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(output, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(layout, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var_explicit_location"));
  		}
  		// .var_struct
  		{
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(output));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var_struct"));
  		}
  		// .var_struct_explicit_location
  		if (!(isGL45 && inBlockArray))
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(output, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(layout));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var_struct_explicit_location"));
  		}
  		// .var_array
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(output));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var_array"));
  		}
  		// .var_array_explicit_location
  		if (!(isGL45 && inBlockArray))
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(output, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(layout));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var_array_explicit_location"));
  		}
  	}
  	else if (lastStage == glu::SHADERTYPE_FRAGMENT)
  	{
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(output, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var"));
  		}
  		// .var_explicit_location
  		if (!(isGL45 && inBlockArray))
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(output, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(layout, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var_explicit_location"));
  		}
  		// .var_array
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(output));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var_array"));
  		}
  		// .var_array_explicit_location
  		if (!(isGL45 && inBlockArray))
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(output, glu::Layout(1)));
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(layout));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var_array_explicit_location"));
  		}
  	}
  	else if (lastStage == glu::SHADERTYPE_TESSELLATION_CONTROL)
  	{
  		// arrayed interface
  		const ResourceDefinition::Node::SharedPtr patchOutput(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_PATCH_OUT));
  
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(output, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var"));
  		}
  		// .var_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(output, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(layout, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var_explicit_location"));
  		}
  		// extension forbids use arrays of structs
  		// extension forbids use array of arrays
  
  		// .patch_var
  		{
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(patchOutput, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var"));
  		}
  		// .patch_var_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(patchOutput, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(layout, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var_explicit_location"));
  		}
  		// .patch_var_struct
  		{
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(patchOutput));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var_struct"));
  		}
  		// .patch_var_struct_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(patchOutput, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(layout));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(structMbr, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var_struct_explicit_location"));
  		}
  		// .patch_var_array
  		{
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(patchOutput));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var_array"));
  		}
  		// .patch_var_array_explicit_location
  		{
  			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(patchOutput, glu::Layout(2)));
  			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(layout));
  			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "patch_var_array_explicit_location"));
  		}
  	}
  	else if (lastStage == glu::SHADERTYPE_COMPUTE)
  	{
  		// nada
  	}
  	else
  		DE_ASSERT(false);
  }
  
  static void generateProgramInputOutputReferencedByCases (Context& context, tcu::TestCaseGroup* targetGroup, glu::Storage storage)
  {
  	// all whole pipelines
  	targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_vertex_fragment",			"",	storage,	ProgramInputOutputReferencedByCase::CASE_VERTEX_FRAGMENT));
  	targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_vertex_tess_fragment",		"",	storage,	ProgramInputOutputReferencedByCase::CASE_VERTEX_TESS_FRAGMENT));
  	targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_vertex_geo_fragment",		"",	storage,	ProgramInputOutputReferencedByCase::CASE_VERTEX_GEO_FRAGMENT));
  	targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_vertex_tess_geo_fragment",	"",	storage,	ProgramInputOutputReferencedByCase::CASE_VERTEX_TESS_GEO_FRAGMENT));
  
  	// all partial pipelines
  	targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_separable_vertex",		"",	storage,	ProgramInputOutputReferencedByCase::CASE_SEPARABLE_VERTEX));
  	targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_separable_fragment",	"",	storage,	ProgramInputOutputReferencedByCase::CASE_SEPARABLE_FRAGMENT));
  	targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_separable_geometry",	"",	storage,	ProgramInputOutputReferencedByCase::CASE_SEPARABLE_GEOMETRY));
  	targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_separable_tess_eval",	"",	storage,	ProgramInputOutputReferencedByCase::CASE_SEPARABLE_TESS_EVAL));
  	targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_separable_tess_ctrl",	"",	storage,	ProgramInputOutputReferencedByCase::CASE_SEPARABLE_TESS_CTRL));
  
  	// patch
  	if (storage == glu::STORAGE_IN)
  		targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_separable_tess_eval_patch_in", "", glu::STORAGE_PATCH_IN, ProgramInputOutputReferencedByCase::CASE_SEPARABLE_TESS_EVAL));
  	else if (storage == glu::STORAGE_OUT)
  		targetGroup->addChild(new ProgramInputOutputReferencedByCase(context, "referenced_by_separable_tess_ctrl_patch_out", "", glu::STORAGE_PATCH_OUT, ProgramInputOutputReferencedByCase::CASE_SEPARABLE_TESS_CTRL));
  	else
  		DE_ASSERT(false);
  }
  
  template <ProgramInterface interface>
  static void generateProgramInputOutputTypeBasicTypeCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, bool allowMatrixCases, int expandLevel)
  {
  	static const struct
  	{
  		glu::DataType	type;
  		bool			isMatrix;
  		int				level;
  	} variableTypes[] =
  	{
  		{ glu::TYPE_FLOAT,			false,		0	},
  		{ glu::TYPE_INT,			false,		1	},
  		{ glu::TYPE_UINT,			false,		1	},
  		{ glu::TYPE_FLOAT_VEC2,		false,		2	},
  		{ glu::TYPE_FLOAT_VEC3,		false,		1	},
  		{ glu::TYPE_FLOAT_VEC4,		false,		2	},
  		{ glu::TYPE_INT_VEC2,		false,		0	},
  		{ glu::TYPE_INT_VEC3,		false,		2	},
  		{ glu::TYPE_INT_VEC4,		false,		2	},
  		{ glu::TYPE_UINT_VEC2,		false,		2	},
  		{ glu::TYPE_UINT_VEC3,		false,		2	},
  		{ glu::TYPE_UINT_VEC4,		false,		0	},
  		{ glu::TYPE_FLOAT_MAT2,		true,		2	},
  		{ glu::TYPE_FLOAT_MAT2X3,	true,		2	},
  		{ glu::TYPE_FLOAT_MAT2X4,	true,		2	},
  		{ glu::TYPE_FLOAT_MAT3X2,	true,		0	},
  		{ glu::TYPE_FLOAT_MAT3,		true,		2	},
  		{ glu::TYPE_FLOAT_MAT3X4,	true,		2	},
  		{ glu::TYPE_FLOAT_MAT4X2,	true,		2	},
  		{ glu::TYPE_FLOAT_MAT4X3,	true,		2	},
  		{ glu::TYPE_FLOAT_MAT4,		true,		2	},
  	};
  
  	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(variableTypes); ++ndx)
  	{
  		if (!allowMatrixCases && variableTypes[ndx].isMatrix)
  			continue;
  
  		if (variableTypes[ndx].level <= expandLevel)
  		{
  			const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, variableTypes[ndx].type));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(interface, PROGRAMRESOURCEPROP_TYPE)));
  		}
  	}
  }
  
  static void generateProgramInputTypeBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, deUint32 presentShadersMask, bool isGL45)
  {
  	DE_UNREF(isGL45);
  	const bool									inDefaultBlock						= parentStructure->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK;
  	const ResourceDefinition::Node::SharedPtr	input								= (inDefaultBlock)
  																						? (ResourceDefinition::Node::SharedPtr(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_IN)))
  																						: (parentStructure);
  	const glu::ShaderType						firstStage							= getShaderMaskFirstStage(presentShadersMask);
  	const int									interfaceBlockExpansionReducement	= (!inDefaultBlock) ? (1) : (0); // lesser expansions on block members to keep test counts reasonable
  
  	if (firstStage == glu::SHADERTYPE_VERTEX)
  	{
  		// Only basic types (and no booleans)
  		generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_INPUT>(context, input, targetGroup, true, 2 - interfaceBlockExpansionReducement);
  	}
  	else if (firstStage == glu::SHADERTYPE_FRAGMENT || !inDefaultBlock)
  	{
  		const ResourceDefinition::Node::SharedPtr flatShading(new ResourceDefinition::InterpolationQualifier(input, glu::INTERPOLATION_FLAT));
  
  		// Only basic types, arrays of basic types, struct of basic types (and no booleans)
  		{
  			tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "basic_type", "Basic types");
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_INPUT>(context, flatShading, blockGroup, true, 2 - interfaceBlockExpansionReducement);
  		}
  		{
  			const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(flatShading));
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "array", "Array types");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_INPUT>(context, arrayElement, blockGroup, true, 2 - interfaceBlockExpansionReducement);
  		}
  		{
  			const ResourceDefinition::Node::SharedPtr	structMember	(new ResourceDefinition::StructMember(flatShading));
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "struct", "Struct types");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_INPUT>(context, structMember, blockGroup, true, 2 - interfaceBlockExpansionReducement);
  		}
  	}
  	else if (firstStage == glu::SHADERTYPE_TESSELLATION_CONTROL ||
  			 firstStage == glu::SHADERTYPE_GEOMETRY)
  	{
  		// arrayed interface
  
  		// Only basic types (and no booleans)
  		const ResourceDefinition::Node::SharedPtr arrayElement(new ResourceDefinition::ArrayElement(input, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  		generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_INPUT>(context, arrayElement, targetGroup, true, 2);
  	}
  	else if (firstStage == glu::SHADERTYPE_TESSELLATION_EVALUATION)
  	{
  		// arrayed interface
  		const ResourceDefinition::Node::SharedPtr patchInput(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_PATCH_IN));
  
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr	arrayElem		(new ResourceDefinition::ArrayElement(input, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "basic_type", "Basic types");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_INPUT>(context, arrayElem, blockGroup, true, 2);
  		}
  		// extension forbids use arrays of structs
  		// extension forbids use arrays of arrays
  
  		// .patch_var
  		{
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "patch_var", "Basic types, per-patch");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_INPUT>(context, patchInput, blockGroup, true, 1);
  		}
  		// .patch_var_struct
  		{
  			const ResourceDefinition::Node::SharedPtr	structMbr		(new ResourceDefinition::StructMember(patchInput));
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "patch_var_struct", "Struct types, per-patch");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_INPUT>(context, structMbr, blockGroup, true, 1);
  		}
  		// .patch_var_array
  		{
  			const ResourceDefinition::Node::SharedPtr	arrayElem		(new ResourceDefinition::ArrayElement(patchInput));
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "patch_var_array", "Array types, per-patch");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_INPUT>(context, arrayElem, blockGroup, true, 1);
  		}
  	}
  	else if (firstStage == glu::SHADERTYPE_COMPUTE)
  	{
  		// nada
  	}
  	else
  		DE_ASSERT(false);
  }
  
  static void generateProgramOutputTypeBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, deUint32 presentShadersMask, bool isGL45)
  {
  	DE_UNREF(isGL45);
  	const bool									inDefaultBlock						= parentStructure->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK;
  	const ResourceDefinition::Node::SharedPtr	output								= (inDefaultBlock)
  																						? (ResourceDefinition::Node::SharedPtr(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_OUT)))
  																						: (parentStructure);
  	const glu::ShaderType						lastStage							= getShaderMaskLastStage(presentShadersMask);
  	const int									interfaceBlockExpansionReducement	= (!inDefaultBlock) ? (1) : (0); // lesser expansions on block members to keep test counts reasonable
  
  	if (lastStage == glu::SHADERTYPE_VERTEX						||
  		lastStage == glu::SHADERTYPE_GEOMETRY					||
  		lastStage == glu::SHADERTYPE_TESSELLATION_EVALUATION	||
  		!inDefaultBlock)
  	{
  		const ResourceDefinition::Node::SharedPtr flatShading(new ResourceDefinition::InterpolationQualifier(output, glu::INTERPOLATION_FLAT));
  
  		// Only basic types, arrays of basic types, struct of basic types (and no booleans)
  		{
  			tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "basic_type", "Basic types");
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_OUTPUT>(context, flatShading, blockGroup, true, 2 - interfaceBlockExpansionReducement);
  		}
  		{
  			const ResourceDefinition::Node::SharedPtr	arrayElement			(new ResourceDefinition::ArrayElement(flatShading));
  			tcu::TestCaseGroup* const					blockGroup				= new TestCaseGroup(context, "array", "Array types");
  			const int									typeExpansionReducement	= (lastStage != glu::SHADERTYPE_VERTEX) ? (1) : (0); // lesser expansions on other stages
  			const int									expansionLevel			= 2 - interfaceBlockExpansionReducement - typeExpansionReducement;
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_OUTPUT>(context, arrayElement, blockGroup, true, expansionLevel);
  		}
  		{
  			const ResourceDefinition::Node::SharedPtr	structMember			(new ResourceDefinition::StructMember(flatShading));
  			tcu::TestCaseGroup* const					blockGroup				= new TestCaseGroup(context, "struct", "Struct types");
  			const int									typeExpansionReducement	= (lastStage != glu::SHADERTYPE_VERTEX) ? (1) : (0); // lesser expansions on other stages
  			const int									expansionLevel			= 2 - interfaceBlockExpansionReducement - typeExpansionReducement;
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_OUTPUT>(context, structMember, blockGroup, true, expansionLevel);
  		}
  	}
  	else if (lastStage == glu::SHADERTYPE_FRAGMENT)
  	{
  		// only basic type and basic type array (and no booleans or matrices)
  		{
  			tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "basic_type", "Basic types");
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_OUTPUT>(context, output, blockGroup, false, 2);
  		}
  		{
  			const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(output));
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "array", "Array types");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_OUTPUT>(context, arrayElement, blockGroup, false, 2);
  		}
  	}
  	else if (lastStage == glu::SHADERTYPE_TESSELLATION_CONTROL)
  	{
  		// arrayed interface
  		const ResourceDefinition::Node::SharedPtr patchOutput(new ResourceDefinition::StorageQualifier(parentStructure, glu::STORAGE_PATCH_OUT));
  
  		// .var
  		{
  			const ResourceDefinition::Node::SharedPtr	arrayElem		(new ResourceDefinition::ArrayElement(output, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "basic_type", "Basic types");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_OUTPUT>(context, arrayElem, blockGroup, true, 2);
  		}
  		// extension forbids use arrays of structs
  		// extension forbids use arrays of arrays
  
  		// .patch_var
  		{
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "patch_var", "Basic types, per-patch");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_OUTPUT>(context, patchOutput, blockGroup, true, 1);
  		}
  		// .patch_var_struct
  		{
  			const ResourceDefinition::Node::SharedPtr	structMbr		(new ResourceDefinition::StructMember(patchOutput));
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "patch_var_struct", "Struct types, per-patch");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_OUTPUT>(context, structMbr, blockGroup, true, 1);
  		}
  		// .patch_var_array
  		{
  			const ResourceDefinition::Node::SharedPtr	arrayElem		(new ResourceDefinition::ArrayElement(patchOutput));
  			tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "patch_var_array", "Array types, per-patch");
  
  			targetGroup->addChild(blockGroup);
  			generateProgramInputOutputTypeBasicTypeCases<PROGRAMINTERFACE_PROGRAM_OUTPUT>(context, arrayElem, blockGroup, true, 1);
  		}
  	}
  	else if (lastStage == glu::SHADERTYPE_COMPUTE)
  	{
  		// nada
  	}
  	else
  		DE_ASSERT(false);
  }
  
  class ProgramInputTestGroup : public TestCaseGroup
  {
  public:
  			ProgramInputTestGroup	(Context& context, bool is_GL45);
  	void	init					(void);
  
  private:
  	bool m_isGL45;
  };
  
  ProgramInputTestGroup::ProgramInputTestGroup (Context& context, bool is_GL45)
  	: TestCaseGroup(context, "program_input", "Program input")
  	, m_isGL45(is_GL45)
  {
  }
  
  void ProgramInputTestGroup::init (void)
  {
  	const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
  
  	// .resource_list
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "resource_list", "Resource list");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, true, true, m_isGL45, generateProgramInputResourceListBlockContents);
  	}
  
  	// .array_size
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "array_size", "Array size");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, false, true, m_isGL45, generateProgramInputBasicBlockContents<PROGRAMRESOURCEPROP_ARRAY_SIZE>);
  	}
  
  	// .location
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "location", "Location");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, false, true, m_isGL45, generateProgramInputLocationBlockContents);
  	}
  
  	// .name_length
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "name_length", "Name length");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, false, true, m_isGL45, generateProgramInputBasicBlockContents<PROGRAMRESOURCEPROP_NAME_LENGTH>);
  	}
  
  	// .referenced_by
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "referenced_by", "Reference by shader");
  		addChild(blockGroup);
  		generateProgramInputOutputReferencedByCases(m_context, blockGroup, glu::STORAGE_IN);
  	}
  
  	// .type
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "type", "Type");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, false, true, m_isGL45, generateProgramInputTypeBlockContents);
  	}
  
  	// .is_per_patch
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "is_per_patch", "Is per patch");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, false, true, m_isGL45, generateProgramInputBasicBlockContents<PROGRAMRESOURCEPROP_IS_PER_PATCH>);
  	}
  }
  
  class ProgramOutputTestGroup : public TestCaseGroup
  {
  public:
  			ProgramOutputTestGroup	(Context& context, bool is_GL45);
  	void	init					(void);
  
  private:
  	bool m_isGL45;
  };
  
  ProgramOutputTestGroup::ProgramOutputTestGroup (Context& context, bool is_GL45)
  	: TestCaseGroup(context, "program_output", "Program output")
  	, m_isGL45(is_GL45)
  {
  }
  
  void ProgramOutputTestGroup::init (void)
  {
  	const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
  
  	// .resource_list
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "resource_list", "Resource list");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, true, false, m_isGL45, generateProgramOutputResourceListBlockContents);
  	}
  
  	// .array_size
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "array_size", "Array size");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, false, false, m_isGL45, generateProgramOutputBasicBlockContents<PROGRAMRESOURCEPROP_ARRAY_SIZE>);
  	}
  
  	// .location
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "location", "Location");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, false, false, m_isGL45, generateProgramOutputLocationBlockContents);
  	}
  
  	// .name_length
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "name_length", "Name length");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, false, false, m_isGL45, generateProgramOutputBasicBlockContents<PROGRAMRESOURCEPROP_NAME_LENGTH>);
  	}
  
  	// .referenced_by
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "referenced_by", "Reference by shader");
  		addChild(blockGroup);
  		generateProgramInputOutputReferencedByCases(m_context, blockGroup, glu::STORAGE_OUT);
  	}
  
  	// .type
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "type", "Type");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, false, false, m_isGL45, generateProgramOutputTypeBlockContents);
  	}
  
  	// .is_per_patch
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(m_testCtx, "is_per_patch", "Is per patch");
  		addChild(blockGroup);
  		generateProgramInputOutputShaderCaseBlocks(m_context, blockGroup, glslVersion, false, false, m_isGL45, generateProgramOutputBasicBlockContents<PROGRAMRESOURCEPROP_IS_PER_PATCH>);
  	}
  }
  
  static void generateTransformFeedbackShaderCaseBlocks (Context& context, tcu::TestCaseGroup* targetGroup, glu::GLSLVersion glslVersion, void (*blockContentGenerator)(Context&, const ResourceDefinition::Node::SharedPtr&, tcu::TestCaseGroup*, bool))
  {
  	static const struct
  	{
  		const char*	name;
  		deUint32	stageBits;
  		deUint32	lastStageBit;
  		bool		reducedSet;
  	} pipelines[] =
  	{
  		{
  			"vertex_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT),
  			(1 << glu::SHADERTYPE_VERTEX),
  			false
  		},
  		{
  			"vertex_tess_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT) | (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) | (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION),
  			(1 << glu::SHADERTYPE_TESSELLATION_EVALUATION),
  			true
  		},
  		{
  			"vertex_geo_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT) | (1 << glu::SHADERTYPE_GEOMETRY),
  			(1 << glu::SHADERTYPE_GEOMETRY),
  			true
  		},
  		{
  			"vertex_tess_geo_fragment",
  			(1 << glu::SHADERTYPE_VERTEX) | (1 << glu::SHADERTYPE_FRAGMENT) | (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) | (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION) | (1 << glu::SHADERTYPE_GEOMETRY),
  			(1 << glu::SHADERTYPE_GEOMETRY),
  			true
  		},
  	};
  	static const struct
  	{
  		const char*		name;
  		glu::ShaderType	stage;
  		bool			reducedSet;
  	} singleStageCases[] =
  	{
  		{ "separable_vertex",		glu::SHADERTYPE_VERTEX,						false	},
  		{ "separable_tess_eval",	glu::SHADERTYPE_TESSELLATION_EVALUATION,	true	},
  		{ "separable_geometry",		glu::SHADERTYPE_GEOMETRY,					true	},
  	};
  
  	// monolithic pipeline
  	for (int pipelineNdx = 0; pipelineNdx < DE_LENGTH_OF_ARRAY(pipelines); ++pipelineNdx)
  	{
  		TestCaseGroup* const						blockGroup		= new TestCaseGroup(context, pipelines[pipelineNdx].name, "");
  		const ResourceDefinition::Node::SharedPtr	program			(new ResourceDefinition::Program());
  		const ResourceDefinition::Node::SharedPtr	shaderSet		(new ResourceDefinition::ShaderSet(program,
  																									   glslVersion,
  																									   pipelines[pipelineNdx].stageBits,
  																									   pipelines[pipelineNdx].lastStageBit));
  
  		targetGroup->addChild(blockGroup);
  		blockContentGenerator(context, shaderSet, blockGroup, pipelines[pipelineNdx].reducedSet);
  	}
  
  	// separable pipeline
  	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(singleStageCases); ++ndx)
  	{
  		TestCaseGroup* const						blockGroup			= new TestCaseGroup(context, singleStageCases[ndx].name, "");
  		const ResourceDefinition::Node::SharedPtr	program				(new ResourceDefinition::Program(true));
  		const ResourceDefinition::Node::SharedPtr	shader				(new ResourceDefinition::Shader(program, singleStageCases[ndx].stage, glslVersion));
  
  		targetGroup->addChild(blockGroup);
  		blockContentGenerator(context, shader, blockGroup, singleStageCases[ndx].reducedSet);
  	}
  }
  
  static void generateTransformFeedbackResourceListBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, bool reducedSet)
  {
  	const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(parentStructure));
  	const ResourceDefinition::Node::SharedPtr	output			(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_OUT));
  
  	DE_UNREF(reducedSet);
  
  	// .builtin_gl_position
  	{
  		const ResourceDefinition::Node::SharedPtr xfbTarget(new ResourceDefinition::TransformFeedbackTarget(defaultBlock, "gl_Position"));
  		targetGroup->addChild(new FeedbackResourceListTestCase(context, xfbTarget, "builtin_gl_position"));
  	}
  	// .default_block_basic_type
  	{
  		const ResourceDefinition::Node::SharedPtr xfbTarget	(new ResourceDefinition::TransformFeedbackTarget(output));
  		const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(xfbTarget, glu::TYPE_FLOAT_VEC4));
  		targetGroup->addChild(new FeedbackResourceListTestCase(context, variable, "default_block_basic_type"));
  	}
  	// .default_block_struct_member
  	{
  		const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(output));
  		const ResourceDefinition::Node::SharedPtr xfbTarget	(new ResourceDefinition::TransformFeedbackTarget(structMbr));
  		const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(xfbTarget, glu::TYPE_FLOAT_VEC4));
  		targetGroup->addChild(new FeedbackResourceListTestCase(context, variable, "default_block_struct_member"));
  	}
  	// .default_block_array
  	{
  		const ResourceDefinition::Node::SharedPtr xfbTarget	(new ResourceDefinition::TransformFeedbackTarget(output));
  		const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(xfbTarget));
  		const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  		targetGroup->addChild(new FeedbackResourceListTestCase(context, variable, "default_block_array"));
  	}
  	// .default_block_array_element
  	{
  		const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(output));
  		const ResourceDefinition::Node::SharedPtr xfbTarget	(new ResourceDefinition::TransformFeedbackTarget(arrayElem));
  		const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(xfbTarget, glu::TYPE_FLOAT_VEC4));
  		targetGroup->addChild(new FeedbackResourceListTestCase(context, variable, "default_block_array_element"));
  	}
  }
  
  template <ProgramResourcePropFlags TargetProp>
  static void generateTransformFeedbackVariableBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, bool reducedSet)
  {
  	const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(parentStructure));
  	const ResourceDefinition::Node::SharedPtr	output			(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_OUT));
  
  	DE_UNREF(reducedSet);
  
  	// .builtin_gl_position
  	{
  		const ResourceDefinition::Node::SharedPtr xfbTarget(new ResourceDefinition::TransformFeedbackTarget(defaultBlock, "gl_Position"));
  		targetGroup->addChild(new ResourceTestCase(context, xfbTarget, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING, TargetProp), "builtin_gl_position"));
  	}
  	// .default_block_basic_type
  	{
  		const ResourceDefinition::Node::SharedPtr xfbTarget	(new ResourceDefinition::TransformFeedbackTarget(output));
  		const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(xfbTarget, glu::TYPE_FLOAT_VEC4));
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING, TargetProp), "default_block_basic_type"));
  	}
  	// .default_block_struct_member
  	{
  		const ResourceDefinition::Node::SharedPtr structMbr	(new ResourceDefinition::StructMember(output));
  		const ResourceDefinition::Node::SharedPtr xfbTarget	(new ResourceDefinition::TransformFeedbackTarget(structMbr));
  		const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(xfbTarget, glu::TYPE_FLOAT_VEC4));
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING, TargetProp), "default_block_struct_member"));
  	}
  	// .default_block_array
  	{
  		const ResourceDefinition::Node::SharedPtr xfbTarget	(new ResourceDefinition::TransformFeedbackTarget(output));
  		const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(xfbTarget));
  		const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING, TargetProp), "default_block_array"));
  	}
  	// .default_block_array_element
  	{
  		const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(output));
  		const ResourceDefinition::Node::SharedPtr xfbTarget	(new ResourceDefinition::TransformFeedbackTarget(arrayElem));
  		const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(xfbTarget, glu::TYPE_FLOAT_VEC4));
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING, TargetProp), "default_block_array_element"));
  	}
  }
  
  static void generateTransformFeedbackVariableBasicTypeCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, bool reducedSet)
  {
  	static const struct
  	{
  		glu::DataType	type;
  		bool			important;
  	} variableTypes[] =
  	{
  		{ glu::TYPE_FLOAT,			true	},
  		{ glu::TYPE_INT,			true	},
  		{ glu::TYPE_UINT,			true	},
  
  		{ glu::TYPE_FLOAT_VEC2,		false	},
  		{ glu::TYPE_FLOAT_VEC3,		true	},
  		{ glu::TYPE_FLOAT_VEC4,		false	},
  
  		{ glu::TYPE_INT_VEC2,		false	},
  		{ glu::TYPE_INT_VEC3,		true	},
  		{ glu::TYPE_INT_VEC4,		false	},
  
  		{ glu::TYPE_UINT_VEC2,		true	},
  		{ glu::TYPE_UINT_VEC3,		false	},
  		{ glu::TYPE_UINT_VEC4,		false	},
  
  		{ glu::TYPE_FLOAT_MAT2,		false	},
  		{ glu::TYPE_FLOAT_MAT2X3,	false	},
  		{ glu::TYPE_FLOAT_MAT2X4,	false	},
  		{ glu::TYPE_FLOAT_MAT3X2,	false	},
  		{ glu::TYPE_FLOAT_MAT3,		false	},
  		{ glu::TYPE_FLOAT_MAT3X4,	true	},
  		{ glu::TYPE_FLOAT_MAT4X2,	false	},
  		{ glu::TYPE_FLOAT_MAT4X3,	false	},
  		{ glu::TYPE_FLOAT_MAT4,		false	},
  	};
  
  	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(variableTypes); ++ndx)
  	{
  		if (variableTypes[ndx].important || !reducedSet)
  		{
  			const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, variableTypes[ndx].type));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING, PROGRAMRESOURCEPROP_TYPE)));
  		}
  	}
  }
  
  static void generateTransformFeedbackVariableTypeBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, bool reducedSet)
  {
  	const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(parentStructure));
  	const ResourceDefinition::Node::SharedPtr	output			(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_OUT));
  	const ResourceDefinition::Node::SharedPtr	flatShading		(new ResourceDefinition::InterpolationQualifier(output, glu::INTERPOLATION_FLAT));
  
  	// Only builtins, basic types, arrays of basic types, struct of basic types (and no booleans)
  	{
  		const ResourceDefinition::Node::SharedPtr	xfbTarget		(new ResourceDefinition::TransformFeedbackTarget(defaultBlock, "gl_Position"));
  		tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "builtin", "Built-in outputs");
  
  		targetGroup->addChild(blockGroup);
  		blockGroup->addChild(new ResourceTestCase(context, xfbTarget, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING, PROGRAMRESOURCEPROP_TYPE), "gl_position"));
  	}
  	{
  		const ResourceDefinition::Node::SharedPtr	xfbTarget		(new ResourceDefinition::TransformFeedbackTarget(flatShading));
  		tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "basic_type", "Basic types");
  
  		targetGroup->addChild(blockGroup);
  		generateTransformFeedbackVariableBasicTypeCases(context, xfbTarget, blockGroup, reducedSet);
  	}
  	{
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(flatShading));
  		const ResourceDefinition::Node::SharedPtr	xfbTarget		(new ResourceDefinition::TransformFeedbackTarget(arrayElement));
  		tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "array", "Array types");
  
  		targetGroup->addChild(blockGroup);
  		generateTransformFeedbackVariableBasicTypeCases(context, xfbTarget, blockGroup, reducedSet);
  	}
  	{
  		const ResourceDefinition::Node::SharedPtr	xfbTarget		(new ResourceDefinition::TransformFeedbackTarget(flatShading));
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(xfbTarget));
  		tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "whole_array", "Whole array");
  
  		targetGroup->addChild(blockGroup);
  		generateTransformFeedbackVariableBasicTypeCases(context, arrayElement, blockGroup, reducedSet);
  	}
  	{
  		const ResourceDefinition::Node::SharedPtr	structMember	(new ResourceDefinition::StructMember(flatShading));
  		const ResourceDefinition::Node::SharedPtr	xfbTarget		(new ResourceDefinition::TransformFeedbackTarget(structMember));
  		tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "struct", "Struct types");
  
  		targetGroup->addChild(blockGroup);
  		generateTransformFeedbackVariableBasicTypeCases(context, xfbTarget, blockGroup, reducedSet);
  	}
  }
  
  class TransformFeedbackVaryingTestGroup : public TestCaseGroup
  {
  public:
  			TransformFeedbackVaryingTestGroup	(Context& context);
  	void	init								(void);
  };
  
  TransformFeedbackVaryingTestGroup::TransformFeedbackVaryingTestGroup (Context& context)
  	: TestCaseGroup(context, "transform_feedback_varying", "Transform feedback varyings")
  {
  }
  
  void TransformFeedbackVaryingTestGroup::init (void)
  {
  	const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
  
  	// .resource_list
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "resource_list", "Resource list");
  		addChild(blockGroup);
  		generateTransformFeedbackShaderCaseBlocks(m_context, blockGroup, glslVersion, generateTransformFeedbackResourceListBlockContents);
  	}
  
  	// .array_size
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "array_size", "Array size");
  		addChild(blockGroup);
  		generateTransformFeedbackShaderCaseBlocks(m_context, blockGroup, glslVersion, generateTransformFeedbackVariableBlockContents<PROGRAMRESOURCEPROP_ARRAY_SIZE>);
  	}
  
  	// .name_length
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "name_length", "Name length");
  		addChild(blockGroup);
  		generateTransformFeedbackShaderCaseBlocks(m_context, blockGroup, glslVersion, generateTransformFeedbackVariableBlockContents<PROGRAMRESOURCEPROP_NAME_LENGTH>);
  	}
  
  	// .type
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "type", "Type");
  		addChild(blockGroup);
  		generateTransformFeedbackShaderCaseBlocks(m_context, blockGroup, glslVersion, generateTransformFeedbackVariableTypeBlockContents);
  	}
  }
  
  static void generateBufferVariableBufferCaseBlocks (Context& context, tcu::TestCaseGroup* targetGroup, glu::GLSLVersion glslVersion, void (*blockContentGenerator)(Context&, const ResourceDefinition::Node::SharedPtr&, tcu::TestCaseGroup*))
  {
  	const ResourceDefinition::Node::SharedPtr	program			(new ResourceDefinition::Program());
  	const ResourceDefinition::Node::SharedPtr	shader			(new ResourceDefinition::Shader(program, glu::SHADERTYPE_COMPUTE, glslVersion));
  	const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(shader));
  	const ResourceDefinition::Node::SharedPtr	bufferStorage	(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_BUFFER));
  	const ResourceDefinition::Node::SharedPtr	binding			(new ResourceDefinition::LayoutQualifier(bufferStorage, glu::Layout(-1, 0)));
  
  	// .named_block
  	{
  		const ResourceDefinition::Node::SharedPtr	buffer		(new ResourceDefinition::InterfaceBlock(binding, true));
  		tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "named_block", "Named block");
  
  		targetGroup->addChild(blockGroup);
  
  		blockContentGenerator(context, buffer, blockGroup);
  	}
  
  	// .unnamed_block
  	{
  		const ResourceDefinition::Node::SharedPtr	buffer		(new ResourceDefinition::InterfaceBlock(binding, false));
  		tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "unnamed_block", "Unnamed block");
  
  		targetGroup->addChild(blockGroup);
  
  		blockContentGenerator(context, buffer, blockGroup);
  	}
  
  	// .block_array
  	{
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(binding));
  		const ResourceDefinition::Node::SharedPtr	buffer			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  		tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "block_array", "Block array");
  
  		targetGroup->addChild(blockGroup);
  
  		blockContentGenerator(context, buffer, blockGroup);
  	}
  }
  
  static void generateBufferVariableResourceListBlockContentsProxy (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup)
  {
  	generateBufferBackedResourceListBlockContentCases(context, parentStructure, targetGroup, PROGRAMINTERFACE_BUFFER_VARIABLE, 4);
  }
  
  static void generateBufferVariableArraySizeSubCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup, ProgramResourcePropFlags targetProp, bool sizedArray, bool extendedCases)
  {
  	const ProgramResourceQueryTestTarget	queryTarget		(PROGRAMINTERFACE_BUFFER_VARIABLE, targetProp);
  	tcu::TestCaseGroup*						aggregateGroup;
  
  	// .types
  	if (extendedCases)
  	{
  		tcu::TestCaseGroup* const blockGroup = new tcu::TestCaseGroup(context.getTestContext(), "types", "Types");
  		targetGroup->addChild(blockGroup);
  
  		generateVariableCases(context, parentStructure, blockGroup, queryTarget, (sizedArray) ? (2) : (1), false);
  	}
  
  	// .aggregates
  	if (extendedCases)
  	{
  		aggregateGroup = new tcu::TestCaseGroup(context.getTestContext(), "aggregates", "Aggregate types");
  		targetGroup->addChild(aggregateGroup);
  	}
  	else
  		aggregateGroup = targetGroup;
  
  	// .float_*
  	generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, aggregateGroup, queryTarget.interface, glu::TYPE_FLOAT, (extendedCases && sizedArray) ? (2) : (1), !extendedCases);
  
  	// .bool_*
  	generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, aggregateGroup, queryTarget.interface, glu::TYPE_BOOL, (extendedCases && sizedArray) ? (1) : (0), !extendedCases);
  
  	// .bvec3_*
  	generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, aggregateGroup, queryTarget.interface, glu::TYPE_BOOL_VEC3, (extendedCases && sizedArray) ? (2) : (1), !extendedCases);
  
  	// .vec4_*
  	generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, aggregateGroup, queryTarget.interface, glu::TYPE_FLOAT_VEC4, (extendedCases && sizedArray) ? (2) : (1), !extendedCases);
  
  	// .ivec2_*
  	generateBufferBackedArrayStrideTypeAggregateCases(context, parentStructure, aggregateGroup, queryTarget.interface, glu::TYPE_INT_VEC2, (extendedCases && sizedArray) ? (2) : (1), !extendedCases);
  }
  
  template <ProgramResourcePropFlags TargetProp>
  static void generateBufferVariableArrayCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* const targetGroup)
  {
  	const ProgramResourceQueryTestTarget	queryTarget			(PROGRAMINTERFACE_BUFFER_VARIABLE, TargetProp);
  	const bool								namedNonArrayBlock	= static_cast<const ResourceDefinition::InterfaceBlock*>(parentStructure.get())->m_named && parentStructure->getEnclosingNode()->getType() != ResourceDefinition::Node::TYPE_ARRAY_ELEMENT;
  
  	// .non_array
  	if (namedNonArrayBlock)
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "non_array", "Non-array target");
  		targetGroup->addChild(blockGroup);
  
  		generateVariableCases(context, parentStructure, blockGroup, queryTarget, 1, false);
  	}
  
  	// .sized
  	{
  		const ResourceDefinition::Node::SharedPtr	sized		(new ResourceDefinition::ArrayElement(parentStructure));
  		tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "sized", "Sized target");
  		targetGroup->addChild(blockGroup);
  
  		generateBufferVariableArraySizeSubCases(context, sized, blockGroup, TargetProp, true, namedNonArrayBlock);
  	}
  
  	// .unsized
  	{
  		const ResourceDefinition::Node::SharedPtr	unsized		(new ResourceDefinition::ArrayElement(parentStructure, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  		tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "unsized", "Unsized target");
  		targetGroup->addChild(blockGroup);
  
  		generateBufferVariableArraySizeSubCases(context, unsized, blockGroup, TargetProp, false, namedNonArrayBlock);
  	}
  }
  
  static void generateBufferVariableBlockIndexCases (Context& context, glu::GLSLVersion glslVersion, tcu::TestCaseGroup* const targetGroup)
  {
  	const ResourceDefinition::Node::SharedPtr	program			(new ResourceDefinition::Program());
  	const ResourceDefinition::Node::SharedPtr	shader			(new ResourceDefinition::Shader(program, glu::SHADERTYPE_COMPUTE, glslVersion));
  	const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(shader));
  	const ResourceDefinition::Node::SharedPtr	bufferStorage	(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_BUFFER));
  	const ResourceDefinition::Node::SharedPtr	binding			(new ResourceDefinition::LayoutQualifier(bufferStorage, glu::Layout(-1, 0)));
  
  	// .named_block
  	{
  		const ResourceDefinition::Node::SharedPtr	buffer		(new ResourceDefinition::InterfaceBlock(binding, true));
  		const ResourceDefinition::Node::SharedPtr	variable	(new ResourceDefinition::Variable(buffer, glu::TYPE_FLOAT_VEC4));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_BUFFER_VARIABLE, PROGRAMRESOURCEPROP_BLOCK_INDEX), "named_block"));
  	}
  
  	// .unnamed_block
  	{
  		const ResourceDefinition::Node::SharedPtr	buffer		(new ResourceDefinition::InterfaceBlock(binding, false));
  		const ResourceDefinition::Node::SharedPtr	variable	(new ResourceDefinition::Variable(buffer, glu::TYPE_FLOAT_VEC4));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_BUFFER_VARIABLE, PROGRAMRESOURCEPROP_BLOCK_INDEX), "unnamed_block"));
  	}
  
  	// .block_array
  	{
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(binding));
  		const ResourceDefinition::Node::SharedPtr	buffer			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  		const ResourceDefinition::Node::SharedPtr	variable		(new ResourceDefinition::Variable(buffer, glu::TYPE_FLOAT_VEC4));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_BUFFER_VARIABLE, PROGRAMRESOURCEPROP_BLOCK_INDEX), "block_array"));
  	}
  }
  
  static void generateBufferVariableMatrixCaseBlocks (Context& context, tcu::TestCaseGroup* const targetGroup, glu::GLSLVersion glslVersion, void (*blockContentGenerator)(Context&, const ResourceDefinition::Node::SharedPtr&, tcu::TestCaseGroup*, bool))
  {
  	static const struct
  	{
  		const char*			name;
  		const char*			description;
  		bool				namedBlock;
  		bool				extendedBasicTypeCases;
  		glu::MatrixOrder	order;
  	} children[] =
  	{
  		{ "named_block",				"Named uniform block",		true,	true,	glu::MATRIXORDER_LAST			},
  		{ "named_block_row_major",		"Named uniform block",		true,	false,	glu::MATRIXORDER_ROW_MAJOR		},
  		{ "named_block_col_major",		"Named uniform block",		true,	false,	glu::MATRIXORDER_COLUMN_MAJOR	},
  		{ "unnamed_block",				"Unnamed uniform block",	false,	false,	glu::MATRIXORDER_LAST			},
  		{ "unnamed_block_row_major",	"Unnamed uniform block",	false,	false,	glu::MATRIXORDER_ROW_MAJOR		},
  		{ "unnamed_block_col_major",	"Unnamed uniform block",	false,	false,	glu::MATRIXORDER_COLUMN_MAJOR	},
  	};
  
  	const ResourceDefinition::Node::SharedPtr	program			(new ResourceDefinition::Program());
  	const ResourceDefinition::Node::SharedPtr	shader			(new ResourceDefinition::Shader(program, glu::SHADERTYPE_COMPUTE, glslVersion));
  	const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(shader));
  	const ResourceDefinition::Node::SharedPtr	buffer			(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_BUFFER));
  
  	for (int childNdx = 0; childNdx < (int)DE_LENGTH_OF_ARRAY(children); ++childNdx)
  	{
  		ResourceDefinition::Node::SharedPtr	parentStructure	= buffer;
  		tcu::TestCaseGroup* const			blockGroup		= new TestCaseGroup(context, children[childNdx].name, children[childNdx].description);
  
  		targetGroup->addChild(blockGroup);
  
  		if (children[childNdx].order != glu::MATRIXORDER_LAST)
  		{
  			glu::Layout layout;
  			layout.matrixOrder = children[childNdx].order;
  			parentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::LayoutQualifier(parentStructure, layout));
  		}
  
  		parentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::InterfaceBlock(parentStructure, children[childNdx].namedBlock));
  
  		blockContentGenerator(context, parentStructure, blockGroup, children[childNdx].extendedBasicTypeCases);
  	}
  }
  
  static void generateBufferVariableMatrixVariableBasicTypeCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, ProgramResourcePropFlags targetProp)
  {
  	// all matrix types and some non-matrix
  
  	static const glu::DataType variableTypes[] =
  	{
  		glu::TYPE_FLOAT,
  		glu::TYPE_INT_VEC3,
  		glu::TYPE_FLOAT_MAT2,
  		glu::TYPE_FLOAT_MAT2X3,
  		glu::TYPE_FLOAT_MAT2X4,
  		glu::TYPE_FLOAT_MAT3X2,
  		glu::TYPE_FLOAT_MAT3,
  		glu::TYPE_FLOAT_MAT3X4,
  		glu::TYPE_FLOAT_MAT4X2,
  		glu::TYPE_FLOAT_MAT4X3,
  		glu::TYPE_FLOAT_MAT4,
  	};
  
  	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(variableTypes); ++ndx)
  	{
  		const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, variableTypes[ndx]));
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_BUFFER_VARIABLE, targetProp)));
  	}
  }
  
  static void generateBufferVariableMatrixVariableCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, ProgramResourcePropFlags targetProp)
  {
  	// Basic aggregates
  	generateBufferBackedVariableAggregateTypeCases(context, parentStructure, targetGroup, PROGRAMINTERFACE_BUFFER_VARIABLE, targetProp, glu::TYPE_FLOAT_MAT3X2, "", 2);
  
  	// Unsized array
  	{
  		const ResourceDefinition::Node::SharedPtr	unsized		(new ResourceDefinition::ArrayElement(parentStructure, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  		const ResourceDefinition::Node::SharedPtr	variable	(new ResourceDefinition::Variable(unsized, glu::TYPE_FLOAT_MAT3X2));
  
  		targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_BUFFER_VARIABLE, targetProp), "var_unsized_array"));
  	}
  }
  
  template <ProgramResourcePropFlags TargetProp>
  static void generateBufferVariableMatrixCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, bool extendedTypeCases)
  {
  	// .types
  	if (extendedTypeCases)
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "types", "Types");
  		targetGroup->addChild(blockGroup);
  		generateBufferVariableMatrixVariableBasicTypeCases(context, parentStructure, blockGroup, TargetProp);
  	}
  
  	// .no_qualifier
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "no_qualifier", "No qualifier");
  		targetGroup->addChild(blockGroup);
  		generateBufferVariableMatrixVariableCases(context, parentStructure, blockGroup, TargetProp);
  	}
  
  	// .column_major
  	{
  		const ResourceDefinition::Node::SharedPtr matrixOrder(new ResourceDefinition::LayoutQualifier(parentStructure, glu::Layout(-1, -1, -1, glu::FORMATLAYOUT_LAST, glu::MATRIXORDER_COLUMN_MAJOR)));
  
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "column_major", "Column major qualifier");
  		targetGroup->addChild(blockGroup);
  		generateBufferVariableMatrixVariableCases(context, matrixOrder, blockGroup, TargetProp);
  	}
  
  	// .row_major
  	{
  		const ResourceDefinition::Node::SharedPtr matrixOrder(new ResourceDefinition::LayoutQualifier(parentStructure, glu::Layout(-1, -1, -1, glu::FORMATLAYOUT_LAST, glu::MATRIXORDER_ROW_MAJOR)));
  
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "row_major", "Row major qualifier");
  		targetGroup->addChild(blockGroup);
  		generateBufferVariableMatrixVariableCases(context, matrixOrder, blockGroup, TargetProp);
  	}
  }
  
  static void generateBufferVariableNameLengthCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup)
  {
  	// .sized
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "sized", "Sized target");
  		targetGroup->addChild(blockGroup);
  
  		generateBufferBackedVariableAggregateTypeCases(context, parentStructure, blockGroup, PROGRAMINTERFACE_BUFFER_VARIABLE, PROGRAMRESOURCEPROP_NAME_LENGTH, glu::TYPE_FLOAT, "", 3);
  	}
  
  	// .unsized
  	{
  		const ResourceDefinition::Node::SharedPtr	unsized		(new ResourceDefinition::ArrayElement(parentStructure, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  		tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "unsized", "Unsized target");
  		targetGroup->addChild(blockGroup);
  
  		generateBufferBackedVariableAggregateTypeCases(context, unsized, blockGroup, PROGRAMINTERFACE_BUFFER_VARIABLE, PROGRAMRESOURCEPROP_NAME_LENGTH, glu::TYPE_FLOAT, "", 2);
  	}
  }
  
  static void generateBufferVariableOffsetCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup)
  {
  	// .sized
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "sized", "Sized target");
  		targetGroup->addChild(blockGroup);
  
  		generateBufferBackedVariableAggregateTypeCases(context, parentStructure, blockGroup, PROGRAMINTERFACE_BUFFER_VARIABLE, PROGRAMRESOURCEPROP_OFFSET, glu::TYPE_FLOAT, "", 3);
  	}
  
  	// .unsized
  	{
  		const ResourceDefinition::Node::SharedPtr	unsized		(new ResourceDefinition::ArrayElement(parentStructure, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  		tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "unsized", "Unsized target");
  		targetGroup->addChild(blockGroup);
  
  		generateBufferBackedVariableAggregateTypeCases(context, unsized, blockGroup, PROGRAMINTERFACE_BUFFER_VARIABLE, PROGRAMRESOURCEPROP_OFFSET, glu::TYPE_FLOAT, "", 2);
  	}
  }
  
  static void generateBufferVariableReferencedByBlockContents (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, int expandLevel)
  {
  	DE_UNREF(expandLevel);
  
  	const ProgramResourceQueryTestTarget		queryTarget		(PROGRAMINTERFACE_BUFFER_VARIABLE, PROGRAMRESOURCEPROP_REFERENCED_BY_SHADER);
  	const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(parentStructure));
  	const ResourceDefinition::Node::SharedPtr	storage			(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_BUFFER));
  	const bool									singleShaderCase	= parentStructure->getType() == ResourceDefinition::Node::TYPE_SHADER;
  
  	// .named_block
  	{
  		const ResourceDefinition::Node::SharedPtr	buffer		(new ResourceDefinition::InterfaceBlock(storage, true));
  		tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "named_block", "Named block");
  
  		targetGroup->addChild(blockGroup);
  
  		generateBufferReferencedByShaderInterfaceBlockCases(context, buffer, blockGroup, queryTarget, singleShaderCase);
  	}
  
  	// .unnamed_block
  	{
  		const ResourceDefinition::Node::SharedPtr	buffer		(new ResourceDefinition::InterfaceBlock(storage, false));
  		tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "unnamed_block", "Unnamed block");
  
  		targetGroup->addChild(blockGroup);
  
  		generateBufferReferencedByShaderInterfaceBlockCases(context, buffer, blockGroup, queryTarget, false);
  	}
  
  	// .block_array
  	{
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(storage));
  		const ResourceDefinition::Node::SharedPtr	buffer			(new ResourceDefinition::InterfaceBlock(arrayElement, true));
  		tcu::TestCaseGroup* const					blockGroup	= new TestCaseGroup(context, "block_array", "Block array");
  
  		targetGroup->addChild(blockGroup);
  
  		generateBufferReferencedByShaderInterfaceBlockCases(context, buffer, blockGroup, queryTarget, false);
  	}
  }
  
  template <ProgramResourcePropFlags TargetProp>
  static void generateBufferVariableTopLevelCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup)
  {
  	// basic and aggregate types
  	generateBufferBackedVariableAggregateTypeCases(context, parentStructure, targetGroup, PROGRAMINTERFACE_BUFFER_VARIABLE, TargetProp, glu::TYPE_FLOAT_VEC4, "", 3);
  
  	// basic and aggregate types in an unsized array
  	{
  		const ResourceDefinition::Node::SharedPtr unsized(new ResourceDefinition::ArrayElement(parentStructure, ResourceDefinition::ArrayElement::UNSIZED_ARRAY));
  
  		generateBufferBackedVariableAggregateTypeCases(context, unsized, targetGroup, PROGRAMINTERFACE_BUFFER_VARIABLE, TargetProp, glu::TYPE_FLOAT_VEC4, "_unsized_array", 2);
  	}
  }
  
  static void generateBufferVariableTypeBasicTypeCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, int expandLevel)
  {
  	static const struct
  	{
  		int				level;
  		glu::DataType	dataType;
  	} variableTypes[] =
  	{
  		{ 0,	glu::TYPE_FLOAT			},
  		{ 1,	glu::TYPE_INT			},
  		{ 1,	glu::TYPE_UINT			},
  		{ 1,	glu::TYPE_BOOL			},
  
  		{ 3,	glu::TYPE_FLOAT_VEC2	},
  		{ 1,	glu::TYPE_FLOAT_VEC3	},
  		{ 1,	glu::TYPE_FLOAT_VEC4	},
  
  		{ 3,	glu::TYPE_INT_VEC2		},
  		{ 2,	glu::TYPE_INT_VEC3		},
  		{ 3,	glu::TYPE_INT_VEC4		},
  
  		{ 3,	glu::TYPE_UINT_VEC2		},
  		{ 2,	glu::TYPE_UINT_VEC3		},
  		{ 3,	glu::TYPE_UINT_VEC4		},
  
  		{ 3,	glu::TYPE_BOOL_VEC2		},
  		{ 2,	glu::TYPE_BOOL_VEC3		},
  		{ 3,	glu::TYPE_BOOL_VEC4		},
  
  		{ 2,	glu::TYPE_FLOAT_MAT2	},
  		{ 3,	glu::TYPE_FLOAT_MAT2X3	},
  		{ 3,	glu::TYPE_FLOAT_MAT2X4	},
  		{ 2,	glu::TYPE_FLOAT_MAT3X2	},
  		{ 2,	glu::TYPE_FLOAT_MAT3	},
  		{ 3,	glu::TYPE_FLOAT_MAT3X4	},
  		{ 2,	glu::TYPE_FLOAT_MAT4X2	},
  		{ 3,	glu::TYPE_FLOAT_MAT4X3	},
  		{ 2,	glu::TYPE_FLOAT_MAT4	},
  	};
  
  	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(variableTypes); ++ndx)
  	{
  		if (variableTypes[ndx].level <= expandLevel)
  		{
  			const ResourceDefinition::Node::SharedPtr variable(new ResourceDefinition::Variable(parentStructure, variableTypes[ndx].dataType));
  			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_BUFFER_VARIABLE, PROGRAMRESOURCEPROP_TYPE)));
  		}
  	}
  }
  
  static void generateBufferVariableTypeCases (Context& context, const ResourceDefinition::Node::SharedPtr& parentStructure, tcu::TestCaseGroup* targetGroup, int depth = 3)
  {
  	// .basic_type
  	if (depth > 0)
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(context, "basic_type", "Basic type");
  		targetGroup->addChild(blockGroup);
  		generateBufferVariableTypeBasicTypeCases(context, parentStructure, blockGroup, depth);
  	}
  	else
  	{
  		// flatten bottom-level
  		generateBufferVariableTypeBasicTypeCases(context, parentStructure, targetGroup, depth);
  	}
  
  	// .array
  	if (depth > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr	arrayElement	(new ResourceDefinition::ArrayElement(parentStructure));
  		tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "array", "Arrays");
  
  		targetGroup->addChild(blockGroup);
  		generateBufferVariableTypeCases(context, arrayElement, blockGroup, depth-1);
  	}
  
  	// .struct
  	if (depth > 0)
  	{
  		const ResourceDefinition::Node::SharedPtr	structMember	(new ResourceDefinition::StructMember(parentStructure));
  		tcu::TestCaseGroup* const					blockGroup		= new TestCaseGroup(context, "struct", "Structs");
  
  		targetGroup->addChild(blockGroup);
  		generateBufferVariableTypeCases(context, structMember, blockGroup, depth-1);
  	}
  }
  
  static void generateBufferVariableTypeBlock (Context& context, tcu::TestCaseGroup* targetGroup, glu::GLSLVersion glslVersion)
  {
  	const ResourceDefinition::Node::SharedPtr	program			(new ResourceDefinition::Program());
  	const ResourceDefinition::Node::SharedPtr	shader			(new ResourceDefinition::Shader(program, glu::SHADERTYPE_COMPUTE, glslVersion));
  	const ResourceDefinition::Node::SharedPtr	defaultBlock	(new ResourceDefinition::DefaultBlock(shader));
  	const ResourceDefinition::Node::SharedPtr	buffer			(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_BUFFER));
  	const ResourceDefinition::Node::SharedPtr	block			(new ResourceDefinition::InterfaceBlock(buffer, true));
  
  	generateBufferVariableTypeCases(context, block, targetGroup);
  }
  
  static void generateBufferVariableRandomCase (Context& context, tcu::TestCaseGroup* const targetGroup, glu::GLSLVersion glslVersion, int index, bool onlyExtensionStages)
  {
  	de::Random									rnd					(index * 0x12345);
  	const ResourceDefinition::Node::SharedPtr	shader				= generateRandomShaderSet(rnd, glslVersion, onlyExtensionStages);
  	const glu::DataType							type				= generateRandomDataType(rnd, true);
  	const glu::Layout							layout				= generateRandomVariableLayout(rnd, type, true);
  	const bool									namedBlock			= rnd.getBool();
  	const ResourceDefinition::Node::SharedPtr	defaultBlock		(new ResourceDefinition::DefaultBlock(shader));
  	const ResourceDefinition::Node::SharedPtr	buffer				(new ResourceDefinition::StorageQualifier(defaultBlock, glu::STORAGE_BUFFER));
  	ResourceDefinition::Node::SharedPtr			currentStructure	(new ResourceDefinition::LayoutQualifier(buffer, generateRandomBufferBlockLayout(rnd)));
  
  	if (namedBlock && rnd.getBool())
  		currentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::ArrayElement(currentStructure));
  	currentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::InterfaceBlock(currentStructure, namedBlock));
  
  	currentStructure = ResourceDefinition::Node::SharedPtr(new ResourceDefinition::LayoutQualifier(currentStructure, layout));
  	currentStructure = generateRandomVariableDefinition(rnd, currentStructure, type, layout, true);
  
  	targetGroup->addChild(new ResourceTestCase(context, currentStructure, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_BUFFER_VARIABLE, PROGRAMRESOURCEPROP_BUFFER_VARIABLE_MASK), de::toString(index).c_str()));
  }
  
  static void generateBufferVariableRandomCases (Context& context, tcu::TestCaseGroup* const targetGroup, glu::GLSLVersion glslVersion)
  {
  	const int numBasicCases		= 40;
  	const int numTessGeoCases	= 40;
  
  	for (int ndx = 0; ndx < numBasicCases; ++ndx)
  		generateBufferVariableRandomCase(context, targetGroup, glslVersion, ndx, false);
  	for (int ndx = 0; ndx < numTessGeoCases; ++ndx)
  		generateBufferVariableRandomCase(context, targetGroup, glslVersion, numBasicCases + ndx, true);
  }
  
  class BufferVariableTestGroup : public TestCaseGroup
  {
  public:
  			BufferVariableTestGroup	(Context& context);
  	void	init								(void);
  };
  
  BufferVariableTestGroup::BufferVariableTestGroup (Context& context)
  	: TestCaseGroup(context, "buffer_variable", "Buffer variable")
  {
  }
  
  void BufferVariableTestGroup::init (void)
  {
  	const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType());
  
  	// .resource_list
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "resource_list", "Resource list");
  		addChild(blockGroup);
  		generateBufferVariableBufferCaseBlocks(m_context, blockGroup, glslVersion, generateBufferVariableResourceListBlockContentsProxy);
  	}
  
  	// .array_size
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "array_size", "Array size");
  		addChild(blockGroup);
  		generateBufferVariableBufferCaseBlocks(m_context, blockGroup, glslVersion, generateBufferVariableArrayCases<PROGRAMRESOURCEPROP_ARRAY_SIZE>);
  	}
  
  	// .array_stride
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "array_stride", "Array stride");
  		addChild(blockGroup);
  		generateBufferVariableBufferCaseBlocks(m_context, blockGroup, glslVersion, generateBufferVariableArrayCases<PROGRAMRESOURCEPROP_ARRAY_STRIDE>);
  	}
  
  	// .block_index
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "block_index", "Block index");
  		addChild(blockGroup);
  		generateBufferVariableBlockIndexCases(m_context, glslVersion, blockGroup);
  	}
  
  	// .is_row_major
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "is_row_major", "Is row major");
  		addChild(blockGroup);
  		generateBufferVariableMatrixCaseBlocks(m_context, blockGroup, glslVersion, generateBufferVariableMatrixCases<PROGRAMRESOURCEPROP_MATRIX_ROW_MAJOR>);
  	}
  
  	// .matrix_stride
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "matrix_stride", "Matrix stride");
  		addChild(blockGroup);
  		generateBufferVariableMatrixCaseBlocks(m_context, blockGroup, glslVersion, generateBufferVariableMatrixCases<PROGRAMRESOURCEPROP_MATRIX_STRIDE>);
  	}
  
  	// .name_length
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "name_length", "Name length");
  		addChild(blockGroup);
  		generateBufferVariableBufferCaseBlocks(m_context, blockGroup, glslVersion, generateBufferVariableNameLengthCases);
  	}
  
  	// .offset
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "offset", "Offset");
  		addChild(blockGroup);
  		generateBufferVariableBufferCaseBlocks(m_context, blockGroup, glslVersion, generateBufferVariableOffsetCases);
  	}
  
  	// .referenced_by
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "referenced_by", "Referenced by");
  		addChild(blockGroup);
  		generateReferencedByShaderCaseBlocks(m_context, blockGroup, glslVersion, generateBufferVariableReferencedByBlockContents);
  	}
  
  	// .top_level_array_size
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "top_level_array_size", "Top-level array size");
  		addChild(blockGroup);
  		generateBufferVariableBufferCaseBlocks(m_context, blockGroup, glslVersion, generateBufferVariableTopLevelCases<PROGRAMRESOURCEPROP_TOP_LEVEL_ARRAY_SIZE>);
  	}
  
  	// .top_level_array_stride
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "top_level_array_stride", "Top-level array stride");
  		addChild(blockGroup);
  		generateBufferVariableBufferCaseBlocks(m_context, blockGroup, glslVersion, generateBufferVariableTopLevelCases<PROGRAMRESOURCEPROP_TOP_LEVEL_ARRAY_STRIDE>);
  	}
  
  	// .type
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "type", "Type");
  		addChild(blockGroup);
  		generateBufferVariableTypeBlock(m_context, blockGroup, glslVersion);
  	}
  
  	// .random
  	{
  		tcu::TestCaseGroup* const blockGroup = new TestCaseGroup(m_context, "random", "Random");
  		addChild(blockGroup);
  		generateBufferVariableRandomCases(m_context, blockGroup, glslVersion);
  	}
  }
  
  } // anonymous
  
  ProgramInterfaceQueryTests::ProgramInterfaceQueryTests (Context& context, bool is_GL45)
  	: TestCaseGroup(context, "program_interface_query", "Program interface query tests")
  	, m_isGL45 (is_GL45)
  {
  }
  
  ProgramInterfaceQueryTests::~ProgramInterfaceQueryTests (void)
  {
  }
  
  void ProgramInterfaceQueryTests::init (void)
  {
  	// Misc queries
  
  	// .buffer_limited_query
  	{
  		tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "buffer_limited_query", "Queries limited by the buffer size");
  
  		addChild(group);
  
  		group->addChild(new ResourceNameBufferLimitCase(m_context, "resource_name_query", "Test GetProgramResourceName with too small a buffer"));
  		group->addChild(new ResourceQueryBufferLimitCase(m_context, "resource_query", "Test GetProgramResourceiv with too small a buffer"));
  	}
  
  	// Interfaces
  
  	// .uniform
  	addChild(new UniformInterfaceTestGroup(m_context));
  
  	// .uniform_block
  	addChild(new BufferBackedBlockInterfaceTestGroup(m_context, glu::STORAGE_UNIFORM));
  
  	// .atomic_counter_buffer
  	addChild(new AtomicCounterTestGroup(m_context));
  
  	// .program_input
  	addChild(new ProgramInputTestGroup(m_context, m_isGL45));
  
  	// .program_output
  	addChild(new ProgramOutputTestGroup(m_context, m_isGL45));
  
  	// .transform_feedback_varying
  	addChild(new TransformFeedbackVaryingTestGroup(m_context));
  
  	// .buffer_variable
  	addChild(new BufferVariableTestGroup(m_context));
  
  	// .shader_storage_block
  	addChild(new BufferBackedBlockInterfaceTestGroup(m_context, glu::STORAGE_BUFFER));
  }
  
  } // Functional
  } // gles31
  } // deqp
  