• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
VertexArray(const rsg::ShaderInput * input,int numVertices)78 VertexArray::VertexArray (const rsg::ShaderInput* input, int numVertices)
79 	: m_input			(input)
80 	, m_vertices		(input->getVariable()->getType().getNumElements() * numVertices)
81 {
82 }
83 
TextureManager(void)84 TextureManager::TextureManager (void)
85 {
86 }
87 
~TextureManager(void)88 TextureManager::~TextureManager (void)
89 {
90 }
91 
bindTexture(int unit,const glu::Texture2D * tex2D)92 void TextureManager::bindTexture (int unit, const glu::Texture2D* tex2D)
93 {
94 	m_tex2D[unit] = tex2D;
95 }
96 
bindTexture(int unit,const glu::TextureCube * texCube)97 void TextureManager::bindTexture (int unit, const glu::TextureCube* texCube)
98 {
99 	m_texCube[unit] = texCube;
100 }
101 
getBindings2D(void) const102 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 
getBindingsCube(void) const110 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 
RandomShaderCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const char * name,const char * description,const rsg::ProgramParameters & params)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 
~RandomShaderCase(void)131 RandomShaderCase::~RandomShaderCase (void)
132 {
133 	delete m_tex2D;
134 	delete m_texCube;
135 }
136 
init(void)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] = (deUint16)(quadX + quadY*(m_gridWidth+1));
214 		m_indices[quadNdx*6+1] = (deUint16)(quadX + (quadY+1)*(m_gridWidth+1));
215 		m_indices[quadNdx*6+2] = (deUint16)(quadX + quadY*(m_gridWidth+1) + 1);
216 		m_indices[quadNdx*6+3] = (deUint16)(m_indices[quadNdx*6+2]);
217 		m_indices[quadNdx*6+4] = (deUint16)(m_indices[quadNdx*6+1]);
218 		m_indices[quadNdx*6+5] = (deUint16)(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 
getNumSamplerUniforms(const std::vector<rsg::ShaderInput * > & uniforms)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 
checkShaderLimits(const rsg::Shader & shader) const253 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 
checkProgramLimits(const rsg::Shader & vtxShader,const rsg::Shader & frgShader) const274 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 
getTex2D(void)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 
getTexCube(void)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 
deinit(void)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 
setUniformValue(int location,rsg::ConstValueAccess value)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");
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");
400 			}
401 			break;
402 
403 		default:
404 			TCU_FAIL("Unsupported type");
405 	}
406 }
407 
operator <<(tcu::MessageBuilder & message,rsg::ConstValueAccess value)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 
operator <<(tcu::MessageBuilder & message,rsg::ConstValueRangeAccess valueRange)452 tcu::MessageBuilder& operator<< (tcu::MessageBuilder& message, rsg::ConstValueRangeAccess valueRange)
453 {
454 	return message << valueRange.getMin() << " -> " << valueRange.getMax();
455 }
456 
457 } // anonymous
458 
iterate(void)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