Home | History | Annotate | Download | only in texture
      1 /*-------------------------------------------------------------------------
      2  * Vulkan Conformance Tests
      3  * ------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  * Copyright (c) 2016 The Khronos Group Inc.
      7  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
      8  *
      9  * Licensed under the Apache License, Version 2.0 (the "License");
     10  * you may not use this file except in compliance with the License.
     11  * You may obtain a copy of the License at
     12  *
     13  *      http://www.apache.org/licenses/LICENSE-2.0
     14  *
     15  * Unless required by applicable law or agreed to in writing, software
     16  * distributed under the License is distributed on an "AS IS" BASIS,
     17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     18  * See the License for the specific language governing permissions and
     19  * limitations under the License.
     20  *
     21  *//*!
     22  * \file
     23  * \brief Shadow texture lookup tests.
     24  *//*--------------------------------------------------------------------*/
     25 
     26 #include "vktTextureShadowTests.hpp"
     27 
     28 #include "deMath.h"
     29 #include "deString.h"
     30 #include "deStringUtil.hpp"
     31 #include "gluPixelTransfer.hpp"
     32 #include "gluTextureTestUtil.hpp"
     33 #include "glwEnums.hpp"
     34 #include "glwFunctions.hpp"
     35 #include "tcuImageIO.hpp"
     36 #include "tcuRenderTarget.hpp"
     37 #include "tcuTexCompareVerifier.hpp"
     38 #include "tcuTexVerifierUtil.hpp"
     39 #include "tcuTexture.hpp"
     40 #include "tcuTextureUtil.hpp"
     41 #include "vkImageUtil.hpp"
     42 #include "vkTypeUtil.hpp"
     43 #include "vktTestGroupUtil.hpp"
     44 #include "vktTextureTestUtil.hpp"
     45 
     46 using namespace vk;
     47 
     48 namespace vkt
     49 {
     50 namespace texture
     51 {
     52 namespace
     53 {
     54 
     55 using std::vector;
     56 using std::string;
     57 using tcu::TestLog;
     58 using tcu::Sampler;
     59 using namespace texture::util;
     60 using namespace glu::TextureTestUtil;
     61 
     62 enum
     63 {
     64 	TEXCUBE_VIEWPORT_SIZE	= 28,
     65 	TEX2D_VIEWPORT_WIDTH	= 64,
     66 	TEX2D_VIEWPORT_HEIGHT	= 64
     67 };
     68 
     69 struct TextureShadowCommonTestCaseParameters
     70 {
     71 							TextureShadowCommonTestCaseParameters	(void);
     72 	Sampler::CompareMode	compareOp;
     73 };
     74 
     75 TextureShadowCommonTestCaseParameters::TextureShadowCommonTestCaseParameters (void)
     76 	: compareOp				(Sampler::COMPAREMODE_EQUAL)
     77 {
     78 }
     79 
     80 struct Texture2DShadowTestCaseParameters : public Texture2DTestCaseParameters, public TextureShadowCommonTestCaseParameters
     81 {
     82 };
     83 
     84 bool isFloatingPointDepthFormat (const tcu::TextureFormat& format)
     85 {
     86 	// Only two depth and depth-stencil formats are floating point
     87 	return	(format.order == tcu::TextureFormat::D && format.type == tcu::TextureFormat::FLOAT) ||
     88 			(format.order == tcu::TextureFormat::DS && format.type == tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV);
     89 }
     90 
     91 void clampFloatingPointTexture (const tcu::PixelBufferAccess& access)
     92 {
     93 	DE_ASSERT(isFloatingPointDepthFormat(access.getFormat()));
     94 
     95 	for (int z = 0; z < access.getDepth(); ++z)
     96 	for (int y = 0; y < access.getHeight(); ++y)
     97 	for (int x = 0; x < access.getWidth(); ++x)
     98 		access.setPixDepth(de::clamp(access.getPixDepth(x, y, z), 0.0f, 1.0f), x, y, z);
     99 }
    100 
    101 void clampFloatingPointTexture (tcu::Texture2D& target)
    102 {
    103 	for (int level = 0; level < target.getNumLevels(); ++level)
    104 		if (!target.isLevelEmpty(level))
    105 			clampFloatingPointTexture(target.getLevel(level));
    106 }
    107 
    108 static void clampFloatingPointTexture (tcu::Texture2DArray& target)
    109 {
    110 	for (int level = 0; level < target.getNumLevels(); ++level)
    111 		if (!target.isLevelEmpty(level))
    112 			clampFloatingPointTexture(target.getLevel(level));
    113 }
    114 
    115 void clampFloatingPointTexture (tcu::TextureCube& target)
    116 {
    117 	for (int level = 0; level < target.getNumLevels(); ++level)
    118 		for (int face = tcu::CUBEFACE_NEGATIVE_X; face < tcu::CUBEFACE_LAST; ++face)
    119 			clampFloatingPointTexture(target.getLevelFace(level, (tcu::CubeFace)face));
    120 }
    121 
    122 tcu::PixelFormat getPixelFormat(tcu::TextureFormat texFormat)
    123 {
    124 	const tcu::IVec4			formatBitDepth		= tcu::getTextureFormatBitDepth(tcu::getEffectiveDepthStencilTextureFormat(texFormat, Sampler::MODE_DEPTH));
    125 	return tcu::PixelFormat(formatBitDepth[0], formatBitDepth[1], formatBitDepth[2], formatBitDepth[3]);
    126 }
    127 
    128 template<typename TextureType>
    129 bool verifyTexCompareResult (tcu::TestContext&						testCtx,
    130 							 const tcu::ConstPixelBufferAccess&		result,
    131 							 const TextureType&						src,
    132 							 const float*							texCoord,
    133 							 const ReferenceParams&					sampleParams,
    134 							 const tcu::TexComparePrecision&		comparePrec,
    135 							 const tcu::LodPrecision&				lodPrec,
    136 							 const tcu::PixelFormat&				pixelFormat)
    137 {
    138 	tcu::TestLog&	log					= testCtx.getLog();
    139 	tcu::Surface	reference			(result.getWidth(), result.getHeight());
    140 	tcu::Surface	errorMask			(result.getWidth(), result.getHeight());
    141 	const tcu::Vec3	nonShadowThreshold	= tcu::computeFixedPointThreshold(getBitsVec(pixelFormat)-1).swizzle(1,2,3);
    142 	int				numFailedPixels;
    143 
    144 	// sampleTexture() expects source image to be the same state as it would be in a GL implementation, that is
    145 	// the floating point depth values should be in [0, 1] range as data is clamped during texture upload. Since
    146 	// we don't have a separate "uploading" phase and just reuse the buffer we used for GL-upload, do the clamping
    147 	// here if necessary.
    148 
    149 	if (isFloatingPointDepthFormat(src.getFormat()))
    150 	{
    151 		TextureType clampedSource(src);
    152 
    153 		clampFloatingPointTexture(clampedSource);
    154 
    155 		// sample clamped values
    156 
    157 		sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), clampedSource, texCoord, sampleParams);
    158 		numFailedPixels = computeTextureCompareDiff(result, reference.getAccess(), errorMask.getAccess(), clampedSource, texCoord, sampleParams, comparePrec, lodPrec, nonShadowThreshold);
    159 	}
    160 	else
    161 	{
    162 		// sample raw values (they are guaranteed to be in [0, 1] range as the format cannot represent any other values)
    163 
    164 		sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams);
    165 		numFailedPixels = computeTextureCompareDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, comparePrec, lodPrec, nonShadowThreshold);
    166 	}
    167 
    168 	if (numFailedPixels > 0)
    169 		log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
    170 
    171 	log << TestLog::ImageSet("VerifyResult", "Verification result")
    172 		<< TestLog::Image("Rendered", "Rendered image", result);
    173 
    174 	if (numFailedPixels > 0)
    175 	{
    176 		log << TestLog::Image("Reference", "Ideal reference image", reference)
    177 			<< TestLog::Image("ErrorMask", "Error mask", errorMask);
    178 	}
    179 
    180 	log << TestLog::EndImageSet;
    181 
    182 	return numFailedPixels == 0;
    183 }
    184 
    185 class Texture2DShadowTestInstance : public TestInstance
    186 {
    187 public:
    188 	typedef Texture2DShadowTestCaseParameters	ParameterType;
    189 												Texture2DShadowTestInstance		(Context& context, const ParameterType& testParameters);
    190 												~Texture2DShadowTestInstance	(void);
    191 
    192 	virtual tcu::TestStatus						iterate							(void);
    193 
    194 private:
    195 												Texture2DShadowTestInstance		(const Texture2DShadowTestInstance& other);
    196 	Texture2DShadowTestInstance&				operator=						(const Texture2DShadowTestInstance& other);
    197 
    198 	struct FilterCase
    199 	{
    200 		int			textureIndex;
    201 
    202 		tcu::Vec2	minCoord;
    203 		tcu::Vec2	maxCoord;
    204 		float		ref;
    205 
    206 		FilterCase	(void)
    207 			: textureIndex(-1)
    208 			, ref		(0.0f)
    209 		{
    210 		}
    211 
    212 		FilterCase	(int tex_, const float ref_, const tcu::Vec2& minCoord_, const tcu::Vec2& maxCoord_)
    213 			: textureIndex	(tex_)
    214 			, minCoord		(minCoord_)
    215 			, maxCoord		(maxCoord_)
    216 			, ref			(ref_)
    217 		{
    218 		}
    219 	};
    220 
    221 	const ParameterType&			m_testParameters;
    222 	std::vector<TestTexture2DSp>	m_textures;
    223 	std::vector<FilterCase>			m_cases;
    224 
    225 	TextureRenderer					m_renderer;
    226 
    227 	int								m_caseNdx;
    228 };
    229 
    230 Texture2DShadowTestInstance::Texture2DShadowTestInstance (Context& context, const ParameterType& testParameters)
    231 	: TestInstance			(context)
    232 	, m_testParameters		(testParameters)
    233 	, m_renderer			(context, testParameters.sampleCount, TEX2D_VIEWPORT_WIDTH, TEX2D_VIEWPORT_HEIGHT)
    234 	, m_caseNdx				(0)
    235 {
    236 	// Create 2 textures.
    237 	m_textures.reserve(2);
    238 	for (int ndx = 0; ndx < 2; ndx++)
    239 	{
    240 		m_textures.push_back(TestTexture2DSp(new pipeline::TestTexture2D(vk::mapVkFormat(m_testParameters.format), m_testParameters.width, m_testParameters.height)));
    241 	}
    242 
    243 	const int	numLevels	= m_textures[0]->getNumLevels();
    244 
    245 	// Fill first gradient texture.
    246 	for (int levelNdx = 0; levelNdx < numLevels; ++levelNdx)
    247 	{
    248 		tcu::fillWithComponentGradients(m_textures[0]->getLevel(levelNdx, 0), tcu::Vec4(-0.5f, -0.5f, -0.5f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f));
    249 	}
    250 
    251 	// Fill second with grid texture.
    252 	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    253 	{
    254 		const deUint32	step	= 0x00ffffff / numLevels;
    255 		const deUint32	rgb		= step*levelNdx;
    256 		const deUint32	colorA	= 0xff000000 | rgb;
    257 		const deUint32	colorB	= 0xff000000 | ~rgb;
    258 
    259 		tcu::fillWithGrid(m_textures[1]->getLevel(levelNdx, 0), 4, tcu::RGBA(colorA).toVec(), tcu::RGBA(colorB).toVec());
    260 	}
    261 
    262 	// Upload.
    263 	for (std::vector<TestTexture2DSp>::iterator i = m_textures.begin(); i != m_textures.end(); ++i)
    264 	{
    265 		m_renderer.add2DTexture(*i);
    266 	}
    267 
    268 	// Compute cases.
    269 	{
    270 		const float refInRangeUpper		= (m_testParameters.compareOp == Sampler::COMPAREMODE_EQUAL || m_testParameters.compareOp == Sampler::COMPAREMODE_NOT_EQUAL) ? 1.0f : 0.5f;
    271 		const float refInRangeLower		= (m_testParameters.compareOp == Sampler::COMPAREMODE_EQUAL || m_testParameters.compareOp == Sampler::COMPAREMODE_NOT_EQUAL) ? 0.0f : 0.5f;
    272 		const float refOutOfBoundsUpper	= 1.1f;		// !< lookup function should clamp values to [0, 1] range
    273 		const float refOutOfBoundsLower	= -0.1f;
    274 
    275 		const struct
    276 		{
    277 			const int	texNdx;
    278 			const float	ref;
    279 			const float	lodX;
    280 			const float	lodY;
    281 			const float	oX;
    282 			const float	oY;
    283 		} cases[] =
    284 		{
    285 			{ 0,	refInRangeUpper,		1.6f,	2.9f,	-1.0f,	-2.7f	},
    286 			{ 0,	refInRangeLower,		-2.0f,	-1.35f,	-0.2f,	0.7f	},
    287 			{ 1,	refInRangeUpper,		0.14f,	0.275f,	-1.5f,	-1.1f	},
    288 			{ 1,	refInRangeLower,		-0.92f,	-2.64f,	0.4f,	-0.1f	},
    289 			{ 1,	refOutOfBoundsUpper,	-0.39f,	-0.52f,	0.65f,	0.87f	},
    290 			{ 1,	refOutOfBoundsLower,	-1.55f,	0.65f,	0.35f,	0.91f	},
    291 		};
    292 
    293 		for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
    294 		{
    295 			const int	texNdx	= de::clamp(cases[caseNdx].texNdx, 0, (int)m_textures.size()-1);
    296 			const float ref		= cases[caseNdx].ref;
    297 			const float	lodX	= cases[caseNdx].lodX;
    298 			const float	lodY	= cases[caseNdx].lodY;
    299 			const float	oX		= cases[caseNdx].oX;
    300 			const float	oY		= cases[caseNdx].oY;
    301 			const float	sX		= deFloatExp2(lodX) * float(m_renderer.getRenderWidth()) / float(m_textures[texNdx]->getTexture().getWidth());
    302 			const float	sY		= deFloatExp2(lodY) * float(m_renderer.getRenderHeight()) / float(m_textures[texNdx]->getTexture().getHeight());
    303 
    304 			m_cases.push_back(FilterCase(texNdx, ref, tcu::Vec2(oX, oY), tcu::Vec2(oX+sX, oY+sY)));
    305 		}
    306 	}
    307 
    308 	m_caseNdx = 0;
    309 }
    310 
    311 Texture2DShadowTestInstance::~Texture2DShadowTestInstance (void)
    312 {
    313 	m_textures.clear();
    314 	m_cases.clear();
    315 }
    316 
    317 tcu::TestStatus Texture2DShadowTestInstance::iterate (void)
    318 {
    319 	tcu::TestLog&					log				= m_context.getTestContext().getLog();
    320 	const pipeline::TestTexture2D&	texture			= m_renderer.get2DTexture(m_cases[m_caseNdx].textureIndex);
    321 	const tcu::TextureFormat		texFmt			= texture.getTextureFormat();
    322 	const tcu::TextureFormatInfo	fmtInfo			= tcu::getTextureFormatInfo(texFmt);
    323 	const tcu::ScopedLogSection		section			(log, string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
    324 
    325 	const FilterCase&				curCase			= m_cases[m_caseNdx];
    326 	ReferenceParams					sampleParams	(TEXTURETYPE_2D);
    327 	tcu::Surface					rendered		(m_renderer.getRenderWidth(), m_renderer.getRenderHeight());
    328 	vector<float>					texCoord;
    329 
    330 	// Setup params for reference.
    331 	sampleParams.sampler			= util::createSampler(m_testParameters.wrapS, m_testParameters.wrapT, m_testParameters.minFilter, m_testParameters.magFilter);
    332 	sampleParams.sampler.compare	= m_testParameters.compareOp;
    333 	sampleParams.samplerType		= SAMPLERTYPE_SHADOW;
    334 	sampleParams.lodMode			= LODMODE_EXACT;
    335 	sampleParams.colorBias			= fmtInfo.lookupBias;
    336 	sampleParams.colorScale			= fmtInfo.lookupScale;
    337 	sampleParams.ref				= curCase.ref;
    338 
    339 	log << TestLog::Message << "Compare reference value = " << sampleParams.ref << TestLog::EndMessage;
    340 
    341 	// Compute texture coordinates.
    342 	log << TestLog::Message << "Texture coordinates: " << curCase.minCoord << " -> " << curCase.maxCoord << TestLog::EndMessage;
    343 	computeQuadTexCoord2D(texCoord, curCase.minCoord, curCase.maxCoord);
    344 
    345 	m_renderer.renderQuad(rendered, curCase.textureIndex, &texCoord[0], sampleParams);
    346 
    347 	{
    348 		const tcu::PixelFormat		pixelFormat			= getPixelFormat(vk::mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM));
    349 		tcu::LodPrecision			lodPrecision;
    350 		tcu::TexComparePrecision	texComparePrecision;
    351 
    352 		lodPrecision.derivateBits			= 18;
    353 		lodPrecision.lodBits				= 6;
    354 		texComparePrecision.coordBits		= tcu::IVec3(20,20,0);
    355 		texComparePrecision.uvwBits			= tcu::IVec3(7,7,0);
    356 		texComparePrecision.pcfBits			= 5;
    357 		texComparePrecision.referenceBits	= 16;
    358 		texComparePrecision.resultBits		= pixelFormat.redBits-1;
    359 
    360 		const bool isHighQuality = verifyTexCompareResult(m_context.getTestContext(), rendered.getAccess(), texture.getTexture(),
    361 														  &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    362 
    363 		if (!isHighQuality)
    364 		{
    365 			m_context.getTestContext().getLog() << TestLog::Message << "Warning: Verification assuming high-quality PCF filtering failed." << TestLog::EndMessage;
    366 
    367 			lodPrecision.lodBits			= 4;
    368 			texComparePrecision.uvwBits		= tcu::IVec3(4,4,0);
    369 			texComparePrecision.pcfBits		= 0;
    370 
    371 			const bool isOk = verifyTexCompareResult(m_context.getTestContext(), rendered.getAccess(), texture.getTexture(),
    372 													 &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    373 
    374 			if (!isOk)
    375 			{
    376 				m_context.getTestContext().getLog() << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
    377 				return tcu::TestStatus::fail("Image verification failed");
    378 			}
    379 		}
    380 	}
    381 
    382 	m_caseNdx += 1;
    383 	return m_caseNdx < (int)m_cases.size() ? tcu::TestStatus::incomplete() : tcu::TestStatus::pass("Pass");
    384 }
    385 
    386 struct TextureCubeShadowTestCaseParameters : public TextureShadowCommonTestCaseParameters, public TextureCubeTestCaseParameters
    387 {
    388 };
    389 
    390 class TextureCubeShadowTestInstance : public TestInstance
    391 {
    392 public:
    393 	typedef TextureCubeShadowTestCaseParameters ParameterType;
    394 												TextureCubeShadowTestInstance		(Context& context, const ParameterType& testParameters);
    395 												~TextureCubeShadowTestInstance		(void);
    396 
    397 	virtual tcu::TestStatus						iterate								(void);
    398 
    399 private:
    400 												TextureCubeShadowTestInstance		(const TextureCubeShadowTestInstance& other);
    401 	TextureCubeShadowTestInstance&				operator=							(const TextureCubeShadowTestInstance& other);
    402 
    403 	struct FilterCase
    404 	{
    405 		int						textureIndex;
    406 		tcu::Vec2				bottomLeft;
    407 		tcu::Vec2				topRight;
    408 		float					ref;
    409 
    410 		FilterCase (void)
    411 			: textureIndex	(-1)
    412 			, ref			(0.0f)
    413 		{
    414 		}
    415 
    416 		FilterCase (const int tex_, const float ref_, const tcu::Vec2& bottomLeft_, const tcu::Vec2& topRight_)
    417 			: textureIndex	(tex_)
    418 			, bottomLeft	(bottomLeft_)
    419 			, topRight		(topRight_)
    420 			, ref			(ref_)
    421 		{
    422 		}
    423 	};
    424 
    425 	const ParameterType&		m_testParameters;
    426 	vector<TestTextureCubeSp>	m_textures;
    427 	std::vector<FilterCase>		m_cases;
    428 
    429 	TextureRenderer				m_renderer;
    430 	int							m_caseNdx;
    431 };
    432 
    433 TextureCubeShadowTestInstance::TextureCubeShadowTestInstance (Context& context, const ParameterType& testParameters)
    434 	: TestInstance			(context)
    435 	, m_testParameters		(testParameters)
    436 	, m_renderer			(context, testParameters.sampleCount, TEXCUBE_VIEWPORT_SIZE, TEXCUBE_VIEWPORT_SIZE)
    437 	, m_caseNdx				(0)
    438 {
    439 	const int						numLevels	= deLog2Floor32(m_testParameters.size)+1;
    440 	const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(vk::mapVkFormat(m_testParameters.format));
    441 	const tcu::Vec4					cBias		= fmtInfo.valueMin;
    442 	const tcu::Vec4					cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
    443 
    444 	// Create textures.
    445 
    446 	m_textures.reserve(2);
    447 	for (int ndx = 0; ndx < 2; ndx++)
    448 	{
    449 		m_textures.push_back(TestTextureCubeSp(new pipeline::TestTextureCube(vk::mapVkFormat(m_testParameters.format), m_testParameters.size)));
    450 	}
    451 
    452 	// Fill first with gradient texture.
    453 	static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] =
    454 	{
    455 		{ tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
    456 		{ tcu::Vec4( 0.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
    457 		{ tcu::Vec4(-1.0f,  0.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
    458 		{ tcu::Vec4(-1.0f, -1.0f,  0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
    459 		{ tcu::Vec4(-1.0f, -1.0f, -1.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
    460 		{ tcu::Vec4( 0.0f,  0.0f,  0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }  // positive z
    461 	};
    462 	for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    463 	{
    464 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    465 		{
    466 			tcu::fillWithComponentGradients(m_textures[0]->getLevel(levelNdx, face), gradients[face][0]*cScale + cBias, gradients[face][1]*cScale + cBias);
    467 		}
    468 	}
    469 
    470 	// Fill second with grid texture.
    471 	for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    472 	{
    473 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    474 		{
    475 			const deUint32	step	= 0x00ffffff / (numLevels*tcu::CUBEFACE_LAST);
    476 			const deUint32	rgb		= step*levelNdx*face;
    477 			const deUint32	colorA	= 0xff000000 | rgb;
    478 			const deUint32	colorB	= 0xff000000 | ~rgb;
    479 
    480 			tcu::fillWithGrid(m_textures[1]->getLevel(levelNdx, face), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
    481 		}
    482 	}
    483 
    484 	// Upload.
    485 	for (vector<TestTextureCubeSp>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
    486 	{
    487 		m_renderer.addCubeTexture(*i);
    488 	}
    489 
    490 	// Compute cases
    491 	{
    492 		const float refInRangeUpper		= (m_testParameters.compareOp == Sampler::COMPAREMODE_EQUAL || m_testParameters.compareOp == Sampler::COMPAREMODE_NOT_EQUAL) ? 1.0f : 0.5f;
    493 		const float refInRangeLower		= (m_testParameters.compareOp == Sampler::COMPAREMODE_EQUAL || m_testParameters.compareOp == Sampler::COMPAREMODE_NOT_EQUAL) ? 0.0f : 0.5f;
    494 		const float refOutOfBoundsUpper	= 1.1f;
    495 		const float refOutOfBoundsLower	= -0.1f;
    496 
    497 		m_cases.push_back(FilterCase(0,	refInRangeUpper,		tcu::Vec2(-1.25f, -1.2f),	tcu::Vec2(1.2f, 1.25f)));	// minification
    498 		m_cases.push_back(FilterCase(0,	refInRangeLower,		tcu::Vec2(0.8f, 0.8f),		tcu::Vec2(1.25f, 1.20f)));	// magnification
    499 		m_cases.push_back(FilterCase(1,	refInRangeUpper,		tcu::Vec2(-1.19f, -1.3f),	tcu::Vec2(1.1f, 1.35f)));	// minification
    500 		m_cases.push_back(FilterCase(1,	refInRangeLower,		tcu::Vec2(-1.2f, -1.1f),	tcu::Vec2(-0.8f, -0.8f)));	// magnification
    501 		m_cases.push_back(FilterCase(1,	refOutOfBoundsUpper,	tcu::Vec2(-0.61f, -0.1f),	tcu::Vec2(0.9f, 1.18f)));	// reference value clamp, upper
    502 		m_cases.push_back(FilterCase(1,	refOutOfBoundsLower,	tcu::Vec2(-0.75f, 1.0f),	tcu::Vec2(0.05f, 0.75f)));	// reference value clamp, lower
    503 	}
    504 }
    505 
    506 TextureCubeShadowTestInstance::~TextureCubeShadowTestInstance	(void)
    507 {
    508 }
    509 
    510 static const char* getFaceDesc (const tcu::CubeFace face)
    511 {
    512 	switch (face)
    513 	{
    514 		case tcu::CUBEFACE_NEGATIVE_X:	return "-X";
    515 		case tcu::CUBEFACE_POSITIVE_X:	return "+X";
    516 		case tcu::CUBEFACE_NEGATIVE_Y:	return "-Y";
    517 		case tcu::CUBEFACE_POSITIVE_Y:	return "+Y";
    518 		case tcu::CUBEFACE_NEGATIVE_Z:	return "-Z";
    519 		case tcu::CUBEFACE_POSITIVE_Z:	return "+Z";
    520 		default:
    521 			DE_ASSERT(false);
    522 			return DE_NULL;
    523 	}
    524 }
    525 
    526 tcu::TestStatus TextureCubeShadowTestInstance::iterate (void)
    527 {
    528 
    529 	tcu::TestLog&						log				= m_context.getTestContext().getLog();
    530 	const tcu::ScopedLogSection			iterSection		(log, string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
    531 	const FilterCase&					curCase			= m_cases[m_caseNdx];
    532 	const pipeline::TestTextureCube&	texture			= m_renderer.getCubeTexture(curCase.textureIndex);
    533 
    534 	ReferenceParams						sampleParams	(TEXTURETYPE_CUBE);
    535 
    536 	// Params for reference computation.
    537 	sampleParams.sampler					= util::createSampler(Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, m_testParameters.minFilter, m_testParameters.magFilter);
    538 	sampleParams.sampler.seamlessCubeMap	= true;
    539 	sampleParams.sampler.compare			= m_testParameters.compareOp;
    540 	sampleParams.samplerType				= SAMPLERTYPE_SHADOW;
    541 	sampleParams.lodMode					= LODMODE_EXACT;
    542 	sampleParams.ref						= curCase.ref;
    543 
    544 	log	<< TestLog::Message
    545 		<< "Compare reference value = " << sampleParams.ref << "\n"
    546 		<< "Coordinates: " << curCase.bottomLeft << " -> " << curCase.topRight
    547 		<< TestLog::EndMessage;
    548 
    549 	for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
    550 	{
    551 		const tcu::CubeFace		face		= tcu::CubeFace(faceNdx);
    552 		tcu::Surface			result		(m_renderer.getRenderWidth(), m_renderer.getRenderHeight());
    553 		vector<float>			texCoord;
    554 
    555 		computeQuadTexCoordCube(texCoord, face, curCase.bottomLeft, curCase.topRight);
    556 
    557 		log << TestLog::Message << "Face " << getFaceDesc(face) << TestLog::EndMessage;
    558 
    559 		// \todo Log texture coordinates.
    560 
    561 		m_renderer.renderQuad(result, curCase.textureIndex, &texCoord[0], sampleParams);
    562 
    563 		{
    564 			const tcu::PixelFormat		pixelFormat			= getPixelFormat(vk::mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM));
    565 			tcu::LodPrecision			lodPrecision;
    566 			tcu::TexComparePrecision	texComparePrecision;
    567 
    568 			lodPrecision.derivateBits			= 10;
    569 			lodPrecision.lodBits				= 5;
    570 			texComparePrecision.coordBits		= tcu::IVec3(10,10,10);
    571 			texComparePrecision.uvwBits			= tcu::IVec3(6,6,0);
    572 			texComparePrecision.pcfBits			= 5;
    573 			texComparePrecision.referenceBits	= 16;
    574 			texComparePrecision.resultBits		= pixelFormat.redBits-1;
    575 
    576 			const bool isHighQuality = verifyTexCompareResult(m_context.getTestContext(), result.getAccess(), texture.getTexture(),
    577 															  &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    578 
    579 			if (!isHighQuality)
    580 			{
    581 				log << TestLog::Message << "Warning: Verification assuming high-quality PCF filtering failed." << TestLog::EndMessage;
    582 
    583 				lodPrecision.lodBits			= 4;
    584 				texComparePrecision.uvwBits		= tcu::IVec3(4,4,0);
    585 				texComparePrecision.pcfBits		= 0;
    586 
    587 				const bool isOk = verifyTexCompareResult(m_context.getTestContext(), result.getAccess(), texture.getTexture(),
    588 														 &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    589 
    590 				if (!isOk)
    591 				{
    592 					log << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
    593 					return tcu::TestStatus::fail("Image verification failed");
    594 				}
    595 			}
    596 		}
    597 	}
    598 
    599 	m_caseNdx += 1;
    600 	return m_caseNdx < (int)m_cases.size() ? tcu::TestStatus::incomplete() : tcu::TestStatus::pass("Pass");
    601 }
    602 
    603 struct Texture2DArrayShadowTestCaseParameters : public TextureShadowCommonTestCaseParameters, public Texture2DArrayTestCaseParameters
    604 {
    605 };
    606 
    607 class Texture2DArrayShadowTestInstance : public TestInstance
    608 {
    609 public:
    610 	typedef Texture2DArrayShadowTestCaseParameters	ParameterType;
    611 													Texture2DArrayShadowTestInstance		(Context& context, const ParameterType& testParameters);
    612 													~Texture2DArrayShadowTestInstance		(void);
    613 
    614 	virtual tcu::TestStatus							iterate									(void);
    615 
    616 private:
    617 													Texture2DArrayShadowTestInstance		(const Texture2DArrayShadowTestInstance& other);
    618 	Texture2DArrayShadowTestInstance&				operator=								(const Texture2DArrayShadowTestInstance& other);
    619 
    620 	struct FilterCase
    621 	{
    622 		int							textureIndex;
    623 		tcu::Vec3					minCoord;
    624 		tcu::Vec3					maxCoord;
    625 		float						ref;
    626 
    627 		FilterCase (void)
    628 			: textureIndex	(-1)
    629 			, ref			(0.0f)
    630 		{
    631 		}
    632 
    633 		FilterCase (const int tex_, float ref_, const tcu::Vec3& minCoord_, const tcu::Vec3& maxCoord_)
    634 			: textureIndex	(tex_)
    635 			, minCoord		(minCoord_)
    636 			, maxCoord		(maxCoord_)
    637 			, ref			(ref_)
    638 		{
    639 		}
    640 	};
    641 
    642 	const ParameterType&				m_testParameters;
    643 	std::vector<TestTexture2DArraySp>	m_textures;
    644 	std::vector<FilterCase>				m_cases;
    645 
    646 	TextureRenderer						m_renderer;
    647 
    648 	int									m_caseNdx;
    649 };
    650 
    651 Texture2DArrayShadowTestInstance::Texture2DArrayShadowTestInstance (Context& context, const ParameterType& testParameters)
    652 	: TestInstance			(context)
    653 	, m_testParameters		(testParameters)
    654 	, m_renderer			(context, testParameters.sampleCount, TEX2D_VIEWPORT_WIDTH, TEX2D_VIEWPORT_HEIGHT)
    655 	, m_caseNdx				(0)
    656 {
    657 	const int						numLevels	= deLog2Floor32(de::max(m_testParameters.width, m_testParameters.height))+1;
    658 	const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(vk::mapVkFormat(m_testParameters.format));
    659 	const tcu::Vec4					cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
    660 	const tcu::Vec4					cBias		= fmtInfo.valueMin;
    661 
    662 	// Create 2 textures.
    663 	m_textures.reserve(2);
    664 	for (int ndx = 0; ndx < 2; ndx++)
    665 	{
    666 		m_textures.push_back(TestTexture2DArraySp(new pipeline::TestTexture2DArray(vk::mapVkFormat(m_testParameters.format), m_testParameters.width, m_testParameters.height, m_testParameters.numLayers)));
    667 	}
    668 
    669 	// Fill first gradient texture.
    670 	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    671 	{
    672 		const tcu::Vec4 gMin = tcu::Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias;
    673 		const tcu::Vec4 gMax = tcu::Vec4( 1.0f,  1.0f,  1.0f, 0.0f)*cScale + cBias;
    674 
    675 		tcu::fillWithComponentGradients(m_textures[0]->getTexture().getLevel(levelNdx), gMin, gMax);
    676 	}
    677 
    678 	// Fill second with grid texture.
    679 	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    680 	{
    681 		const deUint32	step	= 0x00ffffff / numLevels;
    682 		const deUint32	rgb		= step*levelNdx;
    683 		const deUint32	colorA	= 0xff000000 | rgb;
    684 		const deUint32	colorB	= 0xff000000 | ~rgb;
    685 
    686 		tcu::fillWithGrid(m_textures[1]->getTexture().getLevel(levelNdx), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
    687 	}
    688 
    689 	// Upload.
    690 	for (std::vector<TestTexture2DArraySp>::iterator i = m_textures.begin(); i != m_textures.end(); ++i)
    691 	{
    692 		m_renderer.add2DArrayTexture(*i);
    693 	}
    694 
    695 	// Compute cases.
    696 	{
    697 		const float refInRangeUpper		= (m_testParameters.compareOp == Sampler::COMPAREMODE_EQUAL || m_testParameters.compareOp == Sampler::COMPAREMODE_NOT_EQUAL) ? 1.0f : 0.5f;
    698 		const float refInRangeLower		= (m_testParameters.compareOp == Sampler::COMPAREMODE_EQUAL || m_testParameters.compareOp == Sampler::COMPAREMODE_NOT_EQUAL) ? 0.0f : 0.5f;
    699 		const float refOutOfBoundsUpper	= 1.1f;		// !< lookup function should clamp values to [0, 1] range
    700 		const float refOutOfBoundsLower	= -0.1f;
    701 
    702 		const struct
    703 		{
    704 			const int	texNdx;
    705 			const float	ref;
    706 			const float	lodX;
    707 			const float	lodY;
    708 			const float	oX;
    709 			const float	oY;
    710 		} cases[] =
    711 		{
    712 			{ 0,	refInRangeUpper,		1.6f,	2.9f,	-1.0f,	-2.7f	},
    713 			{ 0,	refInRangeLower,		-2.0f,	-1.35f,	-0.2f,	0.7f	},
    714 			{ 1,	refInRangeUpper,		0.14f,	0.275f,	-1.5f,	-1.1f	},
    715 			{ 1,	refInRangeLower,		-0.92f,	-2.64f,	0.4f,	-0.1f	},
    716 			{ 1,	refOutOfBoundsUpper,	-0.49f,	-0.22f,	0.45f,	0.97f	},
    717 			{ 1,	refOutOfBoundsLower,	-0.85f,	0.75f,	0.25f,	0.61f	},
    718 		};
    719 
    720 		const float	minLayer	= -0.5f;
    721 		const float	maxLayer	= (float)m_testParameters.numLayers;
    722 
    723 		for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
    724 		{
    725 			const int	tex		= cases[caseNdx].texNdx > 0 ? 1 : 0;
    726 			const float	ref		= cases[caseNdx].ref;
    727 			const float	lodX	= cases[caseNdx].lodX;
    728 			const float	lodY	= cases[caseNdx].lodY;
    729 			const float	oX		= cases[caseNdx].oX;
    730 			const float	oY		= cases[caseNdx].oY;
    731 			const float	sX		= deFloatExp2(lodX) * float(m_renderer.getRenderWidth()) / float(m_textures[tex]->getTexture().getWidth());
    732 			const float	sY		= deFloatExp2(lodY) * float(m_renderer.getRenderHeight()) / float(m_textures[tex]->getTexture().getHeight());
    733 
    734 			m_cases.push_back(FilterCase(tex, ref, tcu::Vec3(oX, oY, minLayer), tcu::Vec3(oX+sX, oY+sY, maxLayer)));
    735 		}
    736 	}
    737 }
    738 
    739 Texture2DArrayShadowTestInstance::~Texture2DArrayShadowTestInstance (void)
    740 {
    741 }
    742 
    743 tcu::TestStatus Texture2DArrayShadowTestInstance::iterate (void)
    744 {
    745 	tcu::TestLog&						log				= m_context.getTestContext().getLog();
    746 	const FilterCase&					curCase			= m_cases[m_caseNdx];
    747 	const pipeline::TestTexture2DArray&	texture			= m_renderer.get2DArrayTexture(curCase.textureIndex);
    748 
    749 	ReferenceParams						sampleParams	(TEXTURETYPE_2D_ARRAY);
    750 	tcu::Surface						rendered		(m_renderer.getRenderWidth(), m_renderer.getRenderHeight());
    751 	const tcu::ScopedLogSection			section			(log, string("Test") + de::toString(m_caseNdx), string("Test ") + de::toString(m_caseNdx));
    752 
    753 	const float							texCoord[]		=
    754 	{
    755 		curCase.minCoord.x(), curCase.minCoord.y(), curCase.minCoord.z(),
    756 		curCase.minCoord.x(), curCase.maxCoord.y(), (curCase.minCoord.z() + curCase.maxCoord.z()) / 2.0f,
    757 		curCase.maxCoord.x(), curCase.minCoord.y(), (curCase.minCoord.z() + curCase.maxCoord.z()) / 2.0f,
    758 		curCase.maxCoord.x(), curCase.maxCoord.y(), curCase.maxCoord.z()
    759 	};
    760 
    761 	// Setup params for reference.
    762 	sampleParams.sampler			= util::createSampler(m_testParameters.wrapS, m_testParameters.wrapT, m_testParameters.minFilter, m_testParameters.magFilter);
    763 	sampleParams.sampler.compare	= m_testParameters.compareOp;
    764 	sampleParams.samplerType		= SAMPLERTYPE_SHADOW;
    765 	sampleParams.lodMode			= LODMODE_EXACT;
    766 	sampleParams.ref				= curCase.ref;
    767 
    768 	log	<< TestLog::Message
    769 		<< "Compare reference value = " << sampleParams.ref << "\n"
    770 		<< "Texture coordinates: " << curCase.minCoord << " -> " << curCase.maxCoord
    771 		<< TestLog::EndMessage;
    772 
    773 	m_renderer.renderQuad(rendered, curCase.textureIndex, &texCoord[0], sampleParams);
    774 
    775 	{
    776 		const tcu::PixelFormat		pixelFormat			= getPixelFormat(vk::mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM));
    777 		tcu::LodPrecision			lodPrecision;
    778 		tcu::TexComparePrecision	texComparePrecision;
    779 
    780 		lodPrecision.derivateBits			= 18;
    781 		lodPrecision.lodBits				= 6;
    782 		texComparePrecision.coordBits		= tcu::IVec3(20,20,20);
    783 		texComparePrecision.uvwBits			= tcu::IVec3(7,7,7);
    784 		texComparePrecision.pcfBits			= 5;
    785 		texComparePrecision.referenceBits	= 16;
    786 		texComparePrecision.resultBits		= pixelFormat.redBits-1;
    787 
    788 		const bool isHighQuality = verifyTexCompareResult(m_context.getTestContext(), rendered.getAccess(), texture.getTexture(),
    789 														  &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    790 
    791 		if (!isHighQuality)
    792 		{
    793 			log << TestLog::Message << "Warning: Verification assuming high-quality PCF filtering failed." << TestLog::EndMessage;
    794 
    795 			lodPrecision.lodBits			= 4;
    796 			texComparePrecision.uvwBits		= tcu::IVec3(4,4,4);
    797 			texComparePrecision.pcfBits		= 0;
    798 
    799 			const bool isOk = verifyTexCompareResult(m_context.getTestContext(), rendered.getAccess(), texture.getTexture(),
    800 													 &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
    801 
    802 			if (!isOk)
    803 			{
    804 				log << TestLog::Message << "ERROR: Verification against low precision requirements failed, failing test case." << TestLog::EndMessage;
    805 				return tcu::TestStatus::fail("Image verification failed");
    806 			}
    807 		}
    808 	}
    809 
    810 	m_caseNdx += 1;
    811 	return m_caseNdx < (int)m_cases.size() ? tcu::TestStatus::incomplete() : tcu::TestStatus::pass("Pass");
    812 }
    813 
    814 void populateTextureShadowTests (tcu::TestCaseGroup* textureShadowTests)
    815 {
    816 	tcu::TestContext&				testCtx				= textureShadowTests->getTestContext();
    817 
    818 	static const struct
    819 	{
    820 		const char*		name;
    821 		const VkFormat	format;
    822 	} formats[] =
    823 	{
    824 		{ "d16_unorm",				VK_FORMAT_D16_UNORM				},
    825 		{ "x8_d24_unorm_pack32",	VK_FORMAT_X8_D24_UNORM_PACK32	},
    826 		{ "d32_sfloat",				VK_FORMAT_D32_SFLOAT			},
    827 		{ "d16_unorm_s8_uint",		VK_FORMAT_D16_UNORM_S8_UINT		},
    828 		{ "d24_unorm_s8_uint",		VK_FORMAT_D24_UNORM_S8_UINT		},
    829 		{ "d32_sfloat_s8_uint",		VK_FORMAT_D32_SFLOAT_S8_UINT	}
    830 	};
    831 
    832 	static const struct
    833 	{
    834 		const char*					name;
    835 		const Sampler::FilterMode	minFilter;
    836 		const Sampler::FilterMode	magFilter;
    837 	} filters[] =
    838 	{
    839 		{ "nearest",				Sampler::NEAREST,					Sampler::NEAREST	},
    840 		{ "linear",					Sampler::LINEAR,					Sampler::LINEAR		},
    841 		{ "nearest_mipmap_nearest",	Sampler::NEAREST_MIPMAP_NEAREST,	Sampler::LINEAR		},
    842 		{ "linear_mipmap_nearest",	Sampler::LINEAR_MIPMAP_NEAREST,		Sampler::LINEAR		},
    843 		{ "nearest_mipmap_linear",	Sampler::NEAREST_MIPMAP_LINEAR,		Sampler::LINEAR		},
    844 		{ "linear_mipmap_linear",	Sampler::LINEAR_MIPMAP_LINEAR,		Sampler::LINEAR		}
    845 	};
    846 
    847 	static const struct
    848 	{
    849 		const char*					name;
    850 		const Sampler::CompareMode	op;
    851 	} compareOp[] =
    852 	{
    853 		{ "less_or_equal",		Sampler::COMPAREMODE_LESS_OR_EQUAL		},
    854 		{ "greater_or_equal",	Sampler::COMPAREMODE_GREATER_OR_EQUAL	},
    855 		{ "less",				Sampler::COMPAREMODE_LESS				},
    856 		{ "greater",			Sampler::COMPAREMODE_GREATER			},
    857 		{ "equal",				Sampler::COMPAREMODE_EQUAL				},
    858 		{ "not_equal",			Sampler::COMPAREMODE_NOT_EQUAL			},
    859 		{ "always",				Sampler::COMPAREMODE_ALWAYS				},
    860 		{ "never",				Sampler::COMPAREMODE_NEVER				}
    861 	};
    862 
    863 	// 2D cases.
    864 	{
    865 		de::MovePtr<tcu::TestCaseGroup>	group2D	(new tcu::TestCaseGroup(testCtx, "2d", "2D texture shadow lookup tests"));
    866 
    867 		for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(filters); filterNdx++)
    868 		{
    869 			de::MovePtr<tcu::TestCaseGroup>	filterGroup	(new tcu::TestCaseGroup(testCtx, filters[filterNdx].name, ""));
    870 
    871 			for (int compareNdx = 0; compareNdx < DE_LENGTH_OF_ARRAY(compareOp); compareNdx++)
    872 			{
    873 				for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
    874 				{
    875 					const string						name			= string(compareOp[compareNdx].name) + "_" + formats[formatNdx].name;
    876 					Texture2DShadowTestCaseParameters	testParameters;
    877 
    878 					testParameters.minFilter	= filters[filterNdx].minFilter;
    879 					testParameters.magFilter	= filters[filterNdx].magFilter;
    880 					testParameters.format		= formats[formatNdx].format;
    881 					testParameters.compareOp	= compareOp[compareNdx].op;
    882 					testParameters.wrapS		= Sampler::REPEAT_GL;
    883 					testParameters.wrapT		= Sampler::REPEAT_GL;
    884 					testParameters.width		= 32;
    885 					testParameters.height		= 64;
    886 
    887 					testParameters.programs.push_back(PROGRAM_2D_SHADOW);
    888 
    889 					filterGroup->addChild(new TextureTestCase<Texture2DShadowTestInstance>(testCtx, name.c_str(), "", testParameters));
    890 				}
    891 			}
    892 
    893 			group2D->addChild(filterGroup.release());
    894 		}
    895 
    896 		textureShadowTests->addChild(group2D.release());
    897 	}
    898 
    899 	// Cubemap cases.
    900 	{
    901 		de::MovePtr<tcu::TestCaseGroup>	groupCube	(new tcu::TestCaseGroup(testCtx, "cube", "Cube map texture shadow lookup tests"));
    902 
    903 		for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(filters); filterNdx++)
    904 		{
    905 			de::MovePtr<tcu::TestCaseGroup>	filterGroup	(new tcu::TestCaseGroup(testCtx, filters[filterNdx].name, ""));
    906 
    907 			for (int compareNdx = 0; compareNdx < DE_LENGTH_OF_ARRAY(compareOp); compareNdx++)
    908 			{
    909 				for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
    910 				{
    911 					const string							name			= string(compareOp[compareNdx].name) + "_" + formats[formatNdx].name;
    912 					TextureCubeShadowTestCaseParameters		testParameters;
    913 
    914 					testParameters.minFilter	= filters[filterNdx].minFilter;
    915 					testParameters.magFilter	= filters[filterNdx].magFilter;
    916 					testParameters.format		= formats[formatNdx].format;
    917 					testParameters.compareOp	= compareOp[compareNdx].op;
    918 					testParameters.wrapS		= Sampler::REPEAT_GL;
    919 					testParameters.wrapT		= Sampler::REPEAT_GL;
    920 					testParameters.size			= 32;
    921 
    922 					testParameters.programs.push_back(PROGRAM_CUBE_SHADOW);
    923 
    924 					filterGroup->addChild(new TextureTestCase<TextureCubeShadowTestInstance>(testCtx, name.c_str(), "", testParameters));
    925 				}
    926 			}
    927 
    928 			groupCube->addChild(filterGroup.release());
    929 		}
    930 
    931 		textureShadowTests->addChild(groupCube.release());
    932 	}
    933 
    934 	// 2D array cases.
    935 	{
    936 		de::MovePtr<tcu::TestCaseGroup>	group2DArray	(new tcu::TestCaseGroup(testCtx, "2d_array", "2D texture array shadow lookup tests"));
    937 
    938 		for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(filters); filterNdx++)
    939 		{
    940 			de::MovePtr<tcu::TestCaseGroup>	filterGroup	(new tcu::TestCaseGroup(testCtx, filters[filterNdx].name, ""));
    941 
    942 			for (int compareNdx = 0; compareNdx < DE_LENGTH_OF_ARRAY(compareOp); compareNdx++)
    943 			{
    944 				for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
    945 				{
    946 					const string							name			= string(compareOp[compareNdx].name) + "_" + formats[formatNdx].name;
    947 					Texture2DArrayShadowTestCaseParameters	testParameters;
    948 
    949 					testParameters.minFilter	= filters[filterNdx].minFilter;
    950 					testParameters.magFilter	= filters[filterNdx].magFilter;
    951 					testParameters.format		= formats[formatNdx].format;
    952 					testParameters.compareOp	= compareOp[compareNdx].op;
    953 					testParameters.wrapS		= Sampler::REPEAT_GL;
    954 					testParameters.wrapT		= Sampler::REPEAT_GL;
    955 					testParameters.width		= 32;
    956 					testParameters.height		= 64;
    957 					testParameters.numLayers	= 8;
    958 
    959 					testParameters.programs.push_back(PROGRAM_2D_ARRAY_SHADOW);
    960 
    961 					filterGroup->addChild(new TextureTestCase<Texture2DArrayShadowTestInstance>(testCtx, name.c_str(), "", testParameters));
    962 				}
    963 			}
    964 
    965 			group2DArray->addChild(filterGroup.release());
    966 		}
    967 
    968 		textureShadowTests->addChild(group2DArray.release());
    969 	}
    970 }
    971 
    972 } // anonymous
    973 
    974 tcu::TestCaseGroup* createTextureShadowTests (tcu::TestContext& testCtx)
    975 {
    976 	return createTestGroup(testCtx, "shadow", "Texture shadow tests.", populateTextureShadowTests);
    977 }
    978 
    979 } // texture
    980 } // vkt
    981