Home | History | Annotate | Download | only in glshared
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL (ES) Module
      3  * -----------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Random shader test case.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "glsRandomShaderCase.hpp"
     25 
     26 #include "gluShaderProgram.hpp"
     27 #include "gluPixelTransfer.hpp"
     28 #include "gluTextureUtil.hpp"
     29 #include "gluStrUtil.hpp"
     30 
     31 #include "tcuImageCompare.hpp"
     32 #include "tcuTestLog.hpp"
     33 
     34 #include "deRandom.hpp"
     35 #include "deStringUtil.hpp"
     36 
     37 #include "rsgProgramGenerator.hpp"
     38 #include "rsgProgramExecutor.hpp"
     39 #include "rsgUtils.hpp"
     40 
     41 #include "tcuTextureUtil.hpp"
     42 #include "tcuRenderTarget.hpp"
     43 
     44 #include "glw.h"
     45 #include "glwFunctions.hpp"
     46 
     47 using std::vector;
     48 using std::string;
     49 using std::pair;
     50 using std::map;
     51 
     52 namespace deqp
     53 {
     54 namespace gls
     55 {
     56 
     57 enum
     58 {
     59 	VIEWPORT_WIDTH			= 64,
     60 	VIEWPORT_HEIGHT			= 64,
     61 
     62 	TEXTURE_2D_WIDTH		= 64,
     63 	TEXTURE_2D_HEIGHT		= 64,
     64 	TEXTURE_2D_FORMAT		= GL_RGBA,
     65 	TEXTURE_2D_DATA_TYPE	= GL_UNSIGNED_BYTE,
     66 
     67 	TEXTURE_CUBE_SIZE		= 16,
     68 	TEXTURE_CUBE_FORMAT		= GL_RGBA,
     69 	TEXTURE_CUBE_DATA_TYPE	= GL_UNSIGNED_BYTE,
     70 
     71 	TEXTURE_WRAP_S			= GL_CLAMP_TO_EDGE,
     72 	TEXTURE_WRAP_T			= GL_CLAMP_TO_EDGE,
     73 
     74 	TEXTURE_MIN_FILTER		= GL_LINEAR,
     75 	TEXTURE_MAG_FILTER		= GL_LINEAR
     76 };
     77 
     78 VertexArray::VertexArray (const rsg::ShaderInput* input, int numVertices)
     79 	: m_input			(input)
     80 	, m_vertices		(input->getVariable()->getType().getNumElements() * numVertices)
     81 {
     82 }
     83 
     84 TextureManager::TextureManager (void)
     85 {
     86 }
     87 
     88 TextureManager::~TextureManager (void)
     89 {
     90 }
     91 
     92 void TextureManager::bindTexture (int unit, const glu::Texture2D* tex2D)
     93 {
     94 	m_tex2D[unit] = tex2D;
     95 }
     96 
     97 void TextureManager::bindTexture (int unit, const glu::TextureCube* texCube)
     98 {
     99 	m_texCube[unit] = texCube;
    100 }
    101 
    102 inline vector<pair<int, const glu::Texture2D*> > TextureManager::getBindings2D (void) const
    103 {
    104 	vector<pair<int, const glu::Texture2D*> > bindings;
    105 	for (map<int, const glu::Texture2D*>::const_iterator i = m_tex2D.begin(); i != m_tex2D.end(); i++)
    106 		bindings.push_back(*i);
    107 	return bindings;
    108 }
    109 
    110 inline vector<pair<int, const glu::TextureCube*> > TextureManager::getBindingsCube (void) const
    111 {
    112 	vector<pair<int, const glu::TextureCube*> > bindings;
    113 	for (map<int, const glu::TextureCube*>::const_iterator i = m_texCube.begin(); i != m_texCube.end(); i++)
    114 		bindings.push_back(*i);
    115 	return bindings;
    116 }
    117 
    118 RandomShaderCase::RandomShaderCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, const rsg::ProgramParameters& params)
    119 	: tcu::TestCase		(testCtx, name, description)
    120 	, m_renderCtx		(renderCtx)
    121 	, m_parameters		(params)
    122 	, m_gridWidth		(1)
    123 	, m_gridHeight		(1)
    124 	, m_vertexShader	(rsg::Shader::TYPE_VERTEX)
    125 	, m_fragmentShader	(rsg::Shader::TYPE_FRAGMENT)
    126 	, m_tex2D			(DE_NULL)
    127 	, m_texCube			(DE_NULL)
    128 {
    129 }
    130 
    131 RandomShaderCase::~RandomShaderCase (void)
    132 {
    133 	delete m_tex2D;
    134 	delete m_texCube;
    135 }
    136 
    137 void RandomShaderCase::init (void)
    138 {
    139 	// Generate shaders
    140 	rsg::ProgramGenerator programGenerator;
    141 	programGenerator.generate(m_parameters, m_vertexShader, m_fragmentShader);
    142 
    143 	checkShaderLimits(m_vertexShader);
    144 	checkShaderLimits(m_fragmentShader);
    145 	checkProgramLimits(m_vertexShader, m_fragmentShader);
    146 
    147 	// Compute uniform values
    148 	std::vector<const rsg::ShaderInput*>	unifiedUniforms;
    149 	de::Random								rnd(m_parameters.seed);
    150 	rsg::computeUnifiedUniforms(m_vertexShader, m_fragmentShader, unifiedUniforms);
    151 	rsg::computeUniformValues(rnd, m_uniforms, unifiedUniforms);
    152 
    153 	// Generate vertices
    154 	const vector<rsg::ShaderInput*>&	inputs		= m_vertexShader.getInputs();
    155 	int									numVertices	= (m_gridWidth+1)*(m_gridHeight+1);
    156 
    157 	for (vector<rsg::ShaderInput*>::const_iterator i = inputs.begin(); i != inputs.end(); i++)
    158 	{
    159 		const rsg::ShaderInput*			input			= *i;
    160 		rsg::ConstValueRangeAccess		valueRange		= input->getValueRange();
    161 		int								numComponents	= input->getVariable()->getType().getNumElements();
    162 		VertexArray						vtxArray(input, numVertices);
    163 		bool							isPosition		= string(input->getVariable()->getName()) == "dEQP_Position";
    164 
    165 		TCU_CHECK(input->getVariable()->getType().getBaseType() == rsg::VariableType::TYPE_FLOAT);
    166 
    167 		for (int vtxNdx = 0; vtxNdx < numVertices; vtxNdx++)
    168 		{
    169 			int		y	= vtxNdx / (m_gridWidth+1);
    170 			int		x	= vtxNdx - y*(m_gridWidth+1);
    171 			float	xf	= (float)x / (float)m_gridWidth;
    172 			float	yf	= (float)y / (float)m_gridHeight;
    173 			float*	dst	= &vtxArray.getVertices()[vtxNdx*numComponents];
    174 
    175 			if (isPosition)
    176 			{
    177 				// Position attribute gets special interpolation handling.
    178 				DE_ASSERT(numComponents == 4);
    179 				dst[0] = -1.0f + xf *  2.0f;
    180 				dst[1] =  1.0f + yf * -2.0f;
    181 				dst[2] = 0.0f;
    182 				dst[3] = 1.0f;
    183 			}
    184 			else
    185 			{
    186 				for (int compNdx = 0; compNdx < numComponents; compNdx++)
    187 				{
    188 					float	minVal	= valueRange.getMin().component(compNdx).asFloat();
    189 					float	maxVal	= valueRange.getMax().component(compNdx).asFloat();
    190 					float	xd, yd;
    191 
    192 					rsg::getVertexInterpolationCoords(xd, yd, xf, yf, compNdx);
    193 
    194 					float	f		= (xd+yd) / 2.0f;
    195 
    196 					dst[compNdx] = minVal + f * (maxVal-minVal);
    197 				}
    198 			}
    199 		}
    200 
    201 		m_vertexArrays.push_back(vtxArray);
    202 	}
    203 
    204 	// Generate indices
    205 	int numQuads	= m_gridWidth*m_gridHeight;
    206 	int numIndices	= numQuads*6;
    207 	m_indices.resize(numIndices);
    208 	for (int quadNdx = 0; quadNdx < numQuads; quadNdx++)
    209 	{
    210 		int	quadY	= quadNdx / (m_gridWidth);
    211 		int quadX	= quadNdx - quadY*m_gridWidth;
    212 
    213 		m_indices[quadNdx*6+0] = quadX + quadY*(m_gridWidth+1);
    214 		m_indices[quadNdx*6+1] = quadX + (quadY+1)*(m_gridWidth+1);
    215 		m_indices[quadNdx*6+2] = quadX + quadY*(m_gridWidth+1) + 1;
    216 		m_indices[quadNdx*6+3] = m_indices[quadNdx*6+2];
    217 		m_indices[quadNdx*6+4] = m_indices[quadNdx*6+1];
    218 		m_indices[quadNdx*6+5] = quadX + (quadY+1)*(m_gridWidth+1) + 1;
    219 	}
    220 
    221 	// Create textures.
    222 	for (vector<rsg::VariableValue>::const_iterator uniformIter = m_uniforms.begin(); uniformIter != m_uniforms.end(); uniformIter++)
    223 	{
    224 		const rsg::VariableType& type = uniformIter->getVariable()->getType();
    225 
    226 		if (!type.isSampler())
    227 			continue;
    228 
    229 		int unitNdx = uniformIter->getValue().asInt(0);
    230 
    231 		if (type == rsg::VariableType(rsg::VariableType::TYPE_SAMPLER_2D, 1))
    232 			m_texManager.bindTexture(unitNdx, getTex2D());
    233 		else if (type == rsg::VariableType(rsg::VariableType::TYPE_SAMPLER_CUBE, 1))
    234 			m_texManager.bindTexture(unitNdx, getTexCube());
    235 		else
    236 			DE_ASSERT(DE_FALSE);
    237 	}
    238 }
    239 
    240 static int getNumSamplerUniforms (const std::vector<rsg::ShaderInput*>& uniforms)
    241 {
    242 	int numSamplers = 0;
    243 
    244 	for (std::vector<rsg::ShaderInput*>::const_iterator it = uniforms.begin(); it != uniforms.end(); ++it)
    245 	{
    246 		if ((*it)->getVariable()->getType().isSampler())
    247 			++numSamplers;
    248 	}
    249 
    250 	return numSamplers;
    251 }
    252 
    253 void RandomShaderCase::checkShaderLimits (const rsg::Shader& shader) const
    254 {
    255 	const int numRequiredSamplers = getNumSamplerUniforms(shader.getUniforms());
    256 
    257 	if (numRequiredSamplers > 0)
    258 	{
    259 		const GLenum	pname			= (shader.getType() == rsg::Shader::TYPE_VERTEX) ? (GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS) : (GL_MAX_TEXTURE_IMAGE_UNITS);
    260 		int				numSupported	= -1;
    261 		GLenum			error;
    262 
    263 		m_renderCtx.getFunctions().getIntegerv(pname, &numSupported);
    264 		error = m_renderCtx.getFunctions().getError();
    265 
    266 		if (error != GL_NO_ERROR)
    267 			throw tcu::TestError("Limit query failed: " + de::toString(glu::getErrorStr(error)));
    268 
    269 		if (numSupported < numRequiredSamplers)
    270 			throw tcu::NotSupportedError("Shader requires " + de::toString(numRequiredSamplers) + " sampler(s). Implementation supports " + de::toString(numSupported));
    271 	}
    272 }
    273 
    274 void RandomShaderCase::checkProgramLimits (const rsg::Shader& vtxShader, const rsg::Shader& frgShader) const
    275 {
    276 	const int numRequiredCombinedSamplers = getNumSamplerUniforms(vtxShader.getUniforms()) + getNumSamplerUniforms(frgShader.getUniforms());
    277 
    278 	if (numRequiredCombinedSamplers > 0)
    279 	{
    280 		int				numSupported	= -1;
    281 		GLenum			error;
    282 
    283 		m_renderCtx.getFunctions().getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numSupported);
    284 		error = m_renderCtx.getFunctions().getError();
    285 
    286 		if (error != GL_NO_ERROR)
    287 			throw tcu::TestError("Limit query failed: " + de::toString(glu::getErrorStr(error)));
    288 
    289 		if (numSupported < numRequiredCombinedSamplers)
    290 			throw tcu::NotSupportedError("Program requires " + de::toString(numRequiredCombinedSamplers) + " sampler(s). Implementation supports " + de::toString(numSupported));
    291 	}
    292 }
    293 
    294 const glu::Texture2D* RandomShaderCase::getTex2D (void)
    295 {
    296 	if (!m_tex2D)
    297 	{
    298 		m_tex2D = new glu::Texture2D(m_renderCtx, TEXTURE_2D_FORMAT, TEXTURE_2D_DATA_TYPE, TEXTURE_2D_WIDTH, TEXTURE_2D_HEIGHT);
    299 
    300 		m_tex2D->getRefTexture().allocLevel(0);
    301 		tcu::fillWithComponentGradients(m_tex2D->getRefTexture().getLevel(0), tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f));
    302 		m_tex2D->upload();
    303 
    304 		// Setup parameters.
    305 		glBindTexture(GL_TEXTURE_2D, m_tex2D->getGLTexture());
    306 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		TEXTURE_WRAP_S);
    307 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		TEXTURE_WRAP_T);
    308 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	TEXTURE_MIN_FILTER);
    309 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	TEXTURE_MAG_FILTER);
    310 
    311 		GLU_CHECK();
    312 	}
    313 
    314 	return m_tex2D;
    315 }
    316 
    317 const glu::TextureCube* RandomShaderCase::getTexCube (void)
    318 {
    319 	if (!m_texCube)
    320 	{
    321 		m_texCube = new glu::TextureCube(m_renderCtx, TEXTURE_CUBE_FORMAT, TEXTURE_CUBE_DATA_TYPE, TEXTURE_CUBE_SIZE);
    322 
    323 		static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] =
    324 		{
    325 			{ tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
    326 			{ tcu::Vec4( 0.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
    327 			{ tcu::Vec4(-1.0f,  0.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
    328 			{ tcu::Vec4(-1.0f, -1.0f,  0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
    329 			{ tcu::Vec4(-1.0f, -1.0f, -1.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
    330 			{ tcu::Vec4( 0.0f,  0.0f,  0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }  // positive z
    331 		};
    332 
    333 		// Fill level 0.
    334 		for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    335 		{
    336 			m_texCube->getRefTexture().allocLevel((tcu::CubeFace)face, 0);
    337 			tcu::fillWithComponentGradients(m_texCube->getRefTexture().getLevelFace(0, (tcu::CubeFace)face), gradients[face][0], gradients[face][1]);
    338 		}
    339 
    340 		m_texCube->upload();
    341 
    342 		// Setup parameters.
    343 		glBindTexture(GL_TEXTURE_CUBE_MAP, m_texCube->getGLTexture());
    344 		glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		TEXTURE_WRAP_S);
    345 		glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		TEXTURE_WRAP_T);
    346 		glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	TEXTURE_MIN_FILTER);
    347 		glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	TEXTURE_MAG_FILTER);
    348 
    349 		GLU_CHECK();
    350 	}
    351 
    352 	return m_texCube;
    353 }
    354 
    355 void RandomShaderCase::deinit (void)
    356 {
    357 	delete m_tex2D;
    358 	delete m_texCube;
    359 
    360 	m_tex2D		= DE_NULL;
    361 	m_texCube	= DE_NULL;
    362 
    363 	// Free up memory
    364 	m_vertexArrays.clear();
    365 	m_indices.clear();
    366 }
    367 
    368 namespace
    369 {
    370 
    371 void setUniformValue (int location, rsg::ConstValueAccess value)
    372 {
    373 	DE_STATIC_ASSERT(sizeof(rsg::Scalar) == sizeof(float));
    374 	DE_STATIC_ASSERT(sizeof(rsg::Scalar) == sizeof(int));
    375 
    376 	switch (value.getType().getBaseType())
    377 	{
    378 		case rsg::VariableType::TYPE_FLOAT:
    379 			switch (value.getType().getNumElements())
    380 			{
    381 				case 1:		glUniform1fv(location, 1, (float*)value.value().getValuePtr());		break;
    382 				case 2:		glUniform2fv(location, 1, (float*)value.value().getValuePtr());		break;
    383 				case 3:		glUniform3fv(location, 1, (float*)value.value().getValuePtr());		break;
    384 				case 4:		glUniform4fv(location, 1, (float*)value.value().getValuePtr());		break;
    385 				default:	TCU_FAIL("Unsupported type");										break;
    386 			}
    387 			break;
    388 
    389 		case rsg::VariableType::TYPE_INT:
    390 		case rsg::VariableType::TYPE_BOOL:
    391 		case rsg::VariableType::TYPE_SAMPLER_2D:
    392 		case rsg::VariableType::TYPE_SAMPLER_CUBE:
    393 			switch (value.getType().getNumElements())
    394 			{
    395 				case 1:		glUniform1iv(location, 1, (int*)value.value().getValuePtr());		break;
    396 				case 2:		glUniform2iv(location, 1, (int*)value.value().getValuePtr());		break;
    397 				case 3:		glUniform3iv(location, 1, (int*)value.value().getValuePtr());		break;
    398 				case 4:		glUniform4iv(location, 1, (int*)value.value().getValuePtr());		break;
    399 				default:	TCU_FAIL("Unsupported type");										break;
    400 			}
    401 			break;
    402 
    403 		default:
    404 			TCU_FAIL("Unsupported type");
    405 	}
    406 }
    407 
    408 tcu::MessageBuilder& operator<< (tcu::MessageBuilder& message, rsg::ConstValueAccess value)
    409 {
    410 	const char*	scalarType	= DE_NULL;
    411 	const char* vecType		= DE_NULL;
    412 
    413 	switch (value.getType().getBaseType())
    414 	{
    415 		case rsg::VariableType::TYPE_FLOAT:			scalarType = "float";	vecType	= "vec";	break;
    416 		case rsg::VariableType::TYPE_INT:			scalarType = "int";		vecType = "ivec";	break;
    417 		case rsg::VariableType::TYPE_BOOL:			scalarType = "bool";	vecType = "bvec";	break;
    418 		case rsg::VariableType::TYPE_SAMPLER_2D:	scalarType = "sampler2D";					break;
    419 		case rsg::VariableType::TYPE_SAMPLER_CUBE:	scalarType = "samplerCube";					break;
    420 		default:
    421 			TCU_FAIL("Unsupported type.");
    422 	}
    423 
    424 	int numElements = value.getType().getNumElements();
    425 	if (numElements == 1)
    426 		message << scalarType << "(";
    427 	else
    428 		message << vecType << numElements << "(";
    429 
    430 	for (int elementNdx = 0; elementNdx < numElements; elementNdx++)
    431 	{
    432 		if (elementNdx > 0)
    433 			message << ", ";
    434 
    435 		switch (value.getType().getBaseType())
    436 		{
    437 			case rsg::VariableType::TYPE_FLOAT:			message << value.component(elementNdx).asFloat();						break;
    438 			case rsg::VariableType::TYPE_INT:			message << value.component(elementNdx).asInt();							break;
    439 			case rsg::VariableType::TYPE_BOOL:			message << (value.component(elementNdx).asBool() ? "true" : "false");	break;
    440 			case rsg::VariableType::TYPE_SAMPLER_2D:	message << value.component(elementNdx).asInt();							break;
    441 			case rsg::VariableType::TYPE_SAMPLER_CUBE:	message << value.component(elementNdx).asInt();							break;
    442 			default:
    443 				DE_ASSERT(DE_FALSE);
    444 		}
    445 	}
    446 
    447 	message << ")";
    448 
    449 	return message;
    450 }
    451 
    452 tcu::MessageBuilder& operator<< (tcu::MessageBuilder& message, rsg::ConstValueRangeAccess valueRange)
    453 {
    454 	return message << valueRange.getMin() << " -> " << valueRange.getMax();
    455 }
    456 
    457 } // anonymous
    458 
    459 RandomShaderCase::IterateResult RandomShaderCase::iterate (void)
    460 {
    461 	tcu::TestLog& log = m_testCtx.getLog();
    462 
    463 	// Compile program
    464 	glu::ShaderProgram program(m_renderCtx, glu::makeVtxFragSources(m_vertexShader.getSource(), m_fragmentShader.getSource()));
    465 	log << program;
    466 
    467 	if (!program.isOk())
    468 	{
    469 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to compile shader");
    470 		return STOP;
    471 	}
    472 
    473 	// Compute random viewport
    474 	de::Random				rnd				(m_parameters.seed);
    475 	int						viewportWidth	= de::min<int>(VIEWPORT_WIDTH,	m_renderCtx.getRenderTarget().getWidth());
    476 	int						viewportHeight	= de::min<int>(VIEWPORT_HEIGHT,	m_renderCtx.getRenderTarget().getHeight());
    477 	int						viewportX		= rnd.getInt(0, m_renderCtx.getRenderTarget().getWidth()	- viewportWidth);
    478 	int						viewportY		= rnd.getInt(0, m_renderCtx.getRenderTarget().getHeight()	- viewportHeight);
    479 	bool					hasAlpha		= m_renderCtx.getRenderTarget().getPixelFormat().alphaBits > 0;
    480 	tcu::TextureLevel		rendered		(tcu::TextureFormat(hasAlpha ? tcu::TextureFormat::RGBA : tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), viewportWidth, viewportHeight);
    481 	tcu::TextureLevel		reference		(tcu::TextureFormat(hasAlpha ? tcu::TextureFormat::RGBA : tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), viewportWidth, viewportHeight);
    482 
    483 	// Reference program executor.
    484 	rsg::ProgramExecutor	executor		(reference.getAccess(), m_gridWidth, m_gridHeight);
    485 
    486 	GLU_CHECK_CALL(glUseProgram(program.getProgram()));
    487 
    488 	// Set up attributes
    489 	for (vector<VertexArray>::const_iterator attribIter = m_vertexArrays.begin(); attribIter != m_vertexArrays.end(); attribIter++)
    490 	{
    491 		GLint location = glGetAttribLocation(program.getProgram(), attribIter->getName());
    492 
    493 		// Print to log.
    494 		log << tcu::TestLog::Message << "attribute[" << location << "]: " << attribIter->getName() << " = " << attribIter->getValueRange() << tcu::TestLog::EndMessage;
    495 
    496 		if (location >= 0)
    497 		{
    498 			glVertexAttribPointer(location, attribIter->getNumComponents(), GL_FLOAT, GL_FALSE, 0, &attribIter->getVertices()[0]);
    499 			glEnableVertexAttribArray(location);
    500 		}
    501 	}
    502 	GLU_CHECK_MSG("After attribute setup");
    503 
    504 	// Uniforms
    505 	for (vector<rsg::VariableValue>::const_iterator uniformIter = m_uniforms.begin(); uniformIter != m_uniforms.end(); uniformIter++)
    506 	{
    507 		GLint location = glGetUniformLocation(program.getProgram(), uniformIter->getVariable()->getName());
    508 
    509 		log << tcu::TestLog::Message << "uniform[" << location << "]: " << uniformIter->getVariable()->getName() << " = " << uniformIter->getValue() << tcu::TestLog::EndMessage;
    510 
    511 		if (location >= 0)
    512 			setUniformValue(location, uniformIter->getValue());
    513 	}
    514 	GLU_CHECK_MSG("After uniform setup");
    515 
    516 	// Textures
    517 	vector<pair<int, const glu::Texture2D*> >	tex2DBindings		= m_texManager.getBindings2D();
    518 	vector<pair<int, const glu::TextureCube*> >	texCubeBindings		= m_texManager.getBindingsCube();
    519 
    520 	for (vector<pair<int, const glu::Texture2D*> >::const_iterator i = tex2DBindings.begin(); i != tex2DBindings.end(); i++)
    521 	{
    522 		int						unitNdx		= i->first;
    523 		const glu::Texture2D*	texture		= i->second;
    524 
    525 		glActiveTexture(GL_TEXTURE0 + unitNdx);
    526 		glBindTexture(GL_TEXTURE_2D, texture->getGLTexture());
    527 
    528 		executor.setTexture(unitNdx, &texture->getRefTexture(), glu::mapGLSampler(TEXTURE_WRAP_S, TEXTURE_WRAP_T, TEXTURE_MIN_FILTER, TEXTURE_MAG_FILTER));
    529 	}
    530 	GLU_CHECK_MSG("After 2D texture setup");
    531 
    532 	for (vector<pair<int, const glu::TextureCube*> >::const_iterator i = texCubeBindings.begin(); i != texCubeBindings.end(); i++)
    533 	{
    534 		int						unitNdx		= i->first;
    535 		const glu::TextureCube*	texture		= i->second;
    536 
    537 		glActiveTexture(GL_TEXTURE0 + unitNdx);
    538 		glBindTexture(GL_TEXTURE_CUBE_MAP, texture->getGLTexture());
    539 
    540 		executor.setTexture(unitNdx, &texture->getRefTexture(), glu::mapGLSampler(TEXTURE_WRAP_S, TEXTURE_WRAP_T, TEXTURE_MIN_FILTER, TEXTURE_MAG_FILTER));
    541 	}
    542 	GLU_CHECK_MSG("After cubemap setup");
    543 
    544 	// Draw and read
    545 	glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
    546 	glDrawElements(GL_TRIANGLES, (GLsizei)m_indices.size(), GL_UNSIGNED_SHORT, &m_indices[0]);
    547 	glFlush();
    548 	GLU_CHECK_MSG("Draw");
    549 
    550 	// Render reference while GPU is doing work
    551 	executor.execute(m_vertexShader, m_fragmentShader, m_uniforms);
    552 
    553 	if (rendered.getFormat().order != tcu::TextureFormat::RGBA || rendered.getFormat().type != tcu::TextureFormat::UNORM_INT8)
    554 	{
    555 		// Read as GL_RGBA8
    556 		tcu::TextureLevel readBuf(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), rendered.getWidth(), rendered.getHeight());
    557 		glu::readPixels(m_renderCtx, viewportX, viewportY, readBuf.getAccess());
    558 		GLU_CHECK_MSG("Read pixels");
    559 		tcu::copy(rendered, readBuf);
    560 	}
    561 	else
    562 		glu::readPixels(m_renderCtx, viewportX, viewportY, rendered.getAccess());
    563 
    564 	// Compare
    565 	{
    566 		float	threshold	= 0.02f;
    567 		bool	imagesOk	= tcu::fuzzyCompare(log, "Result", "Result images", reference.getAccess(), rendered.getAccess(), threshold, tcu::COMPARE_LOG_RESULT);
    568 
    569 		if (imagesOk)
    570 			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    571 		else
    572 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
    573 	}
    574 
    575 	return STOP;
    576 }
    577 
    578 } // gls
    579 } // deqp
    580