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