1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.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 "es3fTextureFilteringTests.hpp"
25#include "glsTextureTestUtil.hpp"
26#include "gluPixelTransfer.hpp"
27#include "gluTexture.hpp"
28#include "gluTextureUtil.hpp"
29#include "tcuTextureUtil.hpp"
30#include "tcuImageCompare.hpp"
31#include "tcuTexLookupVerifier.hpp"
32#include "tcuVectorUtil.hpp"
33#include "deStringUtil.hpp"
34#include "deString.h"
35#include "glwFunctions.hpp"
36#include "glwEnums.hpp"
37
38namespace deqp
39{
40namespace gles3
41{
42namespace Functional
43{
44
45using std::vector;
46using std::string;
47using tcu::TestLog;
48using namespace gls::TextureTestUtil;
49
50enum
51{
52	TEX2D_VIEWPORT_WIDTH		= 64,
53	TEX2D_VIEWPORT_HEIGHT		= 64,
54	TEX2D_MIN_VIEWPORT_WIDTH	= 64,
55	TEX2D_MIN_VIEWPORT_HEIGHT	= 64,
56
57	TEX3D_VIEWPORT_WIDTH		= 64,
58	TEX3D_VIEWPORT_HEIGHT		= 64,
59	TEX3D_MIN_VIEWPORT_WIDTH	= 64,
60	TEX3D_MIN_VIEWPORT_HEIGHT	= 64
61};
62
63class Texture2DFilteringCase : public tcu::TestCase
64{
65public:
66									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 internalFormat, int width, int height);
67									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);
68									~Texture2DFilteringCase		(void);
69
70	void							init						(void);
71	void							deinit						(void);
72	IterateResult					iterate						(void);
73
74private:
75									Texture2DFilteringCase		(const Texture2DFilteringCase& other);
76	Texture2DFilteringCase&			operator=					(const Texture2DFilteringCase& other);
77
78	glu::RenderContext&				m_renderCtx;
79	const glu::ContextInfo&			m_renderCtxInfo;
80
81	const deUint32					m_minFilter;
82	const deUint32					m_magFilter;
83	const deUint32					m_wrapS;
84	const deUint32					m_wrapT;
85
86	const deUint32					m_internalFormat;
87	const int						m_width;
88	const int						m_height;
89
90	const std::vector<std::string>	m_filenames;
91
92	struct FilterCase
93	{
94		const glu::Texture2D*	texture;
95		tcu::Vec2				minCoord;
96		tcu::Vec2				maxCoord;
97
98		FilterCase (void)
99			: texture(DE_NULL)
100		{
101		}
102
103		FilterCase (const glu::Texture2D* tex_, const tcu::Vec2& minCoord_, const tcu::Vec2& maxCoord_)
104			: texture	(tex_)
105			, minCoord	(minCoord_)
106			, maxCoord	(maxCoord_)
107		{
108		}
109	};
110
111	std::vector<glu::Texture2D*>	m_textures;
112	std::vector<FilterCase>			m_cases;
113
114	TextureRenderer					m_renderer;
115
116	int								m_caseNdx;
117};
118
119Texture2DFilteringCase::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 internalFormat, int width, int height)
120	: TestCase			(testCtx, name, desc)
121	, m_renderCtx		(renderCtx)
122	, m_renderCtxInfo	(ctxInfo)
123	, m_minFilter		(minFilter)
124	, m_magFilter		(magFilter)
125	, m_wrapS			(wrapS)
126	, m_wrapT			(wrapT)
127	, m_internalFormat	(internalFormat)
128	, m_width			(width)
129	, m_height			(height)
130	, m_renderer		(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
131	, m_caseNdx			(0)
132{
133}
134
135Texture2DFilteringCase::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)
136	: TestCase			(testCtx, name, desc)
137	, m_renderCtx		(renderCtx)
138	, m_renderCtxInfo	(ctxInfo)
139	, m_minFilter		(minFilter)
140	, m_magFilter		(magFilter)
141	, m_wrapS			(wrapS)
142	, m_wrapT			(wrapT)
143	, m_internalFormat	(GL_NONE)
144	, m_width			(0)
145	, m_height			(0)
146	, m_filenames		(filenames)
147	, m_renderer		(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
148	, m_caseNdx			(0)
149{
150}
151
152Texture2DFilteringCase::~Texture2DFilteringCase (void)
153{
154	deinit();
155}
156
157void 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_internalFormat, m_width, m_height));
172
173			const bool						mipmaps		= true;
174			const int						numLevels	= mipmaps ? deLog2Floor32(de::max(m_width, m_height))+1 : 1;
175			const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
176			const tcu::Vec4					cBias		= fmtInfo.valueMin;
177			const 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.0f, 0.0f, 0.0f, 1.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>(TEX2D_VIEWPORT_WIDTH, m_renderCtx.getRenderTarget().getWidth());
224			const float	viewportH	= (float)de::min<int>(TEX2D_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
251void 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
261Texture2DFilteringCase::IterateResult Texture2DFilteringCase::iterate (void)
262{
263	const glw::Functions&			gl			= m_renderCtx.getFunctions();
264	const RandomViewport			viewport	(m_renderCtx.getRenderTarget(), TEX2D_VIEWPORT_WIDTH, TEX2D_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 < TEX2D_MIN_VIEWPORT_WIDTH || viewport.height < TEX2D_MIN_VIEWPORT_HEIGHT)
274		throw tcu::NotSupportedError("Too small render target", "", __FILE__, __LINE__);
275
276	// Setup params for reference.
277	refParams.sampler		= glu::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		= 18;
305		lodPrecision.lodBits			= 6;
306		lookupPrecision.colorThreshold	= tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
307		lookupPrecision.coordBits		= tcu::IVec3(20,20,0);
308		lookupPrecision.uvwBits			= tcu::IVec3(7,7,0);
309		lookupPrecision.colorMask		= getCompareMask(pixelFormat);
310
311		const bool isHighQuality = verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
312													   &texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
313
314		if (!isHighQuality)
315		{
316			// Evaluate against lower precision requirements.
317			lodPrecision.lodBits	= 4;
318			lookupPrecision.uvwBits	= tcu::IVec3(4,4,0);
319
320			m_testCtx.getLog() << TestLog::Message << "Warning: Verification against high precision requirements failed, trying with lower requirements." << TestLog::EndMessage;
321
322			const bool isOk = verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
323												  &texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
324
325			if (!isOk)
326			{
327				m_testCtx.getLog() << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
328				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
329			}
330			else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
331				m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality filtering result");
332		}
333	}
334
335	m_caseNdx += 1;
336	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
337}
338
339class TextureCubeFilteringCase : public tcu::TestCase
340{
341public:
342									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, bool onlySampleFaceInterior, deUint32 internalFormat, int width, int height);
343									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, bool onlySampleFaceInterior, const std::vector<std::string>& filenames);
344									~TextureCubeFilteringCase	(void);
345
346	void							init						(void);
347	void							deinit						(void);
348	IterateResult					iterate						(void);
349
350private:
351									TextureCubeFilteringCase	(const TextureCubeFilteringCase& other);
352	TextureCubeFilteringCase&		operator=					(const TextureCubeFilteringCase& other);
353
354	glu::RenderContext&				m_renderCtx;
355	const glu::ContextInfo&			m_renderCtxInfo;
356
357	const deUint32					m_minFilter;
358	const deUint32					m_magFilter;
359	const deUint32					m_wrapS;
360	const deUint32					m_wrapT;
361	const bool						m_onlySampleFaceInterior; //!< If true, we avoid sampling anywhere near a face's edges.
362
363	const deUint32					m_internalFormat;
364	const int						m_width;
365	const int						m_height;
366
367	const std::vector<std::string>	m_filenames;
368
369	struct FilterCase
370	{
371		const glu::TextureCube*	texture;
372		tcu::Vec2				bottomLeft;
373		tcu::Vec2				topRight;
374
375		FilterCase (void)
376			: texture(DE_NULL)
377		{
378		}
379
380		FilterCase (const glu::TextureCube* tex_, const tcu::Vec2& bottomLeft_, const tcu::Vec2& topRight_)
381			: texture	(tex_)
382			, bottomLeft(bottomLeft_)
383			, topRight	(topRight_)
384		{
385		}
386	};
387
388	std::vector<glu::TextureCube*>	m_textures;
389	std::vector<FilterCase>			m_cases;
390
391	TextureRenderer					m_renderer;
392
393	int								m_caseNdx;
394};
395
396TextureCubeFilteringCase::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, bool onlySampleFaceInterior, deUint32 internalFormat, int width, int height)
397	: TestCase					(testCtx, name, desc)
398	, m_renderCtx				(renderCtx)
399	, m_renderCtxInfo			(ctxInfo)
400	, m_minFilter				(minFilter)
401	, m_magFilter				(magFilter)
402	, m_wrapS					(wrapS)
403	, m_wrapT					(wrapT)
404	, m_onlySampleFaceInterior	(onlySampleFaceInterior)
405	, m_internalFormat			(internalFormat)
406	, m_width					(width)
407	, m_height					(height)
408	, m_renderer				(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
409	, m_caseNdx					(0)
410{
411}
412
413TextureCubeFilteringCase::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, bool onlySampleFaceInterior, const std::vector<std::string>& filenames)
414	: TestCase					(testCtx, name, desc)
415	, m_renderCtx				(renderCtx)
416	, m_renderCtxInfo			(ctxInfo)
417	, m_minFilter				(minFilter)
418	, m_magFilter				(magFilter)
419	, m_wrapS					(wrapS)
420	, m_wrapT					(wrapT)
421	, m_onlySampleFaceInterior	(onlySampleFaceInterior)
422	, m_internalFormat			(GL_NONE)
423	, m_width					(0)
424	, m_height					(0)
425	, m_filenames				(filenames)
426	, m_renderer				(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
427	, m_caseNdx					(0)
428{
429}
430
431TextureCubeFilteringCase::~TextureCubeFilteringCase (void)
432{
433	deinit();
434}
435
436void TextureCubeFilteringCase::init (void)
437{
438	try
439	{
440		if (!m_filenames.empty())
441		{
442			m_textures.reserve(1);
443			m_textures.push_back(glu::TextureCube::create(m_renderCtx, m_renderCtxInfo, m_testCtx.getArchive(), (int)m_filenames.size() / 6, m_filenames));
444		}
445		else
446		{
447			DE_ASSERT(m_width == m_height);
448			m_textures.reserve(2);
449			for (int ndx = 0; ndx < 2; ndx++)
450				m_textures.push_back(new glu::TextureCube(m_renderCtx, m_internalFormat, m_width));
451
452			const int				numLevels	= deLog2Floor32(de::max(m_width, m_height))+1;
453			tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
454			tcu::Vec4				cBias		= fmtInfo.valueMin;
455			tcu::Vec4				cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
456
457			// Fill first with gradient texture.
458			static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] =
459			{
460				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
461				{ tcu::Vec4(0.5f, 0.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
462				{ tcu::Vec4(0.0f, 0.5f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
463				{ tcu::Vec4(0.0f, 0.0f, 0.5f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
464				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 0.5f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
465				{ tcu::Vec4(0.5f, 0.5f, 0.5f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }  // positive z
466			};
467			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
468			{
469				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
470				{
471					m_textures[0]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
472					tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), gradients[face][0]*cScale + cBias, gradients[face][1]*cScale + cBias);
473				}
474			}
475
476			// Fill second with grid texture.
477			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
478			{
479				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
480				{
481					deUint32	step	= 0x00ffffff / (numLevels*tcu::CUBEFACE_LAST);
482					deUint32	rgb		= step*levelNdx*face;
483					deUint32	colorA	= 0xff000000 | rgb;
484					deUint32	colorB	= 0xff000000 | ~rgb;
485
486					m_textures[1]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
487					tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
488				}
489			}
490
491			// Upload.
492			for (std::vector<glu::TextureCube*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
493				(*i)->upload();
494		}
495
496		// Compute cases
497		{
498			const glu::TextureCube*	tex0	= m_textures[0];
499			const glu::TextureCube* tex1	= m_textures.size() > 1 ? m_textures[1] : tex0;
500
501			if (m_onlySampleFaceInterior)
502			{
503				m_cases.push_back(FilterCase(tex0, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f,  0.8f)));	// minification
504				m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.5f, 0.65f), tcu::Vec2(0.8f,  0.8f)));	// magnification
505				m_cases.push_back(FilterCase(tex1, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f,  0.8f)));	// minification
506				m_cases.push_back(FilterCase(tex1, tcu::Vec2(0.2f, 0.2f), tcu::Vec2(0.6f,  0.5f)));		// magnification
507			}
508			else
509			{
510				if (m_renderCtx.getRenderTarget().getNumSamples() == 0)
511					m_cases.push_back(FilterCase(tex0, tcu::Vec2(-1.25f, -1.2f), tcu::Vec2(1.2f, 1.25f)));	// minification
512				else
513					m_cases.push_back(FilterCase(tex0, tcu::Vec2(-1.19f, -1.3f), tcu::Vec2(1.1f, 1.35f)));	// minification - w/ tweak to avoid hitting triangle edges with face switchpoint
514
515				m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.8f, 0.8f), tcu::Vec2(1.25f, 1.20f)));	// magnification
516				m_cases.push_back(FilterCase(tex1, tcu::Vec2(-1.19f, -1.3f), tcu::Vec2(1.1f, 1.35f)));	// minification
517				m_cases.push_back(FilterCase(tex1, tcu::Vec2(-1.2f, -1.1f), tcu::Vec2(-0.8f, -0.8f)));	// magnification
518			}
519		}
520
521		m_caseNdx = 0;
522		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
523	}
524	catch (...)
525	{
526		// Clean up to save memory.
527		TextureCubeFilteringCase::deinit();
528		throw;
529	}
530}
531
532void TextureCubeFilteringCase::deinit (void)
533{
534	for (std::vector<glu::TextureCube*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
535		delete *i;
536	m_textures.clear();
537
538	m_renderer.clear();
539	m_cases.clear();
540}
541
542static const char* getFaceDesc (const tcu::CubeFace face)
543{
544	switch (face)
545	{
546		case tcu::CUBEFACE_NEGATIVE_X:	return "-X";
547		case tcu::CUBEFACE_POSITIVE_X:	return "+X";
548		case tcu::CUBEFACE_NEGATIVE_Y:	return "-Y";
549		case tcu::CUBEFACE_POSITIVE_Y:	return "+Y";
550		case tcu::CUBEFACE_NEGATIVE_Z:	return "-Z";
551		case tcu::CUBEFACE_POSITIVE_Z:	return "+Z";
552		default:
553			DE_ASSERT(false);
554			return DE_NULL;
555	}
556}
557
558TextureCubeFilteringCase::IterateResult TextureCubeFilteringCase::iterate (void)
559{
560	const glw::Functions&			gl				= m_renderCtx.getFunctions();
561	const int						viewportSize	= 28;
562	const RandomViewport			viewport		(m_renderCtx.getRenderTarget(), viewportSize, viewportSize, deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
563	const tcu::ScopedLogSection		iterSection		(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
564	const FilterCase&				curCase			= m_cases[m_caseNdx];
565	const tcu::TextureFormat&		texFmt			= curCase.texture->getRefTexture().getFormat();
566	const tcu::TextureFormatInfo	fmtInfo			= tcu::getTextureFormatInfo(texFmt);
567	ReferenceParams					sampleParams	(TEXTURETYPE_CUBE);
568
569	if (viewport.width < viewportSize || viewport.height < viewportSize)
570		throw tcu::NotSupportedError("Too small render target", DE_NULL, __FILE__, __LINE__);
571
572	// Setup texture
573	gl.bindTexture	(GL_TEXTURE_CUBE_MAP, curCase.texture->getGLTexture());
574	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	m_minFilter);
575	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	m_magFilter);
576	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		m_wrapS);
577	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		m_wrapT);
578
579	// Other state
580	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
581
582	// Params for reference computation.
583	sampleParams.sampler					= glu::mapGLSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, m_minFilter, m_magFilter);
584	sampleParams.sampler.seamlessCubeMap	= true;
585	sampleParams.samplerType				= getSamplerType(texFmt);
586	sampleParams.colorBias					= fmtInfo.lookupBias;
587	sampleParams.colorScale					= fmtInfo.lookupScale;
588	sampleParams.lodMode					= LODMODE_EXACT;
589
590	m_testCtx.getLog() << TestLog::Message << "Coordinates: " << curCase.bottomLeft << " -> " << curCase.topRight << TestLog::EndMessage;
591
592	for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
593	{
594		const tcu::CubeFace		face		= tcu::CubeFace(faceNdx);
595		tcu::Surface			result		(viewport.width, viewport.height);
596		vector<float>			texCoord;
597
598		computeQuadTexCoordCube(texCoord, face, curCase.bottomLeft, curCase.topRight);
599
600		m_testCtx.getLog() << TestLog::Message << "Face " << getFaceDesc(face) << TestLog::EndMessage;
601
602		// \todo Log texture coordinates.
603
604		m_renderer.renderQuad(0, &texCoord[0], sampleParams);
605		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
606
607		glu::readPixels(m_renderCtx, viewport.x, viewport.y, result.getAccess());
608		GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
609
610		{
611			const bool				isNearestOnly	= m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
612			const tcu::PixelFormat	pixelFormat		= m_renderCtx.getRenderTarget().getPixelFormat();
613			const tcu::IVec4		colorBits		= max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2), tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
614			tcu::LodPrecision		lodPrecision;
615			tcu::LookupPrecision	lookupPrecision;
616
617			lodPrecision.derivateBits		= 10;
618			lodPrecision.lodBits			= 5;
619			lookupPrecision.colorThreshold	= tcu::computeFixedPointThreshold(colorBits) / sampleParams.colorScale;
620			lookupPrecision.coordBits		= tcu::IVec3(10,10,10);
621			lookupPrecision.uvwBits			= tcu::IVec3(6,6,0);
622			lookupPrecision.colorMask		= getCompareMask(pixelFormat);
623
624			const bool isHighQuality = verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(),
625														   &texCoord[0], sampleParams, lookupPrecision, lodPrecision, pixelFormat);
626
627			if (!isHighQuality)
628			{
629				// Evaluate against lower precision requirements.
630				lodPrecision.lodBits	= 4;
631				lookupPrecision.uvwBits	= tcu::IVec3(4,4,0);
632
633				m_testCtx.getLog() << TestLog::Message << "Warning: Verification against high precision requirements failed, trying with lower requirements." << TestLog::EndMessage;
634
635				const bool isOk = verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(),
636													  &texCoord[0], sampleParams, lookupPrecision, lodPrecision, pixelFormat);
637
638				if (!isOk)
639				{
640					m_testCtx.getLog() << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
641					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
642				}
643				else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
644					m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality filtering result");
645			}
646		}
647	}
648
649	m_caseNdx += 1;
650	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
651}
652
653// 2D array filtering
654
655class Texture2DArrayFilteringCase : public TestCase
656{
657public:
658									Texture2DArrayFilteringCase		(Context& context, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 internalFormat, int width, int height, int numLayers);
659									~Texture2DArrayFilteringCase	(void);
660
661	void							init							(void);
662	void							deinit							(void);
663	IterateResult					iterate							(void);
664
665private:
666									Texture2DArrayFilteringCase		(const Texture2DArrayFilteringCase&);
667	Texture2DArrayFilteringCase&	operator=						(const Texture2DArrayFilteringCase&);
668
669	const deUint32					m_minFilter;
670	const deUint32					m_magFilter;
671	const deUint32					m_wrapS;
672	const deUint32					m_wrapT;
673
674	const deUint32					m_internalFormat;
675	const int						m_width;
676	const int						m_height;
677	const int						m_numLayers;
678
679	struct FilterCase
680	{
681		const glu::Texture2DArray*	texture;
682		tcu::Vec2					lod;
683		tcu::Vec2					offset;
684		tcu::Vec2					layerRange;
685
686		FilterCase (void)
687			: texture(DE_NULL)
688		{
689		}
690
691		FilterCase (const glu::Texture2DArray* tex_, const tcu::Vec2& lod_, const tcu::Vec2& offset_, const tcu::Vec2& layerRange_)
692			: texture	(tex_)
693			, lod		(lod_)
694			, offset	(offset_)
695			, layerRange(layerRange_)
696		{
697		}
698	};
699
700	glu::Texture2DArray*			m_gradientTex;
701	glu::Texture2DArray*			m_gridTex;
702
703	TextureRenderer					m_renderer;
704
705	std::vector<FilterCase>			m_cases;
706	int								m_caseNdx;
707};
708
709Texture2DArrayFilteringCase::Texture2DArrayFilteringCase (Context& context, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 internalFormat, int width, int height, int numLayers)
710	: TestCase			(context, name, desc)
711	, m_minFilter		(minFilter)
712	, m_magFilter		(magFilter)
713	, m_wrapS			(wrapS)
714	, m_wrapT			(wrapT)
715	, m_internalFormat	(internalFormat)
716	, m_width			(width)
717	, m_height			(height)
718	, m_numLayers		(numLayers)
719	, m_gradientTex		(DE_NULL)
720	, m_gridTex			(DE_NULL)
721	, m_renderer		(context.getRenderContext(), context.getTestContext().getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
722	, m_caseNdx			(0)
723{
724}
725
726Texture2DArrayFilteringCase::~Texture2DArrayFilteringCase (void)
727{
728	Texture2DArrayFilteringCase::deinit();
729}
730
731void Texture2DArrayFilteringCase::init (void)
732{
733	try
734	{
735		const tcu::TextureFormat		texFmt		= glu::mapGLInternalFormat(m_internalFormat);
736		const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(texFmt);
737		const tcu::Vec4					cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
738		const tcu::Vec4					cBias		= fmtInfo.valueMin;
739		const int						numLevels	= deLog2Floor32(de::max(m_width, m_height)) + 1;
740
741		// Create textures.
742		m_gradientTex	= new glu::Texture2DArray(m_context.getRenderContext(), m_internalFormat, m_width, m_height, m_numLayers);
743		m_gridTex		= new glu::Texture2DArray(m_context.getRenderContext(), m_internalFormat, m_width, m_height, m_numLayers);
744
745		const tcu::IVec4 levelSwz[] =
746		{
747			tcu::IVec4(0,1,2,3),
748			tcu::IVec4(2,1,3,0),
749			tcu::IVec4(3,0,1,2),
750			tcu::IVec4(1,3,2,0),
751		};
752
753		// Fill first gradient texture (gradient direction varies between layers).
754		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
755		{
756			m_gradientTex->getRefTexture().allocLevel(levelNdx);
757
758			const tcu::PixelBufferAccess levelBuf = m_gradientTex->getRefTexture().getLevel(levelNdx);
759
760			for (int layerNdx = 0; layerNdx < m_numLayers; layerNdx++)
761			{
762				const tcu::IVec4	swz		= levelSwz[layerNdx%DE_LENGTH_OF_ARRAY(levelSwz)];
763				const tcu::Vec4		gMin	= tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f).swizzle(swz[0],swz[1],swz[2],swz[3])*cScale + cBias;
764				const tcu::Vec4		gMax	= tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f).swizzle(swz[0],swz[1],swz[2],swz[3])*cScale + cBias;
765
766				tcu::fillWithComponentGradients(tcu::getSubregion(levelBuf, 0, 0, layerNdx, levelBuf.getWidth(), levelBuf.getHeight(), 1), gMin, gMax);
767			}
768		}
769
770		// Fill second with grid texture (each layer has unique colors).
771		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
772		{
773			m_gridTex->getRefTexture().allocLevel(levelNdx);
774
775			const tcu::PixelBufferAccess levelBuf = m_gridTex->getRefTexture().getLevel(levelNdx);
776
777			for (int layerNdx = 0; layerNdx < m_numLayers; layerNdx++)
778			{
779				const deUint32	step	= 0x00ffffff / (numLevels*m_numLayers - 1);
780				const deUint32	rgb		= step * (levelNdx + layerNdx*numLevels);
781				const deUint32	colorA	= 0xff000000 | rgb;
782				const deUint32	colorB	= 0xff000000 | ~rgb;
783
784				tcu::fillWithGrid(tcu::getSubregion(levelBuf, 0, 0, layerNdx, levelBuf.getWidth(), levelBuf.getHeight(), 1),
785								  4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
786			}
787		}
788
789		// Upload.
790		m_gradientTex->upload();
791		m_gridTex->upload();
792
793		// Test cases
794		m_cases.push_back(FilterCase(m_gradientTex,	tcu::Vec2( 1.5f,  2.8f  ),	tcu::Vec2(-1.0f, -2.7f), tcu::Vec2(-0.5f, float(m_numLayers)+0.5f)));
795		m_cases.push_back(FilterCase(m_gridTex,		tcu::Vec2( 0.2f,  0.175f),	tcu::Vec2(-2.0f, -3.7f), tcu::Vec2(-0.5f, float(m_numLayers)+0.5f)));
796		m_cases.push_back(FilterCase(m_gridTex,		tcu::Vec2(-0.8f, -2.3f  ),	tcu::Vec2( 0.2f, -0.1f), tcu::Vec2(float(m_numLayers)+0.5f, -0.5f)));
797
798		// Level rounding - only in single-sample configs as multisample configs may produce smooth transition at the middle.
799		if (m_context.getRenderTarget().getNumSamples() == 0)
800			m_cases.push_back(FilterCase(m_gradientTex,	tcu::Vec2(-2.0f, -1.5f  ),	tcu::Vec2(-0.1f,  0.9f), tcu::Vec2(1.50001f, 1.49999f)));
801
802		m_caseNdx = 0;
803		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
804	}
805	catch (...)
806	{
807		// Clean up to save memory.
808		Texture2DArrayFilteringCase::deinit();
809		throw;
810	}
811}
812
813void Texture2DArrayFilteringCase::deinit (void)
814{
815	delete m_gradientTex;
816	delete m_gridTex;
817
818	m_gradientTex	= DE_NULL;
819	m_gridTex		= DE_NULL;
820
821	m_renderer.clear();
822	m_cases.clear();
823}
824
825Texture2DArrayFilteringCase::IterateResult Texture2DArrayFilteringCase::iterate (void)
826{
827	const glw::Functions&			gl			= m_context.getRenderContext().getFunctions();
828	const RandomViewport			viewport	(m_context.getRenderTarget(), TEX3D_VIEWPORT_WIDTH, TEX3D_VIEWPORT_HEIGHT, deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
829	const FilterCase&				curCase		= m_cases[m_caseNdx];
830	const tcu::TextureFormat		texFmt		= curCase.texture->getRefTexture().getFormat();
831	const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(texFmt);
832	const tcu::ScopedLogSection		section		(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
833	ReferenceParams					refParams	(TEXTURETYPE_2D_ARRAY);
834	tcu::Surface					rendered	(viewport.width, viewport.height);
835	tcu::Vec3						texCoord[4];
836
837	if (viewport.width < TEX3D_MIN_VIEWPORT_WIDTH || viewport.height < TEX3D_MIN_VIEWPORT_HEIGHT)
838		throw tcu::NotSupportedError("Too small render target", "", __FILE__, __LINE__);
839
840	// Setup params for reference.
841	refParams.sampler		= glu::mapGLSampler(m_wrapS, m_wrapT, m_wrapT, m_minFilter, m_magFilter);
842	refParams.samplerType	= getSamplerType(texFmt);
843	refParams.lodMode		= LODMODE_EXACT;
844	refParams.colorBias		= fmtInfo.lookupBias;
845	refParams.colorScale	= fmtInfo.lookupScale;
846
847	// Compute texture coordinates.
848	m_testCtx.getLog() << TestLog::Message << "Approximate lod per axis = " << curCase.lod << ", offset = " << curCase.offset << TestLog::EndMessage;
849
850	{
851		const float	lodX	= curCase.lod.x();
852		const float	lodY	= curCase.lod.y();
853		const float	oX		= curCase.offset.x();
854		const float	oY		= curCase.offset.y();
855		const float	sX		= deFloatExp2(lodX)*float(viewport.width)	/ float(m_gradientTex->getRefTexture().getWidth());
856		const float	sY		= deFloatExp2(lodY)*float(viewport.height)	/ float(m_gradientTex->getRefTexture().getHeight());
857		const float	l0		= curCase.layerRange.x();
858		const float	l1		= curCase.layerRange.y();
859
860		texCoord[0] = tcu::Vec3(oX,		oY,		l0);
861		texCoord[1] = tcu::Vec3(oX,		oY+sY,	l0*0.5f + l1*0.5f);
862		texCoord[2] = tcu::Vec3(oX+sX,	oY,		l0*0.5f + l1*0.5f);
863		texCoord[3] = tcu::Vec3(oX+sX,	oY+sY,	l1);
864	}
865
866	gl.bindTexture	(GL_TEXTURE_2D_ARRAY, curCase.texture->getGLTexture());
867	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,	m_minFilter);
868	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER,	m_magFilter);
869	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S,		m_wrapS);
870	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T,		m_wrapT);
871
872	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
873	m_renderer.renderQuad(0, (const float*)&texCoord[0], refParams);
874	glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, rendered.getAccess());
875
876	{
877		const bool				isNearestOnly	= m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
878		const tcu::PixelFormat	pixelFormat		= m_context.getRenderTarget().getPixelFormat();
879		const tcu::IVec4		colorBits		= max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2), tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
880		tcu::LodPrecision		lodPrecision;
881		tcu::LookupPrecision	lookupPrecision;
882
883		lodPrecision.derivateBits		= 18;
884		lodPrecision.lodBits			= 6;
885		lookupPrecision.colorThreshold	= tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
886		lookupPrecision.coordBits		= tcu::IVec3(20,20,20);
887		lookupPrecision.uvwBits			= tcu::IVec3(7,7,0);
888		lookupPrecision.colorMask		= getCompareMask(pixelFormat);
889
890		const bool isHighQuality = verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
891													   (const float*)&texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
892
893		if (!isHighQuality)
894		{
895			// Evaluate against lower precision requirements.
896			lodPrecision.lodBits	= 4;
897			lookupPrecision.uvwBits	= tcu::IVec3(4,4,0);
898
899			m_testCtx.getLog() << TestLog::Message << "Warning: Verification against high precision requirements failed, trying with lower requirements." << TestLog::EndMessage;
900
901			const bool isOk = verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
902												  (const float*)&texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
903
904			if (!isOk)
905			{
906				m_testCtx.getLog() << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
907				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
908			}
909			else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
910				m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality filtering result");
911		}
912	}
913
914	m_caseNdx += 1;
915	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
916}
917
918// 3D filtering
919
920class Texture3DFilteringCase : public TestCase
921{
922public:
923									Texture3DFilteringCase		(Context& context, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 wrapR, deUint32 internalFormat, int width, int height, int depth);
924									~Texture3DFilteringCase		(void);
925
926	void							init						(void);
927	void							deinit						(void);
928	IterateResult					iterate						(void);
929
930private:
931									Texture3DFilteringCase		(const Texture3DFilteringCase& other);
932	Texture3DFilteringCase&			operator=					(const Texture3DFilteringCase& other);
933
934	const deUint32					m_minFilter;
935	const deUint32					m_magFilter;
936	const deUint32					m_wrapS;
937	const deUint32					m_wrapT;
938	const deUint32					m_wrapR;
939
940	const deUint32					m_internalFormat;
941	const int						m_width;
942	const int						m_height;
943	const int						m_depth;
944
945	struct FilterCase
946	{
947		const glu::Texture3D*	texture;
948		tcu::Vec3				lod;
949		tcu::Vec3				offset;
950
951		FilterCase (void)
952			: texture(DE_NULL)
953		{
954		}
955
956		FilterCase (const glu::Texture3D* tex_, const tcu::Vec3& lod_, const tcu::Vec3& offset_)
957			: texture	(tex_)
958			, lod		(lod_)
959			, offset	(offset_)
960		{
961		}
962	};
963
964	glu::Texture3D*					m_gradientTex;
965	glu::Texture3D*					m_gridTex;
966
967	TextureRenderer					m_renderer;
968
969	std::vector<FilterCase>			m_cases;
970	int								m_caseNdx;
971};
972
973Texture3DFilteringCase::Texture3DFilteringCase (Context& context, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 wrapR, deUint32 internalFormat, int width, int height, int depth)
974	: TestCase			(context, name, desc)
975	, m_minFilter		(minFilter)
976	, m_magFilter		(magFilter)
977	, m_wrapS			(wrapS)
978	, m_wrapT			(wrapT)
979	, m_wrapR			(wrapR)
980	, m_internalFormat	(internalFormat)
981	, m_width			(width)
982	, m_height			(height)
983	, m_depth			(depth)
984	, m_gradientTex		(DE_NULL)
985	, m_gridTex			(DE_NULL)
986	, m_renderer		(context.getRenderContext(), context.getTestContext().getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
987	, m_caseNdx			(0)
988{
989}
990
991Texture3DFilteringCase::~Texture3DFilteringCase (void)
992{
993	Texture3DFilteringCase::deinit();
994}
995
996void Texture3DFilteringCase::init (void)
997{
998	try
999	{
1000		const tcu::TextureFormat		texFmt		= glu::mapGLInternalFormat(m_internalFormat);
1001		const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(texFmt);
1002		const tcu::Vec4					cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
1003		const tcu::Vec4					cBias		= fmtInfo.valueMin;
1004		const int						numLevels	= deLog2Floor32(de::max(de::max(m_width, m_height), m_depth)) + 1;
1005
1006		// Create textures.
1007		m_gradientTex	= new glu::Texture3D(m_context.getRenderContext(), m_internalFormat, m_width, m_height, m_depth);
1008		m_gridTex		= new glu::Texture3D(m_context.getRenderContext(), m_internalFormat, m_width, m_height, m_depth);
1009
1010		// Fill first gradient texture.
1011		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
1012		{
1013			tcu::Vec4 gMin = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f)*cScale + cBias;
1014			tcu::Vec4 gMax = tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)*cScale + cBias;
1015
1016			m_gradientTex->getRefTexture().allocLevel(levelNdx);
1017			tcu::fillWithComponentGradients(m_gradientTex->getRefTexture().getLevel(levelNdx), gMin, gMax);
1018		}
1019
1020		// Fill second with grid texture.
1021		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
1022		{
1023			deUint32	step	= 0x00ffffff / numLevels;
1024			deUint32	rgb		= step*levelNdx;
1025			deUint32	colorA	= 0xff000000 | rgb;
1026			deUint32	colorB	= 0xff000000 | ~rgb;
1027
1028			m_gridTex->getRefTexture().allocLevel(levelNdx);
1029			tcu::fillWithGrid(m_gridTex->getRefTexture().getLevel(levelNdx), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
1030		}
1031
1032		// Upload.
1033		m_gradientTex->upload();
1034		m_gridTex->upload();
1035
1036		// Test cases
1037		m_cases.push_back(FilterCase(m_gradientTex,	tcu::Vec3(1.5f, 2.8f, 1.0f),	tcu::Vec3(-1.0f, -2.7f, -2.275f)));
1038		m_cases.push_back(FilterCase(m_gradientTex,	tcu::Vec3(-2.0f, -1.5f, -1.8f),	tcu::Vec3(-0.1f, 0.9f, -0.25f)));
1039		m_cases.push_back(FilterCase(m_gridTex,		tcu::Vec3(0.2f, 0.175f, 0.3f),	tcu::Vec3(-2.0f, -3.7f, -1.825f)));
1040		m_cases.push_back(FilterCase(m_gridTex,		tcu::Vec3(-0.8f, -2.3f, -2.5f),	tcu::Vec3(0.2f, -0.1f, 1.325f)));
1041
1042		m_caseNdx = 0;
1043		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1044	}
1045	catch (...)
1046	{
1047		// Clean up to save memory.
1048		Texture3DFilteringCase::deinit();
1049		throw;
1050	}
1051}
1052
1053void Texture3DFilteringCase::deinit (void)
1054{
1055	delete m_gradientTex;
1056	delete m_gridTex;
1057
1058	m_gradientTex	= DE_NULL;
1059	m_gridTex		= DE_NULL;
1060
1061	m_renderer.clear();
1062	m_cases.clear();
1063}
1064
1065Texture3DFilteringCase::IterateResult Texture3DFilteringCase::iterate (void)
1066{
1067	const glw::Functions&			gl			= m_context.getRenderContext().getFunctions();
1068	const RandomViewport			viewport	(m_context.getRenderTarget(), TEX3D_VIEWPORT_WIDTH, TEX3D_VIEWPORT_HEIGHT, deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
1069	const FilterCase&				curCase		= m_cases[m_caseNdx];
1070	const tcu::TextureFormat		texFmt		= curCase.texture->getRefTexture().getFormat();
1071	const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(texFmt);
1072	const tcu::ScopedLogSection		section		(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
1073	ReferenceParams					refParams	(TEXTURETYPE_3D);
1074	tcu::Surface					rendered	(viewport.width, viewport.height);
1075	tcu::Vec3						texCoord[4];
1076
1077	if (viewport.width < TEX3D_MIN_VIEWPORT_WIDTH || viewport.height < TEX3D_MIN_VIEWPORT_HEIGHT)
1078		throw tcu::NotSupportedError("Too small render target", "", __FILE__, __LINE__);
1079
1080	// Setup params for reference.
1081	refParams.sampler		= glu::mapGLSampler(m_wrapS, m_wrapT, m_wrapR, m_minFilter, m_magFilter);
1082	refParams.samplerType	= getSamplerType(texFmt);
1083	refParams.lodMode		= LODMODE_EXACT;
1084	refParams.colorBias		= fmtInfo.lookupBias;
1085	refParams.colorScale	= fmtInfo.lookupScale;
1086
1087	// Compute texture coordinates.
1088	m_testCtx.getLog() << TestLog::Message << "Approximate lod per axis = " << curCase.lod << ", offset = " << curCase.offset << TestLog::EndMessage;
1089
1090	{
1091		const float	lodX	= curCase.lod.x();
1092		const float	lodY	= curCase.lod.y();
1093		const float	lodZ	= curCase.lod.z();
1094		const float	oX		= curCase.offset.x();
1095		const float	oY		= curCase.offset.y();
1096		const float oZ		= curCase.offset.z();
1097		const float	sX		= deFloatExp2(lodX)*float(viewport.width)							/ float(m_gradientTex->getRefTexture().getWidth());
1098		const float	sY		= deFloatExp2(lodY)*float(viewport.height)							/ float(m_gradientTex->getRefTexture().getHeight());
1099		const float	sZ		= deFloatExp2(lodZ)*float(de::max(viewport.width, viewport.height))	/ float(m_gradientTex->getRefTexture().getDepth());
1100
1101		texCoord[0] = tcu::Vec3(oX,		oY,		oZ);
1102		texCoord[1] = tcu::Vec3(oX,		oY+sY,	oZ + sZ*0.5f);
1103		texCoord[2] = tcu::Vec3(oX+sX,	oY,		oZ + sZ*0.5f);
1104		texCoord[3] = tcu::Vec3(oX+sX,	oY+sY,	oZ + sZ);
1105	}
1106
1107	gl.bindTexture	(GL_TEXTURE_3D, curCase.texture->getGLTexture());
1108	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,	m_minFilter);
1109	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER,	m_magFilter);
1110	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S,		m_wrapS);
1111	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T,		m_wrapT);
1112	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R,		m_wrapR);
1113
1114	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
1115	m_renderer.renderQuad(0, (const float*)&texCoord[0], refParams);
1116	glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, rendered.getAccess());
1117
1118	{
1119		const bool				isNearestOnly	= m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
1120		const tcu::PixelFormat	pixelFormat		= m_context.getRenderTarget().getPixelFormat();
1121		const tcu::IVec4		colorBits		= max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2), tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
1122		tcu::LodPrecision		lodPrecision;
1123		tcu::LookupPrecision	lookupPrecision;
1124
1125		lodPrecision.derivateBits		= 18;
1126		lodPrecision.lodBits			= 6;
1127		lookupPrecision.colorThreshold	= tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
1128		lookupPrecision.coordBits		= tcu::IVec3(20,20,20);
1129		lookupPrecision.uvwBits			= tcu::IVec3(7,7,7);
1130		lookupPrecision.colorMask		= getCompareMask(pixelFormat);
1131
1132		const bool isHighQuality = verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
1133													   (const float*)&texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
1134
1135		if (!isHighQuality)
1136		{
1137			// Evaluate against lower precision requirements.
1138			lodPrecision.lodBits	= 4;
1139			lookupPrecision.uvwBits	= tcu::IVec3(4,4,4);
1140
1141			m_testCtx.getLog() << TestLog::Message << "Warning: Verification against high precision requirements failed, trying with lower requirements." << TestLog::EndMessage;
1142
1143			const bool isOk = verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
1144												  (const float*)&texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
1145
1146			if (!isOk)
1147			{
1148				m_testCtx.getLog() << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
1149				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1150			}
1151			else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
1152				m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality filtering result");
1153		}
1154	}
1155
1156	m_caseNdx += 1;
1157	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
1158}
1159
1160TextureFilteringTests::TextureFilteringTests (Context& context)
1161	: TestCaseGroup(context, "filtering", "Texture Filtering Tests")
1162{
1163}
1164
1165TextureFilteringTests::~TextureFilteringTests (void)
1166{
1167}
1168
1169void TextureFilteringTests::init (void)
1170{
1171	static const struct
1172	{
1173		const char*		name;
1174		deUint32		mode;
1175	} wrapModes[] =
1176	{
1177		{ "clamp",		GL_CLAMP_TO_EDGE },
1178		{ "repeat",		GL_REPEAT },
1179		{ "mirror",		GL_MIRRORED_REPEAT }
1180	};
1181
1182	static const struct
1183	{
1184		const char*		name;
1185		deUint32		mode;
1186	} minFilterModes[] =
1187	{
1188		{ "nearest",				GL_NEAREST					},
1189		{ "linear",					GL_LINEAR					},
1190		{ "nearest_mipmap_nearest",	GL_NEAREST_MIPMAP_NEAREST	},
1191		{ "linear_mipmap_nearest",	GL_LINEAR_MIPMAP_NEAREST	},
1192		{ "nearest_mipmap_linear",	GL_NEAREST_MIPMAP_LINEAR	},
1193		{ "linear_mipmap_linear",	GL_LINEAR_MIPMAP_LINEAR		}
1194	};
1195
1196	static const struct
1197	{
1198		const char*		name;
1199		deUint32		mode;
1200	} magFilterModes[] =
1201	{
1202		{ "nearest",	GL_NEAREST },
1203		{ "linear",		GL_LINEAR }
1204	};
1205
1206	static const struct
1207	{
1208		int width;
1209		int height;
1210	} sizes2D[] =
1211	{
1212		{   4,	  8 },
1213		{  32,	 64 },
1214		{ 128,	128	},
1215		{   3,	  7 },
1216		{  31,	 55 },
1217		{ 127,	 99 }
1218	};
1219
1220	static const struct
1221	{
1222		int width;
1223		int height;
1224	} sizesCube[] =
1225	{
1226		{   8,   8 },
1227		{  64,  64 },
1228		{ 128, 128 },
1229		{   7,   7 },
1230		{  63,  63 }
1231	};
1232
1233	static const struct
1234	{
1235		int width;
1236		int height;
1237		int numLayers;
1238	} sizes2DArray[] =
1239	{
1240		{   4,   8,   8 },
1241		{  32,  64,  16 },
1242		{ 128,  32,  64 },
1243		{   3,   7,   5 },
1244		{  63,  63,  63 }
1245	};
1246
1247	static const struct
1248	{
1249		int width;
1250		int height;
1251		int depth;
1252	} sizes3D[] =
1253	{
1254		{   4,   8,   8 },
1255		{  32,  64,  16 },
1256		{ 128,  32,  64 },
1257		{   3,   7,   5 },
1258		{  63,  63,  63 }
1259	};
1260
1261	static const struct
1262	{
1263		const char*		name;
1264		deUint32		format;
1265	} filterableFormatsByType[] =
1266	{
1267		{ "rgba16f",		GL_RGBA16F			},
1268		{ "r11f_g11f_b10f",	GL_R11F_G11F_B10F	},
1269		{ "rgb9_e5",		GL_RGB9_E5			},
1270		{ "rgba8",			GL_RGBA8			},
1271		{ "rgba8_snorm",	GL_RGBA8_SNORM		},
1272		{ "rgb565",			GL_RGB565			},
1273		{ "rgba4",			GL_RGBA4			},
1274		{ "rgb5_a1",		GL_RGB5_A1			},
1275		{ "srgb8_alpha8",	GL_SRGB8_ALPHA8		},
1276		{ "rgb10_a2",		GL_RGB10_A2			}
1277	};
1278
1279	// 2D texture filtering.
1280	{
1281		tcu::TestCaseGroup* group2D = new tcu::TestCaseGroup(m_testCtx, "2d", "2D Texture Filtering");
1282		addChild(group2D);
1283
1284		// Formats.
1285		tcu::TestCaseGroup* formatsGroup = new tcu::TestCaseGroup(m_testCtx, "formats", "2D Texture Formats");
1286		group2D->addChild(formatsGroup);
1287		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(filterableFormatsByType); fmtNdx++)
1288		{
1289			for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1290			{
1291				deUint32		minFilter	= minFilterModes[filterNdx].mode;
1292				const char*		filterName	= minFilterModes[filterNdx].name;
1293				deUint32		format		= filterableFormatsByType[fmtNdx].format;
1294				const char*		formatName	= filterableFormatsByType[fmtNdx].name;
1295				bool			isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1296				deUint32		magFilter	= isMipmap ? GL_LINEAR : minFilter;
1297				string			name		= string(formatName) + "_" + filterName;
1298				deUint32		wrapS		= GL_REPEAT;
1299				deUint32		wrapT		= GL_REPEAT;
1300				int				width		= 64;
1301				int				height		= 64;
1302
1303				formatsGroup->addChild(new Texture2DFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
1304																  name.c_str(), "",
1305																  minFilter, magFilter,
1306																  wrapS, wrapT,
1307																  format,
1308																  width, height));
1309			}
1310		}
1311
1312		// ETC1 format.
1313		{
1314			std::vector<std::string> filenames;
1315			for (int i = 0; i <= 7; i++)
1316				filenames.push_back(string("data/etc1/photo_helsinki_mip_") + de::toString(i) + ".pkm");
1317
1318			for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1319			{
1320				deUint32		minFilter	= minFilterModes[filterNdx].mode;
1321				const char*		filterName	= minFilterModes[filterNdx].name;
1322				bool			isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1323				deUint32		magFilter	= isMipmap ? GL_LINEAR : minFilter;
1324				string			name		= string("etc1_rgb8_") + filterName;
1325				deUint32		wrapS		= GL_REPEAT;
1326				deUint32		wrapT		= GL_REPEAT;
1327
1328				formatsGroup->addChild(new Texture2DFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
1329																  name.c_str(), "",
1330																  minFilter, magFilter,
1331																  wrapS, wrapT,
1332																  filenames));
1333			}
1334		}
1335
1336		// Sizes.
1337		tcu::TestCaseGroup* sizesGroup = new tcu::TestCaseGroup(m_testCtx, "sizes", "Texture Sizes");
1338		group2D->addChild(sizesGroup);
1339		for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizes2D); sizeNdx++)
1340		{
1341			for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1342			{
1343				deUint32		minFilter	= minFilterModes[filterNdx].mode;
1344				const char*		filterName	= minFilterModes[filterNdx].name;
1345				deUint32		format		= GL_RGBA8;
1346				bool			isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1347				deUint32		magFilter	= isMipmap ? GL_LINEAR : minFilter;
1348				deUint32		wrapS		= GL_REPEAT;
1349				deUint32		wrapT		= GL_REPEAT;
1350				int				width		= sizes2D[sizeNdx].width;
1351				int				height		= sizes2D[sizeNdx].height;
1352				string			name		= de::toString(width) + "x" + de::toString(height) + "_" + filterName;
1353
1354				sizesGroup->addChild(new Texture2DFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
1355																name.c_str(), "",
1356																minFilter, magFilter,
1357																wrapS, wrapT,
1358																format,
1359																width, height));
1360			}
1361		}
1362
1363		// Wrap modes.
1364		tcu::TestCaseGroup* combinationsGroup = new tcu::TestCaseGroup(m_testCtx, "combinations", "Filter and wrap mode combinations");
1365		group2D->addChild(combinationsGroup);
1366		for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); minFilterNdx++)
1367		{
1368			for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilterModes); magFilterNdx++)
1369			{
1370				for (int wrapSNdx = 0; wrapSNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapSNdx++)
1371				{
1372					for (int wrapTNdx = 0; wrapTNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapTNdx++)
1373					{
1374						deUint32		minFilter	= minFilterModes[minFilterNdx].mode;
1375						deUint32		magFilter	= magFilterModes[magFilterNdx].mode;
1376						deUint32		format		= GL_RGBA8;
1377						deUint32		wrapS		= wrapModes[wrapSNdx].mode;
1378						deUint32		wrapT		= wrapModes[wrapTNdx].mode;
1379						int				width		= 63;
1380						int				height		= 57;
1381						string			name		= string(minFilterModes[minFilterNdx].name) + "_" + magFilterModes[magFilterNdx].name + "_" + wrapModes[wrapSNdx].name + "_" + wrapModes[wrapTNdx].name;
1382
1383						combinationsGroup->addChild(new Texture2DFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
1384																			   name.c_str(), "",
1385																			   minFilter, magFilter,
1386																			   wrapS, wrapT,
1387																			   format,
1388																			   width, height));
1389					}
1390				}
1391			}
1392		}
1393	}
1394
1395	// Cube map texture filtering.
1396	{
1397		tcu::TestCaseGroup* groupCube = new tcu::TestCaseGroup(m_testCtx, "cube", "Cube Map Texture Filtering");
1398		addChild(groupCube);
1399
1400		// Formats.
1401		tcu::TestCaseGroup* formatsGroup = new tcu::TestCaseGroup(m_testCtx, "formats", "2D Texture Formats");
1402		groupCube->addChild(formatsGroup);
1403		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(filterableFormatsByType); fmtNdx++)
1404		{
1405			for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1406			{
1407				deUint32		minFilter	= minFilterModes[filterNdx].mode;
1408				const char*		filterName	= minFilterModes[filterNdx].name;
1409				deUint32		format		= filterableFormatsByType[fmtNdx].format;
1410				const char*		formatName	= filterableFormatsByType[fmtNdx].name;
1411				bool			isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1412				deUint32		magFilter	= isMipmap ? GL_LINEAR : minFilter;
1413				string			name		= string(formatName) + "_" + filterName;
1414				deUint32		wrapS		= GL_REPEAT;
1415				deUint32		wrapT		= GL_REPEAT;
1416				int				width		= 64;
1417				int				height		= 64;
1418
1419				formatsGroup->addChild(new TextureCubeFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
1420																	name.c_str(), "",
1421																	minFilter, magFilter,
1422																	wrapS, wrapT,
1423																	false /* always sample exterior as well */,
1424																	format,
1425																	width, height));
1426			}
1427		}
1428
1429		// ETC1 format.
1430		{
1431			static const char* faceExt[] = { "neg_x", "pos_x", "neg_y", "pos_y", "neg_z", "pos_z" };
1432
1433			const int		numLevels	= 7;
1434			vector<string>	filenames;
1435			for (int level = 0; level < numLevels; level++)
1436				for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
1437					filenames.push_back(string("data/etc1/skybox_") + faceExt[face] + "_mip_" + de::toString(level) + ".pkm");
1438
1439			for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1440			{
1441				deUint32		minFilter	= minFilterModes[filterNdx].mode;
1442				const char*		filterName	= minFilterModes[filterNdx].name;
1443				bool			isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1444				deUint32		magFilter	= isMipmap ? GL_LINEAR : minFilter;
1445				string			name		= string("etc1_rgb8_") + filterName;
1446				deUint32		wrapS		= GL_REPEAT;
1447				deUint32		wrapT		= GL_REPEAT;
1448
1449				formatsGroup->addChild(new TextureCubeFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
1450																	name.c_str(), "",
1451																	minFilter, magFilter,
1452																	wrapS, wrapT,
1453																	false /* always sample exterior as well */,
1454																	filenames));
1455			}
1456		}
1457
1458		// Sizes.
1459		tcu::TestCaseGroup* sizesGroup = new tcu::TestCaseGroup(m_testCtx, "sizes", "Texture Sizes");
1460		groupCube->addChild(sizesGroup);
1461		for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizesCube); sizeNdx++)
1462		{
1463			for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1464			{
1465				deUint32		minFilter	= minFilterModes[filterNdx].mode;
1466				const char*		filterName	= minFilterModes[filterNdx].name;
1467				deUint32		format		= GL_RGBA8;
1468				bool			isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1469				deUint32		magFilter	= isMipmap ? GL_LINEAR : minFilter;
1470				deUint32		wrapS		= GL_REPEAT;
1471				deUint32		wrapT		= GL_REPEAT;
1472				int				width		= sizesCube[sizeNdx].width;
1473				int				height		= sizesCube[sizeNdx].height;
1474				string			name		= de::toString(width) + "x" + de::toString(height) + "_" + filterName;
1475
1476				sizesGroup->addChild(new TextureCubeFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
1477																  name.c_str(), "",
1478																  minFilter, magFilter,
1479																  wrapS, wrapT,
1480																  false,
1481																  format,
1482																  width, height));
1483			}
1484		}
1485
1486		// Filter/wrap mode combinations.
1487		tcu::TestCaseGroup* combinationsGroup = new tcu::TestCaseGroup(m_testCtx, "combinations", "Filter and wrap mode combinations");
1488		groupCube->addChild(combinationsGroup);
1489		for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); minFilterNdx++)
1490		{
1491			for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilterModes); magFilterNdx++)
1492			{
1493				for (int wrapSNdx = 0; wrapSNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapSNdx++)
1494				{
1495					for (int wrapTNdx = 0; wrapTNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapTNdx++)
1496					{
1497						deUint32		minFilter	= minFilterModes[minFilterNdx].mode;
1498						deUint32		magFilter	= magFilterModes[magFilterNdx].mode;
1499						deUint32		format		= GL_RGBA8;
1500						deUint32		wrapS		= wrapModes[wrapSNdx].mode;
1501						deUint32		wrapT		= wrapModes[wrapTNdx].mode;
1502						int				width		= 63;
1503						int				height		= 63;
1504						string			name		= string(minFilterModes[minFilterNdx].name) + "_" + magFilterModes[magFilterNdx].name + "_" + wrapModes[wrapSNdx].name + "_" + wrapModes[wrapTNdx].name;
1505
1506						combinationsGroup->addChild(new TextureCubeFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
1507																				 name.c_str(), "",
1508																				 minFilter, magFilter,
1509																				 wrapS, wrapT,
1510																				 false,
1511																				 format,
1512																				 width, height));
1513					}
1514				}
1515			}
1516		}
1517
1518		// Cases with no visible cube edges.
1519		tcu::TestCaseGroup* onlyFaceInteriorGroup = new tcu::TestCaseGroup(m_testCtx, "no_edges_visible", "Don't sample anywhere near a face's edges");
1520		groupCube->addChild(onlyFaceInteriorGroup);
1521
1522		for (int isLinearI = 0; isLinearI <= 1; isLinearI++)
1523		{
1524			bool		isLinear	= isLinearI != 0;
1525			deUint32	filter		= isLinear ? GL_LINEAR : GL_NEAREST;
1526
1527			onlyFaceInteriorGroup->addChild(new TextureCubeFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
1528																		 isLinear ? "linear" : "nearest", "",
1529																		 filter, filter,
1530																		 GL_REPEAT, GL_REPEAT,
1531																		 true,
1532																		 GL_RGBA8,
1533																		 63, 63));
1534		}
1535	}
1536
1537	// 2D array texture filtering.
1538	{
1539		tcu::TestCaseGroup* const group2DArray = new tcu::TestCaseGroup(m_testCtx, "2d_array", "2D Array Texture Filtering");
1540		addChild(group2DArray);
1541
1542		// Formats.
1543		tcu::TestCaseGroup* const formatsGroup = new tcu::TestCaseGroup(m_testCtx, "formats", "2D Array Texture Formats");
1544		group2DArray->addChild(formatsGroup);
1545		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(filterableFormatsByType); fmtNdx++)
1546		{
1547			for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1548			{
1549				deUint32		minFilter	= minFilterModes[filterNdx].mode;
1550				const char*		filterName	= minFilterModes[filterNdx].name;
1551				deUint32		format		= filterableFormatsByType[fmtNdx].format;
1552				const char*		formatName	= filterableFormatsByType[fmtNdx].name;
1553				bool			isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1554				deUint32		magFilter	= isMipmap ? GL_LINEAR : minFilter;
1555				string			name		= string(formatName) + "_" + filterName;
1556				deUint32		wrapS		= GL_REPEAT;
1557				deUint32		wrapT		= GL_REPEAT;
1558				int				width		= 128;
1559				int				height		= 128;
1560				int				numLayers	= 8;
1561
1562				formatsGroup->addChild(new Texture2DArrayFilteringCase(m_context,
1563																	   name.c_str(), "",
1564																	   minFilter, magFilter,
1565																	   wrapS, wrapT,
1566																	   format,
1567																	   width, height, numLayers));
1568			}
1569		}
1570
1571		// Sizes.
1572		tcu::TestCaseGroup* sizesGroup = new tcu::TestCaseGroup(m_testCtx, "sizes", "Texture Sizes");
1573		group2DArray->addChild(sizesGroup);
1574		for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizes2DArray); sizeNdx++)
1575		{
1576			for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1577			{
1578				deUint32		minFilter	= minFilterModes[filterNdx].mode;
1579				const char*		filterName	= minFilterModes[filterNdx].name;
1580				deUint32		format		= GL_RGBA8;
1581				bool			isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1582				deUint32		magFilter	= isMipmap ? GL_LINEAR : minFilter;
1583				deUint32		wrapS		= GL_REPEAT;
1584				deUint32		wrapT		= GL_REPEAT;
1585				int				width		= sizes2DArray[sizeNdx].width;
1586				int				height		= sizes2DArray[sizeNdx].height;
1587				int				numLayers	= sizes2DArray[sizeNdx].numLayers;
1588				string			name		= de::toString(width) + "x" + de::toString(height) + "x" + de::toString(numLayers) + "_" + filterName;
1589
1590				sizesGroup->addChild(new Texture2DArrayFilteringCase(m_context,
1591																	 name.c_str(), "",
1592																	 minFilter, magFilter,
1593																	 wrapS, wrapT,
1594																	 format,
1595																	 width, height, numLayers));
1596			}
1597		}
1598
1599		// Wrap modes.
1600		tcu::TestCaseGroup* const combinationsGroup = new tcu::TestCaseGroup(m_testCtx, "combinations", "Filter and wrap mode combinations");
1601		group2DArray->addChild(combinationsGroup);
1602		for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); minFilterNdx++)
1603		{
1604			for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilterModes); magFilterNdx++)
1605			{
1606				for (int wrapSNdx = 0; wrapSNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapSNdx++)
1607				{
1608					for (int wrapTNdx = 0; wrapTNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapTNdx++)
1609					{
1610						deUint32		minFilter	= minFilterModes[minFilterNdx].mode;
1611						deUint32		magFilter	= magFilterModes[magFilterNdx].mode;
1612						deUint32		format		= GL_RGBA8;
1613						deUint32		wrapS		= wrapModes[wrapSNdx].mode;
1614						deUint32		wrapT		= wrapModes[wrapTNdx].mode;
1615						int				width		= 123;
1616						int				height		= 107;
1617						int				numLayers	= 7;
1618						string			name		= string(minFilterModes[minFilterNdx].name) + "_" + magFilterModes[magFilterNdx].name + "_" + wrapModes[wrapSNdx].name + "_" + wrapModes[wrapTNdx].name;
1619
1620						combinationsGroup->addChild(new Texture2DArrayFilteringCase(m_context,
1621																					name.c_str(), "",
1622																					minFilter, magFilter,
1623																					wrapS, wrapT,
1624																					format,
1625																					width, height, numLayers));
1626					}
1627				}
1628			}
1629		}
1630	}
1631
1632	// 3D texture filtering.
1633	{
1634		tcu::TestCaseGroup* group3D = new tcu::TestCaseGroup(m_testCtx, "3d", "3D Texture Filtering");
1635		addChild(group3D);
1636
1637		// Formats.
1638		tcu::TestCaseGroup* formatsGroup = new tcu::TestCaseGroup(m_testCtx, "formats", "3D Texture Formats");
1639		group3D->addChild(formatsGroup);
1640		for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(filterableFormatsByType); fmtNdx++)
1641		{
1642			for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1643			{
1644				deUint32		minFilter	= minFilterModes[filterNdx].mode;
1645				const char*		filterName	= minFilterModes[filterNdx].name;
1646				deUint32		format		= filterableFormatsByType[fmtNdx].format;
1647				const char*		formatName	= filterableFormatsByType[fmtNdx].name;
1648				bool			isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1649				deUint32		magFilter	= isMipmap ? GL_LINEAR : minFilter;
1650				string			name		= string(formatName) + "_" + filterName;
1651				deUint32		wrapS		= GL_REPEAT;
1652				deUint32		wrapT		= GL_REPEAT;
1653				deUint32		wrapR		= GL_REPEAT;
1654				int				width		= 64;
1655				int				height		= 64;
1656				int				depth		= 64;
1657
1658				formatsGroup->addChild(new Texture3DFilteringCase(m_context,
1659																  name.c_str(), "",
1660																  minFilter, magFilter,
1661																  wrapS, wrapT, wrapR,
1662																  format,
1663																  width, height, depth));
1664			}
1665		}
1666
1667		// Sizes.
1668		tcu::TestCaseGroup* sizesGroup = new tcu::TestCaseGroup(m_testCtx, "sizes", "Texture Sizes");
1669		group3D->addChild(sizesGroup);
1670		for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizes3D); sizeNdx++)
1671		{
1672			for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1673			{
1674				deUint32		minFilter	= minFilterModes[filterNdx].mode;
1675				const char*		filterName	= minFilterModes[filterNdx].name;
1676				deUint32		format		= GL_RGBA8;
1677				bool			isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1678				deUint32		magFilter	= isMipmap ? GL_LINEAR : minFilter;
1679				deUint32		wrapS		= GL_REPEAT;
1680				deUint32		wrapT		= GL_REPEAT;
1681				deUint32		wrapR		= GL_REPEAT;
1682				int				width		= sizes3D[sizeNdx].width;
1683				int				height		= sizes3D[sizeNdx].height;
1684				int				depth		= sizes3D[sizeNdx].depth;
1685				string			name		= de::toString(width) + "x" + de::toString(height) + "x" + de::toString(depth) + "_" + filterName;
1686
1687				sizesGroup->addChild(new Texture3DFilteringCase(m_context,
1688																name.c_str(), "",
1689																minFilter, magFilter,
1690																wrapS, wrapT, wrapR,
1691																format,
1692																width, height, depth));
1693			}
1694		}
1695
1696		// Wrap modes.
1697		tcu::TestCaseGroup* combinationsGroup = new tcu::TestCaseGroup(m_testCtx, "combinations", "Filter and wrap mode combinations");
1698		group3D->addChild(combinationsGroup);
1699		for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); minFilterNdx++)
1700		{
1701			for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilterModes); magFilterNdx++)
1702			{
1703				for (int wrapSNdx = 0; wrapSNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapSNdx++)
1704				{
1705					for (int wrapTNdx = 0; wrapTNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapTNdx++)
1706					{
1707						for (int wrapRNdx = 0; wrapRNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapRNdx++)
1708						{
1709							deUint32		minFilter	= minFilterModes[minFilterNdx].mode;
1710							deUint32		magFilter	= magFilterModes[magFilterNdx].mode;
1711							deUint32		format		= GL_RGBA8;
1712							deUint32		wrapS		= wrapModes[wrapSNdx].mode;
1713							deUint32		wrapT		= wrapModes[wrapTNdx].mode;
1714							deUint32		wrapR		= wrapModes[wrapRNdx].mode;
1715							int				width		= 63;
1716							int				height		= 57;
1717							int				depth		= 67;
1718							string			name		= string(minFilterModes[minFilterNdx].name) + "_" + magFilterModes[magFilterNdx].name + "_" + wrapModes[wrapSNdx].name + "_" + wrapModes[wrapTNdx].name + "_" + wrapModes[wrapRNdx].name;
1719
1720							combinationsGroup->addChild(new Texture3DFilteringCase(m_context,
1721																				   name.c_str(), "",
1722																				   minFilter, magFilter,
1723																				   wrapS, wrapT, wrapR,
1724																				   format,
1725																				   width, height, depth));
1726						}
1727					}
1728				}
1729			}
1730		}
1731	}
1732}
1733
1734} // Functional
1735} // gles3
1736} // deqp
1737