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