Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 2.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 "es2fTextureFilteringTests.hpp"
     25 #include "glsTextureTestUtil.hpp"
     26 #include "gluTexture.hpp"
     27 #include "gluStrUtil.hpp"
     28 #include "gluTextureUtil.hpp"
     29 #include "gluPixelTransfer.hpp"
     30 #include "tcuTestLog.hpp"
     31 #include "tcuTextureUtil.hpp"
     32 #include "tcuTexLookupVerifier.hpp"
     33 #include "tcuVectorUtil.hpp"
     34 #include "deStringUtil.hpp"
     35 #include "glwFunctions.hpp"
     36 #include "glwEnums.hpp"
     37 
     38 namespace deqp
     39 {
     40 namespace gles2
     41 {
     42 namespace Functional
     43 {
     44 
     45 using tcu::TestLog;
     46 using std::vector;
     47 using std::string;
     48 using tcu::Sampler;
     49 using namespace glu;
     50 using namespace gls::TextureTestUtil;
     51 
     52 enum
     53 {
     54 	VIEWPORT_WIDTH		= 64,
     55 	VIEWPORT_HEIGHT		= 64,
     56 	MIN_VIEWPORT_WIDTH	= 64,
     57 	MIN_VIEWPORT_HEIGHT	= 64
     58 };
     59 
     60 class Texture2DFilteringCase : public tcu::TestCase
     61 {
     62 public:
     63 									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 format, deUint32 dataType, int width, int height);
     64 									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);
     65 									~Texture2DFilteringCase		(void);
     66 
     67 	void							init						(void);
     68 	void							deinit						(void);
     69 	IterateResult					iterate						(void);
     70 
     71 private:
     72 									Texture2DFilteringCase		(const Texture2DFilteringCase& other);
     73 	Texture2DFilteringCase&			operator=					(const Texture2DFilteringCase& other);
     74 
     75 	glu::RenderContext&				m_renderCtx;
     76 	const glu::ContextInfo&			m_renderCtxInfo;
     77 
     78 	const deUint32					m_minFilter;
     79 	const deUint32					m_magFilter;
     80 	const deUint32					m_wrapS;
     81 	const deUint32					m_wrapT;
     82 
     83 	const deUint32					m_format;
     84 	const deUint32					m_dataType;
     85 	const int						m_width;
     86 	const int						m_height;
     87 
     88 	const std::vector<std::string>	m_filenames;
     89 
     90 	struct FilterCase
     91 	{
     92 		const glu::Texture2D*	texture;
     93 		tcu::Vec2				minCoord;
     94 		tcu::Vec2				maxCoord;
     95 
     96 		FilterCase (void)
     97 			: texture(DE_NULL)
     98 		{
     99 		}
    100 
    101 		FilterCase (const glu::Texture2D* tex_, const tcu::Vec2& minCoord_, const tcu::Vec2& maxCoord_)
    102 			: texture	(tex_)
    103 			, minCoord	(minCoord_)
    104 			, maxCoord	(maxCoord_)
    105 		{
    106 		}
    107 	};
    108 
    109 	std::vector<glu::Texture2D*>	m_textures;
    110 	std::vector<FilterCase>			m_cases;
    111 
    112 	TextureRenderer					m_renderer;
    113 
    114 	int								m_caseNdx;
    115 };
    116 
    117 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 format, deUint32 dataType, int width, int height)
    118 	: TestCase			(testCtx, name, desc)
    119 	, m_renderCtx		(renderCtx)
    120 	, m_renderCtxInfo	(ctxInfo)
    121 	, m_minFilter		(minFilter)
    122 	, m_magFilter		(magFilter)
    123 	, m_wrapS			(wrapS)
    124 	, m_wrapT			(wrapT)
    125 	, m_format			(format)
    126 	, m_dataType		(dataType)
    127 	, m_width			(width)
    128 	, m_height			(height)
    129 	, m_renderer		(renderCtx, testCtx, glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
    130 	, m_caseNdx			(0)
    131 {
    132 }
    133 
    134 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)
    135 	: TestCase			(testCtx, name, desc)
    136 	, m_renderCtx		(renderCtx)
    137 	, m_renderCtxInfo	(ctxInfo)
    138 	, m_minFilter		(minFilter)
    139 	, m_magFilter		(magFilter)
    140 	, m_wrapS			(wrapS)
    141 	, m_wrapT			(wrapT)
    142 	, m_format			(GL_NONE)
    143 	, m_dataType		(GL_NONE)
    144 	, m_width			(0)
    145 	, m_height			(0)
    146 	, m_filenames		(filenames)
    147 	, m_renderer		(renderCtx, testCtx, glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
    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_format, m_dataType, m_width, m_height));
    172 
    173 			bool					mipmaps		= deIsPowerOfTwo32(m_width) && deIsPowerOfTwo32(m_height);
    174 			int						numLevels	= mipmaps ? deLog2Floor32(de::max(m_width, m_height))+1 : 1;
    175 			tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
    176 			tcu::Vec4				cBias		= fmtInfo.valueMin;
    177 			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.5f, -0.5f, -0.5f, 2.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>(VIEWPORT_WIDTH, m_renderCtx.getRenderTarget().getWidth());
    224 			const float	viewportH	= (float)de::min<int>(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(), VIEWPORT_WIDTH, 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 < MIN_VIEWPORT_WIDTH || viewport.height < MIN_VIEWPORT_HEIGHT)
    274 		throw tcu::NotSupportedError("Too small viewport", "", __FILE__, __LINE__);
    275 
    276 	// Setup params for reference.
    277 	refParams.sampler		= 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		= 8;
    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 isOk = verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
    312 											  &texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
    313 
    314 		if (!isOk)
    315 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
    316 	}
    317 
    318 	m_caseNdx += 1;
    319 	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
    320 }
    321 
    322 
    323 class TextureCubeFilteringCase : public tcu::TestCase
    324 {
    325 public:
    326 									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, deUint32 format, deUint32 dataType, int width, int height);
    327 									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, const std::vector<std::string>& filenames);
    328 									~TextureCubeFilteringCase	(void);
    329 
    330 	void							init						(void);
    331 	void							deinit						(void);
    332 	IterateResult					iterate						(void);
    333 
    334 private:
    335 									TextureCubeFilteringCase	(const TextureCubeFilteringCase& other);
    336 	TextureCubeFilteringCase&		operator=					(const TextureCubeFilteringCase& other);
    337 
    338 	glu::RenderContext&				m_renderCtx;
    339 	const glu::ContextInfo&			m_renderCtxInfo;
    340 
    341 	const deUint32					m_minFilter;
    342 	const deUint32					m_magFilter;
    343 	const deUint32					m_wrapS;
    344 	const deUint32					m_wrapT;
    345 
    346 	const deUint32					m_format;
    347 	const deUint32					m_dataType;
    348 	const int						m_width;
    349 	const int						m_height;
    350 
    351 	const std::vector<std::string>	m_filenames;
    352 
    353 	struct FilterCase
    354 	{
    355 		const glu::TextureCube*	texture;
    356 		tcu::Vec2				bottomLeft;
    357 		tcu::Vec2				topRight;
    358 
    359 		FilterCase (void)
    360 			: texture(DE_NULL)
    361 		{
    362 		}
    363 
    364 		FilterCase (const glu::TextureCube* tex_, const tcu::Vec2& bottomLeft_, const tcu::Vec2& topRight_)
    365 			: texture	(tex_)
    366 			, bottomLeft(bottomLeft_)
    367 			, topRight	(topRight_)
    368 		{
    369 		}
    370 	};
    371 
    372 	std::vector<glu::TextureCube*>	m_textures;
    373 	std::vector<FilterCase>			m_cases;
    374 
    375 	TextureRenderer					m_renderer;
    376 
    377 	int								m_caseNdx;
    378 };
    379 
    380 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, deUint32 format, deUint32 dataType, int width, int height)
    381 	: TestCase					(testCtx, name, desc)
    382 	, m_renderCtx				(renderCtx)
    383 	, m_renderCtxInfo			(ctxInfo)
    384 	, m_minFilter				(minFilter)
    385 	, m_magFilter				(magFilter)
    386 	, m_wrapS					(wrapS)
    387 	, m_wrapT					(wrapT)
    388 	, m_format					(format)
    389 	, m_dataType				(dataType)
    390 	, m_width					(width)
    391 	, m_height					(height)
    392 	, m_renderer				(renderCtx, testCtx, glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
    393 	, m_caseNdx					(0)
    394 {
    395 }
    396 
    397 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, const std::vector<std::string>& filenames)
    398 	: TestCase					(testCtx, name, desc)
    399 	, m_renderCtx				(renderCtx)
    400 	, m_renderCtxInfo			(ctxInfo)
    401 	, m_minFilter				(minFilter)
    402 	, m_magFilter				(magFilter)
    403 	, m_wrapS					(wrapS)
    404 	, m_wrapT					(wrapT)
    405 	, m_format					(GL_NONE)
    406 	, m_dataType				(GL_NONE)
    407 	, m_width					(0)
    408 	, m_height					(0)
    409 	, m_filenames				(filenames)
    410 	, m_renderer				(renderCtx, testCtx, glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
    411 	, m_caseNdx					(0)
    412 {
    413 }
    414 
    415 TextureCubeFilteringCase::~TextureCubeFilteringCase (void)
    416 {
    417 	deinit();
    418 }
    419 
    420 void TextureCubeFilteringCase::init (void)
    421 {
    422 	try
    423 	{
    424 		if (!m_filenames.empty())
    425 		{
    426 			m_textures.reserve(1);
    427 			m_textures.push_back(glu::TextureCube::create(m_renderCtx, m_renderCtxInfo, m_testCtx.getArchive(), (int)m_filenames.size() / 6, m_filenames));
    428 		}
    429 		else
    430 		{
    431 			DE_ASSERT(m_width == m_height);
    432 			m_textures.reserve(2);
    433 			for (int ndx = 0; ndx < 2; ndx++)
    434 				m_textures.push_back(new glu::TextureCube(m_renderCtx, m_format, m_dataType, m_width));
    435 
    436 			const bool				mipmaps		= deIsPowerOfTwo32(m_width) && deIsPowerOfTwo32(m_height);
    437 			const int				numLevels	= mipmaps ? deLog2Floor32(de::max(m_width, m_height))+1 : 1;
    438 			tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
    439 			tcu::Vec4				cBias		= fmtInfo.valueMin;
    440 			tcu::Vec4				cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
    441 
    442 			// Fill first with gradient texture.
    443 			static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] =
    444 			{
    445 				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
    446 				{ tcu::Vec4(0.5f, 0.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
    447 				{ tcu::Vec4(0.0f, 0.5f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
    448 				{ tcu::Vec4(0.0f, 0.0f, 0.5f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
    449 				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 0.5f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
    450 				{ tcu::Vec4(0.5f, 0.5f, 0.5f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }  // positive z
    451 			};
    452 			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    453 			{
    454 				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    455 				{
    456 					m_textures[0]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
    457 					tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), gradients[face][0]*cScale + cBias, gradients[face][1]*cScale + cBias);
    458 				}
    459 			}
    460 
    461 			// Fill second with grid texture.
    462 			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    463 			{
    464 				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    465 				{
    466 					deUint32	step	= 0x00ffffff / (numLevels*tcu::CUBEFACE_LAST);
    467 					deUint32	rgb		= step*levelNdx*face;
    468 					deUint32	colorA	= 0xff000000 | rgb;
    469 					deUint32	colorB	= 0xff000000 | ~rgb;
    470 
    471 					m_textures[1]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
    472 					tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
    473 				}
    474 			}
    475 
    476 			// Upload.
    477 			for (std::vector<glu::TextureCube*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
    478 				(*i)->upload();
    479 		}
    480 
    481 		// Compute cases
    482 		{
    483 			const glu::TextureCube*	tex0	= m_textures[0];
    484 			const glu::TextureCube* tex1	= m_textures.size() > 1 ? m_textures[1] : tex0;
    485 
    486 			// \note Coordinates are chosen so that they only sample face interior. ES3 has changed edge sampling behavior
    487 			//		 and hw is not expected to implement both modes.
    488 			m_cases.push_back(FilterCase(tex0, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f, 0.8f)));	// minification
    489 			m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.5f, 0.65f), tcu::Vec2(0.8f, 0.8f)));		// magnification
    490 			m_cases.push_back(FilterCase(tex1, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f, 0.8f)));	// minification
    491 			m_cases.push_back(FilterCase(tex1, tcu::Vec2(0.2f, 0.2f), tcu::Vec2(0.6f, 0.5f)));		// magnification
    492 		}
    493 
    494 		m_caseNdx = 0;
    495 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    496 	}
    497 	catch (...)
    498 	{
    499 		// Clean up to save memory.
    500 		TextureCubeFilteringCase::deinit();
    501 		throw;
    502 	}
    503 }
    504 
    505 void TextureCubeFilteringCase::deinit (void)
    506 {
    507 	for (std::vector<glu::TextureCube*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
    508 		delete *i;
    509 	m_textures.clear();
    510 
    511 	m_renderer.clear();
    512 	m_cases.clear();
    513 }
    514 
    515 static const char* getFaceDesc (const tcu::CubeFace face)
    516 {
    517 	switch (face)
    518 	{
    519 		case tcu::CUBEFACE_NEGATIVE_X:	return "-X";
    520 		case tcu::CUBEFACE_POSITIVE_X:	return "+X";
    521 		case tcu::CUBEFACE_NEGATIVE_Y:	return "-Y";
    522 		case tcu::CUBEFACE_POSITIVE_Y:	return "+Y";
    523 		case tcu::CUBEFACE_NEGATIVE_Z:	return "-Z";
    524 		case tcu::CUBEFACE_POSITIVE_Z:	return "+Z";
    525 		default:
    526 			DE_ASSERT(false);
    527 			return DE_NULL;
    528 	}
    529 }
    530 
    531 TextureCubeFilteringCase::IterateResult TextureCubeFilteringCase::iterate (void)
    532 {
    533 	const glw::Functions&			gl				= m_renderCtx.getFunctions();
    534 	const int						viewportSize	= 28;
    535 	const RandomViewport			viewport		(m_renderCtx.getRenderTarget(), viewportSize, viewportSize, deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
    536 	const tcu::ScopedLogSection		iterSection		(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
    537 	const FilterCase&				curCase			= m_cases[m_caseNdx];
    538 	const tcu::TextureFormat&		texFmt			= curCase.texture->getRefTexture().getFormat();
    539 	const tcu::TextureFormatInfo	fmtInfo			= tcu::getTextureFormatInfo(texFmt);
    540 	ReferenceParams					sampleParams	(TEXTURETYPE_CUBE);
    541 
    542 	if (viewport.width < viewportSize || viewport.height < viewportSize)
    543 		throw tcu::NotSupportedError("Too small render target", DE_NULL, __FILE__, __LINE__);
    544 
    545 	// Setup texture
    546 	gl.bindTexture	(GL_TEXTURE_CUBE_MAP, curCase.texture->getGLTexture());
    547 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	m_minFilter);
    548 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	m_magFilter);
    549 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		m_wrapS);
    550 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		m_wrapT);
    551 
    552 	// Other state
    553 	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
    554 
    555 	// Params for reference computation.
    556 	sampleParams.sampler					= glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
    557 	sampleParams.sampler.seamlessCubeMap	= true;
    558 	sampleParams.samplerType				= getSamplerType(texFmt);
    559 	sampleParams.colorBias					= fmtInfo.lookupBias;
    560 	sampleParams.colorScale					= fmtInfo.lookupScale;
    561 	sampleParams.lodMode					= LODMODE_EXACT;
    562 
    563 	m_testCtx.getLog() << TestLog::Message << "Coordinates: " << curCase.bottomLeft << " -> " << curCase.topRight << TestLog::EndMessage;
    564 
    565 	for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
    566 	{
    567 		const tcu::CubeFace		face		= tcu::CubeFace(faceNdx);
    568 		tcu::Surface			result		(viewport.width, viewport.height);
    569 		vector<float>			texCoord;
    570 
    571 		computeQuadTexCoordCube(texCoord, face, curCase.bottomLeft, curCase.topRight);
    572 
    573 		m_testCtx.getLog() << TestLog::Message << "Face " << getFaceDesc(face) << TestLog::EndMessage;
    574 
    575 		// \todo Log texture coordinates.
    576 
    577 		m_renderer.renderQuad(0, &texCoord[0], sampleParams);
    578 		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
    579 
    580 		glu::readPixels(m_renderCtx, viewport.x, viewport.y, result.getAccess());
    581 		GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
    582 
    583 		{
    584 			const bool				isNearestOnly	= m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
    585 			const tcu::PixelFormat	pixelFormat		= m_renderCtx.getRenderTarget().getPixelFormat();
    586 			const tcu::IVec4		colorBits		= max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2), tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
    587 			tcu::LodPrecision		lodPrecision;
    588 			tcu::LookupPrecision	lookupPrecision;
    589 
    590 			lodPrecision.derivateBits		= 5;
    591 			lodPrecision.lodBits			= 3;
    592 			lookupPrecision.colorThreshold	= tcu::computeFixedPointThreshold(colorBits) / sampleParams.colorScale;
    593 			lookupPrecision.coordBits		= tcu::IVec3(10,10,10);
    594 			lookupPrecision.uvwBits			= tcu::IVec3(6,6,0);
    595 			lookupPrecision.colorMask		= getCompareMask(pixelFormat);
    596 
    597 			const bool isOk = verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(),
    598 												  &texCoord[0], sampleParams, lookupPrecision, lodPrecision, pixelFormat);
    599 
    600 			if (!isOk)
    601 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
    602 		}
    603 	}
    604 
    605 	m_caseNdx += 1;
    606 	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
    607 }
    608 
    609 TextureFilteringTests::TextureFilteringTests (Context& context)
    610 	: TestCaseGroup(context, "filtering", "Texture Filtering Tests")
    611 {
    612 }
    613 
    614 TextureFilteringTests::~TextureFilteringTests (void)
    615 {
    616 }
    617 
    618 void TextureFilteringTests::init (void)
    619 {
    620 	tcu::TestCaseGroup* group2D		= new tcu::TestCaseGroup(m_testCtx, "2d",	"2D Texture Filtering");
    621 	tcu::TestCaseGroup*	groupCube	= new tcu::TestCaseGroup(m_testCtx, "cube",	"Cube Map Filtering");
    622 	addChild(group2D);
    623 	addChild(groupCube);
    624 
    625 	static const struct
    626 	{
    627 		const char*		name;
    628 		deUint32		mode;
    629 	} wrapModes[] =
    630 	{
    631 		{ "clamp",		GL_CLAMP_TO_EDGE },
    632 		{ "repeat",		GL_REPEAT },
    633 		{ "mirror",		GL_MIRRORED_REPEAT }
    634 	};
    635 
    636 	static const struct
    637 	{
    638 		const char*		name;
    639 		deUint32		mode;
    640 	} minFilterModes[] =
    641 	{
    642 		{ "nearest",				GL_NEAREST					},
    643 		{ "linear",					GL_LINEAR					},
    644 		{ "nearest_mipmap_nearest",	GL_NEAREST_MIPMAP_NEAREST	},
    645 		{ "linear_mipmap_nearest",	GL_LINEAR_MIPMAP_NEAREST	},
    646 		{ "nearest_mipmap_linear",	GL_NEAREST_MIPMAP_LINEAR	},
    647 		{ "linear_mipmap_linear",	GL_LINEAR_MIPMAP_LINEAR		}
    648 	};
    649 
    650 	static const struct
    651 	{
    652 		const char*		name;
    653 		deUint32		mode;
    654 	} magFilterModes[] =
    655 	{
    656 		{ "nearest",	GL_NEAREST },
    657 		{ "linear",		GL_LINEAR }
    658 	};
    659 
    660 	static const struct
    661 	{
    662 		const char*		name;
    663 		int				width;
    664 		int				height;
    665 	} sizes2D[] =
    666 	{
    667 		{ "pot",		32, 64 },
    668 		{ "npot",		31, 55 }
    669 	};
    670 
    671 	static const struct
    672 	{
    673 		const char*		name;
    674 		int				width;
    675 		int				height;
    676 	} sizesCube[] =
    677 	{
    678 		{ "pot",		64, 64 },
    679 		{ "npot",		63, 63 }
    680 	};
    681 
    682 	static const struct
    683 	{
    684 		const char*		name;
    685 		deUint32		format;
    686 		deUint32		dataType;
    687 	} formats[] =
    688 	{
    689 		{ "rgba8888",	GL_RGBA,			GL_UNSIGNED_BYTE			},
    690 		{ "rgb888",		GL_RGB,				GL_UNSIGNED_BYTE			},
    691 		{ "rgba4444",	GL_RGBA,			GL_UNSIGNED_SHORT_4_4_4_4	},
    692 		{ "l8",			GL_LUMINANCE,		GL_UNSIGNED_BYTE			}
    693 	};
    694 
    695 #define FOR_EACH(ITERATOR, ARRAY, BODY)	\
    696 	for (int ITERATOR = 0; ITERATOR < DE_LENGTH_OF_ARRAY(ARRAY); ITERATOR++)	\
    697 		BODY
    698 
    699 	// 2D cases.
    700 	FOR_EACH(minFilter,		minFilterModes,
    701 	FOR_EACH(magFilter,		magFilterModes,
    702 	FOR_EACH(wrapMode,		wrapModes,
    703 	FOR_EACH(format,		formats,
    704 	FOR_EACH(size,			sizes2D,
    705 		{
    706 			bool isMipmap		= minFilterModes[minFilter].mode != GL_NEAREST && minFilterModes[minFilter].mode != GL_LINEAR;
    707 			bool isClamp		= wrapModes[wrapMode].mode == GL_CLAMP_TO_EDGE;
    708 			bool isRepeat		= wrapModes[wrapMode].mode == GL_REPEAT;
    709 			bool isMagNearest	= magFilterModes[magFilter].mode == GL_NEAREST;
    710 			bool isPotSize		= deIsPowerOfTwo32(sizes2D[size].width) && deIsPowerOfTwo32(sizes2D[size].height);
    711 
    712 			if ((isMipmap || !isClamp) && !isPotSize)
    713 				continue; // Not supported.
    714 
    715 			if ((format != 0) && !(!isMipmap || (isRepeat && isMagNearest)))
    716 				continue; // Skip.
    717 
    718 			string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name + "_" + formats[format].name;
    719 
    720 			if (!isMipmap)
    721 				name += string("_") + sizes2D[size].name;
    722 
    723 			group2D->addChild(new Texture2DFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
    724 														 name.c_str(), "",
    725 														 minFilterModes[minFilter].mode,
    726 														 magFilterModes[magFilter].mode,
    727 														 wrapModes[wrapMode].mode,
    728 														 wrapModes[wrapMode].mode,
    729 														 formats[format].format, formats[format].dataType,
    730 														 sizes2D[size].width, sizes2D[size].height));
    731 		})))));
    732 
    733 	// 2D ETC1 texture cases.
    734 	{
    735 		std::vector<std::string> filenames;
    736 		for (int i = 0; i <= 7; i++)
    737 			filenames.push_back(string("data/etc1/photo_helsinki_mip_") + de::toString(i) + ".pkm");
    738 
    739 		FOR_EACH(minFilter,		minFilterModes,
    740 		FOR_EACH(magFilter,		magFilterModes,
    741 		FOR_EACH(wrapMode,		wrapModes,
    742 			{
    743 				string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name + "_etc1";
    744 
    745 				group2D->addChild(new Texture2DFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
    746 															 name.c_str(), "",
    747 															 minFilterModes[minFilter].mode,
    748 															 magFilterModes[magFilter].mode,
    749 															 wrapModes[wrapMode].mode,
    750 															 wrapModes[wrapMode].mode,
    751 															 filenames));
    752 			})));
    753 	}
    754 
    755 	// Cubemap cases.
    756 	FOR_EACH(minFilter,		minFilterModes,
    757 	FOR_EACH(magFilter,		magFilterModes,
    758 	FOR_EACH(wrapMode,		wrapModes,
    759 	FOR_EACH(format,		formats,
    760 	FOR_EACH(size,			sizesCube,
    761 		{
    762 			bool isMipmap		= minFilterModes[minFilter].mode != GL_NEAREST && minFilterModes[minFilter].mode != GL_LINEAR;
    763 			bool isClamp		= wrapModes[wrapMode].mode == GL_CLAMP_TO_EDGE;
    764 			bool isRepeat		= wrapModes[wrapMode].mode == GL_REPEAT;
    765 			bool isMagNearest	= magFilterModes[magFilter].mode == GL_NEAREST;
    766 			bool isPotSize		= deIsPowerOfTwo32(sizesCube[size].width) && deIsPowerOfTwo32(sizesCube[size].height);
    767 
    768 			if ((isMipmap || !isClamp) && !isPotSize)
    769 				continue; // Not supported.
    770 
    771 			if (format != 0 && !(!isMipmap || (isRepeat && isMagNearest)))
    772 				continue; // Skip.
    773 
    774 			string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name + "_" + formats[format].name;
    775 
    776 			if (!isMipmap)
    777 				name += string("_") + sizesCube[size].name;
    778 
    779 			groupCube->addChild(new TextureCubeFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
    780 															 name.c_str(), "",
    781 															 minFilterModes[minFilter].mode,
    782 															 magFilterModes[magFilter].mode,
    783 															 wrapModes[wrapMode].mode,
    784 															 wrapModes[wrapMode].mode,
    785 															 formats[format].format, formats[format].dataType,
    786 															 sizesCube[size].width, sizesCube[size].height));
    787 		})))));
    788 
    789 	// Cubemap ETC1 cases
    790 	{
    791 		static const char* faceExt[] = { "neg_x", "pos_x", "neg_y", "pos_y", "neg_z", "pos_z" };
    792 
    793 		const int		numLevels	= 7;
    794 		vector<string>	filenames;
    795 		for (int level = 0; level < numLevels; level++)
    796 			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    797 				filenames.push_back(string("data/etc1/skybox_") + faceExt[face] + "_mip_" + de::toString(level) + ".pkm");
    798 
    799 		FOR_EACH(minFilter,		minFilterModes,
    800 		FOR_EACH(magFilter,		magFilterModes,
    801 			{
    802 				string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_clamp_etc1";
    803 
    804 				groupCube->addChild(new TextureCubeFilteringCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
    805 																 name.c_str(), "",
    806 																 minFilterModes[minFilter].mode,
    807 																 magFilterModes[magFilter].mode,
    808 																 GL_CLAMP_TO_EDGE,
    809 																 GL_CLAMP_TO_EDGE,
    810 																 filenames));
    811 			}));
    812 	}
    813 }
    814 
    815 } // Functional
    816 } // gles2
    817 } // deqp
    818