Home | History | Annotate | Download | only in shaderrender
      1 /*------------------------------------------------------------------------
      2  * Vulkan Conformance Tests
      3  * ------------------------
      4  *
      5  * Copyright (c) 2016 The Khronos Group Inc.
      6  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
      7  * Copyright (c) 2016 The Android Open Source Project
      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 GLSL textureGather[Offset[s]] tests.
     24  *//*--------------------------------------------------------------------*/
     25 
     26 #include "vktShaderRenderTextureGatherTests.hpp"
     27 #include "vktShaderRender.hpp"
     28 #include "vkImageUtil.hpp"
     29 #include "gluTextureUtil.hpp"
     30 #include "tcuTexture.hpp"
     31 #include "tcuTextureUtil.hpp"
     32 #include "tcuSurface.hpp"
     33 #include "tcuTestLog.hpp"
     34 #include "tcuVectorUtil.hpp"
     35 #include "tcuTexLookupVerifier.hpp"
     36 #include "tcuTexCompareVerifier.hpp"
     37 #include "tcuPixelFormat.hpp"
     38 #include "tcuCommandLine.hpp"
     39 #include "deUniquePtr.hpp"
     40 #include "deStringUtil.hpp"
     41 #include "deRandom.hpp"
     42 
     43 using tcu::ConstPixelBufferAccess;
     44 using tcu::PixelBufferAccess;
     45 using tcu::TestLog;
     46 using tcu::IVec2;
     47 using tcu::IVec3;
     48 using tcu::IVec4;
     49 using tcu::UVec4;
     50 using tcu::Vec2;
     51 using tcu::Vec3;
     52 using tcu::Vec4;
     53 using de::MovePtr;
     54 
     55 using std::string;
     56 using std::vector;
     57 
     58 namespace vkt
     59 {
     60 namespace sr
     61 {
     62 namespace
     63 {
     64 
     65 typedef ShaderRenderCaseInstance::ImageBackingMode ImageBackingMode;
     66 
     67 enum
     68 {
     69 	SPEC_MAX_MIN_OFFSET = -8,
     70 	SPEC_MIN_MAX_OFFSET = 7
     71 };
     72 
     73 enum TextureType
     74 {
     75 	TEXTURETYPE_2D,
     76 	TEXTURETYPE_2D_ARRAY,
     77 	TEXTURETYPE_CUBE,
     78 
     79 	TEXTURETYPE_LAST
     80 };
     81 
     82 // \note TextureTestUtil functions are copied from glsTextureTestUtil
     83 namespace TextureTestUtil
     84 {
     85 
     86 inline tcu::IVec4 getBitsVec (const tcu::PixelFormat& format)
     87 {
     88 	return tcu::IVec4(format.redBits, format.greenBits, format.blueBits, format.alphaBits);
     89 }
     90 
     91 inline tcu::BVec4 getCompareMask (const tcu::PixelFormat& format)
     92 {
     93 	return tcu::BVec4(format.redBits	> 0,
     94 					  format.greenBits	> 0,
     95 					  format.blueBits	> 0,
     96 					  format.alphaBits	> 0);
     97 }
     98 
     99 void computeQuadTexCoord2D (std::vector<float>& dst, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight)
    100 {
    101 	dst.resize(4*2);
    102 
    103 	dst[0] = bottomLeft.x();	dst[1] = bottomLeft.y();
    104 	dst[2] = bottomLeft.x();	dst[3] = topRight.y();
    105 	dst[4] = topRight.x();		dst[5] = bottomLeft.y();
    106 	dst[6] = topRight.x();		dst[7] = topRight.y();
    107 }
    108 
    109 void computeQuadTexCoord2DArray (std::vector<float>& dst, int layerNdx, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight)
    110 {
    111 	dst.resize(4*3);
    112 
    113 	dst[0] = bottomLeft.x();	dst[ 1] = bottomLeft.y();	dst[ 2] = (float)layerNdx;
    114 	dst[3] = bottomLeft.x();	dst[ 4] = topRight.y();		dst[ 5] = (float)layerNdx;
    115 	dst[6] = topRight.x();		dst[ 7] = bottomLeft.y();	dst[ 8] = (float)layerNdx;
    116 	dst[9] = topRight.x();		dst[10] = topRight.y();		dst[11] = (float)layerNdx;
    117 }
    118 
    119 void computeQuadTexCoordCube (std::vector<float>& dst, tcu::CubeFace face, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight)
    120 {
    121 	int		sRow		= 0;
    122 	int		tRow		= 0;
    123 	int		mRow		= 0;
    124 	float	sSign		= 1.0f;
    125 	float	tSign		= 1.0f;
    126 	float	mSign		= 1.0f;
    127 
    128 	switch (face)
    129 	{
    130 		case tcu::CUBEFACE_NEGATIVE_X: mRow = 0; sRow = 2; tRow = 1; mSign = -1.0f;				   tSign = -1.0f;	break;
    131 		case tcu::CUBEFACE_POSITIVE_X: mRow = 0; sRow = 2; tRow = 1;				sSign = -1.0f; tSign = -1.0f;	break;
    132 		case tcu::CUBEFACE_NEGATIVE_Y: mRow = 1; sRow = 0; tRow = 2; mSign = -1.0f;				   tSign = -1.0f;	break;
    133 		case tcu::CUBEFACE_POSITIVE_Y: mRow = 1; sRow = 0; tRow = 2;												break;
    134 		case tcu::CUBEFACE_NEGATIVE_Z: mRow = 2; sRow = 0; tRow = 1; mSign = -1.0f; sSign = -1.0f; tSign = -1.0f;	break;
    135 		case tcu::CUBEFACE_POSITIVE_Z: mRow = 2; sRow = 0; tRow = 1;							   tSign = -1.0f;	break;
    136 		default:
    137 			DE_ASSERT(DE_FALSE);
    138 			return;
    139 	}
    140 
    141 	dst.resize(3*4);
    142 
    143 	dst[0+mRow] = mSign;
    144 	dst[3+mRow] = mSign;
    145 	dst[6+mRow] = mSign;
    146 	dst[9+mRow] = mSign;
    147 
    148 	dst[0+sRow] = sSign * bottomLeft.x();
    149 	dst[3+sRow] = sSign * bottomLeft.x();
    150 	dst[6+sRow] = sSign * topRight.x();
    151 	dst[9+sRow] = sSign * topRight.x();
    152 
    153 	dst[0+tRow] = tSign * bottomLeft.y();
    154 	dst[3+tRow] = tSign * topRight.y();
    155 	dst[6+tRow] = tSign * bottomLeft.y();
    156 	dst[9+tRow] = tSign * topRight.y();
    157 }
    158 
    159 } // TextureTestUtil
    160 
    161 // Round-to-zero int division, because pre-c++11 it's somewhat implementation-defined for negative values.
    162 static inline int divRoundToZero (int a, int b)
    163 {
    164 	return de::abs(a) / de::abs(b) * deSign32(a) * deSign32(b);
    165 }
    166 
    167 static void fillWithRandomColorTiles (const PixelBufferAccess& dst, const Vec4& minVal, const Vec4& maxVal, deUint32 seed)
    168 {
    169 	const int	numCols		= dst.getWidth()  >= 7 ? 7 : dst.getWidth();
    170 	const int	numRows		= dst.getHeight() >= 5 ? 5 : dst.getHeight();
    171 	de::Random	rnd			(seed);
    172 
    173 	for (int slice = 0; slice < dst.getDepth(); slice++)
    174 	for (int row = 0; row < numRows; row++)
    175 	for (int col = 0; col < numCols; col++)
    176 	{
    177 		const int	yBegin	= (row+0)*dst.getHeight()/numRows;
    178 		const int	yEnd	= (row+1)*dst.getHeight()/numRows;
    179 		const int	xBegin	= (col+0)*dst.getWidth()/numCols;
    180 		const int	xEnd	= (col+1)*dst.getWidth()/numCols;
    181 		Vec4		color;
    182 		for (int i = 0; i < 4; i++)
    183 			color[i] = rnd.getFloat(minVal[i], maxVal[i]);
    184 		tcu::clear(tcu::getSubregion(dst, xBegin, yBegin, slice, xEnd-xBegin, yEnd-yBegin, 1), color);
    185 	}
    186 }
    187 
    188 static inline bool isDepthFormat (const tcu::TextureFormat& fmt)
    189 {
    190 	return fmt.order == tcu::TextureFormat::D || fmt.order == tcu::TextureFormat::DS;
    191 }
    192 
    193 static inline bool isUnormFormatType (tcu::TextureFormat::ChannelType type)
    194 {
    195 	return type == tcu::TextureFormat::UNORM_INT8	||
    196 		   type == tcu::TextureFormat::UNORM_INT16	||
    197 		   type == tcu::TextureFormat::UNORM_INT32;
    198 }
    199 
    200 static inline bool isSIntFormatType (tcu::TextureFormat::ChannelType type)
    201 {
    202 	return type == tcu::TextureFormat::SIGNED_INT8	||
    203 		   type == tcu::TextureFormat::SIGNED_INT16	||
    204 		   type == tcu::TextureFormat::SIGNED_INT32;
    205 }
    206 
    207 static inline bool isUIntFormatType (tcu::TextureFormat::ChannelType type)
    208 {
    209 	return type == tcu::TextureFormat::UNSIGNED_INT8	||
    210 		   type == tcu::TextureFormat::UNSIGNED_INT16	||
    211 		   type == tcu::TextureFormat::UNSIGNED_INT32;
    212 }
    213 
    214 enum TextureSwizzleComponent
    215 {
    216 	TEXTURESWIZZLECOMPONENT_R = 0,
    217 	TEXTURESWIZZLECOMPONENT_G,
    218 	TEXTURESWIZZLECOMPONENT_B,
    219 	TEXTURESWIZZLECOMPONENT_A,
    220 	TEXTURESWIZZLECOMPONENT_ZERO,
    221 	TEXTURESWIZZLECOMPONENT_ONE,
    222 
    223 	TEXTURESWIZZLECOMPONENT_LAST
    224 };
    225 
    226 static std::ostream& operator<< (std::ostream& stream, TextureSwizzleComponent comp)
    227 {
    228 	switch (comp)
    229 	{
    230 		case TEXTURESWIZZLECOMPONENT_R:		return stream << "RED";
    231 		case TEXTURESWIZZLECOMPONENT_G:		return stream << "GREEN";
    232 		case TEXTURESWIZZLECOMPONENT_B:		return stream << "BLUE";
    233 		case TEXTURESWIZZLECOMPONENT_A:		return stream << "ALPHA";
    234 		case TEXTURESWIZZLECOMPONENT_ZERO:	return stream << "ZERO";
    235 		case TEXTURESWIZZLECOMPONENT_ONE:	return stream << "ONE";
    236 		default: DE_ASSERT(false); return stream;
    237 	}
    238 }
    239 
    240 struct MaybeTextureSwizzle
    241 {
    242 public:
    243 	static MaybeTextureSwizzle						createNoneTextureSwizzle	(void);
    244 	static MaybeTextureSwizzle						createSomeTextureSwizzle	(void);
    245 
    246 	bool											isSome						(void) const;
    247 	bool											isNone						(void) const;
    248 	bool											isIdentitySwizzle			(void) const;
    249 
    250 	tcu::Vector<TextureSwizzleComponent, 4>&		getSwizzle					(void);
    251 	const tcu::Vector<TextureSwizzleComponent, 4>&	getSwizzle					(void) const;
    252 
    253 private:
    254 													MaybeTextureSwizzle			(void);
    255 
    256 	tcu::Vector<TextureSwizzleComponent, 4>			m_swizzle;
    257 	bool											m_isSome;
    258 };
    259 
    260 static std::ostream& operator<< (std::ostream& stream, const MaybeTextureSwizzle& comp)
    261 {
    262 	if (comp.isNone())
    263 		stream << "[default swizzle state]";
    264 	else
    265 		stream << "(" << comp.getSwizzle()[0]
    266 			   << ", " << comp.getSwizzle()[1]
    267 			   << ", " << comp.getSwizzle()[2]
    268 			   << ", " << comp.getSwizzle()[3]
    269 			   << ")";
    270 
    271 	return stream;
    272 }
    273 
    274 MaybeTextureSwizzle MaybeTextureSwizzle::createNoneTextureSwizzle (void)
    275 {
    276 	MaybeTextureSwizzle swizzle;
    277 
    278 	swizzle.m_swizzle[0] = TEXTURESWIZZLECOMPONENT_LAST;
    279 	swizzle.m_swizzle[1] = TEXTURESWIZZLECOMPONENT_LAST;
    280 	swizzle.m_swizzle[2] = TEXTURESWIZZLECOMPONENT_LAST;
    281 	swizzle.m_swizzle[3] = TEXTURESWIZZLECOMPONENT_LAST;
    282 	swizzle.m_isSome = false;
    283 
    284 	return swizzle;
    285 }
    286 
    287 MaybeTextureSwizzle MaybeTextureSwizzle::createSomeTextureSwizzle (void)
    288 {
    289 	MaybeTextureSwizzle swizzle;
    290 
    291 	swizzle.m_swizzle[0] = TEXTURESWIZZLECOMPONENT_R;
    292 	swizzle.m_swizzle[1] = TEXTURESWIZZLECOMPONENT_G;
    293 	swizzle.m_swizzle[2] = TEXTURESWIZZLECOMPONENT_B;
    294 	swizzle.m_swizzle[3] = TEXTURESWIZZLECOMPONENT_A;
    295 	swizzle.m_isSome = true;
    296 
    297 	return swizzle;
    298 }
    299 
    300 bool MaybeTextureSwizzle::isSome (void) const
    301 {
    302 	return m_isSome;
    303 }
    304 
    305 bool MaybeTextureSwizzle::isNone (void) const
    306 {
    307 	return !m_isSome;
    308 }
    309 
    310 bool MaybeTextureSwizzle::isIdentitySwizzle (void) const
    311 {
    312 	return	m_isSome									&&
    313 			m_swizzle[0] == TEXTURESWIZZLECOMPONENT_R	&&
    314 			m_swizzle[1] == TEXTURESWIZZLECOMPONENT_G	&&
    315 			m_swizzle[2] == TEXTURESWIZZLECOMPONENT_B	&&
    316 			m_swizzle[3] == TEXTURESWIZZLECOMPONENT_A;
    317 }
    318 
    319 tcu::Vector<TextureSwizzleComponent, 4>& MaybeTextureSwizzle::getSwizzle (void)
    320 {
    321 	return m_swizzle;
    322 }
    323 
    324 const tcu::Vector<TextureSwizzleComponent, 4>& MaybeTextureSwizzle::getSwizzle (void) const
    325 {
    326 	return m_swizzle;
    327 }
    328 
    329 MaybeTextureSwizzle::MaybeTextureSwizzle (void)
    330 	: m_swizzle	(TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST)
    331 	, m_isSome	(false)
    332 {
    333 }
    334 
    335 static vk::VkComponentSwizzle getTextureSwizzleComponent (TextureSwizzleComponent c)
    336 {
    337 	switch (c)
    338 	{
    339 		case TEXTURESWIZZLECOMPONENT_R:		return vk::VK_COMPONENT_SWIZZLE_R;
    340 		case TEXTURESWIZZLECOMPONENT_G:		return vk::VK_COMPONENT_SWIZZLE_G;
    341 		case TEXTURESWIZZLECOMPONENT_B:		return vk::VK_COMPONENT_SWIZZLE_B;
    342 		case TEXTURESWIZZLECOMPONENT_A:		return vk::VK_COMPONENT_SWIZZLE_A;
    343 		case TEXTURESWIZZLECOMPONENT_ZERO:	return vk::VK_COMPONENT_SWIZZLE_ZERO;
    344 		case TEXTURESWIZZLECOMPONENT_ONE:	return vk::VK_COMPONENT_SWIZZLE_ONE;
    345 		default: DE_ASSERT(false); return (vk::VkComponentSwizzle)0;
    346 	}
    347 }
    348 
    349 template <typename T>
    350 static inline T swizzleColorChannel (const tcu::Vector<T, 4>& src, TextureSwizzleComponent swizzle)
    351 {
    352 	switch (swizzle)
    353 	{
    354 		case TEXTURESWIZZLECOMPONENT_R:		return src[0];
    355 		case TEXTURESWIZZLECOMPONENT_G:		return src[1];
    356 		case TEXTURESWIZZLECOMPONENT_B:		return src[2];
    357 		case TEXTURESWIZZLECOMPONENT_A:		return src[3];
    358 		case TEXTURESWIZZLECOMPONENT_ZERO:	return (T)0;
    359 		case TEXTURESWIZZLECOMPONENT_ONE:	return (T)1;
    360 		default: DE_ASSERT(false); return (T)-1;
    361 	}
    362 }
    363 
    364 template <typename T>
    365 static inline tcu::Vector<T, 4> swizzleColor (const tcu::Vector<T, 4>& src, const MaybeTextureSwizzle& swizzle)
    366 {
    367 	DE_ASSERT(swizzle.isSome());
    368 
    369 	tcu::Vector<T, 4> result;
    370 	for (int i = 0; i < 4; i++)
    371 		result[i] = swizzleColorChannel(src, swizzle.getSwizzle()[i]);
    372 	return result;
    373 }
    374 
    375 template <typename T>
    376 static void swizzlePixels (const PixelBufferAccess& dst, const ConstPixelBufferAccess& src, const MaybeTextureSwizzle& swizzle)
    377 {
    378 	DE_ASSERT(dst.getWidth()  == src.getWidth()  &&
    379 			  dst.getHeight() == src.getHeight() &&
    380 			  dst.getDepth()  == src.getDepth());
    381 	for (int z = 0; z < src.getDepth(); z++)
    382 	for (int y = 0; y < src.getHeight(); y++)
    383 	for (int x = 0; x < src.getWidth(); x++)
    384 		dst.setPixel(swizzleColor(src.getPixelT<T>(x, y, z), swizzle), x, y, z);
    385 }
    386 
    387 static void swizzlePixels (const PixelBufferAccess& dst, const ConstPixelBufferAccess& src, const MaybeTextureSwizzle& swizzle)
    388 {
    389 	if (isDepthFormat(dst.getFormat()))
    390 		DE_ASSERT(swizzle.isNone() || swizzle.isIdentitySwizzle());
    391 
    392 	if (swizzle.isNone() || swizzle.isIdentitySwizzle())
    393 		tcu::copy(dst, src);
    394 	else if (isUnormFormatType(dst.getFormat().type))
    395 		swizzlePixels<float>(dst, src, swizzle);
    396 	else if (isUIntFormatType(dst.getFormat().type))
    397 		swizzlePixels<deUint32>(dst, src, swizzle);
    398 	else if (isSIntFormatType(dst.getFormat().type))
    399 		swizzlePixels<deInt32>(dst, src, swizzle);
    400 	else
    401 		DE_ASSERT(false);
    402 }
    403 
    404 static void swizzleTexture (tcu::Texture2D& dst, const tcu::Texture2D& src, const MaybeTextureSwizzle& swizzle)
    405 {
    406 	dst = tcu::Texture2D(src.getFormat(), src.getWidth(), src.getHeight());
    407 	for (int levelNdx = 0; levelNdx < src.getNumLevels(); levelNdx++)
    408 	{
    409 		if (src.isLevelEmpty(levelNdx))
    410 			continue;
    411 		dst.allocLevel(levelNdx);
    412 		swizzlePixels(dst.getLevel(levelNdx), src.getLevel(levelNdx), swizzle);
    413 	}
    414 }
    415 
    416 static void swizzleTexture (tcu::Texture2DArray& dst, const tcu::Texture2DArray& src, const MaybeTextureSwizzle& swizzle)
    417 {
    418 	dst = tcu::Texture2DArray(src.getFormat(), src.getWidth(), src.getHeight(), src.getNumLayers());
    419 	for (int levelNdx = 0; levelNdx < src.getNumLevels(); levelNdx++)
    420 	{
    421 		if (src.isLevelEmpty(levelNdx))
    422 			continue;
    423 		dst.allocLevel(levelNdx);
    424 		swizzlePixels(dst.getLevel(levelNdx), src.getLevel(levelNdx), swizzle);
    425 	}
    426 }
    427 
    428 static void swizzleTexture (tcu::TextureCube& dst, const tcu::TextureCube& src, const MaybeTextureSwizzle& swizzle)
    429 {
    430 	dst = tcu::TextureCube(src.getFormat(), src.getSize());
    431 	for (int faceI = 0; faceI < tcu::CUBEFACE_LAST; faceI++)
    432 	{
    433 		const tcu::CubeFace face = (tcu::CubeFace)faceI;
    434 		for (int levelNdx = 0; levelNdx < src.getNumLevels(); levelNdx++)
    435 		{
    436 			if (src.isLevelEmpty(face, levelNdx))
    437 				continue;
    438 			dst.allocLevel(face, levelNdx);
    439 			swizzlePixels(dst.getLevelFace(levelNdx, face), src.getLevelFace(levelNdx, face), swizzle);
    440 		}
    441 	}
    442 }
    443 
    444 static tcu::Texture2DView getOneLevelSubView (const tcu::Texture2DView& view, int level)
    445 {
    446 	return tcu::Texture2DView(1, view.getLevels() + level);
    447 }
    448 
    449 static tcu::Texture2DArrayView getOneLevelSubView (const tcu::Texture2DArrayView& view, int level)
    450 {
    451 	return tcu::Texture2DArrayView(1, view.getLevels() + level);
    452 }
    453 
    454 static tcu::TextureCubeView getOneLevelSubView (const tcu::TextureCubeView& view, int level)
    455 {
    456 	const tcu::ConstPixelBufferAccess* levels[tcu::CUBEFACE_LAST];
    457 
    458 	for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    459 		levels[face] = view.getFaceLevels((tcu::CubeFace)face) + level;
    460 
    461 	return tcu::TextureCubeView(1, levels);
    462 }
    463 
    464 class PixelOffsets
    465 {
    466 public:
    467 	virtual void operator() (const IVec2& pixCoord, IVec2 (&dst)[4]) const = 0;
    468 	virtual ~PixelOffsets (void) {}
    469 };
    470 
    471 class MultiplePixelOffsets : public PixelOffsets
    472 {
    473 public:
    474 	MultiplePixelOffsets (const IVec2& a,
    475 						  const IVec2& b,
    476 						  const IVec2& c,
    477 						  const IVec2& d)
    478 	{
    479 		m_offsets[0] = a;
    480 		m_offsets[1] = b;
    481 		m_offsets[2] = c;
    482 		m_offsets[3] = d;
    483 	}
    484 
    485 	void operator() (const IVec2& /* pixCoord */, IVec2 (&dst)[4]) const
    486 	{
    487 		for (int i = 0; i < DE_LENGTH_OF_ARRAY(dst); i++)
    488 			dst[i] = m_offsets[i];
    489 	}
    490 
    491 private:
    492 	IVec2 m_offsets[4];
    493 };
    494 
    495 class SinglePixelOffsets : public MultiplePixelOffsets
    496 {
    497 public:
    498 	SinglePixelOffsets (const IVec2& offset)
    499 		: MultiplePixelOffsets(offset + IVec2(0, 1),
    500 							   offset + IVec2(1, 1),
    501 							   offset + IVec2(1, 0),
    502 							   offset + IVec2(0, 0))
    503 	{
    504 	}
    505 };
    506 
    507 class DynamicSinglePixelOffsets : public PixelOffsets
    508 {
    509 public:
    510 	DynamicSinglePixelOffsets (const IVec2& offsetRange) : m_offsetRange(offsetRange) {}
    511 
    512 	void operator() (const IVec2& pixCoord, IVec2 (&dst)[4]) const
    513 	{
    514 		const int offsetRangeSize = m_offsetRange.y() - m_offsetRange.x() + 1;
    515 		SinglePixelOffsets(tcu::mod(pixCoord.swizzle(1,0), IVec2(offsetRangeSize)) + m_offsetRange.x())(IVec2(), dst);
    516 	}
    517 
    518 private:
    519 	IVec2 m_offsetRange;
    520 };
    521 
    522 template <typename T>
    523 static inline T triQuadInterpolate (const T (&values)[4], float xFactor, float yFactor)
    524 {
    525 	if (xFactor + yFactor < 1.0f)
    526 		return values[0] + (values[2]-values[0])*xFactor		+ (values[1]-values[0])*yFactor;
    527 	else
    528 		return values[3] + (values[1]-values[3])*(1.0f-xFactor)	+ (values[2]-values[3])*(1.0f-yFactor);
    529 }
    530 
    531 template <int N>
    532 static inline void computeTexCoordVecs (const vector<float>& texCoords, tcu::Vector<float, N> (&dst)[4])
    533 {
    534 	DE_ASSERT((int)texCoords.size() == 4*N);
    535 	for (int i = 0; i < 4; i++)
    536 	for (int j = 0; j < N; j++)
    537 		dst[i][j] = texCoords[i*N + j];
    538 }
    539 
    540 #if defined(DE_DEBUG)
    541 // Whether offsets correspond to the sample offsets used with plain textureGather().
    542 static inline bool isZeroOffsetOffsets (const IVec2 (&offsets)[4])
    543 {
    544 	IVec2 ref[4];
    545 	SinglePixelOffsets(IVec2(0))(IVec2(), ref);
    546 	return std::equal(DE_ARRAY_BEGIN(offsets),
    547 					  DE_ARRAY_END(offsets),
    548 					  DE_ARRAY_BEGIN(ref));
    549 }
    550 #endif
    551 
    552 template <typename ColorScalarType>
    553 static tcu::Vector<ColorScalarType, 4> gatherOffsets (const tcu::Texture2DView& texture, const tcu::Sampler& sampler, const Vec2& coord, int componentNdx, const IVec2 (&offsets)[4])
    554 {
    555 	return texture.gatherOffsets(sampler, coord.x(), coord.y(), componentNdx, offsets).cast<ColorScalarType>();
    556 }
    557 
    558 template <typename ColorScalarType>
    559 static tcu::Vector<ColorScalarType, 4> gatherOffsets (const tcu::Texture2DArrayView& texture, const tcu::Sampler& sampler, const Vec3& coord, int componentNdx, const IVec2 (&offsets)[4])
    560 {
    561 	return texture.gatherOffsets(sampler, coord.x(), coord.y(), coord.z(), componentNdx, offsets).cast<ColorScalarType>();
    562 }
    563 
    564 template <typename ColorScalarType>
    565 static tcu::Vector<ColorScalarType, 4> gatherOffsets (const tcu::TextureCubeView& texture, const tcu::Sampler& sampler, const Vec3& coord, int componentNdx, const IVec2 (&offsets)[4])
    566 {
    567 	DE_ASSERT(isZeroOffsetOffsets(offsets));
    568 	DE_UNREF(offsets);
    569 	return texture.gather(sampler, coord.x(), coord.y(), coord.z(), componentNdx).cast<ColorScalarType>();
    570 }
    571 
    572 static Vec4 gatherOffsetsCompare (const tcu::Texture2DView& texture, const tcu::Sampler& sampler, float refZ, const Vec2& coord, const IVec2 (&offsets)[4])
    573 {
    574 	return texture.gatherOffsetsCompare(sampler, refZ, coord.x(), coord.y(), offsets);
    575 }
    576 
    577 static Vec4 gatherOffsetsCompare (const tcu::Texture2DArrayView& texture, const tcu::Sampler& sampler, float refZ, const Vec3& coord, const IVec2 (&offsets)[4])
    578 {
    579 	return texture.gatherOffsetsCompare(sampler, refZ, coord.x(), coord.y(), coord.z(), offsets);
    580 }
    581 
    582 static Vec4 gatherOffsetsCompare (const tcu::TextureCubeView& texture, const tcu::Sampler& sampler, float refZ, const Vec3& coord, const IVec2 (&offsets)[4])
    583 {
    584 	DE_ASSERT(isZeroOffsetOffsets(offsets));
    585 	DE_UNREF(offsets);
    586 	return texture.gatherCompare(sampler, refZ, coord.x(), coord.y(), coord.z());
    587 }
    588 
    589 template <typename PrecType, typename ColorScalarT>
    590 static bool isGatherOffsetsResultValid (const tcu::TextureCubeView&				texture,
    591 										const tcu::Sampler&						sampler,
    592 										const PrecType&							prec,
    593 										const Vec3&								coord,
    594 										int										componentNdx,
    595 										const IVec2								(&offsets)[4],
    596 										const tcu::Vector<ColorScalarT, 4>&		result)
    597 {
    598 	DE_ASSERT(isZeroOffsetOffsets(offsets));
    599 	DE_UNREF(offsets);
    600 	return tcu::isGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
    601 }
    602 
    603 static bool isGatherOffsetsCompareResultValid (const tcu::TextureCubeView&		texture,
    604 											   const tcu::Sampler&				sampler,
    605 											   const tcu::TexComparePrecision&	prec,
    606 											   const Vec3&						coord,
    607 											   const IVec2						(&offsets)[4],
    608 											   float							cmpReference,
    609 											   const Vec4&						result)
    610 {
    611 	DE_ASSERT(isZeroOffsetOffsets(offsets));
    612 	DE_UNREF(offsets);
    613 	return tcu::isGatherCompareResultValid(texture, sampler, prec, coord, cmpReference, result);
    614 }
    615 
    616 template <typename ColorScalarType, typename PrecType, typename TexViewT, typename TexCoordT>
    617 static bool verifyGatherOffsets (TestLog&						log,
    618 								 const ConstPixelBufferAccess&	result,
    619 								 const TexViewT&				texture,
    620 								 const TexCoordT				(&texCoords)[4],
    621 								 const tcu::Sampler&			sampler,
    622 								 const PrecType&				lookupPrec,
    623 								 int							componentNdx,
    624 								 const PixelOffsets&			getPixelOffsets)
    625 {
    626 	typedef tcu::Vector<ColorScalarType, 4> ColorVec;
    627 
    628 	const int					width			= result.getWidth();
    629 	const int					height			= result.getWidth();
    630 	tcu::TextureLevel			ideal			(result.getFormat(), width, height);
    631 	const PixelBufferAccess		idealAccess		= ideal.getAccess();
    632 	tcu::Surface				errorMask		(width, height);
    633 	bool						success			= true;
    634 
    635 	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toVec());
    636 
    637 	for (int py = 0; py < height; py++)
    638 	for (int px = 0; px < width; px++)
    639 	{
    640 		IVec2		offsets[4];
    641 		getPixelOffsets(IVec2(px, py), offsets);
    642 
    643 		const Vec2			viewportCoord	= (Vec2((float)px, (float)py) + 0.5f) / Vec2((float)width, (float)height);
    644 		const TexCoordT		texCoord		= triQuadInterpolate(texCoords, viewportCoord.x(), viewportCoord.y());
    645 		const ColorVec		resultPix		= result.getPixelT<ColorScalarType>(px, py);
    646 		const ColorVec		idealPix		= gatherOffsets<ColorScalarType>(texture, sampler, texCoord, componentNdx, offsets);
    647 
    648 		idealAccess.setPixel(idealPix, px, py);
    649 
    650 		if (tcu::boolAny(tcu::logicalAnd(lookupPrec.colorMask,
    651 										 tcu::greaterThan(tcu::absDiff(resultPix, idealPix),
    652 														  lookupPrec.colorThreshold.template cast<ColorScalarType>()))))
    653 		{
    654 			if (!isGatherOffsetsResultValid(texture, sampler, lookupPrec, texCoord, componentNdx, offsets, resultPix))
    655 			{
    656 				errorMask.setPixel(px, py, tcu::RGBA::red());
    657 				success = false;
    658 			}
    659 		}
    660 	}
    661 
    662 	log << TestLog::ImageSet("VerifyResult", "Verification result")
    663 		<< TestLog::Image("Rendered", "Rendered image", result);
    664 
    665 	if (!success)
    666 	{
    667 		log << TestLog::Image("Reference", "Ideal reference image", ideal)
    668 			<< TestLog::Image("ErrorMask", "Error mask", errorMask);
    669 	}
    670 
    671 	log << TestLog::EndImageSet;
    672 
    673 	return success;
    674 }
    675 
    676 class PixelCompareRefZ
    677 {
    678 public:
    679 	virtual float operator() (const IVec2& pixCoord) const = 0;
    680 };
    681 
    682 class PixelCompareRefZDefault : public PixelCompareRefZ
    683 {
    684 public:
    685 	PixelCompareRefZDefault (const IVec2& renderSize) : m_renderSize(renderSize) {}
    686 
    687 	float operator() (const IVec2& pixCoord) const
    688 	{
    689 		return ((float)pixCoord.x() + 0.5f) / (float)m_renderSize.x();
    690 	}
    691 
    692 private:
    693 	IVec2 m_renderSize;
    694 };
    695 
    696 template <typename TexViewT, typename TexCoordT>
    697 static bool verifyGatherOffsetsCompare (TestLog&							log,
    698 										const ConstPixelBufferAccess&		result,
    699 										const TexViewT&						texture,
    700 										const TexCoordT						(&texCoords)[4],
    701 										const tcu::Sampler&					sampler,
    702 										const tcu::TexComparePrecision&		compPrec,
    703 										const PixelCompareRefZ&				getPixelRefZ,
    704 										const PixelOffsets&					getPixelOffsets)
    705 {
    706 	const int					width			= result.getWidth();
    707 	const int					height			= result.getWidth();
    708 	tcu::Surface				ideal			(width, height);
    709 	const PixelBufferAccess		idealAccess		= ideal.getAccess();
    710 	tcu::Surface				errorMask		(width, height);
    711 	bool						success			= true;
    712 
    713 	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toVec());
    714 
    715 	for (int py = 0; py < height; py++)
    716 	for (int px = 0; px < width; px++)
    717 	{
    718 		IVec2		offsets[4];
    719 		getPixelOffsets(IVec2(px, py), offsets);
    720 
    721 		const Vec2			viewportCoord	= (Vec2((float)px, (float)py) + 0.5f) / Vec2((float)width, (float)height);
    722 		const TexCoordT		texCoord		= triQuadInterpolate(texCoords, viewportCoord.x(), viewportCoord.y());
    723 		const float			refZ			= getPixelRefZ(IVec2(px, py));
    724 		const Vec4			resultPix		= result.getPixel(px, py);
    725 		const Vec4			idealPix		= gatherOffsetsCompare(texture, sampler, refZ, texCoord, offsets);
    726 
    727 		idealAccess.setPixel(idealPix, px, py);
    728 
    729 		if (!tcu::boolAll(tcu::equal(resultPix, idealPix)))
    730 		{
    731 			if (!isGatherOffsetsCompareResultValid(texture, sampler, compPrec, texCoord, offsets, refZ, resultPix))
    732 			{
    733 				errorMask.setPixel(px, py, tcu::RGBA::red());
    734 				success = false;
    735 			}
    736 		}
    737 	}
    738 
    739 	log << TestLog::ImageSet("VerifyResult", "Verification result")
    740 		<< TestLog::Image("Rendered", "Rendered image", result);
    741 
    742 	if (!success)
    743 	{
    744 		log << TestLog::Image("Reference", "Ideal reference image", ideal)
    745 			<< TestLog::Image("ErrorMask", "Error mask", errorMask);
    746 	}
    747 
    748 	log << TestLog::EndImageSet;
    749 
    750 	return success;
    751 }
    752 
    753 enum GatherType
    754 {
    755 	GATHERTYPE_BASIC = 0,
    756 	GATHERTYPE_OFFSET,
    757 	GATHERTYPE_OFFSET_DYNAMIC,
    758 	GATHERTYPE_OFFSETS,
    759 
    760 	GATHERTYPE_LAST
    761 };
    762 
    763 enum GatherCaseFlags
    764 {
    765 	GATHERCASE_DONT_SAMPLE_CUBE_CORNERS	= (1<<0)	//!< For cube map cases: do not sample cube corners
    766 };
    767 
    768 enum OffsetSize
    769 {
    770 	OFFSETSIZE_NONE = 0,
    771 	OFFSETSIZE_MINIMUM_REQUIRED,
    772 	OFFSETSIZE_IMPLEMENTATION_MAXIMUM,
    773 
    774 	OFFSETSIZE_LAST
    775 };
    776 
    777 static inline const char* gatherTypeName (GatherType type)
    778 {
    779 	switch (type)
    780 	{
    781 		case GATHERTYPE_BASIC:				return "basic";
    782 		case GATHERTYPE_OFFSET:				return "offset";
    783 		case GATHERTYPE_OFFSET_DYNAMIC:		return "offset_dynamic";
    784 		case GATHERTYPE_OFFSETS:			return "offsets";
    785 		default: DE_ASSERT(false); return DE_NULL;
    786 	}
    787 }
    788 
    789 static inline const char* gatherTypeDescription (GatherType type)
    790 {
    791 	switch (type)
    792 	{
    793 		case GATHERTYPE_BASIC:				return "textureGather";
    794 		case GATHERTYPE_OFFSET:				return "textureGatherOffset";
    795 		case GATHERTYPE_OFFSET_DYNAMIC:		return "textureGatherOffset with dynamic offsets";
    796 		case GATHERTYPE_OFFSETS:			return "textureGatherOffsets";
    797 		default: DE_ASSERT(false); return DE_NULL;
    798 	}
    799 }
    800 
    801 static inline bool requireGpuShader5 (GatherType gatherType, OffsetSize offsetSize)
    802 {
    803 	return gatherType == GATHERTYPE_OFFSET_DYNAMIC || gatherType == GATHERTYPE_OFFSETS
    804 		|| offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM; // \note Implementation limits are not available while generating the shaders, they are passed dynamically at runtime
    805 }
    806 
    807 struct GatherArgs
    808 {
    809 	int		componentNdx;	// If negative, implicit component index 0 is used (i.e. the parameter is not given).
    810 	IVec2	offsets[4];		// \note Unless GATHERTYPE_OFFSETS is used, only offsets[0] is relevant; also, for GATHERTYPE_OFFSET_DYNAMIC, none are relevant.
    811 
    812 	GatherArgs (void)
    813 		: componentNdx(-1)
    814 	{
    815 		std::fill(DE_ARRAY_BEGIN(offsets), DE_ARRAY_END(offsets), IVec2());
    816 	}
    817 
    818 	GatherArgs (int comp,
    819 				const IVec2& off0 = IVec2(),
    820 				const IVec2& off1 = IVec2(),
    821 				const IVec2& off2 = IVec2(),
    822 				const IVec2& off3 = IVec2())
    823 		: componentNdx(comp)
    824 	{
    825 		offsets[0] = off0;
    826 		offsets[1] = off1;
    827 		offsets[2] = off2;
    828 		offsets[3] = off3;
    829 	}
    830 };
    831 
    832 static MovePtr<PixelOffsets> makePixelOffsetsFunctor (GatherType gatherType, const GatherArgs& gatherArgs, const IVec2& offsetRange)
    833 {
    834 	if (gatherType == GATHERTYPE_BASIC || gatherType == GATHERTYPE_OFFSET)
    835 	{
    836 		const IVec2 offset = gatherType == GATHERTYPE_BASIC ? IVec2(0) : gatherArgs.offsets[0];
    837 		return MovePtr<PixelOffsets>(new SinglePixelOffsets(offset));
    838 	}
    839 	else if (gatherType == GATHERTYPE_OFFSET_DYNAMIC)
    840 	{
    841 		return MovePtr<PixelOffsets>(new DynamicSinglePixelOffsets(offsetRange));
    842 	}
    843 	else if (gatherType == GATHERTYPE_OFFSETS)
    844 		return MovePtr<PixelOffsets>(new MultiplePixelOffsets(gatherArgs.offsets[0],
    845 															  gatherArgs.offsets[1],
    846 															  gatherArgs.offsets[2],
    847 															  gatherArgs.offsets[3]));
    848 	else
    849 	{
    850 		DE_ASSERT(false);
    851 		return MovePtr<PixelOffsets>(DE_NULL);
    852 	}
    853 }
    854 
    855 static inline glu::DataType getSamplerType (TextureType textureType, const tcu::TextureFormat& format)
    856 {
    857 	if (isDepthFormat(format))
    858 	{
    859 		switch (textureType)
    860 		{
    861 			case TEXTURETYPE_2D:		return glu::TYPE_SAMPLER_2D_SHADOW;
    862 			case TEXTURETYPE_2D_ARRAY:	return glu::TYPE_SAMPLER_2D_ARRAY_SHADOW;
    863 			case TEXTURETYPE_CUBE:		return glu::TYPE_SAMPLER_CUBE_SHADOW;
    864 			default: DE_ASSERT(false); return glu::TYPE_LAST;
    865 		}
    866 	}
    867 	else
    868 	{
    869 		switch (textureType)
    870 		{
    871 			case TEXTURETYPE_2D:		return glu::getSampler2DType(format);
    872 			case TEXTURETYPE_2D_ARRAY:	return glu::getSampler2DArrayType(format);
    873 			case TEXTURETYPE_CUBE:		return glu::getSamplerCubeType(format);
    874 			default: DE_ASSERT(false); return glu::TYPE_LAST;
    875 		}
    876 	}
    877 }
    878 
    879 static inline glu::DataType getSamplerGatherResultType (glu::DataType samplerType)
    880 {
    881 	switch (samplerType)
    882 	{
    883 		case glu::TYPE_SAMPLER_2D_SHADOW:
    884 		case glu::TYPE_SAMPLER_2D_ARRAY_SHADOW:
    885 		case glu::TYPE_SAMPLER_CUBE_SHADOW:
    886 		case glu::TYPE_SAMPLER_2D:
    887 		case glu::TYPE_SAMPLER_2D_ARRAY:
    888 		case glu::TYPE_SAMPLER_CUBE:
    889 			return glu::TYPE_FLOAT_VEC4;
    890 
    891 		case glu::TYPE_INT_SAMPLER_2D:
    892 		case glu::TYPE_INT_SAMPLER_2D_ARRAY:
    893 		case glu::TYPE_INT_SAMPLER_CUBE:
    894 			return glu::TYPE_INT_VEC4;
    895 
    896 		case glu::TYPE_UINT_SAMPLER_2D:
    897 		case glu::TYPE_UINT_SAMPLER_2D_ARRAY:
    898 		case glu::TYPE_UINT_SAMPLER_CUBE:
    899 			return glu::TYPE_UINT_VEC4;
    900 
    901 		default:
    902 			DE_ASSERT(false);
    903 			return glu::TYPE_LAST;
    904 	}
    905 }
    906 
    907 static inline int getNumTextureSamplingDimensions (TextureType type)
    908 {
    909 	switch (type)
    910 	{
    911 		case TEXTURETYPE_2D:		return 2;
    912 		case TEXTURETYPE_2D_ARRAY:	return 3;
    913 		case TEXTURETYPE_CUBE:		return 3;
    914 		default: DE_ASSERT(false); return -1;
    915 	}
    916 }
    917 
    918 vector<GatherArgs> generateBasic2DCaseIterations (GatherType gatherType, const tcu::TextureFormat& textureFormat, const IVec2& offsetRange)
    919 {
    920 	const int			numComponentCases	= isDepthFormat(textureFormat) ? 1 : 4+1; // \note For non-depth textures, test explicit components 0 to 3 and implicit component 0.
    921 	vector<GatherArgs>	result;
    922 
    923 	for (int componentCaseNdx = 0; componentCaseNdx < numComponentCases; componentCaseNdx++)
    924 	{
    925 		const int componentNdx = componentCaseNdx - 1;
    926 
    927 		switch (gatherType)
    928 		{
    929 			case GATHERTYPE_BASIC:
    930 				result.push_back(GatherArgs(componentNdx));
    931 				break;
    932 
    933 			case GATHERTYPE_OFFSET:
    934 			{
    935 				const int min	= offsetRange.x();
    936 				const int max	= offsetRange.y();
    937 				const int hmin	= divRoundToZero(min, 2);
    938 				const int hmax	= divRoundToZero(max, 2);
    939 
    940 				result.push_back(GatherArgs(componentNdx, IVec2(min, max)));
    941 
    942 				if (componentCaseNdx == 0) // Don't test all offsets variants for all color components (they should be pretty orthogonal).
    943 				{
    944 					result.push_back(GatherArgs(componentNdx, IVec2(min,	min)));
    945 					result.push_back(GatherArgs(componentNdx, IVec2(max,	min)));
    946 					result.push_back(GatherArgs(componentNdx, IVec2(max,	max)));
    947 
    948 					result.push_back(GatherArgs(componentNdx, IVec2(0,		hmax)));
    949 					result.push_back(GatherArgs(componentNdx, IVec2(hmin,	0)));
    950 					result.push_back(GatherArgs(componentNdx, IVec2(0,		0)));
    951 				}
    952 
    953 				break;
    954 			}
    955 
    956 			case GATHERTYPE_OFFSET_DYNAMIC:
    957 				result.push_back(GatherArgs(componentNdx));
    958 				break;
    959 
    960 			case GATHERTYPE_OFFSETS:
    961 			{
    962 				const int min	= offsetRange.x();
    963 				const int max	= offsetRange.y();
    964 				const int hmin	= divRoundToZero(min, 2);
    965 				const int hmax	= divRoundToZero(max, 2);
    966 
    967 				result.push_back(GatherArgs(componentNdx,
    968 											IVec2(min,	min),
    969 											IVec2(min,	max),
    970 											IVec2(max,	min),
    971 											IVec2(max,	max)));
    972 
    973 				if (componentCaseNdx == 0) // Don't test all offsets variants for all color components (they should be pretty orthogonal).
    974 					result.push_back(GatherArgs(componentNdx,
    975 												IVec2(min,	hmax),
    976 												IVec2(hmin,	max),
    977 												IVec2(0,	hmax),
    978 												IVec2(hmax,	0)));
    979 				break;
    980 			}
    981 
    982 			default:
    983 				DE_ASSERT(false);
    984 		}
    985 	}
    986 
    987 	return result;
    988 }
    989 
    990 struct GatherCaseBaseParams
    991 {
    992 	GatherType					gatherType;
    993 	OffsetSize					offsetSize;
    994 	tcu::TextureFormat			textureFormat;
    995 	tcu::Sampler::CompareMode	shadowCompareMode;
    996 	tcu::Sampler::WrapMode		wrapS;
    997 	tcu::Sampler::WrapMode		wrapT;
    998 	MaybeTextureSwizzle			textureSwizzle;
    999 	tcu::Sampler::FilterMode	minFilter;
   1000 	tcu::Sampler::FilterMode	magFilter;
   1001 	int							baseLevel;
   1002 	deUint32					flags;
   1003 	TextureType					textureType;
   1004 	ImageBackingMode			sparseCase;
   1005 
   1006 	GatherCaseBaseParams (const TextureType					textureType_,
   1007 						  const GatherType					gatherType_,
   1008 						  const OffsetSize					offsetSize_,
   1009 						  const tcu::TextureFormat			textureFormat_,
   1010 						  const tcu::Sampler::CompareMode	shadowCompareMode_,
   1011 						  const tcu::Sampler::WrapMode		wrapS_,
   1012 						  const tcu::Sampler::WrapMode		wrapT_,
   1013 						  const MaybeTextureSwizzle&		textureSwizzle_,
   1014 						  const tcu::Sampler::FilterMode	minFilter_,
   1015 						  const tcu::Sampler::FilterMode	magFilter_,
   1016 						  const int							baseLevel_,
   1017 						  const deUint32					flags_,
   1018 						  const ImageBackingMode			sparseCase_)
   1019 		: gatherType			(gatherType_)
   1020 		, offsetSize			(offsetSize_)
   1021 		, textureFormat			(textureFormat_)
   1022 		, shadowCompareMode		(shadowCompareMode_)
   1023 		, wrapS					(wrapS_)
   1024 		, wrapT					(wrapT_)
   1025 		, textureSwizzle		(textureSwizzle_)
   1026 		, minFilter				(minFilter_)
   1027 		, magFilter				(magFilter_)
   1028 		, baseLevel				(baseLevel_)
   1029 		, flags					(flags_)
   1030 		, textureType			(textureType_)
   1031 		, sparseCase			(sparseCase_)
   1032 	{}
   1033 
   1034 	GatherCaseBaseParams (void)
   1035 		: gatherType			(GATHERTYPE_LAST)
   1036 		, offsetSize			(OFFSETSIZE_LAST)
   1037 		, textureFormat			()
   1038 		, shadowCompareMode		(tcu::Sampler::COMPAREMODE_LAST)
   1039 		, wrapS					(tcu::Sampler::WRAPMODE_LAST)
   1040 		, wrapT					(tcu::Sampler::WRAPMODE_LAST)
   1041 		, textureSwizzle		(MaybeTextureSwizzle::createNoneTextureSwizzle())
   1042 		, minFilter				(tcu::Sampler::FILTERMODE_LAST)
   1043 		, magFilter				(tcu::Sampler::FILTERMODE_LAST)
   1044 		, baseLevel				(0)
   1045 		, flags					(0)
   1046 		, textureType			(TEXTURETYPE_LAST)
   1047 		, sparseCase			(ShaderRenderCaseInstance::IMAGE_BACKING_MODE_REGULAR)
   1048 	{}
   1049 };
   1050 
   1051 IVec2 getOffsetRange (const OffsetSize offsetSize, const vk::VkPhysicalDeviceLimits& deviceLimits)
   1052 {
   1053 	switch (offsetSize)
   1054 	{
   1055 		case OFFSETSIZE_NONE:
   1056 			return IVec2(0);
   1057 
   1058 		case OFFSETSIZE_MINIMUM_REQUIRED:
   1059 			// \note Defined by spec.
   1060 			return IVec2(SPEC_MAX_MIN_OFFSET,
   1061 						 SPEC_MIN_MAX_OFFSET);
   1062 
   1063 		case OFFSETSIZE_IMPLEMENTATION_MAXIMUM:
   1064 			return IVec2(deviceLimits.minTexelGatherOffset, deviceLimits.maxTexelGatherOffset);
   1065 
   1066 		default:
   1067 			DE_ASSERT(false);
   1068 			return IVec2(-1);
   1069 	}
   1070 }
   1071 
   1072 IVec2 getOffsetRange (const OffsetSize offsetSize)
   1073 {
   1074 	switch (offsetSize)
   1075 	{
   1076 		case OFFSETSIZE_NONE:
   1077 			return IVec2(0);
   1078 
   1079 		case OFFSETSIZE_MINIMUM_REQUIRED:
   1080 			// \note Defined by spec.
   1081 			return IVec2(SPEC_MAX_MIN_OFFSET,
   1082 						 SPEC_MIN_MAX_OFFSET);
   1083 
   1084 		case OFFSETSIZE_IMPLEMENTATION_MAXIMUM:
   1085 			DE_FATAL("Not known");
   1086 			return IVec2(-1);
   1087 
   1088 		default:
   1089 			DE_ASSERT(false);
   1090 			return IVec2(-1);
   1091 	}
   1092 }
   1093 
   1094 class TextureGatherInstance : public ShaderRenderCaseInstance
   1095 {
   1096 public:
   1097 										TextureGatherInstance		(Context&						context,
   1098 																	 const GatherCaseBaseParams&	baseParams);
   1099 	virtual								~TextureGatherInstance		(void);
   1100 
   1101 	virtual tcu::TestStatus				iterate						(void);
   1102 
   1103 protected:
   1104 	void								init						(void);
   1105 
   1106 	virtual int							getNumIterations			(void) const = 0;
   1107 	virtual GatherArgs					getGatherArgs				(int iterationNdx) const = 0;
   1108 
   1109 	virtual void						setupDefaultInputs			(void);
   1110 	virtual void						setupUniforms				(const tcu::Vec4&);
   1111 
   1112 	template <typename TexViewT, typename TexCoordT>
   1113 	bool								verify						(const ConstPixelBufferAccess&		rendered,
   1114 																	 const TexViewT&					texture,
   1115 																	 const TexCoordT					(&bottomLeft)[4],
   1116 																	 const GatherArgs&					gatherArgs) const;
   1117 
   1118 	virtual TextureBindingSp			createTexture				(void) = 0;
   1119 	virtual vector<float>				computeQuadTexCoord			(int iterationNdx) const = 0;
   1120 	virtual bool						verify						(int iterationNdx, const ConstPixelBufferAccess& rendered) const = 0;
   1121 
   1122 protected:
   1123 	static const IVec2					RENDER_SIZE;
   1124 
   1125 	const GatherCaseBaseParams			m_baseParams;
   1126 
   1127 private:
   1128 	const tcu::TextureFormat			m_colorBufferFormat;
   1129 	int									m_currentIteration;
   1130 };
   1131 
   1132 const IVec2 TextureGatherInstance::RENDER_SIZE = IVec2(64, 64);
   1133 
   1134 TextureGatherInstance::TextureGatherInstance (Context&						context,
   1135 											  const GatherCaseBaseParams&	baseParams)
   1136 	: ShaderRenderCaseInstance	(context, false, DE_NULL, DE_NULL, DE_NULL, baseParams.sparseCase)
   1137 	, m_baseParams				(baseParams)
   1138 	, m_colorBufferFormat		(tcu::TextureFormat(tcu::TextureFormat::RGBA,
   1139 													isDepthFormat(baseParams.textureFormat) ? tcu::TextureFormat::UNORM_INT8 : baseParams.textureFormat.type))
   1140 	, m_currentIteration		(0)
   1141 {
   1142 	DE_ASSERT((m_baseParams.gatherType == GATHERTYPE_BASIC) == (m_baseParams.offsetSize == OFFSETSIZE_NONE));
   1143 	DE_ASSERT((m_baseParams.shadowCompareMode != tcu::Sampler::COMPAREMODE_NONE) == isDepthFormat(m_baseParams.textureFormat));
   1144 	DE_ASSERT(isUnormFormatType(m_colorBufferFormat.type)						||
   1145 			  m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT8		||
   1146 			  m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT16	||
   1147 			  m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT8		||
   1148 			  m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT16);
   1149 	DE_ASSERT(glu::isGLInternalColorFormatFilterable(glu::getInternalFormat(m_colorBufferFormat)) ||
   1150 			  (m_baseParams.magFilter == tcu::Sampler::NEAREST && (m_baseParams.minFilter == tcu::Sampler::NEAREST || m_baseParams.minFilter == tcu::Sampler::NEAREST_MIPMAP_NEAREST)));
   1151 	DE_ASSERT(m_baseParams.textureType == TEXTURETYPE_CUBE || !(m_baseParams.flags & GATHERCASE_DONT_SAMPLE_CUBE_CORNERS));
   1152 
   1153 	m_renderSize				= RENDER_SIZE.asUint();
   1154 	m_colorFormat				= vk::mapTextureFormat(m_colorBufferFormat);
   1155 }
   1156 
   1157 TextureGatherInstance::~TextureGatherInstance (void)
   1158 {
   1159 }
   1160 
   1161 void TextureGatherInstance::init (void)
   1162 {
   1163 	TestLog&						log					= m_context.getTestContext().getLog();
   1164 	TextureBindingSp				textureBinding;
   1165 	TextureBinding::Parameters		textureParams;
   1166 
   1167 	// Check prerequisites.
   1168 	if (requireGpuShader5(m_baseParams.gatherType, m_baseParams.offsetSize))
   1169 	{
   1170 		const vk::VkPhysicalDeviceFeatures&		deviceFeatures	= m_context.getDeviceFeatures();
   1171 		if (!deviceFeatures.shaderImageGatherExtended)
   1172 			TCU_THROW(NotSupportedError, "Extended set of image gather instructions are not supported");
   1173 	}
   1174 
   1175 	// Log and check implementation offset limits, if appropriate.
   1176 	if (m_baseParams.offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1177 	{
   1178 		const IVec2		offsetRange		= getOffsetRange(m_baseParams.offsetSize, m_context.getDeviceProperties().limits);
   1179 		log << TestLog::Integer("ImplementationMinTextureGatherOffset", "Implementation's value for minTexelGatherOffset", "", QP_KEY_TAG_NONE, offsetRange[0])
   1180 			<< TestLog::Integer("ImplementationMaxTextureGatherOffset", "Implementation's value for maxTexelGatherOffset", "", QP_KEY_TAG_NONE, offsetRange[1]);
   1181 		TCU_CHECK_MSG(offsetRange[0] <= SPEC_MAX_MIN_OFFSET, ("minTexelGatherOffset must be at most " + de::toString((int)SPEC_MAX_MIN_OFFSET)).c_str());
   1182 		TCU_CHECK_MSG(offsetRange[1] >= SPEC_MIN_MAX_OFFSET, ("maxTexelGatherOffset must be at least " + de::toString((int)SPEC_MIN_MAX_OFFSET)).c_str());
   1183 	}
   1184 
   1185 	// Initialize texture.
   1186 
   1187 	textureBinding = createTexture();
   1188 
   1189 	if (m_baseParams.textureSwizzle.isSome())
   1190 	{
   1191 		const tcu::Vector<TextureSwizzleComponent, 4>&	swizzle		= m_baseParams.textureSwizzle.getSwizzle();
   1192 
   1193 		const vk::VkComponentMapping					components	=
   1194 		{
   1195 			getTextureSwizzleComponent(swizzle[0]),
   1196 			getTextureSwizzleComponent(swizzle[1]),
   1197 			getTextureSwizzleComponent(swizzle[2]),
   1198 			getTextureSwizzleComponent(swizzle[3])
   1199 		};
   1200 
   1201 		textureParams.componentMapping = components;
   1202 	}
   1203 
   1204 	if (m_baseParams.baseLevel != 0)
   1205 		textureParams.baseMipLevel = m_baseParams.baseLevel;
   1206 
   1207 	textureBinding->setParameters(textureParams);
   1208 	m_textures.push_back(textureBinding);
   1209 
   1210 	log << TestLog::Message << "Texture base level is " << m_baseParams.baseLevel << TestLog::EndMessage
   1211 		<< TestLog::Message << "s and t wrap modes are "
   1212 							<< vk::mapWrapMode(m_baseParams.wrapS) << " and "
   1213 							<< vk::mapWrapMode(m_baseParams.wrapT) << ", respectively" << TestLog::EndMessage
   1214 		<< TestLog::Message << "Minification and magnification filter modes are "
   1215 							<< vk::mapFilterMode(m_baseParams.minFilter) << " and "
   1216 							<< vk::mapFilterMode(m_baseParams.magFilter) << ", respectively "
   1217 							<< "(note that they should have no effect on gather result)"
   1218 							<< TestLog::EndMessage
   1219 		<< TestLog::Message << "Using texture swizzle " << m_baseParams.textureSwizzle << TestLog::EndMessage;
   1220 
   1221 	if (m_baseParams.shadowCompareMode != tcu::Sampler::COMPAREMODE_NONE)
   1222 		log << TestLog::Message << "Using texture compare func " << vk::mapCompareMode(m_baseParams.shadowCompareMode) << TestLog::EndMessage;
   1223 }
   1224 
   1225 void TextureGatherInstance::setupDefaultInputs (void)
   1226 {
   1227 	const int				numVertices						= 4;
   1228 	const float				position[4*2]					=
   1229 	{
   1230 		-1.0f, -1.0f,
   1231 		-1.0f, +1.0f,
   1232 		+1.0f, -1.0f,
   1233 		+1.0f, +1.0f,
   1234 	};
   1235 	const float				normalizedCoord[4*2]			=
   1236 	{
   1237 		0.0f, 0.0f,
   1238 		0.0f, 1.0f,
   1239 		1.0f, 0.0f,
   1240 		1.0f, 1.0f,
   1241 	};
   1242 	const vector<float>		texCoord						= computeQuadTexCoord(m_currentIteration);
   1243 	const bool				needNormalizedCoordInShader		= m_baseParams.gatherType == GATHERTYPE_OFFSET_DYNAMIC || isDepthFormat(m_baseParams.textureFormat);
   1244 
   1245 	addAttribute(0u, vk::VK_FORMAT_R32G32_SFLOAT, 2 * (deUint32)sizeof(float), numVertices, position);
   1246 
   1247 	if (texCoord.size() == 2*4)
   1248 		addAttribute(1u, vk::VK_FORMAT_R32G32_SFLOAT, 2 * (deUint32)sizeof(float), numVertices, texCoord.data());
   1249 	else if (texCoord.size() == 3*4)
   1250 		addAttribute(1u, vk::VK_FORMAT_R32G32B32_SFLOAT, 3 * (deUint32)sizeof(float), numVertices, texCoord.data());
   1251 	else
   1252 		DE_ASSERT(false);
   1253 
   1254 	if (needNormalizedCoordInShader)
   1255 		addAttribute(2u, vk::VK_FORMAT_R32G32_SFLOAT, 2 * (deUint32)sizeof(float), numVertices, normalizedCoord);
   1256 }
   1257 
   1258 tcu::TestStatus TextureGatherInstance::iterate (void)
   1259 {
   1260 	TestLog&						log						= m_context.getTestContext().getLog();
   1261 	const tcu::ScopedLogSection		iterationSection		(log, "Iteration" + de::toString(m_currentIteration), "Iteration " + de::toString(m_currentIteration));
   1262 
   1263 	// Render.
   1264 
   1265 	{
   1266 		const deUint32				numVertices		= 4;
   1267 		const deUint32				numTriangles	= 2;
   1268 		const deUint16				indices[6]		= { 0, 1, 2, 2, 1, 3 };
   1269 		const vector<float>			texCoord		= computeQuadTexCoord(m_currentIteration);
   1270 
   1271 		if (texCoord.size() == 2*4)
   1272 		{
   1273 			Vec2 texCoordVec[4];
   1274 			computeTexCoordVecs(texCoord, texCoordVec);
   1275 			log << TestLog::Message << "Texture coordinates run from " << texCoordVec[0] << " to " << texCoordVec[3] << TestLog::EndMessage;
   1276 		}
   1277 		else if (texCoord.size() == 3*4)
   1278 		{
   1279 			Vec3 texCoordVec[4];
   1280 			computeTexCoordVecs(texCoord, texCoordVec);
   1281 			log << TestLog::Message << "Texture coordinates run from " << texCoordVec[0] << " to " << texCoordVec[3] << TestLog::EndMessage;
   1282 		}
   1283 		else
   1284 			DE_ASSERT(false);
   1285 
   1286 		m_vertexShaderName		= "vert";
   1287 		m_fragmentShaderName	= "frag_" + de::toString(m_currentIteration);
   1288 
   1289 		setup();
   1290 
   1291 		render(numVertices, numTriangles, indices);
   1292 	}
   1293 
   1294 	// Verify result.
   1295 
   1296 	if (!verify(m_currentIteration, getResultImage().getAccess()))
   1297 		return tcu::TestStatus::fail("Result verification failed");
   1298 
   1299 	m_currentIteration++;
   1300 	if (m_currentIteration == getNumIterations())
   1301 		return tcu::TestStatus::pass("Pass");
   1302 	else
   1303 		return tcu::TestStatus::incomplete();
   1304 }
   1305 
   1306 void TextureGatherInstance::setupUniforms (const tcu::Vec4&)
   1307 {
   1308 	deUint32	binding		= 0;
   1309 
   1310 	useSampler(binding++, 0u);
   1311 
   1312 	if (m_baseParams.gatherType == GATHERTYPE_OFFSET_DYNAMIC)
   1313 		addUniform(binding++, vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, sizeof(tcu::Vec2), RENDER_SIZE.asFloat().getPtr());
   1314 
   1315 	if (m_baseParams.offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1316 	{
   1317 		if (m_baseParams.gatherType == GATHERTYPE_OFFSET)
   1318 		{
   1319 			const GatherArgs&	gatherArgs		= getGatherArgs(m_currentIteration);
   1320 			addUniform(binding++, vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, sizeof(tcu::IVec2), gatherArgs.offsets[0].getPtr());
   1321 		}
   1322 		else if (m_baseParams.gatherType == GATHERTYPE_OFFSET_DYNAMIC)
   1323 		{
   1324 			const IVec2&		offsetRange		= getOffsetRange(m_baseParams.offsetSize, m_context.getDeviceProperties().limits);
   1325 			addUniform(binding++, vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, sizeof(tcu::IVec2), offsetRange.getPtr());
   1326 		}
   1327 		else
   1328 			DE_ASSERT(false);
   1329 	}
   1330 }
   1331 
   1332 template <typename TexViewT, typename TexCoordT>
   1333 bool TextureGatherInstance::verify (const ConstPixelBufferAccess&	rendered,
   1334 								const TexViewT&					texture,
   1335 								const TexCoordT					(&texCoords)[4],
   1336 								const GatherArgs&				gatherArgs) const
   1337 {
   1338 	TestLog& log = m_context.getTestContext().getLog();
   1339 
   1340 	{
   1341 		DE_ASSERT(m_colorBufferFormat.order == tcu::TextureFormat::RGBA);
   1342 		DE_ASSERT(m_colorBufferFormat.type == tcu::TextureFormat::UNORM_INT8		||
   1343 				  m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT8		||
   1344 				  m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT8);
   1345 
   1346 		const MovePtr<PixelOffsets>		pixelOffsets	= makePixelOffsetsFunctor(m_baseParams.gatherType, gatherArgs, getOffsetRange(m_baseParams.offsetSize, m_context.getDeviceProperties().limits));
   1347 		const tcu::PixelFormat			pixelFormat		= tcu::PixelFormat(8,8,8,8);
   1348 		const IVec4						colorBits		= tcu::max(TextureTestUtil::getBitsVec(pixelFormat) - 1, tcu::IVec4(0));
   1349 		const IVec3						coordBits		= m_baseParams.textureType == TEXTURETYPE_2D			? IVec3(20,20,0)
   1350 														: m_baseParams.textureType == TEXTURETYPE_CUBE			? IVec3(10,10,10)
   1351 														: m_baseParams.textureType == TEXTURETYPE_2D_ARRAY		? IVec3(20,20,20)
   1352 														: IVec3(-1);
   1353 		const IVec3						uvwBits			= m_baseParams.textureType == TEXTURETYPE_2D			? IVec3(7,7,0)
   1354 														: m_baseParams.textureType == TEXTURETYPE_CUBE			? IVec3(6,6,0)
   1355 														: m_baseParams.textureType == TEXTURETYPE_2D_ARRAY		? IVec3(7,7,7)
   1356 														: IVec3(-1);
   1357 		tcu::Sampler					sampler;
   1358 		sampler.wrapS		= m_baseParams.wrapS;
   1359 		sampler.wrapT		= m_baseParams.wrapT;
   1360 		sampler.compare		= m_baseParams.shadowCompareMode;
   1361 
   1362 		if (isDepthFormat(m_baseParams.textureFormat))
   1363 		{
   1364 			tcu::TexComparePrecision comparePrec;
   1365 			comparePrec.coordBits		= coordBits;
   1366 			comparePrec.uvwBits			= uvwBits;
   1367 			comparePrec.referenceBits	= 16;
   1368 			comparePrec.resultBits		= pixelFormat.redBits-1;
   1369 
   1370 			return verifyGatherOffsetsCompare(log, rendered, texture, texCoords, sampler, comparePrec, PixelCompareRefZDefault(RENDER_SIZE), *pixelOffsets);
   1371 		}
   1372 		else
   1373 		{
   1374 			const int componentNdx = de::max(0, gatherArgs.componentNdx);
   1375 
   1376 			if (isUnormFormatType(m_baseParams.textureFormat.type))
   1377 			{
   1378 				tcu::LookupPrecision lookupPrec;
   1379 				lookupPrec.colorThreshold	= tcu::computeFixedPointThreshold(colorBits);
   1380 				lookupPrec.coordBits		= coordBits;
   1381 				lookupPrec.uvwBits			= uvwBits;
   1382 				lookupPrec.colorMask		= TextureTestUtil::getCompareMask(pixelFormat);
   1383 				return verifyGatherOffsets<float>(log, rendered, texture, texCoords, sampler, lookupPrec, componentNdx, *pixelOffsets);
   1384 			}
   1385 			else if (isUIntFormatType(m_baseParams.textureFormat.type) || isSIntFormatType(m_baseParams.textureFormat.type))
   1386 			{
   1387 				tcu::IntLookupPrecision		lookupPrec;
   1388 				lookupPrec.colorThreshold	= UVec4(0);
   1389 				lookupPrec.coordBits		= coordBits;
   1390 				lookupPrec.uvwBits			= uvwBits;
   1391 				lookupPrec.colorMask		= TextureTestUtil::getCompareMask(pixelFormat);
   1392 
   1393 				if (isUIntFormatType(m_baseParams.textureFormat.type))
   1394 					return verifyGatherOffsets<deUint32>(log, rendered, texture, texCoords, sampler, lookupPrec, componentNdx, *pixelOffsets);
   1395 				else if (isSIntFormatType(m_baseParams.textureFormat.type))
   1396 					return verifyGatherOffsets<deInt32>(log, rendered, texture, texCoords, sampler, lookupPrec, componentNdx, *pixelOffsets);
   1397 				else
   1398 				{
   1399 					DE_ASSERT(false);
   1400 					return false;
   1401 				}
   1402 			}
   1403 			else
   1404 			{
   1405 				DE_ASSERT(false);
   1406 				return false;
   1407 			}
   1408 		}
   1409 	}
   1410 }
   1411 
   1412 glu::VertexSource genVertexShaderSource (bool requireGpuShader5, int numTexCoordComponents, bool useNormalizedCoordInput)
   1413 {
   1414 	DE_ASSERT(numTexCoordComponents == 2 || numTexCoordComponents == 3);
   1415 
   1416 	const string		texCoordType	= "vec" + de::toString(numTexCoordComponents);
   1417 	std::ostringstream	vert;
   1418 
   1419 	vert << "#version 310 es\n";
   1420 
   1421 	if (requireGpuShader5)
   1422 		vert << "#extension GL_EXT_gpu_shader5 : require\n";
   1423 
   1424 	vert << "\n"
   1425 			"layout (location = 0) in highp vec2 a_position;\n"
   1426 			"layout (location = 1) in highp " << texCoordType << " a_texCoord;\n";
   1427 
   1428 	if (useNormalizedCoordInput)
   1429 		vert << "layout (location = 2) in highp vec2 a_normalizedCoord; // (0,0) to (1,1)\n";
   1430 
   1431 	vert << "\n"
   1432 			"layout (location = 0) out highp " << texCoordType << " v_texCoord;\n";
   1433 
   1434 	if (useNormalizedCoordInput)
   1435 		vert << "layout (location = 1) out highp vec2 v_normalizedCoord;\n";
   1436 
   1437 	vert << "\n"
   1438 			"void main (void)\n"
   1439 			"{\n"
   1440 			"	gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);\n"
   1441 			"	v_texCoord = a_texCoord;\n";
   1442 
   1443 	if (useNormalizedCoordInput)
   1444 		vert << "	v_normalizedCoord = a_normalizedCoord;\n";
   1445 
   1446 	vert << "}\n";
   1447 
   1448 	return glu::VertexSource(vert.str());
   1449 }
   1450 
   1451 glu::FragmentSource genFragmentShaderSource (bool					requireGpuShader5,
   1452 											 int					numTexCoordComponents,
   1453 											 glu::DataType			samplerType,
   1454 											 const string&			funcCall,
   1455 											 bool					useNormalizedCoordInput,
   1456 											 bool					usePixCoord,
   1457 											 OffsetSize				offsetSize,
   1458 											 const ImageBackingMode	sparseCase)
   1459 {
   1460 	DE_ASSERT(glu::isDataTypeSampler(samplerType));
   1461 	DE_ASSERT(de::inRange(numTexCoordComponents, 2, 3));
   1462 	DE_ASSERT(!usePixCoord || useNormalizedCoordInput);
   1463 
   1464 	const string		texCoordType	= "vec" + de::toString(numTexCoordComponents);
   1465 	deUint32			binding			= 0;
   1466 	std::ostringstream	frag;
   1467 	const string		outType			= glu::getDataTypeName(getSamplerGatherResultType(samplerType));
   1468 
   1469 	if (sparseCase == ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE)
   1470 		frag	<< "#version 450\n"
   1471 				<< "#extension GL_ARB_sparse_texture2 : require\n";
   1472 	else
   1473 		frag << "#version 310 es\n";
   1474 
   1475 	if (requireGpuShader5)
   1476 		frag << "#extension GL_EXT_gpu_shader5 : require\n";
   1477 
   1478 	frag << "\n"
   1479 			"layout (location = 0) out mediump " << outType << " o_color;\n"
   1480 			"\n"
   1481 			"layout (location = 0) in highp " << texCoordType << " v_texCoord;\n";
   1482 
   1483 	if (useNormalizedCoordInput)
   1484 		frag << "layout (location = 1) in highp vec2 v_normalizedCoord;\n";
   1485 
   1486 	frag << "\n"
   1487 			"layout (binding = " << binding++ << ") uniform highp " << glu::getDataTypeName(samplerType) << " u_sampler;\n";
   1488 
   1489 	if (usePixCoord)
   1490 		frag << "layout (binding = " << binding++ << ") uniform viewportSize { highp vec2 u_viewportSize; };\n";
   1491 
   1492 	if (offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1493 		frag << "layout (binding = " << binding++ << ") uniform offset { highp ivec2 u_offset; };\n";
   1494 
   1495 	frag << "\n"
   1496 			"void main(void)\n"
   1497 			"{\n";
   1498 
   1499 	if (usePixCoord)
   1500 		frag << "	ivec2 pixCoord = ivec2(v_normalizedCoord*u_viewportSize);\n";
   1501 
   1502 	if (sparseCase == ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE)
   1503 	{
   1504 		// Texel declaration
   1505 		frag << "\t" << outType << " texel;\n";
   1506 		frag << "\tint success = " << funcCall << ";\n";
   1507 
   1508 		// Check sparse validity, and handle each case
   1509 		frag << "\tif (sparseTexelsResidentARB(success))\n"
   1510 			 << "\t\to_color = texel;\n"
   1511 			 <<	"\telse\n"
   1512 			 << "\t\to_color = " << outType << "(0.0, 0.0, 0.0, 1.0);\n";
   1513 	}
   1514 	else
   1515 	{
   1516 		frag << "\t\to_color = " << funcCall << ";\n";
   1517 	}
   1518 
   1519 	frag << "}\n";
   1520 
   1521 	return glu::FragmentSource(frag.str());
   1522 }
   1523 
   1524 string genGatherFuncCall (GatherType				gatherType,
   1525 						  const tcu::TextureFormat&	textureFormat,
   1526 						  const GatherArgs&			gatherArgs,
   1527 						  const string&				refZExpr,
   1528 						  const IVec2&				offsetRange,
   1529 						  int						indentationDepth,
   1530 						  OffsetSize				offsetSize,
   1531 						  const ImageBackingMode	sparseCase)
   1532 {
   1533 	string result;
   1534 
   1535 	if (sparseCase == ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE)
   1536 	{
   1537 		switch (gatherType)
   1538 		{
   1539 			case GATHERTYPE_BASIC:
   1540 				result += "sparseTextureGatherARB";
   1541 				break;
   1542 			case GATHERTYPE_OFFSET: // \note Fallthrough.
   1543 			case GATHERTYPE_OFFSET_DYNAMIC:
   1544 				result += "sparseTextureGatherOffsetARB";
   1545 				break;
   1546 			case GATHERTYPE_OFFSETS:
   1547 				result += "sparseTextureGatherOffsetsARB";
   1548 				break;
   1549 			default:
   1550 				DE_ASSERT(false);
   1551 		}
   1552 	}
   1553 	else
   1554 	{
   1555 		switch (gatherType)
   1556 		{
   1557 			case GATHERTYPE_BASIC:
   1558 				result += "textureGather";
   1559 				break;
   1560 			case GATHERTYPE_OFFSET: // \note Fallthrough.
   1561 			case GATHERTYPE_OFFSET_DYNAMIC:
   1562 				result += "textureGatherOffset";
   1563 				break;
   1564 			case GATHERTYPE_OFFSETS:
   1565 				result += "textureGatherOffsets";
   1566 				break;
   1567 			default:
   1568 				DE_ASSERT(false);
   1569 		}
   1570 	}
   1571 
   1572 	result += "(u_sampler, v_texCoord";
   1573 
   1574 	if (isDepthFormat(textureFormat))
   1575 	{
   1576 		DE_ASSERT(gatherArgs.componentNdx < 0);
   1577 		result += ", " + refZExpr;
   1578 	}
   1579 
   1580 	if (gatherType == GATHERTYPE_OFFSET ||
   1581 		gatherType == GATHERTYPE_OFFSET_DYNAMIC ||
   1582 		gatherType == GATHERTYPE_OFFSETS)
   1583 	{
   1584 		result += ", ";
   1585 		switch (gatherType)
   1586 		{
   1587 			case GATHERTYPE_OFFSET:
   1588 				if (offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1589 					result += "u_offset";
   1590 				else
   1591 					result += "ivec2" + de::toString(gatherArgs.offsets[0]);
   1592 				break;
   1593 
   1594 			case GATHERTYPE_OFFSET_DYNAMIC:
   1595 				if (offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1596 					result += "pixCoord.yx % ivec2(u_offset.y - u_offset.x + 1) + u_offset.x";
   1597 				else
   1598 					result += "pixCoord.yx % ivec2(" + de::toString(offsetRange.y() - offsetRange.x() + 1) + ") + " + de::toString(offsetRange.x());
   1599 				break;
   1600 
   1601 			case GATHERTYPE_OFFSETS:
   1602 				DE_ASSERT(offsetSize != OFFSETSIZE_IMPLEMENTATION_MAXIMUM);
   1603 				result += "ivec2[4](\n"
   1604 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[0]) + ",\n"
   1605 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[1]) + ",\n"
   1606 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[2]) + ",\n"
   1607 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[3]) + ")\n"
   1608 						  + string(indentationDepth, '\t') + "\t";
   1609 				break;
   1610 
   1611 			default:
   1612 				DE_ASSERT(false);
   1613 		}
   1614 	}
   1615 
   1616 	if (sparseCase == ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE)
   1617 		result += ", texel";
   1618 
   1619 	if (gatherArgs.componentNdx >= 0)
   1620 	{
   1621 		DE_ASSERT(gatherArgs.componentNdx < 4);
   1622 		result += ", " + de::toString(gatherArgs.componentNdx);
   1623 	}
   1624 
   1625 	result += ")";
   1626 
   1627 	return result;
   1628 }
   1629 
   1630 // \todo [2016-07-08 pyry] Re-use programs if sources are identical
   1631 
   1632 void genGatherPrograms (vk::SourceCollections& programCollection, const GatherCaseBaseParams& baseParams, const vector<GatherArgs>& iterations)
   1633 {
   1634 	const int					numIterations		= (int)iterations.size();
   1635 	const string				refZExpr			= "v_normalizedCoord.x";
   1636 	const IVec2&				offsetRange			= baseParams.offsetSize != OFFSETSIZE_IMPLEMENTATION_MAXIMUM ? getOffsetRange(baseParams.offsetSize) : IVec2(0);
   1637 	const bool					usePixCoord			= baseParams.gatherType == GATHERTYPE_OFFSET_DYNAMIC;
   1638 	const bool					useNormalizedCoord	= usePixCoord || isDepthFormat(baseParams.textureFormat);
   1639 	const bool					isDynamicOffset		= baseParams.gatherType == GATHERTYPE_OFFSET_DYNAMIC;
   1640 	const bool					isShadow			= isDepthFormat(baseParams.textureFormat);
   1641 	const glu::DataType			samplerType			= getSamplerType(baseParams.textureType, baseParams.textureFormat);
   1642 	const int					numDims				= getNumTextureSamplingDimensions(baseParams.textureType);
   1643 	glu::VertexSource			vert				= genVertexShaderSource(requireGpuShader5(baseParams.gatherType, baseParams.offsetSize), numDims, isDynamicOffset || isShadow);
   1644 
   1645 	programCollection.glslSources.add("vert") << vert;
   1646 
   1647 	for (int iterNdx = 0; iterNdx < numIterations; iterNdx++)
   1648 	{
   1649 		const GatherArgs&		gatherArgs			= iterations[iterNdx];
   1650 		const string			funcCall			= genGatherFuncCall(baseParams.gatherType, baseParams.textureFormat, gatherArgs, refZExpr, offsetRange, 1, baseParams.offsetSize, baseParams.sparseCase);
   1651 		glu::FragmentSource		frag				= genFragmentShaderSource(requireGpuShader5(baseParams.gatherType, baseParams.offsetSize), numDims, samplerType, funcCall, useNormalizedCoord, usePixCoord, baseParams.offsetSize, baseParams.sparseCase);
   1652 
   1653 		programCollection.glslSources.add("frag_" + de::toString(iterNdx)) << frag;
   1654 	}
   1655 }
   1656 
   1657 // 2D
   1658 
   1659 class TextureGather2DInstance : public TextureGatherInstance
   1660 {
   1661 public:
   1662 									TextureGather2DInstance				(Context&						context,
   1663 																		 const GatherCaseBaseParams&	baseParams,
   1664 																		 const IVec2&					textureSize,
   1665 																		 const vector<GatherArgs>&		iterations);
   1666 	virtual							~TextureGather2DInstance			(void);
   1667 
   1668 protected:
   1669 	virtual int						getNumIterations					(void) const				{ return (int)m_iterations.size();	}
   1670 	virtual GatherArgs				getGatherArgs						(int iterationNdx) const	{ return m_iterations[iterationNdx];}
   1671 
   1672 	virtual TextureBindingSp		createTexture						(void);
   1673 	virtual vector<float>			computeQuadTexCoord					(int iterationNdx) const;
   1674 	virtual bool					verify								(int iterationNdx, const ConstPixelBufferAccess& rendered) const;
   1675 
   1676 private:
   1677 	const IVec2						m_textureSize;
   1678 	const vector<GatherArgs>		m_iterations;
   1679 
   1680 	tcu::Texture2D					m_swizzledTexture;
   1681 };
   1682 
   1683 TextureGather2DInstance::TextureGather2DInstance (Context&						context,
   1684 												  const GatherCaseBaseParams&	baseParams,
   1685 												  const IVec2&					textureSize,
   1686 												  const vector<GatherArgs>&		iterations)
   1687 	: TextureGatherInstance		(context, baseParams)
   1688 	, m_textureSize				(textureSize)
   1689 	, m_iterations				(iterations)
   1690 	, m_swizzledTexture			(tcu::TextureFormat(), 1, 1)
   1691 {
   1692 	init();
   1693 }
   1694 
   1695 TextureGather2DInstance::~TextureGather2DInstance (void)
   1696 {
   1697 }
   1698 
   1699 vector<float> TextureGather2DInstance::computeQuadTexCoord (int /* iterationNdx */) const
   1700 {
   1701 	vector<float> res;
   1702 	TextureTestUtil::computeQuadTexCoord2D(res, Vec2(-0.3f, -0.4f), Vec2(1.5f, 1.6f));
   1703 	return res;
   1704 }
   1705 
   1706 TextureBindingSp TextureGather2DInstance::createTexture (void)
   1707 {
   1708 	TestLog&						log			= m_context.getTestContext().getLog();
   1709 	const tcu::TextureFormatInfo	texFmtInfo	= tcu::getTextureFormatInfo(m_baseParams.textureFormat);
   1710 	MovePtr<tcu::Texture2D>			texture		= MovePtr<tcu::Texture2D>(new tcu::Texture2D(m_baseParams.textureFormat, m_textureSize.x(), m_textureSize.y()));
   1711 	const tcu::Sampler				sampler		(m_baseParams.wrapS, m_baseParams.wrapT, tcu::Sampler::REPEAT_GL,
   1712 												 m_baseParams.minFilter, m_baseParams.magFilter,
   1713 												 0.0f /* LOD threshold */, true /* normalized coords */, m_baseParams.shadowCompareMode);
   1714 
   1715 	{
   1716 		const int	levelBegin	= m_baseParams.baseLevel;
   1717 		const int	levelEnd	= texture->getNumLevels();
   1718 		DE_ASSERT(m_baseParams.baseLevel < texture->getNumLevels());
   1719 
   1720 		for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
   1721 		{
   1722 			texture->allocLevel(levelNdx);
   1723 			const PixelBufferAccess& level = texture->getLevel(levelNdx);
   1724 			fillWithRandomColorTiles(level, texFmtInfo.valueMin, texFmtInfo.valueMax, (deUint32)m_context.getTestContext().getCommandLine().getBaseSeed());
   1725 			log << TestLog::Image("InputTextureLevel" + de::toString(levelNdx), "Input texture, level " + de::toString(levelNdx), level)
   1726 				<< TestLog::Message << "Note: texture level's size is " << IVec2(level.getWidth(), level.getHeight()) << TestLog::EndMessage;
   1727 		}
   1728 
   1729 		swizzleTexture(m_swizzledTexture, *texture, m_baseParams.textureSwizzle);
   1730 	}
   1731 
   1732 	return TextureBindingSp(new TextureBinding(texture.release(), sampler));
   1733 }
   1734 
   1735 bool TextureGather2DInstance::verify (int iterationNdx, const ConstPixelBufferAccess& rendered) const
   1736 {
   1737 	Vec2 texCoords[4];
   1738 	computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
   1739 	return TextureGatherInstance::verify(rendered, getOneLevelSubView(tcu::Texture2DView(m_swizzledTexture), m_baseParams.baseLevel), texCoords, m_iterations[iterationNdx]);
   1740 }
   1741 
   1742 class TextureGather2DCase : public TestCase
   1743 {
   1744 public:
   1745 									TextureGather2DCase					(tcu::TestContext&					testCtx,
   1746 																		 const string&						name,
   1747 																		 const string&						description,
   1748 																		 const GatherType					gatherType,
   1749 																		 const OffsetSize					offsetSize,
   1750 																		 const tcu::TextureFormat			textureFormat,
   1751 																		 const tcu::Sampler::CompareMode	shadowCompareMode,
   1752 																		 const tcu::Sampler::WrapMode		wrapS,
   1753 																		 const tcu::Sampler::WrapMode		wrapT,
   1754 																		 const MaybeTextureSwizzle&			textureSwizzle,
   1755 																		 const tcu::Sampler::FilterMode		minFilter,
   1756 																		 const tcu::Sampler::FilterMode		magFilter,
   1757 																		 const int							baseLevel,
   1758 																		 const deUint32						flags,
   1759 																		 const IVec2&						textureSize,
   1760 																		 const ImageBackingMode				sparseCase);
   1761 	virtual							~TextureGather2DCase				(void);
   1762 
   1763 	virtual void					initPrograms						(vk::SourceCollections& dst) const;
   1764 	virtual	TestInstance*			createInstance						(Context& context) const;
   1765 
   1766 private:
   1767 	const GatherCaseBaseParams		m_baseParams;
   1768 	const IVec2						m_textureSize;
   1769 };
   1770 
   1771 TextureGather2DCase::TextureGather2DCase (tcu::TestContext&						testCtx,
   1772 										  const string&							name,
   1773 										  const string&							description,
   1774 										  const GatherType						gatherType,
   1775 										  const OffsetSize						offsetSize,
   1776 										  const tcu::TextureFormat				textureFormat,
   1777 										  const tcu::Sampler::CompareMode		shadowCompareMode,
   1778 										  const tcu::Sampler::WrapMode			wrapS,
   1779 										  const tcu::Sampler::WrapMode			wrapT,
   1780 										  const MaybeTextureSwizzle&			textureSwizzle,
   1781 										  const tcu::Sampler::FilterMode		minFilter,
   1782 										  const tcu::Sampler::FilterMode		magFilter,
   1783 										  const int								baseLevel,
   1784 										  const deUint32						flags,
   1785 										  const IVec2&							textureSize,
   1786 										  const ImageBackingMode				sparseCase)
   1787 	: TestCase		(testCtx, name, description)
   1788 	, m_baseParams	(TEXTURETYPE_2D, gatherType, offsetSize, textureFormat, shadowCompareMode, wrapS, wrapT, textureSwizzle, minFilter, magFilter, baseLevel, flags, sparseCase)
   1789 	, m_textureSize	(textureSize)
   1790 {
   1791 }
   1792 
   1793 TextureGather2DCase::~TextureGather2DCase (void)
   1794 {
   1795 }
   1796 
   1797 void TextureGather2DCase::initPrograms (vk::SourceCollections& dst) const
   1798 {
   1799 	const vector<GatherArgs>	iterations	= generateBasic2DCaseIterations(m_baseParams.gatherType,
   1800 																			m_baseParams.textureFormat,
   1801 																			m_baseParams.offsetSize != OFFSETSIZE_IMPLEMENTATION_MAXIMUM ? getOffsetRange(m_baseParams.offsetSize) : IVec2(0));
   1802 
   1803 	genGatherPrograms(dst, m_baseParams, iterations);
   1804 }
   1805 
   1806 TestInstance* TextureGather2DCase::createInstance (Context& context) const
   1807 {
   1808 	const vector<GatherArgs>	iterations	= generateBasic2DCaseIterations(m_baseParams.gatherType,
   1809 																			m_baseParams.textureFormat,
   1810 																			getOffsetRange(m_baseParams.offsetSize, context.getDeviceProperties().limits));
   1811 
   1812 	return new TextureGather2DInstance(context, m_baseParams, m_textureSize, iterations);
   1813 }
   1814 
   1815 // 2D array
   1816 
   1817 struct Gather2DArrayArgs
   1818 {
   1819 	GatherArgs	gatherArgs;
   1820 	int			layerNdx;
   1821 
   1822 	operator GatherArgs() const { return gatherArgs; }
   1823 };
   1824 
   1825 vector<Gather2DArrayArgs> generate2DArrayCaseIterations (GatherType					gatherType,
   1826 														 const tcu::TextureFormat&	textureFormat,
   1827 														 const IVec2&				offsetRange,
   1828 														 const IVec3&				textureSize)
   1829 {
   1830 	const vector<GatherArgs>	basicIterations	= generateBasic2DCaseIterations(gatherType, textureFormat, offsetRange);
   1831 	vector<Gather2DArrayArgs>	iterations;
   1832 
   1833 	// \note Out-of-bounds layer indices are tested too.
   1834 	for (int layerNdx = -1; layerNdx < textureSize.z()+1; layerNdx++)
   1835 	{
   1836 		// Don't duplicate all cases for all layers.
   1837 		if (layerNdx == 0)
   1838 		{
   1839 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   1840 			{
   1841 				iterations.push_back(Gather2DArrayArgs());
   1842 				iterations.back().gatherArgs = basicIterations[basicNdx];
   1843 				iterations.back().layerNdx = layerNdx;
   1844 			}
   1845 		}
   1846 		else
   1847 		{
   1848 			// For other layers than 0, only test one component and one set of offsets per layer.
   1849 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   1850 			{
   1851 				if (isDepthFormat(textureFormat) || basicIterations[basicNdx].componentNdx == (layerNdx + 2) % 4)
   1852 				{
   1853 					iterations.push_back(Gather2DArrayArgs());
   1854 					iterations.back().gatherArgs = basicIterations[basicNdx];
   1855 					iterations.back().layerNdx = layerNdx;
   1856 					break;
   1857 				}
   1858 			}
   1859 		}
   1860 	}
   1861 
   1862 	return iterations;
   1863 }
   1864 
   1865 class TextureGather2DArrayInstance : public TextureGatherInstance
   1866 {
   1867 public:
   1868 									TextureGather2DArrayInstance		(Context&							context,
   1869 																		 const GatherCaseBaseParams&		baseParams,
   1870 																		 const IVec3&						textureSize,
   1871 																		 const vector<Gather2DArrayArgs>&	iterations);
   1872 	virtual							~TextureGather2DArrayInstance		(void);
   1873 
   1874 protected:
   1875 	virtual int						getNumIterations					(void) const				{ return (int)m_iterations.size();				}
   1876 	virtual GatherArgs				getGatherArgs						(int iterationNdx) const	{ return m_iterations[iterationNdx].gatherArgs;	}
   1877 
   1878 	virtual TextureBindingSp		createTexture						(void);
   1879 	virtual vector<float>			computeQuadTexCoord					(int iterationNdx) const;
   1880 	virtual bool					verify								(int iterationNdx, const ConstPixelBufferAccess& rendered) const;
   1881 
   1882 private:
   1883 	const IVec3						m_textureSize;
   1884 	const vector<Gather2DArrayArgs>	m_iterations;
   1885 
   1886 	tcu::Texture2DArray				m_swizzledTexture;
   1887 };
   1888 
   1889 TextureGather2DArrayInstance::TextureGather2DArrayInstance (Context&							context,
   1890 															const GatherCaseBaseParams&			baseParams,
   1891 															const IVec3&						textureSize,
   1892 															const vector<Gather2DArrayArgs>&	iterations)
   1893 	: TextureGatherInstance		(context, baseParams)
   1894 	, m_textureSize				(textureSize)
   1895 	, m_iterations				(iterations)
   1896 	, m_swizzledTexture			(tcu::TextureFormat(), 1, 1, 1)
   1897 {
   1898 	init();
   1899 }
   1900 
   1901 TextureGather2DArrayInstance::~TextureGather2DArrayInstance (void)
   1902 {
   1903 }
   1904 
   1905 vector<float> TextureGather2DArrayInstance::computeQuadTexCoord (int iterationNdx) const
   1906 {
   1907 	vector<float> res;
   1908 	TextureTestUtil::computeQuadTexCoord2DArray(res, m_iterations[iterationNdx].layerNdx, Vec2(-0.3f, -0.4f), Vec2(1.5f, 1.6f));
   1909 	return res;
   1910 }
   1911 
   1912 TextureBindingSp TextureGather2DArrayInstance::createTexture (void)
   1913 {
   1914 	TestLog&						log			= m_context.getTestContext().getLog();
   1915 	const tcu::TextureFormatInfo	texFmtInfo	= tcu::getTextureFormatInfo(m_baseParams.textureFormat);
   1916 	MovePtr<tcu::Texture2DArray>	texture		= MovePtr<tcu::Texture2DArray>(new tcu::Texture2DArray(m_baseParams.textureFormat, m_textureSize.x(), m_textureSize.y(), m_textureSize.z()));
   1917 	const tcu::Sampler				sampler		(m_baseParams.wrapS, m_baseParams.wrapT, tcu::Sampler::REPEAT_GL,
   1918 												 m_baseParams.minFilter, m_baseParams.magFilter,
   1919 												 0.0f /* LOD threshold */, true /* normalized coords */, m_baseParams.shadowCompareMode);
   1920 
   1921 	{
   1922 		const int	levelBegin	= m_baseParams.baseLevel;
   1923 		const int	levelEnd	= texture->getNumLevels();
   1924 		DE_ASSERT(m_baseParams.baseLevel < texture->getNumLevels());
   1925 
   1926 		for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
   1927 		{
   1928 			texture->allocLevel(levelNdx);
   1929 			const PixelBufferAccess& level = texture->getLevel(levelNdx);
   1930 			fillWithRandomColorTiles(level, texFmtInfo.valueMin, texFmtInfo.valueMax, (deUint32)m_context.getTestContext().getCommandLine().getBaseSeed());
   1931 
   1932 			log << TestLog::ImageSet("InputTextureLevel", "Input texture, level " + de::toString(levelNdx));
   1933 			for (int layerNdx = 0; layerNdx < m_textureSize.z(); layerNdx++)
   1934 				log << TestLog::Image("InputTextureLevel" + de::toString(layerNdx) + "Layer" + de::toString(layerNdx),
   1935 									  "Layer " + de::toString(layerNdx),
   1936 									  tcu::getSubregion(level, 0, 0, layerNdx, level.getWidth(), level.getHeight(), 1));
   1937 			log << TestLog::EndImageSet
   1938 				<< TestLog::Message << "Note: texture level's size is " << IVec3(level.getWidth(), level.getHeight(), level.getDepth()) << TestLog::EndMessage;
   1939 		}
   1940 
   1941 		swizzleTexture(m_swizzledTexture, *texture, m_baseParams.textureSwizzle);
   1942 	}
   1943 
   1944 	return TextureBindingSp(new TextureBinding(texture.release(), sampler));
   1945 }
   1946 
   1947 bool TextureGather2DArrayInstance::verify (int iterationNdx, const ConstPixelBufferAccess& rendered) const
   1948 {
   1949 	Vec3 texCoords[4];
   1950 	computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
   1951 	return TextureGatherInstance::verify(rendered, getOneLevelSubView(tcu::Texture2DArrayView(m_swizzledTexture), m_baseParams.baseLevel), texCoords, m_iterations[iterationNdx].gatherArgs);
   1952 }
   1953 
   1954 class TextureGather2DArrayCase : public TestCase
   1955 {
   1956 public:
   1957 									TextureGather2DArrayCase			(tcu::TestContext&					testCtx,
   1958 																		 const string&						name,
   1959 																		 const string&						description,
   1960 																		 const GatherType					gatherType,
   1961 																		 const OffsetSize					offsetSize,
   1962 																		 const tcu::TextureFormat			textureFormat,
   1963 																		 const tcu::Sampler::CompareMode	shadowCompareMode,
   1964 																		 const tcu::Sampler::WrapMode		wrapS,
   1965 																		 const tcu::Sampler::WrapMode		wrapT,
   1966 																		 const MaybeTextureSwizzle&			textureSwizzle,
   1967 																		 const tcu::Sampler::FilterMode		minFilter,
   1968 																		 const tcu::Sampler::FilterMode		magFilter,
   1969 																		 const int							baseLevel,
   1970 																		 const deUint32						flags,
   1971 																		 const IVec3&						textureSize,
   1972 																		 const ImageBackingMode				sparseCase);
   1973 	virtual							~TextureGather2DArrayCase			(void);
   1974 
   1975 	virtual void					initPrograms						(vk::SourceCollections& dst) const;
   1976 	virtual	TestInstance*			createInstance						(Context& context) const;
   1977 
   1978 private:
   1979 	const GatherCaseBaseParams		m_baseParams;
   1980 	const IVec3						m_textureSize;
   1981 };
   1982 
   1983 TextureGather2DArrayCase::TextureGather2DArrayCase (tcu::TestContext&					testCtx,
   1984 													const string&						name,
   1985 													const string&						description,
   1986 													const GatherType					gatherType,
   1987 													const OffsetSize					offsetSize,
   1988 													const tcu::TextureFormat			textureFormat,
   1989 													const tcu::Sampler::CompareMode		shadowCompareMode,
   1990 													const tcu::Sampler::WrapMode		wrapS,
   1991 													const tcu::Sampler::WrapMode		wrapT,
   1992 													const MaybeTextureSwizzle&			textureSwizzle,
   1993 													const tcu::Sampler::FilterMode		minFilter,
   1994 													const tcu::Sampler::FilterMode		magFilter,
   1995 													const int							baseLevel,
   1996 													const deUint32						flags,
   1997 													const IVec3&						textureSize,
   1998 													const ImageBackingMode				sparseCase)
   1999 	: TestCase			(testCtx, name, description)
   2000 	, m_baseParams		(TEXTURETYPE_2D_ARRAY, gatherType, offsetSize, textureFormat, shadowCompareMode, wrapS, wrapT, textureSwizzle, minFilter, magFilter, baseLevel, flags, sparseCase)
   2001 	, m_textureSize		(textureSize)
   2002 {
   2003 }
   2004 
   2005 TextureGather2DArrayCase::~TextureGather2DArrayCase (void)
   2006 {
   2007 }
   2008 
   2009 void TextureGather2DArrayCase::initPrograms (vk::SourceCollections& dst) const
   2010 {
   2011 	const vector<Gather2DArrayArgs>		iterations	= generate2DArrayCaseIterations(m_baseParams.gatherType,
   2012 																					m_baseParams.textureFormat,
   2013 																					m_baseParams.offsetSize != OFFSETSIZE_IMPLEMENTATION_MAXIMUM ? getOffsetRange(m_baseParams.offsetSize) : IVec2(0),
   2014 																					m_textureSize);
   2015 
   2016 	genGatherPrograms(dst, m_baseParams, vector<GatherArgs>(iterations.begin(), iterations.end()));
   2017 }
   2018 
   2019 TestInstance* TextureGather2DArrayCase::createInstance (Context& context) const
   2020 {
   2021 	const vector<Gather2DArrayArgs>		iterations	= generate2DArrayCaseIterations(m_baseParams.gatherType,
   2022 																					m_baseParams.textureFormat,
   2023 																					getOffsetRange(m_baseParams.offsetSize, context.getDeviceProperties().limits),
   2024 																					m_textureSize);
   2025 
   2026 	return new TextureGather2DArrayInstance(context, m_baseParams, m_textureSize, iterations);
   2027 }
   2028 
   2029 // Cube
   2030 
   2031 struct GatherCubeArgs
   2032 {
   2033 	GatherArgs		gatherArgs;
   2034 	tcu::CubeFace	face;
   2035 
   2036 	operator GatherArgs() const { return gatherArgs; }
   2037 };
   2038 
   2039 vector<GatherCubeArgs> generateCubeCaseIterations (GatherType gatherType, const tcu::TextureFormat& textureFormat, const IVec2& offsetRange)
   2040 {
   2041 	const vector<GatherArgs>	basicIterations = generateBasic2DCaseIterations(gatherType, textureFormat, offsetRange);
   2042 	vector<GatherCubeArgs>		iterations;
   2043 
   2044 	for (int cubeFaceI = 0; cubeFaceI < tcu::CUBEFACE_LAST; cubeFaceI++)
   2045 	{
   2046 		const tcu::CubeFace cubeFace = (tcu::CubeFace)cubeFaceI;
   2047 
   2048 		// Don't duplicate all cases for all faces.
   2049 		if (cubeFaceI == 0)
   2050 		{
   2051 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   2052 			{
   2053 				iterations.push_back(GatherCubeArgs());
   2054 				iterations.back().gatherArgs = basicIterations[basicNdx];
   2055 				iterations.back().face = cubeFace;
   2056 			}
   2057 		}
   2058 		else
   2059 		{
   2060 			// For other faces than first, only test one component per face.
   2061 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   2062 			{
   2063 				if (isDepthFormat(textureFormat) || basicIterations[basicNdx].componentNdx == cubeFaceI % 4)
   2064 				{
   2065 					iterations.push_back(GatherCubeArgs());
   2066 					iterations.back().gatherArgs = basicIterations[basicNdx];
   2067 					iterations.back().face = cubeFace;
   2068 					break;
   2069 				}
   2070 			}
   2071 		}
   2072 	}
   2073 
   2074 	return iterations;
   2075 }
   2076 
   2077 class TextureGatherCubeInstance : public TextureGatherInstance
   2078 {
   2079 public:
   2080 									TextureGatherCubeInstance			(Context&							context,
   2081 																		 const GatherCaseBaseParams&		baseParams,
   2082 																		 const int							textureSize,
   2083 																		 const vector<GatherCubeArgs>&		iterations);
   2084 	virtual							~TextureGatherCubeInstance			(void);
   2085 
   2086 protected:
   2087 	virtual int						getNumIterations					(void) const				{ return (int)m_iterations.size();				}
   2088 	virtual GatherArgs				getGatherArgs						(int iterationNdx) const	{ return m_iterations[iterationNdx].gatherArgs;	}
   2089 
   2090 	virtual TextureBindingSp		createTexture						(void);
   2091 	virtual vector<float>			computeQuadTexCoord					(int iterationNdx) const;
   2092 	virtual bool					verify								(int iterationNdx, const ConstPixelBufferAccess& rendered) const;
   2093 
   2094 private:
   2095 	const int						m_textureSize;
   2096 	const vector<GatherCubeArgs>	m_iterations;
   2097 
   2098 	tcu::TextureCube				m_swizzledTexture;
   2099 };
   2100 
   2101 TextureGatherCubeInstance::TextureGatherCubeInstance (Context&							context,
   2102 													  const GatherCaseBaseParams&		baseParams,
   2103 													  const int							textureSize,
   2104 													  const vector<GatherCubeArgs>&		iterations)
   2105 	: TextureGatherInstance		(context, baseParams)
   2106 	, m_textureSize				(textureSize)
   2107 	, m_iterations				(iterations)
   2108 	, m_swizzledTexture			(tcu::TextureFormat(), 1)
   2109 {
   2110 	init();
   2111 }
   2112 
   2113 TextureGatherCubeInstance::~TextureGatherCubeInstance (void)
   2114 {
   2115 }
   2116 
   2117 vector<float> TextureGatherCubeInstance::computeQuadTexCoord (int iterationNdx) const
   2118 {
   2119 	const bool		corners	= (m_baseParams.flags & GATHERCASE_DONT_SAMPLE_CUBE_CORNERS) == 0;
   2120 	const Vec2		minC	= corners ? Vec2(-1.2f) : Vec2(-0.6f, -1.2f);
   2121 	const Vec2		maxC	= corners ? Vec2( 1.2f) : Vec2( 0.6f,  1.2f);
   2122 	vector<float>	res;
   2123 	TextureTestUtil::computeQuadTexCoordCube(res, m_iterations[iterationNdx].face, minC, maxC);
   2124 	return res;
   2125 }
   2126 
   2127 TextureBindingSp TextureGatherCubeInstance::createTexture (void)
   2128 {
   2129 	TestLog&						log			= m_context.getTestContext().getLog();
   2130 	const tcu::TextureFormatInfo	texFmtInfo	= tcu::getTextureFormatInfo(m_baseParams.textureFormat);
   2131 	MovePtr<tcu::TextureCube>		texture		= MovePtr<tcu::TextureCube>(new tcu::TextureCube(m_baseParams.textureFormat, m_textureSize));
   2132 	const tcu::Sampler				sampler		(m_baseParams.wrapS, m_baseParams.wrapT, tcu::Sampler::REPEAT_GL,
   2133 												 m_baseParams.minFilter, m_baseParams.magFilter,
   2134 												 0.0f /* LOD threshold */, true /* normalized coords */, m_baseParams.shadowCompareMode,
   2135 												 0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
   2136 
   2137 	{
   2138 		const int	levelBegin	= m_baseParams.baseLevel;
   2139 		const int	levelEnd	= texture->getNumLevels();
   2140 		DE_ASSERT(m_baseParams.baseLevel < texture->getNumLevels());
   2141 
   2142 		for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
   2143 		{
   2144 			log << TestLog::ImageSet("InputTextureLevel" + de::toString(levelNdx), "Input texture, level " + de::toString(levelNdx));
   2145 
   2146 			for (int cubeFaceI = 0; cubeFaceI < tcu::CUBEFACE_LAST; cubeFaceI++)
   2147 			{
   2148 				const tcu::CubeFace			cubeFace	= (tcu::CubeFace)cubeFaceI;
   2149 				texture->allocLevel(cubeFace, levelNdx);
   2150 				const PixelBufferAccess&	levelFace	= texture->getLevelFace(levelNdx, cubeFace);
   2151 				fillWithRandomColorTiles(levelFace, texFmtInfo.valueMin, texFmtInfo.valueMax, (deUint32)m_context.getTestContext().getCommandLine().getBaseSeed() ^ (deUint32)cubeFaceI);
   2152 
   2153 				log << TestLog::Image("InputTextureLevel" + de::toString(levelNdx) + "Face" + de::toString((int)cubeFace), de::toString(cubeFace), levelFace);
   2154 			}
   2155 
   2156 			log << TestLog::EndImageSet
   2157 				<< TestLog::Message << "Note: texture level's size is " << texture->getLevelFace(levelNdx, tcu::CUBEFACE_NEGATIVE_X).getWidth() << TestLog::EndMessage;
   2158 		}
   2159 
   2160 		swizzleTexture(m_swizzledTexture, *texture, m_baseParams.textureSwizzle);
   2161 	}
   2162 
   2163 	return TextureBindingSp(new TextureBinding(texture.release(), sampler));
   2164 }
   2165 
   2166 bool TextureGatherCubeInstance::verify (int iterationNdx, const ConstPixelBufferAccess& rendered) const
   2167 {
   2168 	Vec3 texCoords[4];
   2169 	computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
   2170 	return TextureGatherInstance::verify(rendered, getOneLevelSubView(tcu::TextureCubeView(m_swizzledTexture), m_baseParams.baseLevel), texCoords, m_iterations[iterationNdx].gatherArgs);
   2171 }
   2172 
   2173 // \note Cube case always uses just basic textureGather(); offset versions are not defined for cube maps.
   2174 class TextureGatherCubeCase : public TestCase
   2175 {
   2176 public:
   2177 									TextureGatherCubeCase				(tcu::TestContext&					testCtx,
   2178 																		 const string&						name,
   2179 																		 const string&						description,
   2180 																		 const tcu::TextureFormat			textureFormat,
   2181 																		 const tcu::Sampler::CompareMode	shadowCompareMode,
   2182 																		 const tcu::Sampler::WrapMode		wrapS,
   2183 																		 const tcu::Sampler::WrapMode		wrapT,
   2184 																		 const MaybeTextureSwizzle&			textureSwizzle,
   2185 																		 const tcu::Sampler::FilterMode		minFilter,
   2186 																		 const tcu::Sampler::FilterMode		magFilter,
   2187 																		 const int							baseLevel,
   2188 																		 const deUint32						flags,
   2189 																		 const int							textureSize,
   2190 																		 const ImageBackingMode				sparseCase);
   2191 	virtual							~TextureGatherCubeCase				(void);
   2192 
   2193 	virtual void					initPrograms						(vk::SourceCollections& dst) const;
   2194 	virtual	TestInstance*			createInstance						(Context& context) const;
   2195 
   2196 private:
   2197 	const GatherCaseBaseParams		m_baseParams;
   2198 	const int						m_textureSize;
   2199 };
   2200 
   2201 TextureGatherCubeCase::TextureGatherCubeCase (tcu::TestContext&						testCtx,
   2202 											  const string&							name,
   2203 											  const string&							description,
   2204 											  const tcu::TextureFormat				textureFormat,
   2205 											  const tcu::Sampler::CompareMode		shadowCompareMode,
   2206 											  const tcu::Sampler::WrapMode			wrapS,
   2207 											  const tcu::Sampler::WrapMode			wrapT,
   2208 											  const MaybeTextureSwizzle&			textureSwizzle,
   2209 											  const tcu::Sampler::FilterMode		minFilter,
   2210 											  const tcu::Sampler::FilterMode		magFilter,
   2211 											  const int								baseLevel,
   2212 											  const deUint32						flags,
   2213 											  const int								textureSize,
   2214 											  const ImageBackingMode				sparseCase)
   2215 	: TestCase			(testCtx, name, description)
   2216 	, m_baseParams		(TEXTURETYPE_CUBE, GATHERTYPE_BASIC, OFFSETSIZE_NONE, textureFormat, shadowCompareMode, wrapS, wrapT, textureSwizzle, minFilter, magFilter, baseLevel, flags, sparseCase)
   2217 	, m_textureSize		(textureSize)
   2218 {
   2219 }
   2220 
   2221 TextureGatherCubeCase::~TextureGatherCubeCase (void)
   2222 {
   2223 }
   2224 
   2225 void TextureGatherCubeCase::initPrograms (vk::SourceCollections& dst) const
   2226 {
   2227 	const vector<GatherCubeArgs>	iterations	= generateCubeCaseIterations(m_baseParams.gatherType,
   2228 																			 m_baseParams.textureFormat,
   2229 																			 m_baseParams.offsetSize != OFFSETSIZE_IMPLEMENTATION_MAXIMUM ? getOffsetRange(m_baseParams.offsetSize) : IVec2(0));
   2230 
   2231 	genGatherPrograms(dst, m_baseParams, vector<GatherArgs>(iterations.begin(), iterations.end()));
   2232 }
   2233 
   2234 TestInstance* TextureGatherCubeCase::createInstance (Context& context) const
   2235 {
   2236 	const vector<GatherCubeArgs>	iterations	= generateCubeCaseIterations(m_baseParams.gatherType,
   2237 																			 m_baseParams.textureFormat,
   2238 																			 getOffsetRange(m_baseParams.offsetSize, context.getDeviceProperties().limits));
   2239 
   2240 	return new TextureGatherCubeInstance(context, m_baseParams, m_textureSize, iterations);
   2241 }
   2242 
   2243 class TextureGatherTests : public tcu::TestCaseGroup
   2244 {
   2245 public:
   2246 								TextureGatherTests				(tcu::TestContext& context);
   2247 	virtual						~TextureGatherTests				(void);
   2248 	virtual void				init							(void);
   2249 
   2250 private:
   2251 								TextureGatherTests				(const TextureGatherTests&);		// not allowed!
   2252 	TextureGatherTests&			operator=						(const TextureGatherTests&);		// not allowed!
   2253 };
   2254 
   2255 TextureGatherTests::TextureGatherTests (tcu::TestContext& context)
   2256 	: TestCaseGroup(context, "texture_gather", "textureGather* tests")
   2257 {
   2258 }
   2259 
   2260 TextureGatherTests::~TextureGatherTests (void)
   2261 {
   2262 }
   2263 
   2264 static inline TestCase* makeTextureGatherCase (TextureType					textureType,
   2265 											   tcu::TestContext&			testCtx,
   2266 											   const string&				name,
   2267 											   const string&				description,
   2268 											   GatherType					gatherType,
   2269 											   OffsetSize					offsetSize,
   2270 											   tcu::TextureFormat			textureFormat,
   2271 											   tcu::Sampler::CompareMode	shadowCompareMode,
   2272 											   tcu::Sampler::WrapMode		wrapS,
   2273 											   tcu::Sampler::WrapMode		wrapT,
   2274 											   const MaybeTextureSwizzle&	texSwizzle,
   2275 											   tcu::Sampler::FilterMode		minFilter,
   2276 											   tcu::Sampler::FilterMode		magFilter,
   2277 											   int							baseLevel,
   2278 											   const IVec3&					textureSize,
   2279 											   deUint32						flags = 0,
   2280 											   const ImageBackingMode		sparseCase = ShaderRenderCaseInstance::IMAGE_BACKING_MODE_REGULAR)
   2281 {
   2282 	switch (textureType)
   2283 	{
   2284 		case TEXTURETYPE_2D:
   2285 			return new TextureGather2DCase(testCtx, name, description, gatherType, offsetSize, textureFormat, shadowCompareMode,
   2286 										   wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, flags, textureSize.swizzle(0, 1), sparseCase);
   2287 
   2288 		case TEXTURETYPE_2D_ARRAY:
   2289 			return new TextureGather2DArrayCase(testCtx, name, description, gatherType, offsetSize, textureFormat, shadowCompareMode,
   2290 												wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, flags, textureSize, sparseCase);
   2291 
   2292 		case TEXTURETYPE_CUBE:
   2293 			DE_ASSERT(gatherType == GATHERTYPE_BASIC);
   2294 			DE_ASSERT(offsetSize == OFFSETSIZE_NONE);
   2295 			return new TextureGatherCubeCase(testCtx, name, description, textureFormat, shadowCompareMode,
   2296 											 wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, flags, textureSize.x(), sparseCase);
   2297 
   2298 		default:
   2299 			DE_ASSERT(false);
   2300 			return DE_NULL;
   2301 	}
   2302 }
   2303 
   2304 static inline const char* compareModeName (tcu::Sampler::CompareMode mode)
   2305 {
   2306 	switch (mode)
   2307 	{
   2308 		case tcu::Sampler::COMPAREMODE_LESS:				return "less";
   2309 		case tcu::Sampler::COMPAREMODE_LESS_OR_EQUAL:		return "less_or_equal";
   2310 		case tcu::Sampler::COMPAREMODE_GREATER:				return "greater";
   2311 		case tcu::Sampler::COMPAREMODE_GREATER_OR_EQUAL:	return "greater_or_equal";
   2312 		case tcu::Sampler::COMPAREMODE_EQUAL:				return "equal";
   2313 		case tcu::Sampler::COMPAREMODE_NOT_EQUAL:			return "not_equal";
   2314 		case tcu::Sampler::COMPAREMODE_ALWAYS:				return "always";
   2315 		case tcu::Sampler::COMPAREMODE_NEVER:				return "never";
   2316 		default: DE_ASSERT(false); return DE_NULL;
   2317 	}
   2318 }
   2319 
   2320 void TextureGatherTests::init (void)
   2321 {
   2322 	const struct
   2323 	{
   2324 		const char* name;
   2325 		TextureType type;
   2326 	} textureTypes[] =
   2327 	{
   2328 		{ "2d",			TEXTURETYPE_2D			},
   2329 		{ "2d_array",	TEXTURETYPE_2D_ARRAY	},
   2330 		{ "cube",		TEXTURETYPE_CUBE		}
   2331 	};
   2332 
   2333 	const struct
   2334 	{
   2335 		const char*			name;
   2336 		tcu::TextureFormat	format;
   2337 	} formats[] =
   2338 	{
   2339 		{ "rgba8",		tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNORM_INT8)		},
   2340 		{ "rgba8ui",	tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNSIGNED_INT8)	},
   2341 		{ "rgba8i",		tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::SIGNED_INT8)	},
   2342 		{ "depth32f",	tcu::TextureFormat(tcu::TextureFormat::D,		tcu::TextureFormat::FLOAT)			}
   2343 	};
   2344 
   2345 	const struct
   2346 	{
   2347 		const char*		name;
   2348 		IVec3			size;
   2349 	} textureSizes[] =
   2350 	{
   2351 		{ "size_pot",	IVec3(64, 64, 3) },
   2352 		{ "size_npot",	IVec3(17, 23, 3) }
   2353 	};
   2354 
   2355 	const struct
   2356 	{
   2357 		const char*				name;
   2358 		tcu::Sampler::WrapMode	mode;
   2359 	} wrapModes[] =
   2360 	{
   2361 		{ "clamp_to_edge",		tcu::Sampler::CLAMP_TO_EDGE			},
   2362 		{ "repeat",				tcu::Sampler::REPEAT_GL				},
   2363 		{ "mirrored_repeat",	tcu::Sampler::MIRRORED_REPEAT_GL	}
   2364 	};
   2365 
   2366 	for (int gatherTypeI = 0; gatherTypeI < GATHERTYPE_LAST; gatherTypeI++)
   2367 	{
   2368 		const GatherType		gatherType			= (GatherType)gatherTypeI;
   2369 		TestCaseGroup* const	gatherTypeGroup		= new TestCaseGroup(m_testCtx, gatherTypeName(gatherType), gatherTypeDescription(gatherType));
   2370 		addChild(gatherTypeGroup);
   2371 
   2372 		for (int offsetSizeI = 0; offsetSizeI < OFFSETSIZE_LAST; offsetSizeI++)
   2373 		{
   2374 			const OffsetSize offsetSize = (OffsetSize)offsetSizeI;
   2375 			if ((gatherType == GATHERTYPE_BASIC) != (offsetSize == OFFSETSIZE_NONE))
   2376 				continue;
   2377 
   2378 			if (gatherType == GATHERTYPE_OFFSETS && offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM) // \note offsets argument must be compile-time constant
   2379 				continue;
   2380 
   2381 			TestCaseGroup* const offsetSizeGroup = offsetSize == OFFSETSIZE_NONE ?
   2382 													gatherTypeGroup :
   2383 													new TestCaseGroup(m_testCtx,
   2384 																	  offsetSize == OFFSETSIZE_MINIMUM_REQUIRED				? "min_required_offset"
   2385 																	  : offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM		? "implementation_offset"
   2386 																	  : DE_NULL,
   2387 																	  offsetSize == OFFSETSIZE_MINIMUM_REQUIRED				? "Use offsets within Vulkan minimum required range"
   2388 																	  : offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM		? "Use offsets within the implementation range"
   2389 																	  : DE_NULL);
   2390 
   2391 			if (offsetSizeGroup != gatherTypeGroup)
   2392 				gatherTypeGroup->addChild(offsetSizeGroup);
   2393 
   2394 			for (int textureTypeNdx = 0; textureTypeNdx < DE_LENGTH_OF_ARRAY(textureTypes); textureTypeNdx++)
   2395 			{
   2396 				const TextureType textureType = textureTypes[textureTypeNdx].type;
   2397 
   2398 				if (textureType == TEXTURETYPE_CUBE && gatherType != GATHERTYPE_BASIC)
   2399 					continue;
   2400 
   2401 				TestCaseGroup* const textureTypeGroup = new TestCaseGroup(m_testCtx, textureTypes[textureTypeNdx].name, "");
   2402 				offsetSizeGroup->addChild(textureTypeGroup);
   2403 
   2404 				for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
   2405 				{
   2406 					const tcu::TextureFormat&	format			= formats[formatNdx].format;
   2407 					TestCaseGroup* const		formatGroup		= new TestCaseGroup(m_testCtx, formats[formatNdx].name, "");
   2408 					textureTypeGroup->addChild(formatGroup);
   2409 
   2410 					for (int noCornersI = 0; noCornersI <= ((textureType == TEXTURETYPE_CUBE)?1:0); noCornersI++)
   2411 					{
   2412 						const bool				noCorners		= noCornersI!= 0;
   2413 						TestCaseGroup* const	cornersGroup	= noCorners
   2414 																? new TestCaseGroup(m_testCtx, "no_corners", "Test case variants that don't sample around cube map corners")
   2415 																: formatGroup;
   2416 
   2417 						if (formatGroup != cornersGroup)
   2418 							formatGroup->addChild(cornersGroup);
   2419 
   2420 						for (int textureSizeNdx = 0; textureSizeNdx < DE_LENGTH_OF_ARRAY(textureSizes); textureSizeNdx++)
   2421 						{
   2422 							const IVec3&			textureSize			= textureSizes[textureSizeNdx].size;
   2423 							TestCaseGroup* const	textureSizeGroup	= new TestCaseGroup(m_testCtx, textureSizes[textureSizeNdx].name, "");
   2424 							cornersGroup->addChild(textureSizeGroup);
   2425 
   2426 							for (int compareModeI = 0; compareModeI < tcu::Sampler::COMPAREMODE_LAST; compareModeI++)
   2427 							{
   2428 								const tcu::Sampler::CompareMode compareMode = (tcu::Sampler::CompareMode)compareModeI;
   2429 
   2430 								if ((compareMode != tcu::Sampler::COMPAREMODE_NONE) != isDepthFormat(format))
   2431 									continue;
   2432 
   2433 								if (compareMode != tcu::Sampler::COMPAREMODE_NONE &&
   2434 									compareMode != tcu::Sampler::COMPAREMODE_LESS &&
   2435 									compareMode != tcu::Sampler::COMPAREMODE_GREATER)
   2436 									continue;
   2437 
   2438 								TestCaseGroup* const compareModeGroup = compareMode == tcu::Sampler::COMPAREMODE_NONE ?
   2439 																			textureSizeGroup :
   2440 																			new TestCaseGroup(m_testCtx,
   2441 																							  (string() + "compare_" + compareModeName(compareMode)).c_str(),
   2442 																							  "");
   2443 								if (compareModeGroup != textureSizeGroup)
   2444 									textureSizeGroup->addChild(compareModeGroup);
   2445 
   2446 								for (int wrapCaseNdx = 0; wrapCaseNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapCaseNdx++)
   2447 								{
   2448 									const int						wrapSNdx	= wrapCaseNdx;
   2449 									const int						wrapTNdx	= (wrapCaseNdx + 1) % DE_LENGTH_OF_ARRAY(wrapModes);
   2450 									const tcu::Sampler::WrapMode	wrapS		= wrapModes[wrapSNdx].mode;
   2451 									const tcu::Sampler::WrapMode	wrapT		= wrapModes[wrapTNdx].mode;
   2452 
   2453 									const string caseName = string() + wrapModes[wrapSNdx].name + "_" + wrapModes[wrapTNdx].name;
   2454 
   2455 									compareModeGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, caseName.c_str(), "", gatherType, offsetSize, format, compareMode, wrapS, wrapT,
   2456 																					 MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, textureSize,
   2457 																					 noCorners ? GATHERCASE_DONT_SAMPLE_CUBE_CORNERS : 0));
   2458 									compareModeGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, "sparse_" + caseName, "", gatherType, offsetSize, format, compareMode, wrapS, wrapT,
   2459 																					 MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, textureSize,
   2460 																					 noCorners ? GATHERCASE_DONT_SAMPLE_CUBE_CORNERS : 0, ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE));
   2461 								}
   2462 							}
   2463 						}
   2464 					}
   2465 
   2466 					if (offsetSize != OFFSETSIZE_MINIMUM_REQUIRED || gatherType == GATHERTYPE_OFFSETS) // Don't test all features for both offset size types, as they should be rather orthogonal.
   2467 					{
   2468 						if (!isDepthFormat(format))
   2469 						{
   2470 							TestCaseGroup* const swizzleGroup = new TestCaseGroup(m_testCtx, "texture_swizzle", "");
   2471 							formatGroup->addChild(swizzleGroup);
   2472 
   2473 							DE_STATIC_ASSERT(TEXTURESWIZZLECOMPONENT_R == 0);
   2474 							for (int swizzleCaseNdx = 0; swizzleCaseNdx < TEXTURESWIZZLECOMPONENT_LAST; swizzleCaseNdx++)
   2475 							{
   2476 								MaybeTextureSwizzle	swizzle	= MaybeTextureSwizzle::createSomeTextureSwizzle();
   2477 								string				caseName;
   2478 
   2479 								for (int i = 0; i < 4; i++)
   2480 								{
   2481 									swizzle.getSwizzle()[i] = (TextureSwizzleComponent)((swizzleCaseNdx + i) % (int)TEXTURESWIZZLECOMPONENT_LAST);
   2482 									caseName += (i > 0 ? "_" : "") + de::toLower(de::toString(swizzle.getSwizzle()[i]));
   2483 								}
   2484 
   2485 								swizzleGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, caseName.c_str(), "", gatherType, offsetSize, format,
   2486 																			 tcu::Sampler::COMPAREMODE_NONE, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2487 																			 swizzle, tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, IVec3(64, 64, 3)));
   2488 								swizzleGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, "sparse_" + caseName, "", gatherType, offsetSize, format,
   2489 																			 tcu::Sampler::COMPAREMODE_NONE, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2490 																			 swizzle, tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, IVec3(64, 64, 3), 0, ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE));
   2491 							}
   2492 						}
   2493 
   2494 						{
   2495 							TestCaseGroup* const filterModeGroup = new TestCaseGroup(m_testCtx, "filter_mode", "Test that filter modes have no effect");
   2496 							formatGroup->addChild(filterModeGroup);
   2497 
   2498 							const struct
   2499 							{
   2500 								const char*					name;
   2501 								tcu::Sampler::FilterMode	filter;
   2502 							} magFilters[] =
   2503 							{
   2504 								{ "linear",		tcu::Sampler::LINEAR	},
   2505 								{ "nearest",	tcu::Sampler::NEAREST	}
   2506 							};
   2507 
   2508 							const struct
   2509 							{
   2510 								const char*					name;
   2511 								tcu::Sampler::FilterMode	filter;
   2512 							} minFilters[] =
   2513 							{
   2514 								// \note Don't test NEAREST here, as it's covered by other cases.
   2515 								{ "linear",						tcu::Sampler::LINEAR					},
   2516 								{ "nearest_mipmap_nearest",		tcu::Sampler::NEAREST_MIPMAP_NEAREST	},
   2517 								{ "nearest_mipmap_linear",		tcu::Sampler::NEAREST_MIPMAP_LINEAR		},
   2518 								{ "linear_mipmap_nearest",		tcu::Sampler::LINEAR_MIPMAP_NEAREST		},
   2519 								{ "linear_mipmap_linear",		tcu::Sampler::LINEAR_MIPMAP_LINEAR		},
   2520 							};
   2521 
   2522 							for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilters); minFilterNdx++)
   2523 							for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilters); magFilterNdx++)
   2524 							{
   2525 								const tcu::Sampler::FilterMode		minFilter		= minFilters[minFilterNdx].filter;
   2526 								const tcu::Sampler::FilterMode		magFilter		= magFilters[magFilterNdx].filter;
   2527 								const tcu::Sampler::CompareMode		compareMode		= isDepthFormat(format) ? tcu::Sampler::COMPAREMODE_LESS : tcu::Sampler::COMPAREMODE_NONE;
   2528 
   2529 								if ((isUnormFormatType(format.type) || isDepthFormat(format)) && magFilter == tcu::Sampler::NEAREST)
   2530 									continue; // Covered by other cases.
   2531 								if ((isUIntFormatType(format.type) || isSIntFormatType(format.type)) &&
   2532 									(magFilter != tcu::Sampler::NEAREST || minFilter != tcu::Sampler::NEAREST_MIPMAP_NEAREST))
   2533 									continue;
   2534 
   2535 								const string caseName = string() + "min_" + minFilters[minFilterNdx].name + "_mag_" + magFilters[magFilterNdx].name;
   2536 
   2537 								filterModeGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, caseName.c_str(), "", gatherType, offsetSize, format, compareMode,
   2538 																				tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, MaybeTextureSwizzle::createNoneTextureSwizzle(),
   2539 																				minFilter, magFilter, 0, IVec3(64, 64, 3)));
   2540 								filterModeGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, "sparse_" + caseName, "", gatherType, offsetSize, format, compareMode,
   2541 																				tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, MaybeTextureSwizzle::createNoneTextureSwizzle(),
   2542 																				minFilter, magFilter, 0, IVec3(64, 64, 3), 0, ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE));
   2543 							}
   2544 						}
   2545 
   2546 						{
   2547 							TestCaseGroup* const baseLevelGroup = new TestCaseGroup(m_testCtx, "base_level", "");
   2548 							formatGroup->addChild(baseLevelGroup);
   2549 
   2550 							for (int baseLevel = 1; baseLevel <= 2; baseLevel++)
   2551 							{
   2552 								const string						caseName		= "level_" + de::toString(baseLevel);
   2553 								const tcu::Sampler::CompareMode		compareMode		= isDepthFormat(format) ? tcu::Sampler::COMPAREMODE_LESS : tcu::Sampler::COMPAREMODE_NONE;
   2554 								baseLevelGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, caseName.c_str(), "", gatherType, offsetSize, format,
   2555 																			   compareMode, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2556 																			   MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST, tcu::Sampler::NEAREST,
   2557 																			   baseLevel, IVec3(64, 64, 3)));
   2558 								baseLevelGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, "sparse_" + caseName, "", gatherType, offsetSize, format,
   2559 																			   compareMode, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2560 																			   MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST, tcu::Sampler::NEAREST,
   2561 																			   baseLevel, IVec3(64, 64, 3), 0, ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE));
   2562 							}
   2563 						}
   2564 					}
   2565 				}
   2566 			}
   2567 		}
   2568 	}
   2569 }
   2570 
   2571 } // anonymous
   2572 
   2573 tcu::TestCaseGroup* createTextureGatherTests (tcu::TestContext& testCtx)
   2574 {
   2575 	return new TextureGatherTests(testCtx);
   2576 }
   2577 
   2578 } // sr
   2579 } // vkt
   2580