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
43using std::vector;
44using std::string;
45using std::pair;
46using std::map;
47
48namespace deqp
49{
50namespace gls
51{
52
53enum
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
74VertexArray::VertexArray (const rsg::ShaderInput* input, int numVertices)
75	: m_input			(input)
76	, m_vertices		(input->getVariable()->getType().getNumElements() * numVertices)
77{
78}
79
80TextureManager::TextureManager (void)
81{
82}
83
84TextureManager::~TextureManager (void)
85{
86}
87
88void TextureManager::bindTexture (int unit, const glu::Texture2D* tex2D)
89{
90	m_tex2D[unit] = tex2D;
91}
92
93void TextureManager::bindTexture (int unit, const glu::TextureCube* texCube)
94{
95	m_texCube[unit] = texCube;
96}
97
98inline 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
106inline 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
114RandomShaderCase::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
127RandomShaderCase::~RandomShaderCase (void)
128{
129	delete m_tex2D;
130	delete m_texCube;
131}
132
133void 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
232const 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
255const 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
293void 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
306namespace
307{
308
309void 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
346tcu::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
390tcu::MessageBuilder& operator<< (tcu::MessageBuilder& message, rsg::ConstValueRangeAccess valueRange)
391{
392	return message << valueRange.getMin() << " -> " << valueRange.getMax();
393}
394
395} // anonymous
396
397RandomShaderCase::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