1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Texture filtering tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es31fTextureFilteringTests.hpp"
25
26#include "glsTextureTestUtil.hpp"
27
28#include "gluPixelTransfer.hpp"
29#include "gluTexture.hpp"
30#include "gluTextureUtil.hpp"
31
32#include "tcuCommandLine.hpp"
33#include "tcuTextureUtil.hpp"
34#include "tcuImageCompare.hpp"
35#include "tcuTexLookupVerifier.hpp"
36#include "tcuVectorUtil.hpp"
37
38#include "deStringUtil.hpp"
39#include "deString.h"
40
41#include "glwFunctions.hpp"
42#include "glwEnums.hpp"
43
44namespace deqp
45{
46namespace gles31
47{
48namespace Functional
49{
50
51using std::vector;
52using std::string;
53using tcu::TestLog;
54using namespace gls::TextureTestUtil;
55
56static const char* getFaceDesc (const tcu::CubeFace face)
57{
58	switch (face)
59	{
60		case tcu::CUBEFACE_NEGATIVE_X:	return "-X";
61		case tcu::CUBEFACE_POSITIVE_X:	return "+X";
62		case tcu::CUBEFACE_NEGATIVE_Y:	return "-Y";
63		case tcu::CUBEFACE_POSITIVE_Y:	return "+Y";
64		case tcu::CUBEFACE_NEGATIVE_Z:	return "-Z";
65		case tcu::CUBEFACE_POSITIVE_Z:	return "+Z";
66		default:
67			DE_ASSERT(false);
68			return DE_NULL;
69	}
70}
71
72static void logCubeArrayTexCoords(TestLog& log, vector<float>& texCoord)
73{
74	const size_t numVerts = texCoord.size() / 4;
75
76	DE_ASSERT(texCoord.size() % 4 == 0);
77
78	for (size_t vertNdx = 0; vertNdx < numVerts; vertNdx++)
79	{
80		const size_t	coordNdx	= vertNdx * 4;
81
82		const float		u			= texCoord[coordNdx + 0];
83		const float		v			= texCoord[coordNdx + 1];
84		const float		w			= texCoord[coordNdx + 2];
85		const float		q			= texCoord[coordNdx + 3];
86
87		log << TestLog::Message
88			<< vertNdx << ": ("
89			<< u << ", "
90			<< v << ", "
91			<< w << ", "
92			<< q << ")"
93			<< TestLog::EndMessage;
94	}
95}
96
97// Cube map array filtering
98
99class TextureCubeArrayFilteringCase : public TestCase
100{
101public:
102									TextureCubeArrayFilteringCase	(Context& context,
103																	 const char* name,
104																	 const char* desc,
105																	 deUint32 minFilter,
106																	 deUint32 magFilter,
107																	 deUint32 wrapS,
108																	 deUint32 wrapT,
109																	 deUint32 internalFormat,
110																	 int size,
111																	 int depth,
112																	 bool onlySampleFaceInterior = false);
113
114									~TextureCubeArrayFilteringCase	(void);
115
116	void							init							(void);
117	void							deinit							(void);
118	IterateResult					iterate							(void);
119
120private:
121									TextureCubeArrayFilteringCase	(const TextureCubeArrayFilteringCase&);
122	TextureCubeArrayFilteringCase&	operator=						(const TextureCubeArrayFilteringCase&);
123
124	const deUint32					m_minFilter;
125	const deUint32					m_magFilter;
126	const deUint32					m_wrapS;
127	const deUint32					m_wrapT;
128
129	const deUint32					m_internalFormat;
130	const int						m_size;
131	const int						m_depth;
132
133	const bool						m_onlySampleFaceInterior; //!< If true, we avoid sampling anywhere near a face's edges.
134
135	struct FilterCase
136	{
137		const glu::TextureCubeArray*	texture;
138		tcu::Vec2						bottomLeft;
139		tcu::Vec2						topRight;
140		tcu::Vec2						layerRange;
141
142		FilterCase (void)
143			: texture(DE_NULL)
144		{
145		}
146
147		FilterCase (const glu::TextureCubeArray* tex_, const tcu::Vec2& bottomLeft_, const tcu::Vec2& topRight_, const tcu::Vec2& layerRange_)
148			: texture		(tex_)
149			, bottomLeft	(bottomLeft_)
150			, topRight		(topRight_)
151			, layerRange	(layerRange_)
152		{
153		}
154	};
155
156	glu::TextureCubeArray*	m_gradientTex;
157	glu::TextureCubeArray*	m_gridTex;
158
159	TextureRenderer			m_renderer;
160
161	std::vector<FilterCase>	m_cases;
162	int						m_caseNdx;
163};
164
165TextureCubeArrayFilteringCase::TextureCubeArrayFilteringCase (Context& context,
166															  const char* name,
167															  const char* desc,
168															  deUint32 minFilter,
169															  deUint32 magFilter,
170															  deUint32 wrapS,
171															  deUint32 wrapT,
172															  deUint32 internalFormat,
173															  int size,
174															  int depth,
175															  bool onlySampleFaceInterior)
176	: TestCase					(context, name, desc)
177	, m_minFilter				(minFilter)
178	, m_magFilter				(magFilter)
179	, m_wrapS					(wrapS)
180	, m_wrapT					(wrapT)
181	, m_internalFormat			(internalFormat)
182	, m_size					(size)
183	, m_depth					(depth)
184	, m_onlySampleFaceInterior	(onlySampleFaceInterior)
185	, m_gradientTex				(DE_NULL)
186	, m_gridTex					(DE_NULL)
187	, m_renderer				(context.getRenderContext(), context.getTestContext().getLog(), glu::GLSL_VERSION_310_ES, glu::PRECISION_HIGHP)
188	, m_caseNdx					(0)
189{
190}
191
192TextureCubeArrayFilteringCase::~TextureCubeArrayFilteringCase (void)
193{
194	TextureCubeArrayFilteringCase::deinit();
195}
196
197void TextureCubeArrayFilteringCase::init (void)
198{
199	if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_texture_cube_map_array"))
200		throw tcu::NotSupportedError("GL_EXT_texture_cube_map_array not supported");
201
202	try
203	{
204		const tcu::TextureFormat		texFmt		= glu::mapGLInternalFormat(m_internalFormat);
205		const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(texFmt);
206		const tcu::Vec4					cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
207		const tcu::Vec4					cBias		= fmtInfo.valueMin;
208		const int						numLevels	= deLog2Floor32(m_size) + 1;
209		const int						numLayers	= m_depth / 6;
210
211		// Create textures.
212		m_gradientTex	= new glu::TextureCubeArray(m_context.getRenderContext(), m_internalFormat, m_size, m_depth);
213		m_gridTex		= new glu::TextureCubeArray(m_context.getRenderContext(), m_internalFormat, m_size, m_depth);
214
215		const tcu::IVec4 levelSwz[] =
216		{
217			tcu::IVec4(0,1,2,3),
218			tcu::IVec4(2,1,3,0),
219			tcu::IVec4(3,0,1,2),
220			tcu::IVec4(1,3,2,0),
221		};
222
223		// Fill first gradient texture (gradient direction varies between layers).
224		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
225		{
226			m_gradientTex->getRefTexture().allocLevel(levelNdx);
227
228			const tcu::PixelBufferAccess levelBuf = m_gradientTex->getRefTexture().getLevel(levelNdx);
229
230			for (int layerFaceNdx = 0; layerFaceNdx < m_depth; layerFaceNdx++)
231			{
232				const tcu::IVec4	swz		= levelSwz[layerFaceNdx % DE_LENGTH_OF_ARRAY(levelSwz)];
233				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;
234				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;
235
236				tcu::fillWithComponentGradients(tcu::getSubregion(levelBuf, 0, 0, layerFaceNdx, levelBuf.getWidth(), levelBuf.getHeight(), 1), gMin, gMax);
237			}
238		}
239
240		// Fill second with grid texture (each layer has unique colors).
241		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
242		{
243			m_gridTex->getRefTexture().allocLevel(levelNdx);
244
245			const tcu::PixelBufferAccess levelBuf = m_gridTex->getRefTexture().getLevel(levelNdx);
246
247			for (int layerFaceNdx = 0; layerFaceNdx < m_depth; layerFaceNdx++)
248			{
249				const deUint32	step	= 0x00ffffff / (numLevels*m_depth - 1);
250				const deUint32	rgb		= step * (levelNdx + layerFaceNdx*numLevels);
251				const deUint32	colorA	= 0xff000000 | rgb;
252				const deUint32	colorB	= 0xff000000 | ~rgb;
253
254				tcu::fillWithGrid(tcu::getSubregion(levelBuf, 0, 0, layerFaceNdx, levelBuf.getWidth(), levelBuf.getHeight(), 1),
255								  4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
256			}
257		}
258
259		// Upload.
260		m_gradientTex->upload();
261		m_gridTex->upload();
262
263		// Test cases
264		{
265			const glu::TextureCubeArray* const	tex0	= m_gradientTex;
266			const glu::TextureCubeArray* const	tex1	= m_gridTex;
267
268			if (m_onlySampleFaceInterior)
269			{
270				m_cases.push_back(FilterCase(tex0, tcu::Vec2(-0.8f, -0.8f),	tcu::Vec2(0.8f,  0.8f),	tcu::Vec2(-0.5f, float(numLayers)+0.5f)));	// minification
271				m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.5f, 0.65f),	tcu::Vec2(0.8f,  0.8f),	tcu::Vec2(-0.5f, float(numLayers)+0.5f)));	// magnification
272				m_cases.push_back(FilterCase(tex1, tcu::Vec2(-0.8f, -0.8f),	tcu::Vec2(0.8f,  0.8f),	tcu::Vec2(float(numLayers)+0.5f, -0.5f)));	// minification
273				m_cases.push_back(FilterCase(tex1, tcu::Vec2(0.2f, 0.2f),	tcu::Vec2(0.6f,  0.5f),	tcu::Vec2(float(numLayers)+0.5f, -0.5f)));	// magnification
274			}
275			else
276			{
277				const bool isSingleSample = (m_context.getRenderTarget().getNumSamples() == 0);
278
279				// minification - w/ tweak to avoid hitting triangle edges with a face switchpoint in multisample configs
280				if (isSingleSample)
281					m_cases.push_back(FilterCase(tex0, tcu::Vec2(-1.25f, -1.2f), tcu::Vec2(1.2f, 1.25f), tcu::Vec2(-0.5f, float(numLayers)+0.5f)));
282				else
283					m_cases.push_back(FilterCase(tex0, tcu::Vec2(-1.19f, -1.3f), tcu::Vec2(1.1f, 1.35f), tcu::Vec2(-0.5f, float(numLayers)+0.5f)));
284
285				m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.8f, 0.8f),		tcu::Vec2(1.25f, 1.20f),	tcu::Vec2(-0.5f, float(numLayers)+0.5f)));	// magnification
286				m_cases.push_back(FilterCase(tex1, tcu::Vec2(-1.19f, -1.3f),	tcu::Vec2(1.1f, 1.35f),		tcu::Vec2(float(numLayers)+0.5f, -0.5f)));	// minification
287				m_cases.push_back(FilterCase(tex1, tcu::Vec2(-1.2f, -1.1f),		tcu::Vec2(-0.8f, -0.8f),	tcu::Vec2(float(numLayers)+0.5f, -0.5f)));	// magnification
288
289				// Layer rounding - only in single-sample configs as multisample configs may produce smooth transition at the middle.
290				if (isSingleSample && (numLayers > 1))
291					m_cases.push_back(FilterCase(tex0,	tcu::Vec2(-2.0f, -1.5f  ),	tcu::Vec2(-0.1f,  0.9f), tcu::Vec2(1.50001f, 1.49999f)));
292			}
293		}
294
295		m_caseNdx = 0;
296		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
297	}
298	catch (...)
299	{
300		// Clean up to save memory.
301		TextureCubeArrayFilteringCase::deinit();
302		throw;
303	}
304}
305
306void TextureCubeArrayFilteringCase::deinit (void)
307{
308	delete m_gradientTex;
309	delete m_gridTex;
310
311	m_gradientTex	= DE_NULL;
312	m_gridTex		= DE_NULL;
313
314	m_renderer.clear();
315	m_cases.clear();
316}
317
318TextureCubeArrayFilteringCase::IterateResult TextureCubeArrayFilteringCase::iterate (void)
319{
320	TestLog&						log				= m_testCtx.getLog();
321	const glu::RenderContext&		renderCtx		= m_context.getRenderContext();
322	const glw::Functions&			gl				= renderCtx.getFunctions();
323	const int						viewportSize	= 28;
324	const deUint32					randomSeed		= deStringHash(getName()) ^ deInt32Hash(m_caseNdx) ^ m_testCtx.getCommandLine().getBaseSeed();
325	const RandomViewport			viewport		(m_context.getRenderTarget(), viewportSize, viewportSize, randomSeed);
326	const FilterCase&				curCase			= m_cases[m_caseNdx];
327	const tcu::TextureFormat		texFmt			= curCase.texture->getRefTexture().getFormat();
328	const tcu::TextureFormatInfo	fmtInfo			= tcu::getTextureFormatInfo(texFmt);
329	const tcu::ScopedLogSection		section			(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
330	ReferenceParams					refParams		(TEXTURETYPE_CUBE_ARRAY);
331
332	if (viewport.width < viewportSize || viewport.height < viewportSize)
333		throw tcu::NotSupportedError("Render target too small", "", __FILE__, __LINE__);
334
335	// Params for reference computation.
336	refParams.sampler					= glu::mapGLSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, m_minFilter, m_magFilter);
337	refParams.sampler.seamlessCubeMap	= true;
338	refParams.samplerType				= getSamplerType(texFmt);
339	refParams.colorBias					= fmtInfo.lookupBias;
340	refParams.colorScale				= fmtInfo.lookupScale;
341	refParams.lodMode					= LODMODE_EXACT;
342
343	gl.bindTexture	(GL_TEXTURE_CUBE_MAP_ARRAY, curCase.texture->getGLTexture());
344	gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER,	m_minFilter);
345	gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER,	m_magFilter);
346	gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S,		m_wrapS);
347	gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T,		m_wrapT);
348
349	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
350
351	m_testCtx.getLog() << TestLog::Message << "Coordinates: " << curCase.bottomLeft << " -> " << curCase.topRight << TestLog::EndMessage;
352
353	for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
354	{
355		const tcu::CubeFace		face		= tcu::CubeFace(faceNdx);
356		tcu::Surface			result		(viewport.width, viewport.height);
357		vector<float>			texCoord;
358
359		computeQuadTexCoordCubeArray(texCoord, face, curCase.bottomLeft, curCase.topRight, curCase.layerRange);
360
361		log << TestLog::Message << "Face " << getFaceDesc(face) << TestLog::EndMessage;
362
363		log << TestLog::Message << "Texture coordinates:" << TestLog::EndMessage;
364
365		logCubeArrayTexCoords(log, texCoord);
366
367		m_renderer.renderQuad(0, &texCoord[0], refParams);
368		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
369
370		glu::readPixels(renderCtx, viewport.x, viewport.y, result.getAccess());
371		GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
372
373		{
374			const bool				isNearestOnly	= m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
375			const tcu::PixelFormat	pixelFormat		= renderCtx.getRenderTarget().getPixelFormat();
376			const tcu::IVec4		coordBits		= tcu::IVec4(10);
377			const tcu::IVec4		colorBits		= max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2), tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
378			tcu::LodPrecision		lodPrecision;
379			tcu::LookupPrecision	lookupPrecision;
380
381			lodPrecision.derivateBits		= 10;
382			lodPrecision.lodBits			= 5;
383			lookupPrecision.colorThreshold	= tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
384			lookupPrecision.coordBits		= coordBits.toWidth<3>();
385			lookupPrecision.uvwBits			= tcu::IVec3(6);
386			lookupPrecision.colorMask		= getCompareMask(pixelFormat);
387
388			const bool isHighQuality = verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(),
389														   &texCoord[0], refParams, lookupPrecision, coordBits, lodPrecision, pixelFormat);
390
391			if (!isHighQuality)
392			{
393				// Evaluate against lower precision requirements.
394				lodPrecision.lodBits	= 4;
395				lookupPrecision.uvwBits	= tcu::IVec3(4);
396
397				m_testCtx.getLog() << TestLog::Message << "Warning: Verification against high precision requirements failed, trying with lower requirements." << TestLog::EndMessage;
398
399				const bool isOk = verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(),
400													  &texCoord[0], refParams, lookupPrecision, coordBits, lodPrecision, pixelFormat);
401
402				if (!isOk)
403				{
404					m_testCtx.getLog() << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
405					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
406				}
407				else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
408					m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality filtering result");
409			}
410		}
411	}
412
413	m_caseNdx += 1;
414	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
415}
416
417TextureFilteringTests::TextureFilteringTests (Context& context)
418	: TestCaseGroup(context, "filtering", "Texture Filtering Tests")
419{
420}
421
422TextureFilteringTests::~TextureFilteringTests (void)
423{
424}
425
426void TextureFilteringTests::init (void)
427{
428	static const struct
429	{
430		const char*		name;
431		deUint32		mode;
432	} wrapModes[] =
433	{
434		{ "clamp",		GL_CLAMP_TO_EDGE },
435		{ "repeat",		GL_REPEAT },
436		{ "mirror",		GL_MIRRORED_REPEAT }
437	};
438
439	static const struct
440	{
441		const char*		name;
442		deUint32		mode;
443	} minFilterModes[] =
444	{
445		{ "nearest",				GL_NEAREST					},
446		{ "linear",					GL_LINEAR					},
447		{ "nearest_mipmap_nearest",	GL_NEAREST_MIPMAP_NEAREST	},
448		{ "linear_mipmap_nearest",	GL_LINEAR_MIPMAP_NEAREST	},
449		{ "nearest_mipmap_linear",	GL_NEAREST_MIPMAP_LINEAR	},
450		{ "linear_mipmap_linear",	GL_LINEAR_MIPMAP_LINEAR		}
451	};
452
453	static const struct
454	{
455		const char*		name;
456		deUint32		mode;
457	} magFilterModes[] =
458	{
459		{ "nearest",	GL_NEAREST },
460		{ "linear",		GL_LINEAR }
461	};
462
463	static const struct
464	{
465		int size;
466		int depth;
467	} sizesCubeArray[] =
468	{
469		{   8,	 6 },
470		{  64,	12 },
471		{ 128,	12 },
472		{   7,	12 },
473		{  63,	18 }
474	};
475
476	static const struct
477	{
478		const char*		name;
479		deUint32		format;
480	} filterableFormatsByType[] =
481	{
482		{ "rgba16f",		GL_RGBA16F			},
483		{ "r11f_g11f_b10f",	GL_R11F_G11F_B10F	},
484		{ "rgb9_e5",		GL_RGB9_E5			},
485		{ "rgba8",			GL_RGBA8			},
486		{ "rgba8_snorm",	GL_RGBA8_SNORM		},
487		{ "rgb565",			GL_RGB565			},
488		{ "rgba4",			GL_RGBA4			},
489		{ "rgb5_a1",		GL_RGB5_A1			},
490		{ "srgb8_alpha8",	GL_SRGB8_ALPHA8		},
491		{ "rgb10_a2",		GL_RGB10_A2			}
492	};
493
494	// Cube map array texture filtering.
495	{
496		tcu::TestCaseGroup* const groupCubeArray = new tcu::TestCaseGroup(m_testCtx, "cube_array", "Cube Map Array Texture Filtering");
497		addChild(groupCubeArray);
498
499		// Formats.
500		{
501			tcu::TestCaseGroup* const formatsGroup = new tcu::TestCaseGroup(m_testCtx, "formats", "Cube Map Array Texture Formats");
502			groupCubeArray->addChild(formatsGroup);
503
504			for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(filterableFormatsByType); fmtNdx++)
505			{
506				for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
507				{
508					const deUint32	minFilter	= minFilterModes[filterNdx].mode;
509					const char*		filterName	= minFilterModes[filterNdx].name;
510					const deUint32	format		= filterableFormatsByType[fmtNdx].format;
511					const char*		formatName	= filterableFormatsByType[fmtNdx].name;
512					const bool		isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
513					const deUint32	magFilter	= isMipmap ? GL_LINEAR : minFilter;
514					const string	name		= string(formatName) + "_" + filterName;
515					const deUint32	wrapS		= GL_REPEAT;
516					const deUint32	wrapT		= GL_REPEAT;
517					const int		size		= 64;
518					const int		depth		= 12;
519
520					formatsGroup->addChild(new TextureCubeArrayFilteringCase(m_context,
521																			 name.c_str(), "",
522																			 minFilter, magFilter,
523																			 wrapS, wrapT,
524																			 format,
525																			 size, depth));
526				}
527			}
528		}
529
530		// Sizes.
531		{
532			tcu::TestCaseGroup* const sizesGroup = new tcu::TestCaseGroup(m_testCtx, "sizes", "Texture Sizes");
533			groupCubeArray->addChild(sizesGroup);
534
535			for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizesCubeArray); sizeNdx++)
536			{
537				for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
538				{
539					const deUint32	minFilter	= minFilterModes[filterNdx].mode;
540					const char*		filterName	= minFilterModes[filterNdx].name;
541					const deUint32	format		= GL_RGBA8;
542					const bool		isMipmap	= minFilter != GL_NEAREST && minFilter != GL_LINEAR;
543					const deUint32	magFilter	= isMipmap ? GL_LINEAR : minFilter;
544					const deUint32	wrapS		= GL_REPEAT;
545					const deUint32	wrapT		= GL_REPEAT;
546					const int		size		= sizesCubeArray[sizeNdx].size;
547					const int		depth		= sizesCubeArray[sizeNdx].depth;
548					const string	name		= de::toString(size) + "x" + de::toString(size) + "x" + de::toString(depth) + "_" + filterName;
549
550					sizesGroup->addChild(new TextureCubeArrayFilteringCase(m_context,
551																		   name.c_str(), "",
552																		   minFilter, magFilter,
553																		   wrapS, wrapT,
554																		   format,
555																		   size, depth));
556				}
557			}
558		}
559
560		// Wrap modes.
561		{
562			tcu::TestCaseGroup* const combinationsGroup = new tcu::TestCaseGroup(m_testCtx, "combinations", "Filter and wrap mode combinations");
563			groupCubeArray->addChild(combinationsGroup);
564
565			for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); minFilterNdx++)
566			{
567				for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilterModes); magFilterNdx++)
568				{
569					for (int wrapSNdx = 0; wrapSNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapSNdx++)
570					{
571						for (int wrapTNdx = 0; wrapTNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapTNdx++)
572						{
573							const deUint32	minFilter	= minFilterModes[minFilterNdx].mode;
574							const deUint32	magFilter	= magFilterModes[magFilterNdx].mode;
575							const deUint32	format		= GL_RGBA8;
576							const deUint32	wrapS		= wrapModes[wrapSNdx].mode;
577							const deUint32	wrapT		= wrapModes[wrapTNdx].mode;
578							const int		size		= 63;
579							const int		depth		= 12;
580							const string	name		= string(minFilterModes[minFilterNdx].name) + "_" + magFilterModes[magFilterNdx].name + "_" + wrapModes[wrapSNdx].name + "_" + wrapModes[wrapTNdx].name;
581
582							combinationsGroup->addChild(new TextureCubeArrayFilteringCase(m_context,
583																						  name.c_str(), "",
584																						  minFilter, magFilter,
585																						  wrapS, wrapT,
586																						  format,
587																						  size, depth));
588						}
589					}
590				}
591			}
592		}
593
594		// Cases with no visible cube edges.
595		{
596			tcu::TestCaseGroup* const onlyFaceInteriorGroup = new tcu::TestCaseGroup(m_testCtx, "no_edges_visible", "Don't sample anywhere near a face's edges");
597			groupCubeArray->addChild(onlyFaceInteriorGroup);
598
599			for (int isLinearI = 0; isLinearI <= 1; isLinearI++)
600			{
601				const bool		isLinear	= isLinearI != 0;
602				const deUint32	filter		= isLinear ? GL_LINEAR : GL_NEAREST;
603
604				onlyFaceInteriorGroup->addChild(new TextureCubeArrayFilteringCase(m_context,
605																				  isLinear ? "linear" : "nearest", "",
606																				  filter, filter,
607																				  GL_REPEAT, GL_REPEAT,
608																				  GL_RGBA8,
609																				  63, 12,
610																				  true));
611			}
612		}
613	}
614}
615
616} // Functional
617} // gles31
618} // deqp
619