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