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 filtering tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es2fTextureFilteringTests.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 #include "tcuTexLookupVerifier.hpp"
33 #include "tcuVectorUtil.hpp"
34 #include "deStringUtil.hpp"
35 #include "glwFunctions.hpp"
36 #include "glwEnums.hpp"
37 
38 namespace deqp
39 {
40 namespace gles2
41 {
42 namespace Functional
43 {
44 
45 using tcu::TestLog;
46 using std::vector;
47 using std::string;
48 using tcu::Sampler;
49 using namespace glu;
50 using namespace gls::TextureTestUtil;
51 
52 enum
53 {
54 	VIEWPORT_WIDTH		= 64,
55 	VIEWPORT_HEIGHT		= 64,
56 	MIN_VIEWPORT_WIDTH	= 64,
57 	MIN_VIEWPORT_HEIGHT	= 64
58 };
59 
60 class Texture2DFilteringCase : public tcu::TestCase
61 {
62 public:
63 									Texture2DFilteringCase		(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 format, deUint32 dataType, int width, int height);
64 									Texture2DFilteringCase		(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, const std::vector<std::string>& filenames);
65 									~Texture2DFilteringCase		(void);
66 
67 	void							init						(void);
68 	void							deinit						(void);
69 	IterateResult					iterate						(void);
70 
71 private:
72 									Texture2DFilteringCase		(const Texture2DFilteringCase& other);
73 	Texture2DFilteringCase&			operator=					(const Texture2DFilteringCase& other);
74 
75 	glu::RenderContext&				m_renderCtx;
76 	const glu::ContextInfo&			m_renderCtxInfo;
77 
78 	const deUint32					m_minFilter;
79 	const deUint32					m_magFilter;
80 	const deUint32					m_wrapS;
81 	const deUint32					m_wrapT;
82 
83 	const deUint32					m_format;
84 	const deUint32					m_dataType;
85 	const int						m_width;
86 	const int						m_height;
87 
88 	const std::vector<std::string>	m_filenames;
89 
90 	struct FilterCase
91 	{
92 		const glu::Texture2D*	texture;
93 		tcu::Vec2				minCoord;
94 		tcu::Vec2				maxCoord;
95 
FilterCasedeqp::gles2::Functional::Texture2DFilteringCase::FilterCase96 		FilterCase (void)
97 			: texture(DE_NULL)
98 		{
99 		}
100 
FilterCasedeqp::gles2::Functional::Texture2DFilteringCase::FilterCase101 		FilterCase (const glu::Texture2D* tex_, const tcu::Vec2& minCoord_, const tcu::Vec2& maxCoord_)
102 			: texture	(tex_)
103 			, minCoord	(minCoord_)
104 			, maxCoord	(maxCoord_)
105 		{
106 		}
107 	};
108 
109 	std::vector<glu::Texture2D*>	m_textures;
110 	std::vector<FilterCase>			m_cases;
111 
112 	TextureRenderer					m_renderer;
113 
114 	int								m_caseNdx;
115 };
116 
Texture2DFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,deUint32 minFilter,deUint32 magFilter,deUint32 wrapS,deUint32 wrapT,deUint32 format,deUint32 dataType,int width,int height)117 Texture2DFilteringCase::Texture2DFilteringCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 format, deUint32 dataType, int width, int height)
118 	: TestCase			(testCtx, name, desc)
119 	, m_renderCtx		(renderCtx)
120 	, m_renderCtxInfo	(ctxInfo)
121 	, m_minFilter		(minFilter)
122 	, m_magFilter		(magFilter)
123 	, m_wrapS			(wrapS)
124 	, m_wrapT			(wrapT)
125 	, m_format			(format)
126 	, m_dataType		(dataType)
127 	, m_width			(width)
128 	, m_height			(height)
129 	, m_renderer		(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
130 	, m_caseNdx			(0)
131 {
132 }
133 
Texture2DFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,deUint32 minFilter,deUint32 magFilter,deUint32 wrapS,deUint32 wrapT,const std::vector<std::string> & filenames)134 Texture2DFilteringCase::Texture2DFilteringCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, const std::vector<std::string>& filenames)
135 	: TestCase			(testCtx, name, desc)
136 	, m_renderCtx		(renderCtx)
137 	, m_renderCtxInfo	(ctxInfo)
138 	, m_minFilter		(minFilter)
139 	, m_magFilter		(magFilter)
140 	, m_wrapS			(wrapS)
141 	, m_wrapT			(wrapT)
142 	, m_format			(GL_NONE)
143 	, m_dataType		(GL_NONE)
144 	, m_width			(0)
145 	, m_height			(0)
146 	, m_filenames		(filenames)
147 	, m_renderer		(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
148 	, m_caseNdx			(0)
149 {
150 }
151 
~Texture2DFilteringCase(void)152 Texture2DFilteringCase::~Texture2DFilteringCase (void)
153 {
154 	deinit();
155 }
156 
init(void)157 void Texture2DFilteringCase::init (void)
158 {
159 	try
160 	{
161 		if (!m_filenames.empty())
162 		{
163 			m_textures.reserve(1);
164 			m_textures.push_back(glu::Texture2D::create(m_renderCtx, m_renderCtxInfo, m_testCtx.getArchive(), (int)m_filenames.size(), m_filenames));
165 		}
166 		else
167 		{
168 			// Create 2 textures.
169 			m_textures.reserve(2);
170 			for (int ndx = 0; ndx < 2; ndx++)
171 				m_textures.push_back(new glu::Texture2D(m_renderCtx, m_format, m_dataType, m_width, m_height));
172 
173 			bool					mipmaps		= deIsPowerOfTwo32(m_width) && deIsPowerOfTwo32(m_height);
174 			int						numLevels	= mipmaps ? deLog2Floor32(de::max(m_width, m_height))+1 : 1;
175 			tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
176 			tcu::Vec4				cBias		= fmtInfo.valueMin;
177 			tcu::Vec4				cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
178 
179 			// Fill first gradient texture.
180 			for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
181 			{
182 				tcu::Vec4 gMin = tcu::Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias;
183 				tcu::Vec4 gMax = tcu::Vec4( 1.0f,  1.0f,  1.0f, 0.0f)*cScale + cBias;
184 
185 				m_textures[0]->getRefTexture().allocLevel(levelNdx);
186 				tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax);
187 			}
188 
189 			// Fill second with grid texture.
190 			for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
191 			{
192 				deUint32	step	= 0x00ffffff / numLevels;
193 				deUint32	rgb		= step*levelNdx;
194 				deUint32	colorA	= 0xff000000 | rgb;
195 				deUint32	colorB	= 0xff000000 | ~rgb;
196 
197 				m_textures[1]->getRefTexture().allocLevel(levelNdx);
198 				tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
199 			}
200 
201 			// Upload.
202 			for (std::vector<glu::Texture2D*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
203 				(*i)->upload();
204 		}
205 
206 		// Compute cases.
207 		{
208 			const struct
209 			{
210 				int		texNdx;
211 				float	lodX;
212 				float	lodY;
213 				float	oX;
214 				float	oY;
215 			} cases[] =
216 			{
217 				{ 0,	1.6f,	2.9f,	-1.0f,	-2.7f	},
218 				{ 0,	-2.0f,	-1.35f,	-0.2f,	0.7f	},
219 				{ 1,	0.14f,	0.275f,	-1.5f,	-1.1f	},
220 				{ 1,	-0.92f,	-2.64f,	0.4f,	-0.1f	},
221 			};
222 
223 			const float	viewportW	= (float)de::min<int>(VIEWPORT_WIDTH, m_renderCtx.getRenderTarget().getWidth());
224 			const float	viewportH	= (float)de::min<int>(VIEWPORT_HEIGHT, m_renderCtx.getRenderTarget().getHeight());
225 
226 			for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
227 			{
228 				const int	texNdx	= de::clamp(cases[caseNdx].texNdx, 0, (int)m_textures.size()-1);
229 				const float	lodX	= cases[caseNdx].lodX;
230 				const float	lodY	= cases[caseNdx].lodY;
231 				const float	oX		= cases[caseNdx].oX;
232 				const float	oY		= cases[caseNdx].oY;
233 				const float	sX		= deFloatExp2(lodX)*viewportW / float(m_textures[texNdx]->getRefTexture().getWidth());
234 				const float	sY		= deFloatExp2(lodY)*viewportH / float(m_textures[texNdx]->getRefTexture().getHeight());
235 
236 				m_cases.push_back(FilterCase(m_textures[texNdx], tcu::Vec2(oX, oY), tcu::Vec2(oX+sX, oY+sY)));
237 			}
238 		}
239 
240 		m_caseNdx = 0;
241 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
242 	}
243 	catch (...)
244 	{
245 		// Clean up to save memory.
246 		Texture2DFilteringCase::deinit();
247 		throw;
248 	}
249 }
250 
deinit(void)251 void Texture2DFilteringCase::deinit (void)
252 {
253 	for (std::vector<glu::Texture2D*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
254 		delete *i;
255 	m_textures.clear();
256 
257 	m_renderer.clear();
258 	m_cases.clear();
259 }
260 
iterate(void)261 Texture2DFilteringCase::IterateResult Texture2DFilteringCase::iterate (void)
262 {
263 	const glw::Functions&			gl			= m_renderCtx.getFunctions();
264 	const RandomViewport			viewport	(m_renderCtx.getRenderTarget(), VIEWPORT_WIDTH, VIEWPORT_HEIGHT, deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
265 	const tcu::TextureFormat		texFmt		= m_textures[0]->getRefTexture().getFormat();
266 	const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(texFmt);
267 	const FilterCase&				curCase		= m_cases[m_caseNdx];
268 	const tcu::ScopedLogSection		section		(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
269 	ReferenceParams					refParams	(TEXTURETYPE_2D);
270 	tcu::Surface					rendered	(viewport.width, viewport.height);
271 	vector<float>					texCoord;
272 
273 	if (viewport.width < MIN_VIEWPORT_WIDTH || viewport.height < MIN_VIEWPORT_HEIGHT)
274 		throw tcu::NotSupportedError("Too small viewport", "", __FILE__, __LINE__);
275 
276 	// Setup params for reference.
277 	refParams.sampler		= mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
278 	refParams.samplerType	= getSamplerType(texFmt);
279 	refParams.lodMode		= LODMODE_EXACT;
280 	refParams.colorBias		= fmtInfo.lookupBias;
281 	refParams.colorScale	= fmtInfo.lookupScale;
282 
283 	// Compute texture coordinates.
284 	m_testCtx.getLog() << TestLog::Message << "Texture coordinates: " << curCase.minCoord << " -> " << curCase.maxCoord << TestLog::EndMessage;
285 	computeQuadTexCoord2D(texCoord, curCase.minCoord, curCase.maxCoord);
286 
287 	gl.bindTexture	(GL_TEXTURE_2D, curCase.texture->getGLTexture());
288 	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	m_minFilter);
289 	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	m_magFilter);
290 	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		m_wrapS);
291 	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		m_wrapT);
292 
293 	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
294 	m_renderer.renderQuad(0, &texCoord[0], refParams);
295 	glu::readPixels(m_renderCtx, viewport.x, viewport.y, rendered.getAccess());
296 
297 	{
298 		const bool				isNearestOnly	= m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
299 		const tcu::PixelFormat	pixelFormat		= m_renderCtx.getRenderTarget().getPixelFormat();
300 		const tcu::IVec4		colorBits		= max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2), tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
301 		tcu::LodPrecision		lodPrecision;
302 		tcu::LookupPrecision	lookupPrecision;
303 
304 		lodPrecision.derivateBits		= 7;
305 		lodPrecision.lodBits			= 4;
306 		lookupPrecision.colorThreshold	= tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
307 		lookupPrecision.coordBits		= tcu::IVec3(9,9,0); // mediump interpolation
308 		lookupPrecision.uvwBits			= tcu::IVec3(5,5,0);
309 		lookupPrecision.colorMask		= getCompareMask(pixelFormat);
310 
311 		const bool isOk = verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
312 											  &texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
313 
314 		if (!isOk)
315 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
316 	}
317 
318 	m_caseNdx += 1;
319 	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
320 }
321 
322 
323 class TextureCubeFilteringCase : public tcu::TestCase
324 {
325 public:
326 									TextureCubeFilteringCase	(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 format, deUint32 dataType, int width, int height);
327 									TextureCubeFilteringCase	(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, const std::vector<std::string>& filenames);
328 									~TextureCubeFilteringCase	(void);
329 
330 	void							init						(void);
331 	void							deinit						(void);
332 	IterateResult					iterate						(void);
333 
334 private:
335 									TextureCubeFilteringCase	(const TextureCubeFilteringCase& other);
336 	TextureCubeFilteringCase&		operator=					(const TextureCubeFilteringCase& other);
337 
338 	glu::RenderContext&				m_renderCtx;
339 	const glu::ContextInfo&			m_renderCtxInfo;
340 
341 	const deUint32					m_minFilter;
342 	const deUint32					m_magFilter;
343 	const deUint32					m_wrapS;
344 	const deUint32					m_wrapT;
345 
346 	const deUint32					m_format;
347 	const deUint32					m_dataType;
348 	const int						m_width;
349 	const int						m_height;
350 
351 	const std::vector<std::string>	m_filenames;
352 
353 	struct FilterCase
354 	{
355 		const glu::TextureCube*	texture;
356 		tcu::Vec2				bottomLeft;
357 		tcu::Vec2				topRight;
358 
FilterCasedeqp::gles2::Functional::TextureCubeFilteringCase::FilterCase359 		FilterCase (void)
360 			: texture(DE_NULL)
361 		{
362 		}
363 
FilterCasedeqp::gles2::Functional::TextureCubeFilteringCase::FilterCase364 		FilterCase (const glu::TextureCube* tex_, const tcu::Vec2& bottomLeft_, const tcu::Vec2& topRight_)
365 			: texture	(tex_)
366 			, bottomLeft(bottomLeft_)
367 			, topRight	(topRight_)
368 		{
369 		}
370 	};
371 
372 	std::vector<glu::TextureCube*>	m_textures;
373 	std::vector<FilterCase>			m_cases;
374 
375 	TextureRenderer					m_renderer;
376 
377 	int								m_caseNdx;
378 };
379 
TextureCubeFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,deUint32 minFilter,deUint32 magFilter,deUint32 wrapS,deUint32 wrapT,deUint32 format,deUint32 dataType,int width,int height)380 TextureCubeFilteringCase::TextureCubeFilteringCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 format, deUint32 dataType, int width, int height)
381 	: TestCase					(testCtx, name, desc)
382 	, m_renderCtx				(renderCtx)
383 	, m_renderCtxInfo			(ctxInfo)
384 	, m_minFilter				(minFilter)
385 	, m_magFilter				(magFilter)
386 	, m_wrapS					(wrapS)
387 	, m_wrapT					(wrapT)
388 	, m_format					(format)
389 	, m_dataType				(dataType)
390 	, m_width					(width)
391 	, m_height					(height)
392 	, m_renderer				(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
393 	, m_caseNdx					(0)
394 {
395 }
396 
TextureCubeFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,deUint32 minFilter,deUint32 magFilter,deUint32 wrapS,deUint32 wrapT,const std::vector<std::string> & filenames)397 TextureCubeFilteringCase::TextureCubeFilteringCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, const std::vector<std::string>& filenames)
398 	: TestCase					(testCtx, name, desc)
399 	, m_renderCtx				(renderCtx)
400 	, m_renderCtxInfo			(ctxInfo)
401 	, m_minFilter				(minFilter)
402 	, m_magFilter				(magFilter)
403 	, m_wrapS					(wrapS)
404 	, m_wrapT					(wrapT)
405 	, m_format					(GL_NONE)
406 	, m_dataType				(GL_NONE)
407 	, m_width					(0)
408 	, m_height					(0)
409 	, m_filenames				(filenames)
410 	, m_renderer				(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
411 	, m_caseNdx					(0)
412 {
413 }
414 
~TextureCubeFilteringCase(void)415 TextureCubeFilteringCase::~TextureCubeFilteringCase (void)
416 {
417 	deinit();
418 }
419 
init(void)420 void TextureCubeFilteringCase::init (void)
421 {
422 	try
423 	{
424 		if (!m_filenames.empty())
425 		{
426 			m_textures.reserve(1);
427 			m_textures.push_back(glu::TextureCube::create(m_renderCtx, m_renderCtxInfo, m_testCtx.getArchive(), (int)m_filenames.size() / 6, m_filenames));
428 		}
429 		else
430 		{
431 			DE_ASSERT(m_width == m_height);
432 			m_textures.reserve(2);
433 			for (int ndx = 0; ndx < 2; ndx++)
434 				m_textures.push_back(new glu::TextureCube(m_renderCtx, m_format, m_dataType, m_width));
435 
436 			const bool				mipmaps		= deIsPowerOfTwo32(m_width) && deIsPowerOfTwo32(m_height);
437 			const int				numLevels	= mipmaps ? deLog2Floor32(de::max(m_width, m_height))+1 : 1;
438 			tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
439 			tcu::Vec4				cBias		= fmtInfo.valueMin;
440 			tcu::Vec4				cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
441 
442 			// Fill first with gradient texture.
443 			static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] =
444 			{
445 				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
446 				{ tcu::Vec4(0.5f, 0.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
447 				{ tcu::Vec4(0.0f, 0.5f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
448 				{ tcu::Vec4(0.0f, 0.0f, 0.5f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
449 				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 0.5f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
450 				{ tcu::Vec4(0.5f, 0.5f, 0.5f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }  // positive z
451 			};
452 			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
453 			{
454 				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
455 				{
456 					m_textures[0]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
457 					tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), gradients[face][0]*cScale + cBias, gradients[face][1]*cScale + cBias);
458 				}
459 			}
460 
461 			// Fill second with grid texture.
462 			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
463 			{
464 				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
465 				{
466 					deUint32	step	= 0x00ffffff / (numLevels*tcu::CUBEFACE_LAST);
467 					deUint32	rgb		= step*levelNdx*face;
468 					deUint32	colorA	= 0xff000000 | rgb;
469 					deUint32	colorB	= 0xff000000 | ~rgb;
470 
471 					m_textures[1]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
472 					tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
473 				}
474 			}
475 
476 			// Upload.
477 			for (std::vector<glu::TextureCube*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
478 				(*i)->upload();
479 		}
480 
481 		// Compute cases
482 		{
483 			const glu::TextureCube*	tex0	= m_textures[0];
484 			const glu::TextureCube* tex1	= m_textures.size() > 1 ? m_textures[1] : tex0;
485 
486 			// \note Coordinates are chosen so that they only sample face interior. ES3 has changed edge sampling behavior
487 			//		 and hw is not expected to implement both modes.
488 			m_cases.push_back(FilterCase(tex0, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f, 0.8f)));	// minification
489 			m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.5f, 0.65f), tcu::Vec2(0.8f, 0.8f)));		// magnification
490 			m_cases.push_back(FilterCase(tex1, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f, 0.8f)));	// minification
491 			m_cases.push_back(FilterCase(tex1, tcu::Vec2(0.2f, 0.2f), tcu::Vec2(0.6f, 0.5f)));		// magnification
492 		}
493 
494 		m_caseNdx = 0;
495 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
496 	}
497 	catch (...)
498 	{
499 		// Clean up to save memory.
500 		TextureCubeFilteringCase::deinit();
501 		throw;
502 	}
503 }
504 
deinit(void)505 void TextureCubeFilteringCase::deinit (void)
506 {
507 	for (std::vector<glu::TextureCube*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
508 		delete *i;
509 	m_textures.clear();
510 
511 	m_renderer.clear();
512 	m_cases.clear();
513 }
514 
getFaceDesc(const tcu::CubeFace face)515 static const char* getFaceDesc (const tcu::CubeFace face)
516 {
517 	switch (face)
518 	{
519 		case tcu::CUBEFACE_NEGATIVE_X:	return "-X";
520 		case tcu::CUBEFACE_POSITIVE_X:	return "+X";
521 		case tcu::CUBEFACE_NEGATIVE_Y:	return "-Y";
522 		case tcu::CUBEFACE_POSITIVE_Y:	return "+Y";
523 		case tcu::CUBEFACE_NEGATIVE_Z:	return "-Z";
524 		case tcu::CUBEFACE_POSITIVE_Z:	return "+Z";
525 		default:
526 			DE_ASSERT(false);
527 			return DE_NULL;
528 	}
529 }
530 
iterate(void)531 TextureCubeFilteringCase::IterateResult TextureCubeFilteringCase::iterate (void)
532 {
533 	const glw::Functions&			gl				= m_renderCtx.getFunctions();
534 	const int						viewportSize	= 28;
535 	const RandomViewport			viewport		(m_renderCtx.getRenderTarget(), viewportSize, viewportSize, deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
536 	const tcu::ScopedLogSection		iterSection		(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
537 	const FilterCase&				curCase			= m_cases[m_caseNdx];
538 	const tcu::TextureFormat&		texFmt			= curCase.texture->getRefTexture().getFormat();
539 	const tcu::TextureFormatInfo	fmtInfo			= tcu::getTextureFormatInfo(texFmt);
540 	ReferenceParams					sampleParams	(TEXTURETYPE_CUBE);
541 
542 	if (viewport.width < viewportSize || viewport.height < viewportSize)
543 		throw tcu::NotSupportedError("Too small render target", DE_NULL, __FILE__, __LINE__);
544 
545 	// Setup texture
546 	gl.bindTexture	(GL_TEXTURE_CUBE_MAP, curCase.texture->getGLTexture());
547 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	m_minFilter);
548 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	m_magFilter);
549 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		m_wrapS);
550 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		m_wrapT);
551 
552 	// Other state
553 	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
554 
555 	// Params for reference computation.
556 	sampleParams.sampler					= glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
557 	sampleParams.sampler.seamlessCubeMap	= true;
558 	sampleParams.samplerType				= getSamplerType(texFmt);
559 	sampleParams.colorBias					= fmtInfo.lookupBias;
560 	sampleParams.colorScale					= fmtInfo.lookupScale;
561 	sampleParams.lodMode					= LODMODE_EXACT;
562 
563 	m_testCtx.getLog() << TestLog::Message << "Coordinates: " << curCase.bottomLeft << " -> " << curCase.topRight << TestLog::EndMessage;
564 
565 	for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
566 	{
567 		const tcu::CubeFace		face		= tcu::CubeFace(faceNdx);
568 		tcu::Surface			result		(viewport.width, viewport.height);
569 		vector<float>			texCoord;
570 
571 		computeQuadTexCoordCube(texCoord, face, curCase.bottomLeft, curCase.topRight);
572 
573 		m_testCtx.getLog() << TestLog::Message << "Face " << getFaceDesc(face) << TestLog::EndMessage;
574 
575 		// \todo Log texture coordinates.
576 
577 		m_renderer.renderQuad(0, &texCoord[0], sampleParams);
578 		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
579 
580 		glu::readPixels(m_renderCtx, viewport.x, viewport.y, result.getAccess());
581 		GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
582 
583 		{
584 			const bool				isNearestOnly	= m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
585 			const tcu::PixelFormat	pixelFormat		= m_renderCtx.getRenderTarget().getPixelFormat();
586 			const tcu::IVec4		colorBits		= max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2), tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
587 			tcu::LodPrecision		lodPrecision;
588 			tcu::LookupPrecision	lookupPrecision;
589 
590 			lodPrecision.derivateBits		= 5;
591 			lodPrecision.lodBits			= 3;
592 			lookupPrecision.colorThreshold	= tcu::computeFixedPointThreshold(colorBits) / sampleParams.colorScale;
593 			lookupPrecision.coordBits		= tcu::IVec3(9,9,9); // mediump interpolation
594 			lookupPrecision.uvwBits			= tcu::IVec3(5,5,0);
595 			lookupPrecision.colorMask		= getCompareMask(pixelFormat);
596 
597 			const bool isOk = verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(),
598 												  &texCoord[0], sampleParams, lookupPrecision, lodPrecision, pixelFormat);
599 
600 			if (!isOk)
601 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
602 		}
603 	}
604 
605 	m_caseNdx += 1;
606 	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
607 }
608 
TextureFilteringTests(Context & context)609 TextureFilteringTests::TextureFilteringTests (Context& context)
610 	: TestCaseGroup(context, "filtering", "Texture Filtering Tests")
611 {
612 }
613 
~TextureFilteringTests(void)614 TextureFilteringTests::~TextureFilteringTests (void)
615 {
616 }
617 
init(void)618 void TextureFilteringTests::init (void)
619 {
620 	tcu::TestCaseGroup* group2D		= new tcu::TestCaseGroup(m_testCtx, "2d",	"2D Texture Filtering");
621 	tcu::TestCaseGroup*	groupCube	= new tcu::TestCaseGroup(m_testCtx, "cube",	"Cube Map Filtering");
622 	addChild(group2D);
623 	addChild(groupCube);
624 
625 	static const struct
626 	{
627 		const char*		name;
628 		deUint32		mode;
629 	} wrapModes[] =
630 	{
631 		{ "clamp",		GL_CLAMP_TO_EDGE },
632 		{ "repeat",		GL_REPEAT },
633 		{ "mirror",		GL_MIRRORED_REPEAT }
634 	};
635 
636 	static const struct
637 	{
638 		const char*		name;
639 		deUint32		mode;
640 	} minFilterModes[] =
641 	{
642 		{ "nearest",				GL_NEAREST					},
643 		{ "linear",					GL_LINEAR					},
644 		{ "nearest_mipmap_nearest",	GL_NEAREST_MIPMAP_NEAREST	},
645 		{ "linear_mipmap_nearest",	GL_LINEAR_MIPMAP_NEAREST	},
646 		{ "nearest_mipmap_linear",	GL_NEAREST_MIPMAP_LINEAR	},
647 		{ "linear_mipmap_linear",	GL_LINEAR_MIPMAP_LINEAR		}
648 	};
649 
650 	static const struct
651 	{
652 		const char*		name;
653 		deUint32		mode;
654 	} magFilterModes[] =
655 	{
656 		{ "nearest",	GL_NEAREST },
657 		{ "linear",		GL_LINEAR }
658 	};
659 
660 	static const struct
661 	{
662 		const char*		name;
663 		int				width;
664 		int				height;
665 	} sizes2D[] =
666 	{
667 		{ "pot",		32, 64 },
668 		{ "npot",		31, 55 }
669 	};
670 
671 	static const struct
672 	{
673 		const char*		name;
674 		int				width;
675 		int				height;
676 	} sizesCube[] =
677 	{
678 		{ "pot",		64, 64 },
679 		{ "npot",		63, 63 }
680 	};
681 
682 	static const struct
683 	{
684 		const char*		name;
685 		deUint32		format;
686 		deUint32		dataType;
687 	} formats[] =
688 	{
689 		{ "rgba8888",	GL_RGBA,			GL_UNSIGNED_BYTE			},
690 		{ "rgb888",		GL_RGB,				GL_UNSIGNED_BYTE			},
691 		{ "rgba4444",	GL_RGBA,			GL_UNSIGNED_SHORT_4_4_4_4	},
692 		{ "l8",			GL_LUMINANCE,		GL_UNSIGNED_BYTE			}
693 	};
694 
695 #define FOR_EACH(ITERATOR, ARRAY, BODY)	\
696 	for (int ITERATOR = 0; ITERATOR < DE_LENGTH_OF_ARRAY(ARRAY); ITERATOR++)	\
697 		BODY
698 
699 	// 2D cases.
700 	FOR_EACH(minFilter,		minFilterModes,
701 	FOR_EACH(magFilter,		magFilterModes,
702 	FOR_EACH(wrapMode,		wrapModes,
703 	FOR_EACH(format,		formats,
704 	FOR_EACH(size,			sizes2D,
705 		{
706 			bool isMipmap		= minFilterModes[minFilter].mode != GL_NEAREST && minFilterModes[minFilter].mode != GL_LINEAR;
707 			bool isClamp		= wrapModes[wrapMode].mode == GL_CLAMP_TO_EDGE;
708 			bool isRepeat		= wrapModes[wrapMode].mode == GL_REPEAT;
709 			bool isMagNearest	= magFilterModes[magFilter].mode == GL_NEAREST;
710 			bool isPotSize		= deIsPowerOfTwo32(sizes2D[size].width) && deIsPowerOfTwo32(sizes2D[size].height);
711 
712 			if ((isMipmap || !isClamp) && !isPotSize)
713 				continue; // Not supported.
714 
715 			if ((format != 0) && !(!isMipmap || (isRepeat && isMagNearest)))
716 				continue; // Skip.
717 
718 			string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name + "_" + formats[format].name;
719 
720 			if (!isMipmap)
721 				name += string("_") + sizes2D[size].name;
722 
723 			group2D->addChild(new Texture2DFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
724 														 name.c_str(), "",
725 														 minFilterModes[minFilter].mode,
726 														 magFilterModes[magFilter].mode,
727 														 wrapModes[wrapMode].mode,
728 														 wrapModes[wrapMode].mode,
729 														 formats[format].format, formats[format].dataType,
730 														 sizes2D[size].width, sizes2D[size].height));
731 		})))));
732 
733 	// 2D ETC1 texture cases.
734 	{
735 		std::vector<std::string> filenames;
736 		for (int i = 0; i <= 7; i++)
737 			filenames.push_back(string("data/etc1/photo_helsinki_mip_") + de::toString(i) + ".pkm");
738 
739 		FOR_EACH(minFilter,		minFilterModes,
740 		FOR_EACH(magFilter,		magFilterModes,
741 		FOR_EACH(wrapMode,		wrapModes,
742 			{
743 				string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name + "_etc1";
744 
745 				group2D->addChild(new Texture2DFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
746 															 name.c_str(), "",
747 															 minFilterModes[minFilter].mode,
748 															 magFilterModes[magFilter].mode,
749 															 wrapModes[wrapMode].mode,
750 															 wrapModes[wrapMode].mode,
751 															 filenames));
752 			})));
753 	}
754 
755 	// Cubemap cases.
756 	FOR_EACH(minFilter,		minFilterModes,
757 	FOR_EACH(magFilter,		magFilterModes,
758 	FOR_EACH(wrapMode,		wrapModes,
759 	FOR_EACH(format,		formats,
760 	FOR_EACH(size,			sizesCube,
761 		{
762 			bool isMipmap		= minFilterModes[minFilter].mode != GL_NEAREST && minFilterModes[minFilter].mode != GL_LINEAR;
763 			bool isClamp		= wrapModes[wrapMode].mode == GL_CLAMP_TO_EDGE;
764 			bool isRepeat		= wrapModes[wrapMode].mode == GL_REPEAT;
765 			bool isMagNearest	= magFilterModes[magFilter].mode == GL_NEAREST;
766 			bool isPotSize		= deIsPowerOfTwo32(sizesCube[size].width) && deIsPowerOfTwo32(sizesCube[size].height);
767 
768 			if ((isMipmap || !isClamp) && !isPotSize)
769 				continue; // Not supported.
770 
771 			if (format != 0 && !(!isMipmap || (isRepeat && isMagNearest)))
772 				continue; // Skip.
773 
774 			string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name + "_" + formats[format].name;
775 
776 			if (!isMipmap)
777 				name += string("_") + sizesCube[size].name;
778 
779 			groupCube->addChild(new TextureCubeFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
780 															 name.c_str(), "",
781 															 minFilterModes[minFilter].mode,
782 															 magFilterModes[magFilter].mode,
783 															 wrapModes[wrapMode].mode,
784 															 wrapModes[wrapMode].mode,
785 															 formats[format].format, formats[format].dataType,
786 															 sizesCube[size].width, sizesCube[size].height));
787 		})))));
788 
789 	// Cubemap ETC1 cases
790 	{
791 		static const char* faceExt[] = { "neg_x", "pos_x", "neg_y", "pos_y", "neg_z", "pos_z" };
792 
793 		const int		numLevels	= 7;
794 		vector<string>	filenames;
795 		for (int level = 0; level < numLevels; level++)
796 			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
797 				filenames.push_back(string("data/etc1/skybox_") + faceExt[face] + "_mip_" + de::toString(level) + ".pkm");
798 
799 		FOR_EACH(minFilter,		minFilterModes,
800 		FOR_EACH(magFilter,		magFilterModes,
801 			{
802 				string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_clamp_etc1";
803 
804 				groupCube->addChild(new TextureCubeFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
805 																 name.c_str(), "",
806 																 minFilterModes[minFilter].mode,
807 																 magFilterModes[magFilter].mode,
808 																 GL_CLAMP_TO_EDGE,
809 																 GL_CLAMP_TO_EDGE,
810 																 filenames));
811 			}));
812 	}
813 }
814 
815 } // Functional
816 } // gles2
817 } // deqp
818