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