1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 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 Multisample texture size tests
22 *//*--------------------------------------------------------------------*/
23
24#include "es31fShaderTextureSizeTests.hpp"
25#include "gluRenderContext.hpp"
26#include "gluShaderProgram.hpp"
27#include "gluPixelTransfer.hpp"
28#include "gluContextInfo.hpp"
29#include "glwEnums.hpp"
30#include "glwFunctions.hpp"
31#include "tcuTestLog.hpp"
32#include "tcuStringTemplate.hpp"
33#include "tcuSurface.hpp"
34#include "tcuRenderTarget.hpp"
35#include "deStringUtil.hpp"
36
37using namespace glw;
38
39namespace deqp
40{
41namespace gles31
42{
43namespace Functional
44{
45namespace
46{
47
48static const char* const s_positionVertexShaderSource =	"#version 310 es\n"
49														"in highp vec4 a_position;\n"
50														"void main (void)\n"
51														"{\n"
52														"	gl_Position = a_position;\n"
53														"}\n";
54
55class TextureSizeCase : public TestCase
56{
57public:
58	enum TextureType
59	{
60		TEXTURE_FLOAT_2D = 0,
61		TEXTURE_FLOAT_2D_ARRAY,
62		TEXTURE_INT_2D,
63		TEXTURE_INT_2D_ARRAY,
64		TEXTURE_UINT_2D,
65		TEXTURE_UINT_2D_ARRAY,
66
67		TEXTURE_LAST
68	};
69
70							TextureSizeCase		(Context& context, const char* name, const char* desc, TextureType type, int samples);
71							~TextureSizeCase	(void);
72
73private:
74	void					init				(void);
75	void					deinit				(void);
76	IterateResult			iterate				(void);
77
78	std::string				genFragmentSource	(void);
79	glw::GLenum				getTextureGLTarget	(void);
80	glw::GLenum				getTextureGLInternalFormat (void);
81
82	void					createTexture		(const tcu::IVec3& size);
83	void					deleteTexture		(void);
84	void					runShader			(tcu::Surface& dst, const tcu::IVec3& size);
85	bool					verifyImage			(const tcu::Surface& dst);
86
87	const TextureType		m_type;
88	const int				m_numSamples;
89	const bool				m_isArrayType;
90
91	glw::GLuint				m_texture;
92	glw::GLuint				m_vbo;
93	glu::ShaderProgram*		m_shader;
94	std::vector<tcu::IVec3>	m_iterations;
95	int						m_iteration;
96
97	bool					m_allIterationsPassed;
98	bool					m_allCasesSkipped;
99};
100
101TextureSizeCase::TextureSizeCase (Context& context, const char* name, const char* desc, TextureType type, int samples)
102	: TestCase				(context, name, desc)
103	, m_type				(type)
104	, m_numSamples			(samples)
105	, m_isArrayType			(m_type == TEXTURE_FLOAT_2D_ARRAY || m_type == TEXTURE_INT_2D_ARRAY || m_type == TEXTURE_UINT_2D_ARRAY)
106	, m_texture				(0)
107	, m_vbo					(0)
108	, m_shader				(DE_NULL)
109	, m_iteration			(0)
110	, m_allIterationsPassed	(true)
111	, m_allCasesSkipped		(true)
112{
113	DE_ASSERT(type < TEXTURE_LAST);
114}
115
116TextureSizeCase::~TextureSizeCase (void)
117{
118	deinit();
119}
120
121void TextureSizeCase::init (void)
122{
123	static const tcu::IVec2 testSizes2D[] =
124	{
125		tcu::IVec2(1,	1),
126		tcu::IVec2(1,	4),
127		tcu::IVec2(4,	8),
128		tcu::IVec2(21,	11),
129		tcu::IVec2(107,	254),
130		tcu::IVec2(-1,	3),
131		tcu::IVec2(3,	-1),
132	};
133	static const tcu::IVec3 testSizes3D[] =
134	{
135		tcu::IVec3(1,	1,		1),
136		tcu::IVec3(1,	4,		7),
137		tcu::IVec3(4,	8,		12),
138		tcu::IVec3(21,	11,		9),
139		tcu::IVec3(107,	254,	2),
140		tcu::IVec3(-1,	3,		3),
141		tcu::IVec3(3,	-1,		3),
142		tcu::IVec3(4,	4,		-1),
143	};
144	static const tcu::Vec4 fullscreenQuad[] =
145	{
146		tcu::Vec4(-1.0f,  1.0f, 0.0f, 1.0f),
147		tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
148		tcu::Vec4( 1.0f,  1.0f, 0.0f, 1.0f),
149		tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f)
150	};
151
152	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
153	glw::GLint				maxTextureSize		= 0;
154	glw::GLint				maxTextureLayers	= 0;
155	glw::GLint				maxSamples			= 0;
156
157	gl.getIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
158	gl.getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxTextureLayers);
159	gl.getInternalformativ(getTextureGLTarget(), getTextureGLInternalFormat(), GL_SAMPLES, 1, &maxSamples);
160
161	// requirements
162	if (m_isArrayType && !m_context.getContextInfo().isExtensionSupported("GL_OES_texture_storage_multisample_2d_array"))
163		throw tcu::NotSupportedError("Test requires OES_texture_storage_multisample_2d_array extension");
164	if (m_context.getRenderTarget().getWidth() < 1 || m_context.getRenderTarget().getHeight() < 1)
165		throw tcu::NotSupportedError("rendertarget size must be at least 1x1");
166	if (m_numSamples > maxSamples)
167		throw tcu::NotSupportedError("sample count is not supported");
168
169	// gen shade
170
171	m_shader = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_positionVertexShaderSource) << glu::FragmentSource(genFragmentSource()));
172	m_testCtx.getLog() << *m_shader;
173	if (!m_shader->isOk())
174		throw tcu::TestError("shader build failed");
175
176	// gen buffer
177
178	gl.genBuffers(1, &m_vbo);
179	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
180	gl.bufferData(GL_ARRAY_BUFFER, sizeof(fullscreenQuad), fullscreenQuad, GL_STATIC_DRAW);
181
182	// gen iterations
183
184	m_testCtx.getLog()
185		<< tcu::TestLog::Message
186		<< "GL_MAX_TEXTURE_SIZE = " << maxTextureSize << "\n"
187		<< "GL_MAX_ARRAY_TEXTURE_LAYERS = " << maxTextureLayers
188		<< tcu::TestLog::EndMessage;
189
190	if (!m_isArrayType)
191	{
192		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(testSizes2D); ++ndx)
193		{
194			if (testSizes2D[ndx].x() <= maxTextureSize && testSizes2D[ndx].y() <= maxTextureSize)
195			{
196				const int w = (testSizes2D[ndx].x() < 0) ? (maxTextureSize) : (testSizes2D[ndx].x());
197				const int h = (testSizes2D[ndx].y() < 0) ? (maxTextureSize) : (testSizes2D[ndx].y());
198
199				m_iterations.push_back(tcu::IVec3(w, h, 0));
200			}
201		}
202	}
203	else
204	{
205		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(testSizes3D); ++ndx)
206		{
207			if (testSizes3D[ndx].x() <= maxTextureSize && testSizes3D[ndx].y() <= maxTextureSize && testSizes3D[ndx].z() <= maxTextureLayers)
208			{
209				const int w = (testSizes3D[ndx].x() < 0) ? (maxTextureSize)		: (testSizes3D[ndx].x());
210				const int h = (testSizes3D[ndx].y() < 0) ? (maxTextureSize)		: (testSizes3D[ndx].y());
211				const int d = (testSizes3D[ndx].z() < 0) ? (maxTextureLayers)	: (testSizes3D[ndx].z());
212
213				m_iterations.push_back(tcu::IVec3(w, h, d));
214			}
215		}
216	}
217}
218
219void TextureSizeCase::deinit (void)
220{
221	if (m_texture)
222	{
223		m_context.getRenderContext().getFunctions().deleteTextures(1, &m_texture);
224		m_texture = 0;
225	}
226
227	if (m_vbo)
228	{
229		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_vbo);
230		m_vbo = 0;
231	}
232
233	if (m_shader)
234	{
235		delete m_shader;
236		m_shader = DE_NULL;
237	}
238}
239
240TextureSizeCase::IterateResult TextureSizeCase::iterate (void)
241{
242	tcu::Surface	result		(1, 1);
243	bool			skipTest	= false;
244
245	m_testCtx.getLog() << tcu::TestLog::Message << "\nIteration " << (m_iteration+1) << " / " << (int)m_iterations.size() << tcu::TestLog::EndMessage;
246
247	try
248	{
249		// set texture size
250
251		createTexture(m_iterations[m_iteration]);
252
253		// query texture size
254
255		runShader(result, m_iterations[m_iteration]);
256	}
257	catch (glu::OutOfMemoryError&)
258	{
259		m_testCtx.getLog() << tcu::TestLog::Message << "Got GL_OUT_OF_MEMORY, skipping this size" << tcu::TestLog::EndMessage;
260
261		skipTest = true;
262	}
263
264	// free resources
265
266	deleteTexture();
267
268	// queried value was correct?
269
270	if (!skipTest)
271	{
272		m_allCasesSkipped = false;
273
274		if (!verifyImage(result))
275			m_allIterationsPassed = false;
276	}
277
278	// final result
279
280	if (++m_iteration < (int)m_iterations.size())
281		return CONTINUE;
282
283	if (!m_allIterationsPassed)
284	{
285		m_testCtx.getLog() << tcu::TestLog::Message << "One or more test sizes failed." << tcu::TestLog::EndMessage;
286		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid texture size");
287	}
288	else if (m_allCasesSkipped)
289	{
290		m_testCtx.getLog() << tcu::TestLog::Message << "Could not test any texture size, texture creation failed." << tcu::TestLog::EndMessage;
291		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "All test texture creations failed");
292	}
293	else
294	{
295		m_testCtx.getLog() << tcu::TestLog::Message << "All texture sizes passed." << tcu::TestLog::EndMessage;
296		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
297	}
298
299	return STOP;
300}
301
302std::string TextureSizeCase::genFragmentSource (void)
303{
304	static const char* const templateSource =	"#version 310 es\n"
305												"${EXTENSION_STATEMENT}"
306												"layout(location = 0) out highp vec4 fragColor;\n"
307												"uniform highp ${SAMPLERTYPE} u_sampler;\n"
308												"uniform highp ${SIZETYPE} u_size;\n"
309												"void main (void)\n"
310												"{\n"
311												"	const highp vec4 okColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
312												"	const highp vec4 failColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
313												"	fragColor = (textureSize(u_sampler) == u_size) ? (okColor) : (failColor);\n"
314												"}\n";
315
316	std::map<std::string, std::string> args;
317
318	switch (m_type)
319	{
320		case TEXTURE_FLOAT_2D:			args["SAMPLERTYPE"] = "sampler2DMS";		break;
321		case TEXTURE_FLOAT_2D_ARRAY:	args["SAMPLERTYPE"] = "sampler2DMSArray";	break;
322		case TEXTURE_INT_2D:			args["SAMPLERTYPE"] = "isampler2DMS";		break;
323		case TEXTURE_INT_2D_ARRAY:		args["SAMPLERTYPE"] = "isampler2DMSArray";	break;
324		case TEXTURE_UINT_2D:			args["SAMPLERTYPE"] = "usampler2DMS";		break;
325		case TEXTURE_UINT_2D_ARRAY:		args["SAMPLERTYPE"] = "usampler2DMSArray";	break;
326		default:
327			DE_ASSERT(DE_FALSE);
328	}
329
330	if (!m_isArrayType)
331		args["SIZETYPE"] = "ivec2";
332	else
333		args["SIZETYPE"] = "ivec3";
334
335	if (m_isArrayType)
336		args["EXTENSION_STATEMENT"] = "#extension GL_OES_texture_storage_multisample_2d_array : require\n";
337	else
338		args["EXTENSION_STATEMENT"] = "";
339
340	return tcu::StringTemplate(templateSource).specialize(args);
341}
342
343glw::GLenum TextureSizeCase::getTextureGLTarget (void)
344{
345	switch (m_type)
346	{
347		case TEXTURE_FLOAT_2D:			return GL_TEXTURE_2D_MULTISAMPLE;
348		case TEXTURE_FLOAT_2D_ARRAY:	return GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
349		case TEXTURE_INT_2D:			return GL_TEXTURE_2D_MULTISAMPLE;
350		case TEXTURE_INT_2D_ARRAY:		return GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
351		case TEXTURE_UINT_2D:			return GL_TEXTURE_2D_MULTISAMPLE;
352		case TEXTURE_UINT_2D_ARRAY:		return GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
353		default:
354			DE_ASSERT(DE_FALSE);
355			return 0;
356	}
357}
358
359glw::GLenum TextureSizeCase::getTextureGLInternalFormat (void)
360{
361	switch (m_type)
362	{
363		case TEXTURE_FLOAT_2D:			return GL_RGBA8;
364		case TEXTURE_FLOAT_2D_ARRAY:	return GL_RGBA8;
365		case TEXTURE_INT_2D:			return GL_R8I;
366		case TEXTURE_INT_2D_ARRAY:		return GL_R8I;
367		case TEXTURE_UINT_2D:			return GL_R8UI;
368		case TEXTURE_UINT_2D_ARRAY:		return GL_R8UI;
369		default:
370			DE_ASSERT(DE_FALSE);
371			return 0;
372	}
373}
374
375void TextureSizeCase::createTexture (const tcu::IVec3& size)
376{
377	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
378
379	if (!m_isArrayType)
380		m_testCtx.getLog() << tcu::TestLog::Message << "Creating texture with size " << size.x() << "x" << size.y() << tcu::TestLog::EndMessage;
381	else
382		m_testCtx.getLog() << tcu::TestLog::Message << "Creating texture with size " << size.x() << "x" << size.y() << "x" << size.z() << tcu::TestLog::EndMessage;
383
384	gl.genTextures(1, &m_texture);
385	gl.bindTexture(getTextureGLTarget(), m_texture);
386	GLU_EXPECT_NO_ERROR(gl.getError(), "texture gen");
387
388	if (!m_isArrayType)
389		gl.texStorage2DMultisample(getTextureGLTarget(), m_numSamples, getTextureGLInternalFormat(), size.x(), size.y(), GL_FALSE);
390	else
391		gl.texStorage3DMultisample(getTextureGLTarget(), m_numSamples, getTextureGLInternalFormat(), size.x(), size.y(), size.z(), GL_FALSE);
392	GLU_EXPECT_NO_ERROR(gl.getError(), "texStorage");
393}
394
395void TextureSizeCase::deleteTexture (void)
396{
397	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
398
399	if (m_texture)
400	{
401		gl.deleteTextures(1, &m_texture);
402		m_texture = 0;
403
404		GLU_EXPECT_NO_ERROR(gl.getError(), "texture delete");
405	}
406}
407
408void TextureSizeCase::runShader (tcu::Surface& dst, const tcu::IVec3& size)
409{
410	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
411	const int				positionLoc			= gl.getAttribLocation(m_shader->getProgram(), "a_position");
412	const int				shaderSamplerLoc	= gl.getUniformLocation(m_shader->getProgram(), "u_sampler");
413	const int				shaderSizeLoc		= gl.getUniformLocation(m_shader->getProgram(), "u_size");
414
415	m_testCtx.getLog() << tcu::TestLog::Message << "Running the verification shader." << tcu::TestLog::EndMessage;
416
417	GLU_EXPECT_NO_ERROR(gl.getError(), "preclear");
418	gl.viewport(0, 0, 1, 1);
419	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
420	gl.clear(GL_COLOR_BUFFER_BIT);
421	GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
422
423	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
424	gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
425	gl.enableVertexAttribArray(positionLoc);
426	GLU_EXPECT_NO_ERROR(gl.getError(), "vertexAttrib");
427
428	gl.useProgram(m_shader->getProgram());
429	gl.uniform1i(shaderSamplerLoc, 0);
430	if (m_isArrayType)
431		gl.uniform3iv(shaderSizeLoc, 1, size.getPtr());
432	else
433		gl.uniform2iv(shaderSizeLoc, 1, size.getPtr());
434	GLU_EXPECT_NO_ERROR(gl.getError(), "setup program");
435
436	gl.bindTexture(getTextureGLTarget(), m_texture);
437	GLU_EXPECT_NO_ERROR(gl.getError(), "bindtex");
438
439	gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
440	GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays");
441
442	gl.disableVertexAttribArray(positionLoc);
443	gl.useProgram(0);
444	GLU_EXPECT_NO_ERROR(gl.getError(), "cleanup");
445
446	gl.finish();
447	glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
448	GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels");
449}
450
451bool TextureSizeCase::verifyImage (const tcu::Surface& dst)
452{
453	DE_ASSERT(dst.getWidth() == 1 && dst.getHeight() == 1);
454
455	const int		colorThresholdRed	= 1 << (8 - m_context.getRenderTarget().getPixelFormat().redBits);
456	const int		colorThresholdGreen	= 1 << (8 - m_context.getRenderTarget().getPixelFormat().greenBits);
457	const int		colorThresholdBlue	= 1 << (8 - m_context.getRenderTarget().getPixelFormat().blueBits);
458	const tcu::RGBA	color				= dst.getPixel(0,0);
459
460	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying image." << tcu::TestLog::EndMessage;
461
462	// green
463	if (color.getRed() < colorThresholdRed && color.getGreen() > 255 - colorThresholdGreen && color.getBlue() < colorThresholdBlue)
464	{
465		m_testCtx.getLog() << tcu::TestLog::Message << "Result ok." << tcu::TestLog::EndMessage;
466		return true;
467	}
468	// red
469	else if (color.getRed() > 255 - colorThresholdRed && color.getGreen() < colorThresholdGreen && color.getBlue() < colorThresholdBlue)
470	{
471		m_testCtx.getLog() << tcu::TestLog::Message << "Image size incorrect." << tcu::TestLog::EndMessage;
472		return false;
473	}
474
475	m_testCtx.getLog() << tcu::TestLog::Message << "Expected either green or red pixel, got " << color << tcu::TestLog::EndMessage;
476	return false;
477}
478
479} // anonymous
480
481ShaderTextureSizeTests::ShaderTextureSizeTests (Context& context)
482	: TestCaseGroup(context, "texture_size", "Texture size tests")
483{
484}
485
486ShaderTextureSizeTests::~ShaderTextureSizeTests (void)
487{
488}
489
490void ShaderTextureSizeTests::init (void)
491{
492	static const struct SamplerType
493	{
494		TextureSizeCase::TextureType	type;
495		const char*						name;
496	} samplerTypes[] =
497	{
498		{ TextureSizeCase::TEXTURE_FLOAT_2D,		"texture_2d"			},
499		{ TextureSizeCase::TEXTURE_FLOAT_2D_ARRAY,	"texture_2d_array"		},
500		{ TextureSizeCase::TEXTURE_INT_2D,			"texture_int_2d"		},
501		{ TextureSizeCase::TEXTURE_INT_2D_ARRAY,	"texture_int_2d_array"	},
502		{ TextureSizeCase::TEXTURE_UINT_2D,			"texture_uint_2d"		},
503		{ TextureSizeCase::TEXTURE_UINT_2D_ARRAY,	"texture_uint_2d_array"	},
504	};
505
506	static const int sampleCounts[] = { 1, 4 };
507
508	for (int samplerTypeNdx = 0; samplerTypeNdx < DE_LENGTH_OF_ARRAY(samplerTypes); ++samplerTypeNdx)
509	{
510		for (int sampleCountNdx = 0; sampleCountNdx < DE_LENGTH_OF_ARRAY(sampleCounts); ++sampleCountNdx)
511		{
512			const std::string name = std::string() + "samples_" + de::toString(sampleCounts[sampleCountNdx]) + "_" + samplerTypes[samplerTypeNdx].name;
513			const std::string desc = std::string() + "samples count = " + de::toString(sampleCounts[sampleCountNdx]) + ", type = " + samplerTypes[samplerTypeNdx].name;
514
515			addChild(new TextureSizeCase(m_context, name.c_str(), desc.c_str(), samplerTypes[samplerTypeNdx].type, sampleCounts[sampleCountNdx]));
516		}
517	}
518}
519
520} // Functional
521} // gles31
522} // deqp
523