1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.0 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 Texture size tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es2fTextureSizeTests.hpp"
25#include "glsTextureTestUtil.hpp"
26#include "gluTexture.hpp"
27#include "gluStrUtil.hpp"
28#include "gluTextureUtil.hpp"
29#include "gluPixelTransfer.hpp"
30#include "tcuTestLog.hpp"
31#include "tcuTextureUtil.hpp"
32
33#include "glwEnums.hpp"
34#include "glwFunctions.hpp"
35
36namespace deqp
37{
38namespace gles2
39{
40namespace Functional
41{
42
43using tcu::TestLog;
44using std::vector;
45using std::string;
46using tcu::Sampler;
47using namespace glu;
48using namespace gls::TextureTestUtil;
49using namespace glu::TextureTestUtil;
50
51class Texture2DSizeCase : public tcu::TestCase
52{
53public:
54							Texture2DSizeCase		(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, deUint32 format, deUint32 dataType, int width, int height, bool mipmaps);
55							~Texture2DSizeCase		(void);
56
57	void					init					(void);
58	void					deinit					(void);
59	IterateResult			iterate					(void);
60
61private:
62							Texture2DSizeCase		(const Texture2DSizeCase& other);
63	Texture2DSizeCase&		operator=				(const Texture2DSizeCase& other);
64
65	glu::RenderContext&		m_renderCtx;
66
67	deUint32				m_format;
68	deUint32				m_dataType;
69	int						m_width;
70	int						m_height;
71	bool					m_useMipmaps;
72
73	glu::Texture2D*			m_texture;
74	TextureRenderer			m_renderer;
75};
76
77Texture2DSizeCase::Texture2DSizeCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, deUint32 format, deUint32 dataType, int width, int height, bool mipmaps)
78	: TestCase		(testCtx, name, description)
79	, m_renderCtx	(renderCtx)
80	, m_format		(format)
81	, m_dataType	(dataType)
82	, m_width		(width)
83	, m_height		(height)
84	, m_useMipmaps	(mipmaps)
85	, m_texture		(DE_NULL)
86	, m_renderer	(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
87{
88}
89
90Texture2DSizeCase::~Texture2DSizeCase (void)
91{
92	Texture2DSizeCase::deinit();
93}
94
95void Texture2DSizeCase::init (void)
96{
97	DE_ASSERT(!m_texture);
98	m_texture = new Texture2D(m_renderCtx, m_format, m_dataType, m_width, m_height);
99
100	int numLevels = m_useMipmaps ? deLog2Floor32(de::max(m_width, m_height))+1 : 1;
101
102	// Fill levels.
103	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
104	{
105		m_texture->getRefTexture().allocLevel(levelNdx);
106		tcu::fillWithComponentGradients(m_texture->getRefTexture().getLevel(levelNdx), tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f));
107	}
108}
109
110void Texture2DSizeCase::deinit (void)
111{
112	delete m_texture;
113	m_texture = DE_NULL;
114
115	m_renderer.clear();
116}
117
118Texture2DSizeCase::IterateResult Texture2DSizeCase::iterate (void)
119{
120	const glw::Functions&	gl				= m_renderCtx.getFunctions();
121	TestLog&				log				= m_testCtx.getLog();
122	RandomViewport			viewport		(m_renderCtx.getRenderTarget(), 128, 128, deStringHash(getName()));
123	tcu::Surface			renderedFrame	(viewport.width, viewport.height);
124	tcu::Surface			referenceFrame	(viewport.width, viewport.height);
125	const tcu::IVec4		texBits			= tcu::getTextureFormatBitDepth(glu::mapGLTransferFormat(m_format, m_dataType));
126	const tcu::PixelFormat&	rtFmt			= m_renderCtx.getRenderTarget().getPixelFormat();
127	const tcu::PixelFormat	thresholdFormat(std::min(texBits[0], rtFmt.redBits), std::min(texBits[1], rtFmt.greenBits), std::min(texBits[2], rtFmt.blueBits), std::min(texBits[3], rtFmt.alphaBits));
128	tcu::RGBA				threshold		= thresholdFormat.getColorThreshold() + tcu::RGBA(7,7,7,7);
129	deUint32				wrapS			= GL_CLAMP_TO_EDGE;
130	deUint32				wrapT			= GL_CLAMP_TO_EDGE;
131	// Do not minify with GL_NEAREST. A large POT texture with a small POT render target will produce
132	// indeterminate results.
133	deUint32				minFilter		= m_useMipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_LINEAR;
134	deUint32				magFilter		= GL_NEAREST;
135	vector<float>			texCoord;
136
137	computeQuadTexCoord2D(texCoord, tcu::Vec2(0.0f, 0.0f), tcu::Vec2(1.0f, 1.0f));
138
139	// Setup base viewport.
140	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
141
142	// Upload texture data to GL.
143	m_texture->upload();
144
145	// Bind to unit 0.
146	gl.activeTexture(GL_TEXTURE0);
147	gl.bindTexture(GL_TEXTURE_2D, m_texture->getGLTexture());
148
149	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		wrapS);
150	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		wrapT);
151	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	minFilter);
152	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	magFilter);
153	GLU_EXPECT_NO_ERROR(gl.getError(), "Set texturing state");
154
155	// Draw.
156	m_renderer.renderQuad(0, &texCoord[0], TEXTURETYPE_2D);
157	glu::readPixels(m_renderCtx, viewport.x, viewport.y, renderedFrame.getAccess());
158
159	// Compute reference.
160	sampleTexture(tcu::SurfaceAccess(referenceFrame, m_renderCtx.getRenderTarget().getPixelFormat()), m_texture->getRefTexture(), &texCoord[0], ReferenceParams(TEXTURETYPE_2D, mapGLSampler(wrapS, wrapT, minFilter, magFilter)));
161
162	// Compare and log.
163	bool isOk = compareImages(log, referenceFrame, renderedFrame, threshold);
164
165	m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
166							isOk ? "Pass"				: "Image comparison failed");
167
168	return STOP;
169}
170
171class TextureCubeSizeCase : public tcu::TestCase
172{
173public:
174							TextureCubeSizeCase		(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, deUint32 format, deUint32 dataType, int width, int height, bool mipmaps);
175							~TextureCubeSizeCase	(void);
176
177	void					init					(void);
178	void					deinit					(void);
179	IterateResult			iterate					(void);
180
181private:
182							TextureCubeSizeCase		(const TextureCubeSizeCase& other);
183	TextureCubeSizeCase&	operator=				(const TextureCubeSizeCase& other);
184
185	bool					testFace				(tcu::CubeFace face);
186
187	glu::RenderContext&		m_renderCtx;
188
189	deUint32				m_format;
190	deUint32				m_dataType;
191	int						m_width;
192	int						m_height;
193	bool					m_useMipmaps;
194
195	glu::TextureCube*		m_texture;
196	TextureRenderer			m_renderer;
197
198	int						m_curFace;
199	bool					m_isOk;
200};
201
202TextureCubeSizeCase::TextureCubeSizeCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, deUint32 format, deUint32 dataType, int width, int height, bool mipmaps)
203	: TestCase		(testCtx, name, description)
204	, m_renderCtx	(renderCtx)
205	, m_format		(format)
206	, m_dataType	(dataType)
207	, m_width		(width)
208	, m_height		(height)
209	, m_useMipmaps	(mipmaps)
210	, m_texture		(DE_NULL)
211	, m_renderer	(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
212	, m_curFace		(0)
213	, m_isOk		(false)
214{
215}
216
217TextureCubeSizeCase::~TextureCubeSizeCase (void)
218{
219	TextureCubeSizeCase::deinit();
220}
221
222void TextureCubeSizeCase::init (void)
223{
224	DE_ASSERT(!m_texture);
225	DE_ASSERT(m_width == m_height);
226	m_texture = new TextureCube(m_renderCtx, m_format, m_dataType, m_width);
227
228	static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] =
229	{
230		{ tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
231		{ tcu::Vec4( 0.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
232		{ tcu::Vec4(-1.0f,  0.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
233		{ tcu::Vec4(-1.0f, -1.0f,  0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
234		{ tcu::Vec4(-1.0f, -1.0f, -1.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
235		{ tcu::Vec4( 0.0f,  0.0f,  0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }  // positive z
236	};
237
238	int numLevels = m_useMipmaps ? deLog2Floor32(de::max(m_width, m_height))+1 : 1;
239
240	// Fill levels.
241	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
242	{
243		for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
244		{
245			m_texture->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
246			fillWithComponentGradients(m_texture->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), gradients[face][0], gradients[face][1]);
247		}
248	}
249
250	// Upload texture data to GL.
251	m_texture->upload();
252
253	// Initialize iteration state.
254	m_curFace	= 0;
255	m_isOk		= true;
256}
257
258void TextureCubeSizeCase::deinit (void)
259{
260	delete m_texture;
261	m_texture = DE_NULL;
262
263	m_renderer.clear();
264}
265
266bool TextureCubeSizeCase::testFace (tcu::CubeFace face)
267{
268	const glw::Functions&	gl				= m_renderCtx.getFunctions();
269	TestLog&				log				= m_testCtx.getLog();
270	RandomViewport			viewport		(m_renderCtx.getRenderTarget(), 128, 128, deStringHash(getName())+(deUint32)face);
271	tcu::Surface			renderedFrame	(viewport.width, viewport.height);
272	tcu::Surface			referenceFrame	(viewport.width, viewport.height);
273	const tcu::IVec4		texBits			= tcu::getTextureFormatBitDepth(glu::mapGLTransferFormat(m_format, m_dataType));
274	const tcu::PixelFormat&	rtFmt			= m_renderCtx.getRenderTarget().getPixelFormat();
275	const tcu::PixelFormat	thresholdFormat(std::min(texBits[0], rtFmt.redBits), std::min(texBits[1], rtFmt.greenBits), std::min(texBits[2], rtFmt.blueBits), std::min(texBits[3], rtFmt.alphaBits));
276	tcu::RGBA				threshold		= thresholdFormat.getColorThreshold() + tcu::RGBA(7,7,7,7);
277	deUint32				wrapS			= GL_CLAMP_TO_EDGE;
278	deUint32				wrapT			= GL_CLAMP_TO_EDGE;
279	// Do not minify with GL_NEAREST. A large POT texture with a small POT render target will produce
280	// indeterminate results.
281	deUint32				minFilter		= m_useMipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_LINEAR;
282	deUint32				magFilter		= GL_NEAREST;
283	vector<float>			texCoord;
284
285	computeQuadTexCoordCube(texCoord, face);
286
287	// \todo [2011-10-28 pyry] Image set name / section?
288	log << TestLog::Message << face << TestLog::EndMessage;
289
290	// Setup base viewport.
291	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
292
293	// Bind to unit 0.
294	gl.activeTexture(GL_TEXTURE0);
295	gl.bindTexture(GL_TEXTURE_CUBE_MAP, m_texture->getGLTexture());
296
297	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, wrapS);
298	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, wrapT);
299	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, minFilter);
300	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, magFilter);
301	GLU_EXPECT_NO_ERROR(gl.getError(), "Set texturing state");
302
303	m_renderer.renderQuad(0, &texCoord[0], TEXTURETYPE_CUBE);
304	glu::readPixels(m_renderCtx, viewport.x, viewport.y, renderedFrame.getAccess());
305
306	// Compute reference.
307	Sampler sampler = mapGLSampler(wrapS, wrapT, minFilter, magFilter);
308	sampler.seamlessCubeMap = false;
309	sampleTexture(tcu::SurfaceAccess(referenceFrame, m_renderCtx.getRenderTarget().getPixelFormat()), m_texture->getRefTexture(), &texCoord[0], ReferenceParams(TEXTURETYPE_CUBE, sampler));
310
311	// Compare and log.
312	return compareImages(log, referenceFrame, renderedFrame, threshold);
313}
314
315TextureCubeSizeCase::IterateResult TextureCubeSizeCase::iterate (void)
316{
317	// Execute test for all faces.
318	if (!testFace((tcu::CubeFace)m_curFace))
319		m_isOk = false;
320
321	m_curFace += 1;
322
323	if (m_curFace == tcu::CUBEFACE_LAST)
324	{
325		m_testCtx.setTestResult(m_isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
326								m_isOk ? "Pass"					: "Image comparison failed");
327		return STOP;
328	}
329	else
330		return CONTINUE;
331}
332
333TextureSizeTests::TextureSizeTests (Context& context)
334	: TestCaseGroup(context, "size", "Texture Size Tests")
335{
336}
337
338TextureSizeTests::~TextureSizeTests (void)
339{
340}
341
342void TextureSizeTests::init (void)
343{
344	struct
345	{
346		int	width;
347		int	height;
348	} sizes2D[] =
349	{
350		{   64,   64 }, // Spec-mandated minimum.
351		{   65,   63 },
352		{  512,  512 },
353		{ 1024, 1024 },
354		{ 2048, 2048 }
355	};
356
357	struct
358	{
359		int	width;
360		int	height;
361	} sizesCube[] =
362	{
363		{  15,  15 },
364		{  16,  16 }, // Spec-mandated minimum
365		{  64,  64 },
366		{ 128, 128 },
367		{ 256, 256 },
368		{ 512, 512 }
369	};
370
371	struct
372	{
373		const char*	name;
374		deUint32	format;
375		deUint32	dataType;
376	} formats[] =
377	{
378		{ "l8",			GL_LUMINANCE,		GL_UNSIGNED_BYTE },
379		{ "rgba4444",	GL_RGBA,			GL_UNSIGNED_SHORT_4_4_4_4 },
380		{ "rgb888",		GL_RGB,				GL_UNSIGNED_BYTE },
381		{ "rgba8888",	GL_RGBA,			GL_UNSIGNED_BYTE }
382	};
383
384	// 2D cases.
385	tcu::TestCaseGroup* group2D = new tcu::TestCaseGroup(m_testCtx, "2d", "2D Texture Size Tests");
386	addChild(group2D);
387	for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizes2D); sizeNdx++)
388	{
389		int		width	= sizes2D[sizeNdx].width;
390		int		height	= sizes2D[sizeNdx].height;
391		bool	isPOT	= deIsPowerOfTwo32(width) && deIsPowerOfTwo32(height);
392
393		for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
394		{
395			for (int mipmap = 0; mipmap < (isPOT ? 2 : 1); mipmap++)
396			{
397				std::ostringstream name;
398				name << width << "x" << height << "_" << formats[formatNdx].name << (mipmap ? "_mipmap" : "");
399
400				group2D->addChild(new Texture2DSizeCase(m_testCtx, m_context.getRenderContext(), name.str().c_str(), "",
401														formats[formatNdx].format, formats[formatNdx].dataType,
402														width, height, mipmap != 0));
403			}
404		}
405	}
406
407	// Cubemap cases.
408	tcu::TestCaseGroup* groupCube = new tcu::TestCaseGroup(m_testCtx, "cube", "Cubemap Texture Size Tests");
409	addChild(groupCube);
410	for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizesCube); sizeNdx++)
411	{
412		int		width	= sizesCube[sizeNdx].width;
413		int		height	= sizesCube[sizeNdx].height;
414		bool	isPOT	= deIsPowerOfTwo32(width) && deIsPowerOfTwo32(height);
415
416		for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
417		{
418			for (int mipmap = 0; mipmap < (isPOT ? 2 : 1); mipmap++)
419			{
420				std::ostringstream name;
421				name << width << "x" << height << "_" << formats[formatNdx].name << (mipmap ? "_mipmap" : "");
422
423				groupCube->addChild(new TextureCubeSizeCase(m_testCtx, m_context.getRenderContext(), name.str().c_str(), "",
424															formats[formatNdx].format, formats[formatNdx].dataType,
425															width, height, mipmap != 0));
426			}
427		}
428	}
429}
430
431} // Functional
432} // gles2
433} // deqp
434