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