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 Shadow texture lookup tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es3fTextureShadowTests.hpp"
     25 #include "gluTexture.hpp"
     26 #include "gluPixelTransfer.hpp"
     27 #include "gluTextureUtil.hpp"
     28 #include "glsTextureTestUtil.hpp"
     29 #include "tcuTextureUtil.hpp"
     30 #include "tcuRenderTarget.hpp"
     31 #include "tcuTexCompareVerifier.hpp"
     32 #include "deString.h"
     33 #include "deStringUtil.hpp"
     34 #include "glwFunctions.hpp"
     35 #include "glwEnums.hpp"
     36 
     37 namespace deqp
     38 {
     39 namespace gles3
     40 {
     41 namespace Functional
     42 {
     43 
     44 using std::vector;
     45 using std::string;
     46 using tcu::TestLog;
     47 using namespace deqp::gls::TextureTestUtil;
     48 
     49 enum
     50 {
     51 	TEX2D_VIEWPORT_WIDTH		= 64,
     52 	TEX2D_VIEWPORT_HEIGHT		= 64,
     53 	TEX2D_MIN_VIEWPORT_WIDTH	= 64,
     54 	TEX2D_MIN_VIEWPORT_HEIGHT	= 64
     55 };
     56 
     57 static bool isFloatingPointDepthFormat (const tcu::TextureFormat& format)
     58 {
     59 	// Only two depth and depth-stencil formats are floating point
     60 	return	(format.order == tcu::TextureFormat::D && format.type == tcu::TextureFormat::FLOAT) ||
     61 			(format.order == tcu::TextureFormat::DS && format.type == tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV);
     62 }
     63 
     64 static void clampFloatingPointTexture (const tcu::PixelBufferAccess& access)
     65 {
     66 	DE_ASSERT(isFloatingPointDepthFormat(access.getFormat()));
     67 
     68 	for (int z = 0; z < access.getDepth(); ++z)
     69 	for (int y = 0; y < access.getHeight(); ++y)
     70 	for (int x = 0; x < access.getWidth(); ++x)
     71 		access.setPixDepth( de::clamp(access.getPixDepth(x, y, z), 0.0f, 1.0f), x, y, z);
     72 }
     73 
     74 static void clampFloatingPointTexture (tcu::Texture2D& target)
     75 {
     76 	for (int level = 0; level < target.getNumLevels(); ++level)
     77 		if (!target.isLevelEmpty(level))
     78 			clampFloatingPointTexture(target.getLevel(level));
     79 }
     80 
     81 static void clampFloatingPointTexture (tcu::Texture2DArray& target)
     82 {
     83 	for (int level = 0; level < target.getNumLevels(); ++level)
     84 		if (!target.isLevelEmpty(level))
     85 			clampFloatingPointTexture(target.getLevel(level));
     86 }
     87 
     88 static void clampFloatingPointTexture (tcu::TextureCube& target)
     89 {
     90 	for (int level = 0; level < target.getNumLevels(); ++level)
     91 		for (int face = tcu::CUBEFACE_NEGATIVE_X; face < tcu::CUBEFACE_LAST; ++face)
     92 			clampFloatingPointTexture(target.getLevelFace(level, (tcu::CubeFace)face));
     93 }
     94 
     95 template<typename TextureType>
     96 bool verifyTexCompareResult (tcu::TestContext&						testCtx,
     97 							 const tcu::ConstPixelBufferAccess&		result,
     98 							 const TextureType&						src,
     99 							 const float*							texCoord,
    100 							 const ReferenceParams&					sampleParams,
    101 							 const tcu::TexComparePrecision&		comparePrec,
    102 							 const tcu::LodPrecision&				lodPrec,
    103 							 const tcu::PixelFormat&				pixelFormat)
    104 {
    105 	tcu::TestLog&	log					= testCtx.getLog();
    106 	tcu::Surface	reference			(result.getWidth(), result.getHeight());
    107 	tcu::Surface	errorMask			(result.getWidth(), result.getHeight());
    108 	const tcu::Vec3	nonShadowThreshold	= tcu::computeFixedPointThreshold(getBitsVec(pixelFormat)-1).swizzle(1,2,3);
    109 	int				numFailedPixels;
    110 
    111 	// sampleTexture() expects source image to be the same state as it would be in a GL implementation, that is
    112 	// the floating point depth values should be in [0, 1] range as data is clamped during texture upload. Since
    113 	// we don't have a separate "uploading" phase and just reuse the buffer we used for GL-upload, do the clamping
    114 	// here if necessary.
    115 
    116 	if (isFloatingPointDepthFormat(src.getFormat()))
    117 	{
    118 		TextureType clampedSource(src);
    119 
    120 		clampFloatingPointTexture(clampedSource);
    121 
    122 		// sample clamped values
    123 
    124 		sampleTexture(SurfaceAccess(reference, pixelFormat), clampedSource, texCoord, sampleParams);
    125 		numFailedPixels = computeTextureCompareDiff(result, reference.getAccess(), errorMask.getAccess(), clampedSource, texCoord, sampleParams, comparePrec, lodPrec, nonShadowThreshold);
    126 	}
    127 	else
    128 	{
    129 		// sample raw values (they are guaranteed to be in [0, 1] range as the format cannot represent any other values)
    130 
    131 		sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams);
    132 		numFailedPixels = computeTextureCompareDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, comparePrec, lodPrec, nonShadowThreshold);
    133 	}
    134 
    135 	if (numFailedPixels > 0)
    136 		log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
    137 
    138 	log << TestLog::ImageSet("VerifyResult", "Verification result")
    139 		<< TestLog::Image("Rendered", "Rendered image", result);
    140 
    141 	if (numFailedPixels > 0)
    142 	{
    143 		log << TestLog::Image("Reference", "Ideal reference image", reference)
    144 			<< TestLog::Image("ErrorMask", "Error mask", errorMask);
    145 	}
    146 
    147 	log << TestLog::EndImageSet;
    148 
    149 	return numFailedPixels == 0;
    150 }
    151 
    152 class Texture2DShadowCase : public TestCase
    153 {
    154 public:
    155 									Texture2DShadowCase			(Context& context, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 format, int width, int height, deUint32 compareFunc);
    156 									~Texture2DShadowCase		(void);
    157 
    158 	void							init						(void);
    159 	void							deinit						(void);
    160 	IterateResult					iterate						(void);
    161 
    162 private:
    163 									Texture2DShadowCase			(const Texture2DShadowCase& other);
    164 	Texture2DShadowCase&			operator=					(const Texture2DShadowCase& other);
    165 
    166 	const deUint32					m_minFilter;
    167 	const deUint32					m_magFilter;
    168 	const deUint32					m_wrapS;
    169 	const deUint32					m_wrapT;
    170 	const deUint32					m_format;
    171 	const int						m_width;
    172 	const int						m_height;
    173 	const deUint32					m_compareFunc;
    174 
    175 	struct FilterCase
    176 	{
    177 		const glu::Texture2D*	texture;
    178 		tcu::Vec2				minCoord;
    179 		tcu::Vec2				maxCoord;
    180 		float					ref;
    181 
    182 		FilterCase (void)
    183 			: texture	(DE_NULL)
    184 			, ref		(0.0f)
    185 		{
    186 		}
    187 
    188 		FilterCase (const glu::Texture2D* tex_, const float ref_, const tcu::Vec2& minCoord_, const tcu::Vec2& maxCoord_)
    189 			: texture	(tex_)
    190 			, minCoord	(minCoord_)
    191 			, maxCoord	(maxCoord_)
    192 			, ref		(ref_)
    193 		{
    194 		}
    195 	};
    196 
    197 	std::vector<glu::Texture2D*>	m_textures;
    198 	std::vector<FilterCase>			m_cases;
    199 
    200 	TextureRenderer					m_renderer;
    201 
    202 	int								m_caseNdx;
    203 };
    204 
    205 Texture2DShadowCase::Texture2DShadowCase (Context& context, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 format, int width, int height, deUint32 compareFunc)
    206 	: TestCase			(context, name, desc)
    207 	, m_minFilter		(minFilter)
    208 	, m_magFilter		(magFilter)
    209 	, m_wrapS			(wrapS)
    210 	, m_wrapT			(wrapT)
    211 	, m_format			(format)
    212 	, m_width			(width)
    213 	, m_height			(height)
    214 	, m_compareFunc		(compareFunc)
    215 	, m_renderer		(context.getRenderContext(), context.getTestContext(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
    216 	, m_caseNdx			(0)
    217 {
    218 }
    219 
    220 Texture2DShadowCase::~Texture2DShadowCase (void)
    221 {
    222 	deinit();
    223 }
    224 
    225 void Texture2DShadowCase::init (void)
    226 {
    227 	try
    228 	{
    229 		// Create 2 textures.
    230 		m_textures.reserve(2);
    231 		m_textures.push_back(new glu::Texture2D(m_context.getRenderContext(), m_format, m_width, m_height));
    232 		m_textures.push_back(new glu::Texture2D(m_context.getRenderContext(), m_format, m_width, m_height));
    233 
    234 		int numLevels = m_textures[0]->getRefTexture().getNumLevels();
    235 
    236 		// Fill first gradient texture.
    237 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    238 		{
    239 			m_textures[0]->getRefTexture().allocLevel(levelNdx);
    240 			tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), tcu::Vec4(-0.5f, -0.5f, -0.5f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f));
    241 		}
    242 
    243 		// Fill second with grid texture.
    244 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    245 		{
    246 			deUint32	step	= 0x00ffffff / numLevels;
    247 			deUint32	rgb		= step*levelNdx;
    248 			deUint32	colorA	= 0xff000000 | rgb;
    249 			deUint32	colorB	= 0xff000000 | ~rgb;
    250 
    251 			m_textures[1]->getRefTexture().allocLevel(levelNdx);
    252 			tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, toVec4(tcu::RGBA(colorA)), toVec4(tcu::RGBA(colorB)));
    253 		}
    254 
    255 		// Upload.
    256 		for (std::vector<glu::Texture2D*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
    257 			(*i)->upload();
    258 	}
    259 	catch (const std::exception&)
    260 	{
    261 		// Clean up to save memory.
    262 		Texture2DShadowCase::deinit();
    263 		throw;
    264 	}
    265 
    266 	// Compute cases.
    267 	{
    268 		const float refInRangeUpper		= (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 1.0f : 0.5f;
    269 		const float refInRangeLower		= (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 0.0f : 0.5f;
    270 		const float refOutOfBoundsUpper	= 1.1f;		// !< lookup function should clamp values to [0, 1] range
    271 		const float refOutOfBoundsLower	= -0.1f;
    272 
    273 		const struct
    274 		{
    275 			int		texNdx;
    276 			float	ref;
    277 			float	lodX;
    278 			float	lodY;
    279 			float	oX;
    280 			float	oY;
    281 		} cases[] =
    282 		{
    283 			{ 0,	refInRangeUpper,		1.6f,	2.9f,	-1.0f,	-2.7f	},
    284 			{ 0,	refInRangeLower,		-2.0f,	-1.35f,	-0.2f,	0.7f	},
    285 			{ 1,	refInRangeUpper,		0.14f,	0.275f,	-1.5f,	-1.1f	},
    286 			{ 1,	refInRangeLower,		-0.92f,	-2.64f,	0.4f,	-0.1f	},
    287 			{ 1,	refOutOfBoundsUpper,	-0.39f,	-0.52f,	0.65f,	0.87f	},
    288 			{ 1,	refOutOfBoundsLower,	-1.55f,	0.65f,	0.35f,	0.91f	},
    289 		};
    290 
    291 		const float	viewportW	= (float)de::min<int>(TEX2D_VIEWPORT_WIDTH, m_context.getRenderTarget().getWidth());
    292 		const float	viewportH	= (float)de::min<int>(TEX2D_VIEWPORT_HEIGHT, m_context.getRenderTarget().getHeight());
    293 
    294 		for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
    295 		{
    296 			const int	texNdx	= de::clamp(cases[caseNdx].texNdx, 0, (int)m_textures.size()-1);
    297 			const float ref		= cases[caseNdx].ref;
    298 			const float	lodX	= cases[caseNdx].lodX;
    299 			const float	lodY	= cases[caseNdx].lodY;
    300 			const float	oX		= cases[caseNdx].oX;
    301 			const float	oY		= cases[caseNdx].oY;
    302 			const float	sX		= deFloatExp2(lodX)*viewportW / float(m_textures[texNdx]->getRefTexture().getWidth());
    303 			const float	sY		= deFloatExp2(lodY)*viewportH / float(m_textures[texNdx]->getRefTexture().getHeight());
    304 
    305 			m_cases.push_back(FilterCase(m_textures[texNdx], ref, tcu::Vec2(oX, oY), tcu::Vec2(oX+sX, oY+sY)));
    306 		}
    307 	}
    308 
    309 	m_caseNdx = 0;
    310 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    311 }
    312 
    313 void Texture2DShadowCase::deinit (void)
    314 {
    315 	for (std::vector<glu::Texture2D*>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
    316 		delete *i;
    317 	m_textures.clear();
    318 
    319 	m_renderer.clear();
    320 	m_cases.clear();
    321 }
    322 
    323 Texture2DShadowCase::IterateResult Texture2DShadowCase::iterate (void)
    324 {
    325 	const glw::Functions&			gl				= m_context.getRenderContext().getFunctions();
    326 	const RandomViewport			viewport		(m_context.getRenderTarget(), TEX2D_VIEWPORT_WIDTH, TEX2D_VIEWPORT_HEIGHT, deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
    327 	const FilterCase&				curCase			= m_cases[m_caseNdx];
    328 	const tcu::ScopedLogSection		section			(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
    329 	ReferenceParams					sampleParams	(TEXTURETYPE_2D);
    330 	tcu::Surface					rendered		(viewport.width, viewport.height);
    331 	vector<float>					texCoord;
    332 
    333 	if (viewport.width < TEX2D_MIN_VIEWPORT_WIDTH || viewport.height < TEX2D_MIN_VIEWPORT_HEIGHT)
    334 		throw tcu::NotSupportedError("Too small render target", "", __FILE__, __LINE__);
    335 
    336 	// Setup params for reference.
    337 	sampleParams.sampler			= glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
    338 	sampleParams.sampler.compare	= glu::mapGLCompareFunc(m_compareFunc);
    339 	sampleParams.samplerType		= SAMPLERTYPE_SHADOW;
    340 	sampleParams.lodMode			= LODMODE_EXACT;
    341 	sampleParams.ref				= curCase.ref;
    342 
    343 	m_testCtx.getLog() << TestLog::Message << "Compare reference value =  " << sampleParams.ref << TestLog::EndMessage;
    344 
    345 	// Compute texture coordinates.
    346 	m_testCtx.getLog() << TestLog::Message << "Texture coordinates: " << curCase.minCoord << " -> " << curCase.maxCoord << TestLog::EndMessage;
    347 	computeQuadTexCoord2D(texCoord, curCase.minCoord, curCase.maxCoord);
    348 
    349 	gl.bindTexture	(GL_TEXTURE_2D, curCase.texture->getGLTexture());
    350 	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,		m_minFilter);
    351 	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,		m_magFilter);
    352 	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,			m_wrapS);
    353 	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,			m_wrapT);
    354 	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,	GL_COMPARE_REF_TO_TEXTURE);
    355 	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC,	m_compareFunc);
    356 
    357 	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
    358 	m_renderer.renderQuad(0, &texCoord[0], sampleParams);
    359 	glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, rendered.getAccess());
    360 
    361 	{
    362 		const tcu::PixelFormat		pixelFormat		= m_context.getRenderTarget().getPixelFormat();
    363 		tcu::LodPrecision			lodPrecision;
    364 		tcu::TexComparePrecision	texComparePrecision;
    365 
    366 		lodPrecision.derivateBits			= 18;
    367 		lodPrecision.lodBits				= 6;
    368 		texComparePrecision.coordBits		= tcu::IVec3(20,20,0);
    369 		texComparePrecision.uvwBits			= tcu::IVec3(7,7,0);
    370 		texComparePrecision.pcfBits			= 5;
    371 		texComparePrecision.referenceBits	= 16;
    372 		texComparePrecision.resultBits		= pixelFormat.redBits-1;
    373 
    374 		const bool isHighQuality = verifyTexCompareResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
    375 														  &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    376 
    377 		if (!isHighQuality)
    378 		{
    379 			m_testCtx.getLog() << TestLog::Message << "Warning: Verification assuming high-quality PCF filtering failed." << TestLog::EndMessage;
    380 
    381 			lodPrecision.lodBits			= 4;
    382 			texComparePrecision.uvwBits		= tcu::IVec3(4,4,0);
    383 			texComparePrecision.pcfBits		= 0;
    384 
    385 			const bool isOk = verifyTexCompareResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
    386 													 &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    387 
    388 			if (!isOk)
    389 			{
    390 				m_testCtx.getLog() << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
    391 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
    392 			}
    393 			else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
    394 				m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality result");
    395 		}
    396 	}
    397 
    398 	m_caseNdx += 1;
    399 	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
    400 }
    401 
    402 class TextureCubeShadowCase : public TestCase
    403 {
    404 public:
    405 								TextureCubeShadowCase		(Context& context, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 format, int size, deUint32 compareFunc);
    406 								~TextureCubeShadowCase		(void);
    407 
    408 	void						init						(void);
    409 	void						deinit						(void);
    410 	IterateResult				iterate						(void);
    411 
    412 private:
    413 								TextureCubeShadowCase		(const TextureCubeShadowCase& other);
    414 	TextureCubeShadowCase&		operator=					(const TextureCubeShadowCase& other);
    415 
    416 	const deUint32				m_minFilter;
    417 	const deUint32				m_magFilter;
    418 	const deUint32				m_wrapS;
    419 	const deUint32				m_wrapT;
    420 
    421 	const deUint32				m_format;
    422 	const int					m_size;
    423 
    424 	const deUint32				m_compareFunc;
    425 
    426 	struct FilterCase
    427 	{
    428 		const glu::TextureCube*	texture;
    429 		tcu::Vec2				bottomLeft;
    430 		tcu::Vec2				topRight;
    431 		float					ref;
    432 
    433 		FilterCase (void)
    434 			: texture	(DE_NULL)
    435 			, ref		(0.0f)
    436 		{
    437 		}
    438 
    439 		FilterCase (const glu::TextureCube* tex_, const float ref_, const tcu::Vec2& bottomLeft_, const tcu::Vec2& topRight_)
    440 			: texture	(tex_)
    441 			, bottomLeft(bottomLeft_)
    442 			, topRight	(topRight_)
    443 			, ref		(ref_)
    444 		{
    445 		}
    446 	};
    447 
    448 	glu::TextureCube*			m_gradientTex;
    449 	glu::TextureCube*			m_gridTex;
    450 	std::vector<FilterCase>		m_cases;
    451 
    452 	TextureRenderer				m_renderer;
    453 
    454 	int							m_caseNdx;
    455 };
    456 
    457 TextureCubeShadowCase::TextureCubeShadowCase (Context& context, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 format, int size, deUint32 compareFunc)
    458 	: TestCase			(context, name, desc)
    459 	, m_minFilter		(minFilter)
    460 	, m_magFilter		(magFilter)
    461 	, m_wrapS			(wrapS)
    462 	, m_wrapT			(wrapT)
    463 	, m_format			(format)
    464 	, m_size			(size)
    465 	, m_compareFunc		(compareFunc)
    466 	, m_gradientTex		(DE_NULL)
    467 	, m_gridTex			(DE_NULL)
    468 	, m_renderer		(context.getRenderContext(), m_context.getTestContext(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
    469 	, m_caseNdx			(0)
    470 {
    471 }
    472 
    473 TextureCubeShadowCase::~TextureCubeShadowCase (void)
    474 {
    475 	TextureCubeShadowCase::deinit();
    476 }
    477 
    478 void TextureCubeShadowCase::init (void)
    479 {
    480 	try
    481 	{
    482 		DE_ASSERT(!m_gradientTex && !m_gridTex);
    483 
    484 		int						numLevels	= deLog2Floor32(m_size)+1;
    485 		tcu::TextureFormat		texFmt		= glu::mapGLInternalFormat(m_format);
    486 		tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(texFmt);
    487 		tcu::Vec4				cBias		= fmtInfo.valueMin;
    488 		tcu::Vec4				cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
    489 
    490 		// Create textures.
    491 		m_gradientTex	= new glu::TextureCube(m_context.getRenderContext(), m_format, m_size);
    492 		m_gridTex		= new glu::TextureCube(m_context.getRenderContext(), m_format, m_size);
    493 
    494 		// Fill first with gradient texture.
    495 		static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] =
    496 		{
    497 			{ tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
    498 			{ tcu::Vec4( 0.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
    499 			{ tcu::Vec4(-1.0f,  0.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
    500 			{ tcu::Vec4(-1.0f, -1.0f,  0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
    501 			{ tcu::Vec4(-1.0f, -1.0f, -1.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
    502 			{ tcu::Vec4( 0.0f,  0.0f,  0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }  // positive z
    503 		};
    504 		for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    505 		{
    506 			for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    507 			{
    508 				m_gradientTex->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
    509 				tcu::fillWithComponentGradients(m_gradientTex->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), gradients[face][0]*cScale + cBias, gradients[face][1]*cScale + cBias);
    510 			}
    511 		}
    512 
    513 		// Fill second with grid texture.
    514 		for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    515 		{
    516 			for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    517 			{
    518 				deUint32	step	= 0x00ffffff / (numLevels*tcu::CUBEFACE_LAST);
    519 				deUint32	rgb		= step*levelNdx*face;
    520 				deUint32	colorA	= 0xff000000 | rgb;
    521 				deUint32	colorB	= 0xff000000 | ~rgb;
    522 
    523 				m_gridTex->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
    524 				tcu::fillWithGrid(m_gridTex->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
    525 			}
    526 		}
    527 
    528 		// Upload.
    529 		m_gradientTex->upload();
    530 		m_gridTex->upload();
    531 	}
    532 	catch (const std::exception&)
    533 	{
    534 		// Clean up to save memory.
    535 		TextureCubeShadowCase::deinit();
    536 		throw;
    537 	}
    538 
    539 	// Compute cases
    540 	{
    541 		const float refInRangeUpper		= (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 1.0f : 0.5f;
    542 		const float refInRangeLower		= (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 0.0f : 0.5f;
    543 		const float refOutOfBoundsUpper	= 1.1f;
    544 		const float refOutOfBoundsLower	= -0.1f;
    545 		const bool	singleSample		= m_context.getRenderTarget().getNumSamples() == 0;
    546 
    547 		if (singleSample)
    548 			m_cases.push_back(FilterCase(m_gradientTex,	refInRangeUpper, tcu::Vec2(-1.25f, -1.2f), tcu::Vec2(1.2f, 1.25f)));	// minification
    549 		else
    550 			m_cases.push_back(FilterCase(m_gradientTex,	refInRangeUpper, tcu::Vec2(-1.19f, -1.3f), tcu::Vec2(1.1f, 1.35f)));	// minification - w/ tuned coordinates to avoid hitting triangle edges
    551 
    552 		m_cases.push_back(FilterCase(m_gradientTex,	refInRangeLower,		tcu::Vec2(0.8f, 0.8f), tcu::Vec2(1.25f, 1.20f)));	// magnification
    553 		m_cases.push_back(FilterCase(m_gridTex,		refInRangeUpper,		tcu::Vec2(-1.19f, -1.3f), tcu::Vec2(1.1f, 1.35f)));	// minification
    554 		m_cases.push_back(FilterCase(m_gridTex,		refInRangeLower,		tcu::Vec2(-1.2f, -1.1f), tcu::Vec2(-0.8f, -0.8f)));	// magnification
    555 		m_cases.push_back(FilterCase(m_gridTex,		refOutOfBoundsUpper,	tcu::Vec2(-0.61f, -0.1f), tcu::Vec2(0.9f, 1.18f)));	// reference value clamp, upper
    556 
    557 		if (singleSample)
    558 			m_cases.push_back(FilterCase(m_gridTex,	refOutOfBoundsLower, tcu::Vec2(-0.75f, 1.0f), tcu::Vec2(0.05f, 0.75f)));	// reference value clamp, lower
    559 		else
    560 			m_cases.push_back(FilterCase(m_gridTex,	refOutOfBoundsLower, tcu::Vec2(-0.75f, 1.0f), tcu::Vec2(0.25f, 0.75f)));	// reference value clamp, lower
    561 	}
    562 
    563 	m_caseNdx = 0;
    564 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    565 }
    566 
    567 void TextureCubeShadowCase::deinit (void)
    568 {
    569 	delete m_gradientTex;
    570 	delete m_gridTex;
    571 
    572 	m_gradientTex	= DE_NULL;
    573 	m_gridTex		= DE_NULL;
    574 
    575 	m_renderer.clear();
    576 	m_cases.clear();
    577 }
    578 
    579 static const char* getFaceDesc (const tcu::CubeFace face)
    580 {
    581 	switch (face)
    582 	{
    583 		case tcu::CUBEFACE_NEGATIVE_X:	return "-X";
    584 		case tcu::CUBEFACE_POSITIVE_X:	return "+X";
    585 		case tcu::CUBEFACE_NEGATIVE_Y:	return "-Y";
    586 		case tcu::CUBEFACE_POSITIVE_Y:	return "+Y";
    587 		case tcu::CUBEFACE_NEGATIVE_Z:	return "-Z";
    588 		case tcu::CUBEFACE_POSITIVE_Z:	return "+Z";
    589 		default:
    590 			DE_ASSERT(false);
    591 			return DE_NULL;
    592 	}
    593 }
    594 
    595 TextureCubeShadowCase::IterateResult TextureCubeShadowCase::iterate (void)
    596 {
    597 	const glw::Functions&			gl				= m_context.getRenderContext().getFunctions();
    598 	const int						viewportSize	= 28;
    599 	const RandomViewport			viewport		(m_context.getRenderTarget(), viewportSize, viewportSize, deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
    600 	const tcu::ScopedLogSection		iterSection		(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
    601 	const FilterCase&				curCase			= m_cases[m_caseNdx];
    602 	ReferenceParams					sampleParams	(TEXTURETYPE_CUBE);
    603 
    604 	if (viewport.width < viewportSize || viewport.height < viewportSize)
    605 		throw tcu::NotSupportedError("Too small render target", DE_NULL, __FILE__, __LINE__);
    606 
    607 	// Setup texture
    608 	gl.bindTexture	(GL_TEXTURE_CUBE_MAP, curCase.texture->getGLTexture());
    609 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	m_minFilter);
    610 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	m_magFilter);
    611 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		m_wrapS);
    612 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		m_wrapT);
    613 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE,	GL_COMPARE_REF_TO_TEXTURE);
    614 	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC,	m_compareFunc);
    615 
    616 	// Other state
    617 	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
    618 
    619 	// Params for reference computation.
    620 	sampleParams.sampler					= glu::mapGLSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, m_minFilter, m_magFilter);
    621 	sampleParams.sampler.seamlessCubeMap	= true;
    622 	sampleParams.sampler.compare			= glu::mapGLCompareFunc(m_compareFunc);
    623 	sampleParams.samplerType				= SAMPLERTYPE_SHADOW;
    624 	sampleParams.lodMode					= LODMODE_EXACT;
    625 	sampleParams.ref						= curCase.ref;
    626 
    627 	m_testCtx.getLog()
    628 		<< TestLog::Message
    629 		<< "Compare reference value =  " << sampleParams.ref << "\n"
    630 		<< "Coordinates: " << curCase.bottomLeft << " -> " << curCase.topRight
    631 		<< TestLog::EndMessage;
    632 
    633 	for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
    634 	{
    635 		const tcu::CubeFace		face		= tcu::CubeFace(faceNdx);
    636 		tcu::Surface			result		(viewport.width, viewport.height);
    637 		vector<float>			texCoord;
    638 
    639 		computeQuadTexCoordCube(texCoord, face, curCase.bottomLeft, curCase.topRight);
    640 
    641 		m_testCtx.getLog() << TestLog::Message << "Face " << getFaceDesc(face) << TestLog::EndMessage;
    642 
    643 		// \todo Log texture coordinates.
    644 
    645 		m_renderer.renderQuad(0, &texCoord[0], sampleParams);
    646 		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
    647 
    648 		glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, result.getAccess());
    649 		GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
    650 
    651 		{
    652 			const tcu::PixelFormat		pixelFormat		= m_context.getRenderTarget().getPixelFormat();
    653 			tcu::LodPrecision			lodPrecision;
    654 			tcu::TexComparePrecision	texComparePrecision;
    655 
    656 			lodPrecision.derivateBits			= 10;
    657 			lodPrecision.lodBits				= 5;
    658 			texComparePrecision.coordBits		= tcu::IVec3(10,10,10);
    659 			texComparePrecision.uvwBits			= tcu::IVec3(6,6,0);
    660 			texComparePrecision.pcfBits			= 5;
    661 			texComparePrecision.referenceBits	= 16;
    662 			texComparePrecision.resultBits		= pixelFormat.redBits-1;
    663 
    664 			const bool isHighQuality = verifyTexCompareResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(),
    665 															  &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    666 
    667 			if (!isHighQuality)
    668 			{
    669 				m_testCtx.getLog() << TestLog::Message << "Warning: Verification assuming high-quality PCF filtering failed." << TestLog::EndMessage;
    670 
    671 				lodPrecision.lodBits			= 4;
    672 				texComparePrecision.uvwBits		= tcu::IVec3(4,4,0);
    673 				texComparePrecision.pcfBits		= 0;
    674 
    675 				const bool isOk = verifyTexCompareResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(),
    676 														 &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    677 
    678 				if (!isOk)
    679 				{
    680 					m_testCtx.getLog() << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
    681 					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
    682 				}
    683 				else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
    684 					m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality result");
    685 			}
    686 		}
    687 	}
    688 
    689 	m_caseNdx += 1;
    690 	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
    691 }
    692 
    693 class Texture2DArrayShadowCase : public TestCase
    694 {
    695 public:
    696 								Texture2DArrayShadowCase	(Context& context, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 format, int width, int height, int numLayers, deUint32 compareFunc);
    697 								~Texture2DArrayShadowCase	(void);
    698 
    699 	void						init						(void);
    700 	void						deinit						(void);
    701 	IterateResult				iterate						(void);
    702 
    703 private:
    704 								Texture2DArrayShadowCase	(const Texture2DArrayShadowCase& other);
    705 	Texture2DArrayShadowCase&	operator=					(const Texture2DArrayShadowCase& other);
    706 
    707 	const deUint32				m_minFilter;
    708 	const deUint32				m_magFilter;
    709 	const deUint32				m_wrapS;
    710 	const deUint32				m_wrapT;
    711 
    712 	const deUint32				m_format;
    713 	const int					m_width;
    714 	const int					m_height;
    715 	const int					m_numLayers;
    716 
    717 	const deUint32				m_compareFunc;
    718 
    719 	struct FilterCase
    720 	{
    721 		const glu::Texture2DArray*	texture;
    722 		tcu::Vec3					minCoord;
    723 		tcu::Vec3					maxCoord;
    724 		float						ref;
    725 
    726 		FilterCase (void)
    727 			: texture	(DE_NULL)
    728 			, ref		(0.0f)
    729 		{
    730 		}
    731 
    732 		FilterCase (const glu::Texture2DArray* tex_, float ref_, const tcu::Vec3& minCoord_, const tcu::Vec3& maxCoord_)
    733 			: texture	(tex_)
    734 			, minCoord	(minCoord_)
    735 			, maxCoord	(maxCoord_)
    736 			, ref		(ref_)
    737 		{
    738 		}
    739 	};
    740 
    741 	glu::Texture2DArray*		m_gradientTex;
    742 	glu::Texture2DArray*		m_gridTex;
    743 	std::vector<FilterCase>		m_cases;
    744 
    745 	TextureRenderer				m_renderer;
    746 
    747 	int							m_caseNdx;
    748 };
    749 
    750 Texture2DArrayShadowCase::Texture2DArrayShadowCase (Context& context, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 format, int width, int height, int numLayers, deUint32 compareFunc)
    751 	: TestCase			(context, name, desc)
    752 	, m_minFilter		(minFilter)
    753 	, m_magFilter		(magFilter)
    754 	, m_wrapS			(wrapS)
    755 	, m_wrapT			(wrapT)
    756 	, m_format			(format)
    757 	, m_width			(width)
    758 	, m_height			(height)
    759 	, m_numLayers		(numLayers)
    760 	, m_compareFunc		(compareFunc)
    761 	, m_gradientTex		(DE_NULL)
    762 	, m_gridTex			(DE_NULL)
    763 	, m_renderer		(context.getRenderContext(), context.getTestContext(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
    764 	, m_caseNdx			(0)
    765 {
    766 }
    767 
    768 Texture2DArrayShadowCase::~Texture2DArrayShadowCase (void)
    769 {
    770 	Texture2DArrayShadowCase::deinit();
    771 }
    772 
    773 void Texture2DArrayShadowCase::init (void)
    774 {
    775 	try
    776 	{
    777 		tcu::TextureFormat		texFmt		= glu::mapGLInternalFormat(m_format);
    778 		tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(texFmt);
    779 		tcu::Vec4				cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
    780 		tcu::Vec4				cBias		= fmtInfo.valueMin;
    781 		int						numLevels	= deLog2Floor32(de::max(m_width, m_height)) + 1;
    782 
    783 		// Create textures.
    784 		m_gradientTex	= new glu::Texture2DArray(m_context.getRenderContext(), m_format, m_width, m_height, m_numLayers);
    785 		m_gridTex		= new glu::Texture2DArray(m_context.getRenderContext(), m_format, m_width, m_height, m_numLayers);
    786 
    787 		// Fill first gradient texture.
    788 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    789 		{
    790 			tcu::Vec4 gMin = tcu::Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias;
    791 			tcu::Vec4 gMax = tcu::Vec4( 1.0f,  1.0f,  1.0f, 0.0f)*cScale + cBias;
    792 
    793 			m_gradientTex->getRefTexture().allocLevel(levelNdx);
    794 			tcu::fillWithComponentGradients(m_gradientTex->getRefTexture().getLevel(levelNdx), gMin, gMax);
    795 		}
    796 
    797 		// Fill second with grid texture.
    798 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    799 		{
    800 			deUint32	step	= 0x00ffffff / numLevels;
    801 			deUint32	rgb		= step*levelNdx;
    802 			deUint32	colorA	= 0xff000000 | rgb;
    803 			deUint32	colorB	= 0xff000000 | ~rgb;
    804 
    805 			m_gridTex->getRefTexture().allocLevel(levelNdx);
    806 			tcu::fillWithGrid(m_gridTex->getRefTexture().getLevel(levelNdx), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
    807 		}
    808 
    809 		// Upload.
    810 		m_gradientTex->upload();
    811 		m_gridTex->upload();
    812 	}
    813 	catch (...)
    814 	{
    815 		// Clean up to save memory.
    816 		Texture2DArrayShadowCase::deinit();
    817 		throw;
    818 	}
    819 
    820 	// Compute cases.
    821 	{
    822 		const float refInRangeUpper		= (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 1.0f : 0.5f;
    823 		const float refInRangeLower		= (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 0.0f : 0.5f;
    824 		const float refOutOfBoundsUpper	= 1.1f;		// !< lookup function should clamp values to [0, 1] range
    825 		const float refOutOfBoundsLower	= -0.1f;
    826 
    827 		const struct
    828 		{
    829 			int		texNdx;
    830 			float	ref;
    831 			float	lodX;
    832 			float	lodY;
    833 			float	oX;
    834 			float	oY;
    835 		} cases[] =
    836 		{
    837 			{ 0,	refInRangeUpper,		1.6f,	2.9f,	-1.0f,	-2.7f	},
    838 			{ 0,	refInRangeLower,		-2.0f,	-1.35f,	-0.2f,	0.7f	},
    839 			{ 1,	refInRangeUpper,		0.14f,	0.275f,	-1.5f,	-1.1f	},
    840 			{ 1,	refInRangeLower,		-0.92f,	-2.64f,	0.4f,	-0.1f	},
    841 			{ 1,	refOutOfBoundsUpper,	-0.49f,	-0.22f,	0.45f,	0.97f	},
    842 			{ 1,	refOutOfBoundsLower,	-0.85f,	0.75f,	0.25f,	0.61f	},
    843 		};
    844 
    845 		const float	viewportW	= (float)de::min<int>(TEX2D_VIEWPORT_WIDTH, m_context.getRenderTarget().getWidth());
    846 		const float	viewportH	= (float)de::min<int>(TEX2D_VIEWPORT_HEIGHT, m_context.getRenderTarget().getHeight());
    847 
    848 		const float	minLayer	= -0.5f;
    849 		const float	maxLayer	= (float)m_numLayers;
    850 
    851 		for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
    852 		{
    853 			const glu::Texture2DArray*	tex		= cases[caseNdx].texNdx > 0 ? m_gridTex : m_gradientTex;
    854 			const float					ref		= cases[caseNdx].ref;
    855 			const float					lodX	= cases[caseNdx].lodX;
    856 			const float					lodY	= cases[caseNdx].lodY;
    857 			const float					oX		= cases[caseNdx].oX;
    858 			const float					oY		= cases[caseNdx].oY;
    859 			const float					sX		= deFloatExp2(lodX)*viewportW / float(tex->getRefTexture().getWidth());
    860 			const float					sY		= deFloatExp2(lodY)*viewportH / float(tex->getRefTexture().getHeight());
    861 
    862 			m_cases.push_back(FilterCase(tex, ref, tcu::Vec3(oX, oY, minLayer), tcu::Vec3(oX+sX, oY+sY, maxLayer)));
    863 		}
    864 	}
    865 
    866 	m_caseNdx = 0;
    867 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    868 }
    869 
    870 void Texture2DArrayShadowCase::deinit (void)
    871 {
    872 	delete m_gradientTex;
    873 	delete m_gridTex;
    874 
    875 	m_gradientTex	= DE_NULL;
    876 	m_gridTex		= DE_NULL;
    877 
    878 	m_renderer.clear();
    879 	m_cases.clear();
    880 }
    881 
    882 Texture2DArrayShadowCase::IterateResult Texture2DArrayShadowCase::iterate (void)
    883 {
    884 	const glw::Functions&			gl				= m_context.getRenderContext().getFunctions();
    885 	const RandomViewport			viewport		(m_context.getRenderTarget(), TEX2D_VIEWPORT_WIDTH, TEX2D_VIEWPORT_HEIGHT, deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
    886 	const FilterCase&				curCase			= m_cases[m_caseNdx];
    887 	const tcu::ScopedLogSection		section			(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
    888 	ReferenceParams					sampleParams	(TEXTURETYPE_2D_ARRAY);
    889 	tcu::Surface					rendered		(viewport.width, viewport.height);
    890 
    891 	const float						texCoord[]		=
    892 	{
    893 		curCase.minCoord.x(), curCase.minCoord.y(), curCase.minCoord.z(),
    894 		curCase.minCoord.x(), curCase.maxCoord.y(), (curCase.minCoord.z() + curCase.maxCoord.z()) / 2.0f,
    895 		curCase.maxCoord.x(), curCase.minCoord.y(), (curCase.minCoord.z() + curCase.maxCoord.z()) / 2.0f,
    896 		curCase.maxCoord.x(), curCase.maxCoord.y(), curCase.maxCoord.z()
    897 	};
    898 
    899 	if (viewport.width < TEX2D_MIN_VIEWPORT_WIDTH || viewport.height < TEX2D_MIN_VIEWPORT_HEIGHT)
    900 		throw tcu::NotSupportedError("Too small render target", "", __FILE__, __LINE__);
    901 
    902 	// Setup params for reference.
    903 	sampleParams.sampler			= glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
    904 	sampleParams.sampler.compare	= glu::mapGLCompareFunc(m_compareFunc);
    905 	sampleParams.samplerType		= SAMPLERTYPE_SHADOW;
    906 	sampleParams.lodMode			= LODMODE_EXACT;
    907 	sampleParams.ref				= curCase.ref;
    908 
    909 	m_testCtx.getLog()
    910 		<< TestLog::Message
    911 		<< "Compare reference value =  " << sampleParams.ref << "\n"
    912 		<< "Texture coordinates: " << curCase.minCoord << " -> " << curCase.maxCoord
    913 		<< TestLog::EndMessage;
    914 
    915 	gl.bindTexture	(GL_TEXTURE_2D_ARRAY, curCase.texture->getGLTexture());
    916 	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,	m_minFilter);
    917 	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER,	m_magFilter);
    918 	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S,		m_wrapS);
    919 	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T,		m_wrapT);
    920 	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE,	GL_COMPARE_REF_TO_TEXTURE);
    921 	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC,	m_compareFunc);
    922 
    923 	gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
    924 	m_renderer.renderQuad(0, &texCoord[0], sampleParams);
    925 	glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, rendered.getAccess());
    926 
    927 	{
    928 		const tcu::PixelFormat		pixelFormat		= m_context.getRenderTarget().getPixelFormat();
    929 		tcu::LodPrecision			lodPrecision;
    930 		tcu::TexComparePrecision	texComparePrecision;
    931 
    932 		lodPrecision.derivateBits			= 18;
    933 		lodPrecision.lodBits				= 6;
    934 		texComparePrecision.coordBits		= tcu::IVec3(20,20,20);
    935 		texComparePrecision.uvwBits			= tcu::IVec3(7,7,7);
    936 		texComparePrecision.pcfBits			= 5;
    937 		texComparePrecision.referenceBits	= 16;
    938 		texComparePrecision.resultBits		= pixelFormat.redBits-1;
    939 
    940 		const bool isHighQuality = verifyTexCompareResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
    941 														  &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    942 
    943 		if (!isHighQuality)
    944 		{
    945 			m_testCtx.getLog() << TestLog::Message << "Warning: Verification assuming high-quality PCF filtering failed." << TestLog::EndMessage;
    946 
    947 			lodPrecision.lodBits			= 4;
    948 			texComparePrecision.uvwBits		= tcu::IVec3(4,4,4);
    949 			texComparePrecision.pcfBits		= 0;
    950 
    951 			const bool isOk = verifyTexCompareResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
    952 													 &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    953 
    954 			if (!isOk)
    955 			{
    956 				m_testCtx.getLog() << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
    957 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
    958 			}
    959 			else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
    960 				m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality result");
    961 		}
    962 	}
    963 
    964 	m_caseNdx += 1;
    965 	return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
    966 }
    967 
    968 TextureShadowTests::TextureShadowTests (Context& context)
    969 	: TestCaseGroup(context, "shadow", "Shadow texture lookup tests")
    970 {
    971 }
    972 
    973 TextureShadowTests::~TextureShadowTests (void)
    974 {
    975 }
    976 
    977 void TextureShadowTests::init (void)
    978 {
    979 	static const struct
    980 	{
    981 		const char*		name;
    982 		deUint32		format;
    983 	} formats[] =
    984 	{
    985 		{ "depth_component16",	GL_DEPTH_COMPONENT16	},
    986 		{ "depth_component32f",	GL_DEPTH_COMPONENT32F	},
    987 		{ "depth24_stencil8",	GL_DEPTH24_STENCIL8		}
    988 	};
    989 
    990 	static const struct
    991 	{
    992 		const char*		name;
    993 		deUint32		minFilter;
    994 		deUint32		magFilter;
    995 	} filters[] =
    996 	{
    997 		{ "nearest",				GL_NEAREST,					GL_NEAREST	},
    998 		{ "linear",					GL_LINEAR,					GL_LINEAR	},
    999 		{ "nearest_mipmap_nearest",	GL_NEAREST_MIPMAP_NEAREST,	GL_LINEAR	},
   1000 		{ "linear_mipmap_nearest",	GL_LINEAR_MIPMAP_NEAREST,	GL_LINEAR	},
   1001 		{ "nearest_mipmap_linear",	GL_NEAREST_MIPMAP_LINEAR,	GL_LINEAR	},
   1002 		{ "linear_mipmap_linear",	GL_LINEAR_MIPMAP_LINEAR,	GL_LINEAR	}
   1003 	};
   1004 
   1005 	static const struct
   1006 	{
   1007 		const char*		name;
   1008 		deUint32		func;
   1009 	} compareFuncs[] =
   1010 	{
   1011 		{ "less_or_equal",		GL_LEQUAL	},
   1012 		{ "greater_or_equal",	GL_GEQUAL	},
   1013 		{ "less",				GL_LESS		},
   1014 		{ "greater",			GL_GREATER	},
   1015 		{ "equal",				GL_EQUAL	},
   1016 		{ "not_equal",			GL_NOTEQUAL	},
   1017 		{ "always",				GL_ALWAYS	},
   1018 		{ "never",				GL_NEVER	}
   1019 	};
   1020 
   1021 	// 2D cases.
   1022 	{
   1023 		tcu::TestCaseGroup* group2D = new tcu::TestCaseGroup(m_testCtx, "2d", "2D texture shadow lookup tests");
   1024 		addChild(group2D);
   1025 
   1026 		for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(filters); filterNdx++)
   1027 		{
   1028 			tcu::TestCaseGroup* filterGroup = new tcu::TestCaseGroup(m_testCtx, filters[filterNdx].name, "");
   1029 			group2D->addChild(filterGroup);
   1030 
   1031 			for (int compareNdx = 0; compareNdx < DE_LENGTH_OF_ARRAY(compareFuncs); compareNdx++)
   1032 			{
   1033 				for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
   1034 				{
   1035 					deUint32		minFilter		= filters[filterNdx].minFilter;
   1036 					deUint32		magFilter		= filters[filterNdx].magFilter;
   1037 					deUint32		format			= formats[formatNdx].format;
   1038 					deUint32		compareFunc		= compareFuncs[compareNdx].func;
   1039 					const deUint32	wrapS			= GL_REPEAT;
   1040 					const deUint32	wrapT			= GL_REPEAT;
   1041 					const int		width			= 32;
   1042 					const int		height			= 64;
   1043 					string			name			= string(compareFuncs[compareNdx].name) + "_" + formats[formatNdx].name;
   1044 
   1045 					filterGroup->addChild(new Texture2DShadowCase(m_context, name.c_str(), "", minFilter, magFilter, wrapS, wrapT, format, width, height, compareFunc));
   1046 				}
   1047 			}
   1048 		}
   1049 	}
   1050 
   1051 	// Cubemap cases.
   1052 	{
   1053 		tcu::TestCaseGroup* groupCube = new tcu::TestCaseGroup(m_testCtx, "cube", "Cube map texture shadow lookup tests");
   1054 		addChild(groupCube);
   1055 
   1056 		for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(filters); filterNdx++)
   1057 		{
   1058 			tcu::TestCaseGroup* filterGroup = new tcu::TestCaseGroup(m_testCtx, filters[filterNdx].name, "");
   1059 			groupCube->addChild(filterGroup);
   1060 
   1061 			for (int compareNdx = 0; compareNdx < DE_LENGTH_OF_ARRAY(compareFuncs); compareNdx++)
   1062 			{
   1063 				for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
   1064 				{
   1065 					deUint32		minFilter		= filters[filterNdx].minFilter;
   1066 					deUint32		magFilter		= filters[filterNdx].magFilter;
   1067 					deUint32		format			= formats[formatNdx].format;
   1068 					deUint32		compareFunc		= compareFuncs[compareNdx].func;
   1069 					const deUint32	wrapS			= GL_REPEAT;
   1070 					const deUint32	wrapT			= GL_REPEAT;
   1071 					const int		size			= 32;
   1072 					string			name			= string(compareFuncs[compareNdx].name) + "_" + formats[formatNdx].name;
   1073 
   1074 					filterGroup->addChild(new TextureCubeShadowCase(m_context, name.c_str(), "", minFilter, magFilter, wrapS, wrapT, format, size, compareFunc));
   1075 				}
   1076 			}
   1077 		}
   1078 	}
   1079 
   1080 	// 2D array cases.
   1081 	{
   1082 		tcu::TestCaseGroup* group2DArray = new tcu::TestCaseGroup(m_testCtx, "2d_array", "2D texture array shadow lookup tests");
   1083 		addChild(group2DArray);
   1084 
   1085 		for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(filters); filterNdx++)
   1086 		{
   1087 			tcu::TestCaseGroup* filterGroup = new tcu::TestCaseGroup(m_testCtx, filters[filterNdx].name, "");
   1088 			group2DArray->addChild(filterGroup);
   1089 
   1090 			for (int compareNdx = 0; compareNdx < DE_LENGTH_OF_ARRAY(compareFuncs); compareNdx++)
   1091 			{
   1092 				for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
   1093 				{
   1094 					deUint32		minFilter		= filters[filterNdx].minFilter;
   1095 					deUint32		magFilter		= filters[filterNdx].magFilter;
   1096 					deUint32		format			= formats[formatNdx].format;
   1097 					deUint32		compareFunc		= compareFuncs[compareNdx].func;
   1098 					const deUint32	wrapS			= GL_REPEAT;
   1099 					const deUint32	wrapT			= GL_REPEAT;
   1100 					const int		width			= 32;
   1101 					const int		height			= 64;
   1102 					const int		numLayers		= 8;
   1103 					string			name			= string(compareFuncs[compareNdx].name) + "_" + formats[formatNdx].name;
   1104 
   1105 					filterGroup->addChild(new Texture2DArrayShadowCase(m_context, name.c_str(), "", minFilter, magFilter, wrapS, wrapT, format, width, height, numLayers, compareFunc));
   1106 				}
   1107 			}
   1108 		}
   1109 	}
   1110 }
   1111 
   1112 } // Functional
   1113 } // gles3
   1114 } // deqp
   1115