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
47using std::vector;
48using std::string;
49using std::pair;
50using std::map;
51
52namespace deqp
53{
54namespace gls
55{
56
57enum
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
78VertexArray::VertexArray (const rsg::ShaderInput* input, int numVertices)
79	: m_input			(input)
80	, m_vertices		(input->getVariable()->getType().getNumElements() * numVertices)
81{
82}
83
84TextureManager::TextureManager (void)
85{
86}
87
88TextureManager::~TextureManager (void)
89{
90}
91
92void TextureManager::bindTexture (int unit, const glu::Texture2D* tex2D)
93{
94	m_tex2D[unit] = tex2D;
95}
96
97void TextureManager::bindTexture (int unit, const glu::TextureCube* texCube)
98{
99	m_texCube[unit] = texCube;
100}
101
102inline 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
110inline 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
118RandomShaderCase::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
131RandomShaderCase::~RandomShaderCase (void)
132{
133	delete m_tex2D;
134	delete m_texCube;
135}
136
137void 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
240static 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
253void 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
274void 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
294const 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
317const 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
355void 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
368namespace
369{
370
371void 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
408tcu::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
452tcu::MessageBuilder& operator<< (tcu::MessageBuilder& message, rsg::ConstValueRangeAccess valueRange)
453{
454	return message << valueRange.getMin() << " -> " << valueRange.getMax();
455}
456
457} // anonymous
458
459RandomShaderCase::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