text: /*-------------------------------------------------------------------------
   * drawElements Quality Program OpenGL ES 3.0 Module
   * -------------------------------------------------
   *
   * Copyright 2014 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   *//*!
   * \file
   * \brief Texture wrap mode tests.
   *//*--------------------------------------------------------------------*/
  
  #include "es3fTextureWrapTests.hpp"
  #include "glsTextureTestUtil.hpp"
  #include "gluTexture.hpp"
  #include "gluStrUtil.hpp"
  #include "gluTextureUtil.hpp"
  #include "gluPixelTransfer.hpp"
  #include "tcuTestLog.hpp"
  #include "tcuTextureUtil.hpp"
  #include "tcuCompressedTexture.hpp"
  #include "tcuVectorUtil.hpp"
  #include "tcuTexLookupVerifier.hpp"
  #include "deRandom.hpp"
  #include "deStringUtil.hpp"
  #include "deMemory.h"
  
  #include "glwEnums.hpp"
  #include "glwFunctions.hpp"
  
  namespace deqp
  {
  namespace gles3
  {
  namespace Functional
  {
  
  using tcu::TestLog;
  using tcu::CompressedTexture;
  using tcu::CompressedTexFormat;
  using std::vector;
  using std::string;
  using tcu::Sampler;
  using namespace glu;
  using namespace gls::TextureTestUtil;
  using namespace glu::TextureTestUtil;
  
  //! Checks whether any ASTC version (LDR, HDR, full) is supported.
  static inline bool isASTCSupported (const glu::ContextInfo& contextInfo)
  {
  	const vector<string>& extensions = contextInfo.getExtensions();
  
  	for (int extNdx = 0; extNdx < (int)extensions.size(); extNdx++)
  	{
  		const string& ext = extensions[extNdx];
  
  		if (ext == "GL_KHR_texture_compression_astc_ldr" ||
  			ext == "GL_KHR_texture_compression_astc_hdr" ||
  			ext == "GL_OES_texture_compression_astc")
  			return true;
  	}
  
  	return false;
  }
  
  enum
  {
  	VIEWPORT_WIDTH		= 256,
  	VIEWPORT_HEIGHT		= 256
  };
  
  class TextureWrapCase : public tcu::TestCase
  {
  public:
  									TextureWrapCase			(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* description, deUint32 format, deUint32 dataType, deUint32 wrapS, deUint32 wrapT, deUint32 minFilter, deUint32 magFilter, int width, int height);
  									TextureWrapCase			(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* description, deUint32 wrapS, deUint32 wrapT, deUint32 minFilter, deUint32 magFilter, const std::vector<std::string>& filenames);
  									TextureWrapCase			(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* description, CompressedTexFormat compressedFormat, deUint32 wrapS, deUint32 wrapT, deUint32 minFilter, deUint32 magFilter, int width, int height);
  									~TextureWrapCase		(void);
  
  	void							init					(void);
  	void							deinit					(void);
  	IterateResult					iterate					(void);
  
  private:
  									TextureWrapCase			(const TextureWrapCase& other);
  	TextureWrapCase&				operator=				(const TextureWrapCase& other);
  
  	struct Case
  	{
  		tcu::Vec2 bottomLeft;
  		tcu::Vec2 topRight;
  
  		Case (void) {}
  		Case (const tcu::Vec2& bl, const tcu::Vec2& tr) : bottomLeft(bl), topRight(tr) {}
  	};
  
  	glu::RenderContext&				m_renderCtx;
  	const glu::ContextInfo&			m_renderCtxInfo;
  
  	const deUint32					m_format;
  	const deUint32					m_dataType;
  	const CompressedTexFormat		m_compressedFormat;
  	const deUint32					m_wrapS;
  	const deUint32					m_wrapT;
  	const deUint32					m_minFilter;
  	const deUint32					m_magFilter;
  
  	int								m_width;
  	int								m_height;
  	const std::vector<std::string>	m_filenames;
  
  	vector<Case>					m_cases;
  	int								m_caseNdx;
  
  	glu::Texture2D*					m_texture;
  	TextureRenderer					m_renderer;
  };
  
  TextureWrapCase::TextureWrapCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* description, deUint32 format, deUint32 dataType, deUint32 wrapS, deUint32 wrapT, deUint32 minFilter, deUint32 magFilter, int width, int height)
  	: TestCase				(testCtx, name, description)
  	, m_renderCtx			(renderCtx)
  	, m_renderCtxInfo		(ctxInfo)
  	, m_format				(format)
  	, m_dataType			(dataType)
  	, m_compressedFormat	(tcu::COMPRESSEDTEXFORMAT_LAST)
  	, m_wrapS				(wrapS)
  	, m_wrapT				(wrapT)
  	, m_minFilter			(minFilter)
  	, m_magFilter			(magFilter)
  	, m_width				(width)
  	, m_height				(height)
  	, m_caseNdx				(0)
  	, m_texture				(DE_NULL)
  	, m_renderer			(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_MEDIUMP)
  {
  }
  
  TextureWrapCase::TextureWrapCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* description, deUint32 wrapS, deUint32 wrapT, deUint32 minFilter, deUint32 magFilter, const std::vector<std::string>& filenames)
  	: TestCase				(testCtx, name, description)
  	, m_renderCtx			(renderCtx)
  	, m_renderCtxInfo		(ctxInfo)
  	, m_format				(GL_NONE)
  	, m_dataType			(GL_NONE)
  	, m_compressedFormat	(tcu::COMPRESSEDTEXFORMAT_LAST)
  	, m_wrapS				(wrapS)
  	, m_wrapT				(wrapT)
  	, m_minFilter			(minFilter)
  	, m_magFilter			(magFilter)
  	, m_width				(0)
  	, m_height				(0)
  	, m_filenames			(filenames)
  	, m_caseNdx				(0)
  	, m_texture				(DE_NULL)
  	, m_renderer			(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_MEDIUMP)
  {
  }
  
  TextureWrapCase::TextureWrapCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const char* description, CompressedTexFormat compressedFormat, deUint32 wrapS, deUint32 wrapT, deUint32 minFilter, deUint32 magFilter, int width, int height)
  	: TestCase				(testCtx, name, description)
  	, m_renderCtx			(renderCtx)
  	, m_renderCtxInfo		(ctxInfo)
  	, m_format				(GL_NONE)
  	, m_dataType			(GL_NONE)
  	, m_compressedFormat	(compressedFormat)
  	, m_wrapS				(wrapS)
  	, m_wrapT				(wrapT)
  	, m_minFilter			(minFilter)
  	, m_magFilter			(magFilter)
  	, m_width				(width)
  	, m_height				(height)
  	, m_caseNdx				(0)
  	, m_texture				(DE_NULL)
  	, m_renderer			(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_MEDIUMP)
  {
  }
  
  
  TextureWrapCase::~TextureWrapCase (void)
  {
  	deinit();
  }
  
  void TextureWrapCase::init (void)
  {
  	// Load or generate texture.
  
  	if (!m_filenames.empty())
  	{
  		// Load compressed texture from file.
  
  		DE_ASSERT(m_width == 0 && m_height == 0 && m_format == GL_NONE && m_dataType == GL_NONE);
  
  		m_texture	= glu::Texture2D::create(m_renderCtx, m_renderCtxInfo, m_testCtx.getArchive(), (int)m_filenames.size(), m_filenames);
  		m_width		= m_texture->getRefTexture().getWidth();
  		m_height	= m_texture->getRefTexture().getHeight();
  	}
  	else if (m_compressedFormat != tcu::COMPRESSEDTEXFORMAT_LAST)
  	{
  		// Generate compressed texture.
  
  		DE_ASSERT(m_format == GL_NONE && m_dataType == GL_NONE);
  
  		if (tcu::isEtcFormat(m_compressedFormat))
  		{
  			// Create ETC texture. Any content is valid.
  
  			tcu::CompressedTexture	compressedTexture	(m_compressedFormat, m_width, m_height);
  			const int				dataSize			= compressedTexture.getDataSize();
  			deUint8* const			data				= (deUint8*)compressedTexture.getData();
  			de::Random				rnd					(deStringHash(getName()));
  
  			for (int i = 0; i < dataSize; i++)
  				data[i] = rnd.getUint32() & 0xff;
  
  			m_texture = new glu::Texture2D(m_renderCtx, m_renderCtxInfo, 1, &compressedTexture);
  		}
  		else if (tcu::isAstcFormat(m_compressedFormat))
  		{
  			// Create ASTC texture by picking from a set of pre-generated blocks.
  
  			static const int		BLOCK_SIZE				= 16;
  			static const deUint8	blocks[][BLOCK_SIZE]	=
  			{
  				// \note All of the following blocks are valid in LDR mode.
  				{ 252,	253,	255,	255,	255,	255,	255,	255,	8,		71,		90,		78,		22,		17,		26,		66,		},
  				{ 252,	253,	255,	255,	255,	255,	255,	255,	220,	74,		139,	235,	249,	6,		145,	125		},
  				{ 252,	253,	255,	255,	255,	255,	255,	255,	223,	251,	28,		206,	54,		251,	160,	174		},
  				{ 252,	253,	255,	255,	255,	255,	255,	255,	39,		4,		153,	219,	180,	61,		51,		37		},
  				{ 67,	2,		0,		254,	1,		0,		64,		215,	83,		211,	159,	105,	41,		140,	50,		2		},
  				{ 67,	130,	0,		170,	84,		255,	65,		215,	83,		211,	159,	105,	41,		140,	50,		2		},
  				{ 67,	2,		129,	38,		51,		229,	95,		215,	83,		211,	159,	105,	41,		140,	50,		2		},
  				{ 67,	130,	193,	56,		213,	144,	95,		215,	83,		211,	159,	105,	41,		140,	50,		2		}
  			};
  
  			if (!isASTCSupported(m_renderCtxInfo)) // \note Any level of ASTC support is enough, since we're only using LDR blocks.
  				throw tcu::NotSupportedError("ASTC not supported");
  
  			tcu::CompressedTexture	compressedTexture	(m_compressedFormat, m_width, m_height);
  			const int				dataSize			= compressedTexture.getDataSize();
  			deUint8* const			data				= (deUint8*)compressedTexture.getData();
  			de::Random				rnd					(deStringHash(getName()));
  			DE_ASSERT(dataSize % BLOCK_SIZE == 0);
  
  			for (int i = 0; i < dataSize/BLOCK_SIZE; i++)
  				deMemcpy(&data[i*BLOCK_SIZE], &blocks[rnd.getInt(0, DE_LENGTH_OF_ARRAY(blocks)-1)][0], BLOCK_SIZE);
  
  			// \note All blocks are valid LDR blocks so ASTCMODE_* doesn't change anything
  			m_texture = new glu::Texture2D(m_renderCtx, m_renderCtxInfo, 1, &compressedTexture, tcu::TexDecompressionParams(tcu::TexDecompressionParams::ASTCMODE_LDR));
  		}
  		else
  			DE_ASSERT(false);
  	}
  	else
  	{
  		m_texture = new Texture2D(m_renderCtx, m_format, m_dataType, m_width, m_height);
  
  		// Fill level 0.
  		m_texture->getRefTexture().allocLevel(0);
  		tcu::fillWithComponentGradients(m_texture->getRefTexture().getLevel(0), tcu::Vec4(-0.5f, -0.5f, -0.5f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f));
  
  		m_texture->upload();
  	}
  
  	// Sub-cases.
  
  	m_cases.push_back(Case(tcu::Vec2(-1.5f, -3.0f), tcu::Vec2(1.5f, 2.5f)));
  	m_cases.push_back(Case(tcu::Vec2(-0.5f, 0.75f), tcu::Vec2(0.25f, 1.25f)));
  	DE_ASSERT(m_caseNdx == 0);
  
  	// Initialize to success, set to failure later if needed.
  
  	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  }
  
  void TextureWrapCase::deinit (void)
  {
  	delete m_texture;
  	m_texture = DE_NULL;
  
  	m_renderer.clear();
  }
  
  TextureWrapCase::IterateResult TextureWrapCase::iterate (void)
  {
  	const glw::Functions&			gl								= m_renderCtx.getFunctions();
  	TestLog&						log								= m_testCtx.getLog();
  	const RandomViewport			viewport						(m_renderCtx.getRenderTarget(), VIEWPORT_WIDTH, VIEWPORT_HEIGHT, deStringHash(getName()) + m_caseNdx);
  	tcu::Surface					renderedFrame					(viewport.width, viewport.height);
  	ReferenceParams					refParams						(TEXTURETYPE_2D);
  	const tcu::TextureFormat		texFormat						= m_texture->getRefTexture().getFormat();
  	vector<float>					texCoord;
  	const tcu::TextureFormatInfo	texFormatInfo					= tcu::getTextureFormatInfo(texFormat);
  	// \note For non-sRGB ASTC formats, the values are fp16 in range [0..1], not the range assumed given by tcu::getTextureFormatInfo().
  	const bool						useDefaultColorScaleAndBias		= !tcu::isAstcFormat(m_compressedFormat) || tcu::isAstcSRGBFormat(m_compressedFormat);
  
  	// Bind to unit 0.
  	gl.activeTexture(GL_TEXTURE0);
  	gl.bindTexture(GL_TEXTURE_2D, m_texture->getGLTexture());
  
  	// Setup filtering and wrap modes.
  	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		m_wrapS);
  	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		m_wrapT);
  	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	m_minFilter);
  	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	m_magFilter);
  
  	GLU_EXPECT_NO_ERROR(gl.getError(), "Set texturing state");
  
  	// Parameters for reference images.
  	refParams.sampler		= mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
  	refParams.lodMode		= LODMODE_EXACT;
  	refParams.samplerType	= getSamplerType(m_texture->getRefTexture().getFormat());
  	refParams.colorScale	= useDefaultColorScaleAndBias ? texFormatInfo.lookupScale	: tcu::Vec4(1.0f);
  	refParams.colorBias		= useDefaultColorScaleAndBias ? texFormatInfo.lookupBias	: tcu::Vec4(0.0f);
  
  	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
  	computeQuadTexCoord2D(texCoord, m_cases[m_caseNdx].bottomLeft, m_cases[m_caseNdx].topRight);
  	m_renderer.renderQuad(0, &texCoord[0], refParams);
  	glu::readPixels(m_renderCtx, viewport.x, viewport.y, renderedFrame.getAccess());
  
  	{
  		const tcu::ScopedLogSection		section			(log, string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
  		const bool						isNearestOnly	= m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
  		const bool						isSRGB			= tcu::isSRGB(texFormat);
  		const tcu::PixelFormat			pixelFormat		= m_renderCtx.getRenderTarget().getPixelFormat();
  		const tcu::IVec4				colorBits		= tcu::max(getBitsVec(pixelFormat) - (isNearestOnly && !isSRGB ? 1 : 2), tcu::IVec4(0));
  		tcu::LodPrecision				lodPrecision;
  		tcu::LookupPrecision			lookupPrecision;
  
  		lodPrecision.derivateBits		= 18;
  		lodPrecision.lodBits			= 5;
  		lookupPrecision.colorThreshold	= tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
  		lookupPrecision.coordBits		= tcu::IVec3(20,20,0);
  		lookupPrecision.uvwBits			= tcu::IVec3(5,5,0);
  		lookupPrecision.colorMask		= getCompareMask(pixelFormat);
  
  		log << TestLog::Message << "Note: lookup coordinates: bottom-left " << m_cases[m_caseNdx].bottomLeft << ", top-right " << m_cases[m_caseNdx].topRight << TestLog::EndMessage;
  
  		const bool isOk = verifyTextureResult(m_testCtx, renderedFrame.getAccess(), m_texture->getRefTexture(),
  											  &texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
  
  		if (!isOk)
  			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
  	}
  
  	m_caseNdx++;
  	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
  }
  
  TextureWrapTests::TextureWrapTests (Context& context)
  	: TestCaseGroup(context, "wrap", "Wrap Mode Tests")
  {
  }
  
  TextureWrapTests::~TextureWrapTests (void)
  {
  }
  
  void TextureWrapTests::init (void)
  {
  	static const struct
  	{
  		const char*		name;
  		deUint32		mode;
  	} wrapModes[] =
  	{
  		{ "clamp",		GL_CLAMP_TO_EDGE },
  		{ "repeat",		GL_REPEAT },
  		{ "mirror",		GL_MIRRORED_REPEAT }
  	};
  
  	static const struct
  	{
  		const char*		name;
  		deUint32		mode;
  	} filteringModes[] =
  	{
  		{ "nearest",	GL_NEAREST },
  		{ "linear",		GL_LINEAR }
  	};
  
  #define FOR_EACH(ITERATOR, ARRAY, BODY)	\
  	for (int (ITERATOR) = 0; (ITERATOR) < DE_LENGTH_OF_ARRAY(ARRAY); (ITERATOR)++)	\
  		BODY
  
  	// RGBA8 cases.
  	{
  		static const struct
  		{
  			const char*		name;
  			int				width;
  			int				height;
  		} rgba8Sizes[] =
  		{
  			{ "pot",		64, 128 },
  			{ "npot",		63, 112 }
  		};
  
  		{
  			TestCaseGroup* const rgba8Group = new TestCaseGroup(m_context, "rgba8", "");
  			addChild(rgba8Group);
  
  			FOR_EACH(size,		rgba8Sizes,
  			FOR_EACH(wrapS,		wrapModes,
  			FOR_EACH(wrapT,		wrapModes,
  			FOR_EACH(filter,	filteringModes,
  				{
  					const string name = string("") + wrapModes[wrapS].name + "_" + wrapModes[wrapT].name + "_" + filteringModes[filter].name + "_" + rgba8Sizes[size].name;
  					rgba8Group->addChild(new TextureWrapCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "",
  															 GL_RGBA, GL_UNSIGNED_BYTE,
  															 wrapModes[wrapS].mode,
  															 wrapModes[wrapT].mode,
  															 filteringModes[filter].mode, filteringModes[filter].mode,
  															 rgba8Sizes[size].width, rgba8Sizes[size].height));
  
  				}))));
  		}
  	}
  
  	// ETC1 cases.
  	{
  		TestCaseGroup* const etc1Group = new TestCaseGroup(m_context, "etc1", "");
  		addChild(etc1Group);
  
  		// Power-of-two ETC1 texture
  		std::vector<std::string> potFilenames;
  		potFilenames.push_back("data/etc1/photo_helsinki_mip_0.pkm");
  
  		FOR_EACH(wrapS,		wrapModes,
  		FOR_EACH(wrapT,		wrapModes,
  		FOR_EACH(filter,	filteringModes,
  			{
  				const string name = string("") + wrapModes[wrapS].name + "_" + wrapModes[wrapT].name + "_" + filteringModes[filter].name + "_pot";
  				etc1Group->addChild(new TextureWrapCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "",
  														wrapModes[wrapS].mode,
  														wrapModes[wrapT].mode,
  														filteringModes[filter].mode, filteringModes[filter].mode,
  														potFilenames));
  
  			})));
  
  		std::vector<std::string> npotFilenames;
  		npotFilenames.push_back("data/etc1/photo_helsinki_113x89.pkm");
  
  		// NPOT ETC1 texture
  		FOR_EACH(wrapS,		wrapModes,
  		FOR_EACH(wrapT,		wrapModes,
  		FOR_EACH(filter,	filteringModes,
  			{
  				const string name = string("") + wrapModes[wrapS].name + "_" + wrapModes[wrapT].name + "_" + filteringModes[filter].name + "_npot";
  				etc1Group->addChild(new TextureWrapCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "",
  														wrapModes[wrapS].mode,
  														wrapModes[wrapT].mode,
  														filteringModes[filter].mode, filteringModes[filter].mode,
  														npotFilenames));
  			})));
  	}
  
  	// ETC-2 (and EAC) cases.
  	{
  		static const struct
  		{
  			const char*			name;
  			CompressedTexFormat	format;
  		} etc2Formats[] =
  		{
  			{ "eac_r11",							tcu::COMPRESSEDTEXFORMAT_EAC_R11,							},
  			{ "eac_signed_r11",						tcu::COMPRESSEDTEXFORMAT_EAC_SIGNED_R11,					},
  			{ "eac_rg11",							tcu::COMPRESSEDTEXFORMAT_EAC_RG11,							},
  			{ "eac_signed_rg11",					tcu::COMPRESSEDTEXFORMAT_EAC_SIGNED_RG11,					},
  			{ "etc2_rgb8",							tcu::COMPRESSEDTEXFORMAT_ETC2_RGB8,							},
  			{ "etc2_srgb8",							tcu::COMPRESSEDTEXFORMAT_ETC2_SRGB8,						},
  			{ "etc2_rgb8_punchthrough_alpha1",		tcu::COMPRESSEDTEXFORMAT_ETC2_RGB8_PUNCHTHROUGH_ALPHA1,		},
  			{ "etc2_srgb8_punchthrough_alpha1",		tcu::COMPRESSEDTEXFORMAT_ETC2_SRGB8_PUNCHTHROUGH_ALPHA1,	},
  			{ "etc2_eac_rgba8",						tcu::COMPRESSEDTEXFORMAT_ETC2_EAC_RGBA8,					},
  			{ "etc2_eac_srgb8_alpha8",				tcu::COMPRESSEDTEXFORMAT_ETC2_EAC_SRGB8_ALPHA8,				}
  		};
  
  		static const struct
  		{
  			const char*		name;
  			int				width;
  			int				height;
  		} etc2Sizes[] =
  		{
  			{ "pot",	64,		128	},
  			{ "npot",	123,	107	}
  		};
  
  		for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(etc2Formats); formatNdx++)
  		{
  			TestCaseGroup* const formatGroup = new TestCaseGroup(m_context, etc2Formats[formatNdx].name, "");
  			addChild(formatGroup);
  
  			FOR_EACH(size,		etc2Sizes,
  			FOR_EACH(wrapS,		wrapModes,
  			FOR_EACH(wrapT,		wrapModes,
  			FOR_EACH(filter,	filteringModes,
  				{
  					const string name = string("") + wrapModes[wrapS].name + "_" + wrapModes[wrapT].name + "_" + filteringModes[filter].name + "_" + etc2Sizes[size].name;
  					formatGroup->addChild(new TextureWrapCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "",
  															  etc2Formats[formatNdx].format,
  															  wrapModes[wrapS].mode,
  															  wrapModes[wrapT].mode,
  															  filteringModes[filter].mode, filteringModes[filter].mode,
  															  etc2Sizes[size].width, etc2Sizes[size].height));
  				}))));
  		}
  	}
  
  	// ASTC cases.
  	{
  		for (int formatI = 0; formatI < tcu::COMPRESSEDTEXFORMAT_LAST; formatI++)
  		{
  			const CompressedTexFormat format = (CompressedTexFormat)formatI;
  
  			if (!tcu::isAstcFormat(format))
  				continue;
  
  			{
  				const tcu::IVec3		blockSize		= tcu::getBlockPixelSize(format);
  				const string			formatName		= "astc_" + de::toString(blockSize.x()) + "x" + de::toString(blockSize.y()) + (tcu::isAstcSRGBFormat(format) ? "_srgb" : "");
  				TestCaseGroup* const	formatGroup		= new TestCaseGroup(m_context, formatName.c_str(), "");
  				addChild(formatGroup);
  
  				DE_ASSERT(blockSize.z() == 1);
  
  				// \note This array is NOT static.
  				const struct
  				{
  					const char*		name;
  					int				width;
  					int				height;
  				} formatSizes[] =
  				{
  					{ "divisible",		blockSize.x()*10,		blockSize.y()*10	},
  					{ "not_divisible",	blockSize.x()*10+1,		blockSize.y()*10+1	},
  				};
  
  				FOR_EACH(size,		formatSizes,
  				FOR_EACH(wrapS,		wrapModes,
  				FOR_EACH(wrapT,		wrapModes,
  				FOR_EACH(filter,	filteringModes,
  					{
  						string name = string("") + wrapModes[wrapS].name + "_" + wrapModes[wrapT].name + "_" + filteringModes[filter].name + "_" + formatSizes[size].name;
  						formatGroup->addChild(new TextureWrapCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "",
  																  format,
  																  wrapModes[wrapS].mode,
  																  wrapModes[wrapT].mode,
  																  filteringModes[filter].mode, filteringModes[filter].mode,
  																  formatSizes[size].width, formatSizes[size].height));
  					}))));
  			}
  		}
  	}
  }
  
  } // Functional
  } // gles3
  } // deqp
  