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 
   1087 		default:
   1088 			DE_ASSERT(false);
   1089 			return IVec2(-1);
   1090 	}
   1091 }
   1092 
   1093 class TextureGatherInstance : public ShaderRenderCaseInstance
   1094 {
   1095 public:
   1096 										TextureGatherInstance		(Context&						context,
   1097 																	 const GatherCaseBaseParams&	baseParams);
   1098 	virtual								~TextureGatherInstance		(void);
   1099 
   1100 	virtual tcu::TestStatus				iterate						(void);
   1101 
   1102 protected:
   1103 	void								init						(void);
   1104 
   1105 	virtual int							getNumIterations			(void) const = 0;
   1106 	virtual GatherArgs					getGatherArgs				(int iterationNdx) const = 0;
   1107 
   1108 	virtual void						setupDefaultInputs			(void);
   1109 	virtual void						setupUniforms				(const tcu::Vec4&);
   1110 
   1111 	template <typename TexViewT, typename TexCoordT>
   1112 	bool								verify						(const ConstPixelBufferAccess&		rendered,
   1113 																	 const TexViewT&					texture,
   1114 																	 const TexCoordT					(&bottomLeft)[4],
   1115 																	 const GatherArgs&					gatherArgs) const;
   1116 
   1117 	virtual TextureBindingSp			createTexture				(void) = 0;
   1118 	virtual vector<float>				computeQuadTexCoord			(int iterationNdx) const = 0;
   1119 	virtual bool						verify						(int iterationNdx, const ConstPixelBufferAccess& rendered) const = 0;
   1120 
   1121 protected:
   1122 	static const IVec2					RENDER_SIZE;
   1123 
   1124 	const GatherCaseBaseParams			m_baseParams;
   1125 
   1126 private:
   1127 	const tcu::TextureFormat			m_colorBufferFormat;
   1128 	int									m_currentIteration;
   1129 };
   1130 
   1131 const IVec2 TextureGatherInstance::RENDER_SIZE = IVec2(64, 64);
   1132 
   1133 TextureGatherInstance::TextureGatherInstance (Context&						context,
   1134 											  const GatherCaseBaseParams&	baseParams)
   1135 	: ShaderRenderCaseInstance	(context, false, DE_NULL, DE_NULL, DE_NULL, baseParams.sparseCase)
   1136 	, m_baseParams				(baseParams)
   1137 	, m_colorBufferFormat		(tcu::TextureFormat(tcu::TextureFormat::RGBA,
   1138 													isDepthFormat(baseParams.textureFormat) ? tcu::TextureFormat::UNORM_INT8 : baseParams.textureFormat.type))
   1139 	, m_currentIteration		(0)
   1140 {
   1141 	DE_ASSERT((m_baseParams.gatherType == GATHERTYPE_BASIC) == (m_baseParams.offsetSize == OFFSETSIZE_NONE));
   1142 	DE_ASSERT((m_baseParams.shadowCompareMode != tcu::Sampler::COMPAREMODE_NONE) == isDepthFormat(m_baseParams.textureFormat));
   1143 	DE_ASSERT(isUnormFormatType(m_colorBufferFormat.type)						||
   1144 			  m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT8		||
   1145 			  m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT16	||
   1146 			  m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT8		||
   1147 			  m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT16);
   1148 	DE_ASSERT(glu::isGLInternalColorFormatFilterable(glu::getInternalFormat(m_colorBufferFormat)) ||
   1149 			  (m_baseParams.magFilter == tcu::Sampler::NEAREST && (m_baseParams.minFilter == tcu::Sampler::NEAREST || m_baseParams.minFilter == tcu::Sampler::NEAREST_MIPMAP_NEAREST)));
   1150 	DE_ASSERT(m_baseParams.textureType == TEXTURETYPE_CUBE || !(m_baseParams.flags & GATHERCASE_DONT_SAMPLE_CUBE_CORNERS));
   1151 
   1152 	m_renderSize				= RENDER_SIZE.asUint();
   1153 	m_colorFormat				= vk::mapTextureFormat(m_colorBufferFormat);
   1154 }
   1155 
   1156 TextureGatherInstance::~TextureGatherInstance (void)
   1157 {
   1158 }
   1159 
   1160 void TextureGatherInstance::init (void)
   1161 {
   1162 	TestLog&						log					= m_context.getTestContext().getLog();
   1163 	TextureBindingSp				textureBinding;
   1164 	TextureBinding::Parameters		textureParams;
   1165 
   1166 	// Check prerequisites.
   1167 	if (requireGpuShader5(m_baseParams.gatherType, m_baseParams.offsetSize))
   1168 	{
   1169 		const vk::VkPhysicalDeviceFeatures&		deviceFeatures	= m_context.getDeviceFeatures();
   1170 		if (!deviceFeatures.shaderImageGatherExtended)
   1171 			TCU_THROW(NotSupportedError, "Extended set of image gather instructions are not supported");
   1172 	}
   1173 
   1174 	// Log and check implementation offset limits, if appropriate.
   1175 	if (m_baseParams.offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1176 	{
   1177 		const IVec2		offsetRange		= getOffsetRange(m_baseParams.offsetSize, m_context.getDeviceProperties().limits);
   1178 		log << TestLog::Integer("ImplementationMinTextureGatherOffset", "Implementation's value for minTexelGatherOffset", "", QP_KEY_TAG_NONE, offsetRange[0])
   1179 			<< TestLog::Integer("ImplementationMaxTextureGatherOffset", "Implementation's value for maxTexelGatherOffset", "", QP_KEY_TAG_NONE, offsetRange[1]);
   1180 		TCU_CHECK_MSG(offsetRange[0] <= SPEC_MAX_MIN_OFFSET, ("minTexelGatherOffset must be at most " + de::toString((int)SPEC_MAX_MIN_OFFSET)).c_str());
   1181 		TCU_CHECK_MSG(offsetRange[1] >= SPEC_MIN_MAX_OFFSET, ("maxTexelGatherOffset must be at least " + de::toString((int)SPEC_MIN_MAX_OFFSET)).c_str());
   1182 	}
   1183 
   1184 	// Initialize texture.
   1185 
   1186 	textureBinding = createTexture();
   1187 
   1188 	if (m_baseParams.textureSwizzle.isSome())
   1189 	{
   1190 		const tcu::Vector<TextureSwizzleComponent, 4>&	swizzle		= m_baseParams.textureSwizzle.getSwizzle();
   1191 
   1192 		const vk::VkComponentMapping					components	=
   1193 		{
   1194 			getTextureSwizzleComponent(swizzle[0]),
   1195 			getTextureSwizzleComponent(swizzle[1]),
   1196 			getTextureSwizzleComponent(swizzle[2]),
   1197 			getTextureSwizzleComponent(swizzle[3])
   1198 		};
   1199 
   1200 		textureParams.componentMapping = components;
   1201 	}
   1202 
   1203 	if (m_baseParams.baseLevel != 0)
   1204 		textureParams.baseMipLevel = m_baseParams.baseLevel;
   1205 
   1206 	textureBinding->setParameters(textureParams);
   1207 	m_textures.push_back(textureBinding);
   1208 
   1209 	log << TestLog::Message << "Texture base level is " << m_baseParams.baseLevel << TestLog::EndMessage
   1210 		<< TestLog::Message << "s and t wrap modes are "
   1211 							<< vk::mapWrapMode(m_baseParams.wrapS) << " and "
   1212 							<< vk::mapWrapMode(m_baseParams.wrapT) << ", respectively" << TestLog::EndMessage
   1213 		<< TestLog::Message << "Minification and magnification filter modes are "
   1214 							<< vk::mapFilterMode(m_baseParams.minFilter) << " and "
   1215 							<< vk::mapFilterMode(m_baseParams.magFilter) << ", respectively "
   1216 							<< "(note that they should have no effect on gather result)"
   1217 							<< TestLog::EndMessage
   1218 		<< TestLog::Message << "Using texture swizzle " << m_baseParams.textureSwizzle << TestLog::EndMessage;
   1219 
   1220 	if (m_baseParams.shadowCompareMode != tcu::Sampler::COMPAREMODE_NONE)
   1221 		log << TestLog::Message << "Using texture compare func " << vk::mapCompareMode(m_baseParams.shadowCompareMode) << TestLog::EndMessage;
   1222 }
   1223 
   1224 void TextureGatherInstance::setupDefaultInputs (void)
   1225 {
   1226 	const int				numVertices						= 4;
   1227 	const float				position[4*2]					=
   1228 	{
   1229 		-1.0f, -1.0f,
   1230 		-1.0f, +1.0f,
   1231 		+1.0f, -1.0f,
   1232 		+1.0f, +1.0f,
   1233 	};
   1234 	const float				normalizedCoord[4*2]			=
   1235 	{
   1236 		0.0f, 0.0f,
   1237 		0.0f, 1.0f,
   1238 		1.0f, 0.0f,
   1239 		1.0f, 1.0f,
   1240 	};
   1241 	const vector<float>		texCoord						= computeQuadTexCoord(m_currentIteration);
   1242 	const bool				needNormalizedCoordInShader		= m_baseParams.gatherType == GATHERTYPE_OFFSET_DYNAMIC || isDepthFormat(m_baseParams.textureFormat);
   1243 
   1244 	addAttribute(0u, vk::VK_FORMAT_R32G32_SFLOAT, 2 * (deUint32)sizeof(float), numVertices, position);
   1245 
   1246 	if (texCoord.size() == 2*4)
   1247 		addAttribute(1u, vk::VK_FORMAT_R32G32_SFLOAT, 2 * (deUint32)sizeof(float), numVertices, texCoord.data());
   1248 	else if (texCoord.size() == 3*4)
   1249 		addAttribute(1u, vk::VK_FORMAT_R32G32B32_SFLOAT, 3 * (deUint32)sizeof(float), numVertices, texCoord.data());
   1250 	else
   1251 		DE_ASSERT(false);
   1252 
   1253 	if (needNormalizedCoordInShader)
   1254 		addAttribute(2u, vk::VK_FORMAT_R32G32_SFLOAT, 2 * (deUint32)sizeof(float), numVertices, normalizedCoord);
   1255 }
   1256 
   1257 tcu::TestStatus TextureGatherInstance::iterate (void)
   1258 {
   1259 	TestLog&						log						= m_context.getTestContext().getLog();
   1260 	const tcu::ScopedLogSection		iterationSection		(log, "Iteration" + de::toString(m_currentIteration), "Iteration " + de::toString(m_currentIteration));
   1261 
   1262 	// Render.
   1263 
   1264 	{
   1265 		const deUint32				numVertices		= 4;
   1266 		const deUint32				numTriangles	= 2;
   1267 		const deUint16				indices[6]		= { 0, 1, 2, 2, 1, 3 };
   1268 		const vector<float>			texCoord		= computeQuadTexCoord(m_currentIteration);
   1269 
   1270 		if (texCoord.size() == 2*4)
   1271 		{
   1272 			Vec2 texCoordVec[4];
   1273 			computeTexCoordVecs(texCoord, texCoordVec);
   1274 			log << TestLog::Message << "Texture coordinates run from " << texCoordVec[0] << " to " << texCoordVec[3] << TestLog::EndMessage;
   1275 		}
   1276 		else if (texCoord.size() == 3*4)
   1277 		{
   1278 			Vec3 texCoordVec[4];
   1279 			computeTexCoordVecs(texCoord, texCoordVec);
   1280 			log << TestLog::Message << "Texture coordinates run from " << texCoordVec[0] << " to " << texCoordVec[3] << TestLog::EndMessage;
   1281 		}
   1282 		else
   1283 			DE_ASSERT(false);
   1284 
   1285 		m_vertexShaderName		= "vert";
   1286 		m_fragmentShaderName	= "frag_" + de::toString(m_currentIteration);
   1287 
   1288 		setup();
   1289 
   1290 		render(numVertices, numTriangles, indices);
   1291 	}
   1292 
   1293 	// Verify result.
   1294 
   1295 	if (!verify(m_currentIteration, getResultImage().getAccess()))
   1296 		return tcu::TestStatus::fail("Result verification failed");
   1297 
   1298 	m_currentIteration++;
   1299 	if (m_currentIteration == getNumIterations())
   1300 		return tcu::TestStatus::pass("Pass");
   1301 	else
   1302 		return tcu::TestStatus::incomplete();
   1303 }
   1304 
   1305 void TextureGatherInstance::setupUniforms (const tcu::Vec4&)
   1306 {
   1307 	deUint32	binding		= 0;
   1308 
   1309 	useSampler(binding++, 0u);
   1310 
   1311 	if (m_baseParams.gatherType == GATHERTYPE_OFFSET_DYNAMIC)
   1312 		addUniform(binding++, vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, sizeof(tcu::Vec2), RENDER_SIZE.asFloat().getPtr());
   1313 
   1314 	if (m_baseParams.offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1315 	{
   1316 		if (m_baseParams.gatherType == GATHERTYPE_OFFSET)
   1317 		{
   1318 			const GatherArgs&	gatherArgs		= getGatherArgs(m_currentIteration);
   1319 			addUniform(binding++, vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, sizeof(tcu::IVec2), gatherArgs.offsets[0].getPtr());
   1320 		}
   1321 		else if (m_baseParams.gatherType == GATHERTYPE_OFFSET_DYNAMIC)
   1322 		{
   1323 			const IVec2&		offsetRange		= getOffsetRange(m_baseParams.offsetSize, m_context.getDeviceProperties().limits);
   1324 			addUniform(binding++, vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, sizeof(tcu::IVec2), offsetRange.getPtr());
   1325 		}
   1326 		else
   1327 			DE_ASSERT(false);
   1328 	}
   1329 }
   1330 
   1331 template <typename TexViewT, typename TexCoordT>
   1332 bool TextureGatherInstance::verify (const ConstPixelBufferAccess&	rendered,
   1333 								const TexViewT&					texture,
   1334 								const TexCoordT					(&texCoords)[4],
   1335 								const GatherArgs&				gatherArgs) const
   1336 {
   1337 	TestLog& log = m_context.getTestContext().getLog();
   1338 
   1339 	{
   1340 		DE_ASSERT(m_colorBufferFormat.order == tcu::TextureFormat::RGBA);
   1341 		DE_ASSERT(m_colorBufferFormat.type == tcu::TextureFormat::UNORM_INT8		||
   1342 				  m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT8		||
   1343 				  m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT8);
   1344 
   1345 		const MovePtr<PixelOffsets>		pixelOffsets	= makePixelOffsetsFunctor(m_baseParams.gatherType, gatherArgs, getOffsetRange(m_baseParams.offsetSize, m_context.getDeviceProperties().limits));
   1346 		const tcu::PixelFormat			pixelFormat		= tcu::PixelFormat(8,8,8,8);
   1347 		const IVec4						colorBits		= tcu::max(TextureTestUtil::getBitsVec(pixelFormat) - 1, tcu::IVec4(0));
   1348 		const IVec3						coordBits		= m_baseParams.textureType == TEXTURETYPE_2D			? IVec3(20,20,0)
   1349 														: m_baseParams.textureType == TEXTURETYPE_CUBE			? IVec3(10,10,10)
   1350 														: m_baseParams.textureType == TEXTURETYPE_2D_ARRAY		? IVec3(20,20,20)
   1351 														: IVec3(-1);
   1352 		const IVec3						uvwBits			= m_baseParams.textureType == TEXTURETYPE_2D			? IVec3(7,7,0)
   1353 														: m_baseParams.textureType == TEXTURETYPE_CUBE			? IVec3(6,6,0)
   1354 														: m_baseParams.textureType == TEXTURETYPE_2D_ARRAY		? IVec3(7,7,7)
   1355 														: IVec3(-1);
   1356 		tcu::Sampler					sampler;
   1357 		sampler.wrapS		= m_baseParams.wrapS;
   1358 		sampler.wrapT		= m_baseParams.wrapT;
   1359 		sampler.compare		= m_baseParams.shadowCompareMode;
   1360 
   1361 		if (isDepthFormat(m_baseParams.textureFormat))
   1362 		{
   1363 			tcu::TexComparePrecision comparePrec;
   1364 			comparePrec.coordBits		= coordBits;
   1365 			comparePrec.uvwBits			= uvwBits;
   1366 			comparePrec.referenceBits	= 16;
   1367 			comparePrec.resultBits		= pixelFormat.redBits-1;
   1368 
   1369 			return verifyGatherOffsetsCompare(log, rendered, texture, texCoords, sampler, comparePrec, PixelCompareRefZDefault(RENDER_SIZE), *pixelOffsets);
   1370 		}
   1371 		else
   1372 		{
   1373 			const int componentNdx = de::max(0, gatherArgs.componentNdx);
   1374 
   1375 			if (isUnormFormatType(m_baseParams.textureFormat.type))
   1376 			{
   1377 				tcu::LookupPrecision lookupPrec;
   1378 				lookupPrec.colorThreshold	= tcu::computeFixedPointThreshold(colorBits);
   1379 				lookupPrec.coordBits		= coordBits;
   1380 				lookupPrec.uvwBits			= uvwBits;
   1381 				lookupPrec.colorMask		= TextureTestUtil::getCompareMask(pixelFormat);
   1382 				return verifyGatherOffsets<float>(log, rendered, texture, texCoords, sampler, lookupPrec, componentNdx, *pixelOffsets);
   1383 			}
   1384 			else if (isUIntFormatType(m_baseParams.textureFormat.type) || isSIntFormatType(m_baseParams.textureFormat.type))
   1385 			{
   1386 				tcu::IntLookupPrecision		lookupPrec;
   1387 				lookupPrec.colorThreshold	= UVec4(0);
   1388 				lookupPrec.coordBits		= coordBits;
   1389 				lookupPrec.uvwBits			= uvwBits;
   1390 				lookupPrec.colorMask		= TextureTestUtil::getCompareMask(pixelFormat);
   1391 
   1392 				if (isUIntFormatType(m_baseParams.textureFormat.type))
   1393 					return verifyGatherOffsets<deUint32>(log, rendered, texture, texCoords, sampler, lookupPrec, componentNdx, *pixelOffsets);
   1394 				else if (isSIntFormatType(m_baseParams.textureFormat.type))
   1395 					return verifyGatherOffsets<deInt32>(log, rendered, texture, texCoords, sampler, lookupPrec, componentNdx, *pixelOffsets);
   1396 				else
   1397 				{
   1398 					DE_ASSERT(false);
   1399 					return false;
   1400 				}
   1401 			}
   1402 			else
   1403 			{
   1404 				DE_ASSERT(false);
   1405 				return false;
   1406 			}
   1407 		}
   1408 	}
   1409 }
   1410 
   1411 glu::VertexSource genVertexShaderSource (bool requireGpuShader5, int numTexCoordComponents, bool useNormalizedCoordInput)
   1412 {
   1413 	DE_ASSERT(numTexCoordComponents == 2 || numTexCoordComponents == 3);
   1414 
   1415 	const string		texCoordType	= "vec" + de::toString(numTexCoordComponents);
   1416 	std::ostringstream	vert;
   1417 
   1418 	vert << "#version 310 es\n";
   1419 
   1420 	if (requireGpuShader5)
   1421 		vert << "#extension GL_EXT_gpu_shader5 : require\n";
   1422 
   1423 	vert << "\n"
   1424 			"layout (location = 0) in highp vec2 a_position;\n"
   1425 			"layout (location = 1) in highp " << texCoordType << " a_texCoord;\n";
   1426 
   1427 	if (useNormalizedCoordInput)
   1428 		vert << "layout (location = 2) in highp vec2 a_normalizedCoord; // (0,0) to (1,1)\n";
   1429 
   1430 	vert << "\n"
   1431 			"layout (location = 0) out highp " << texCoordType << " v_texCoord;\n";
   1432 
   1433 	if (useNormalizedCoordInput)
   1434 		vert << "layout (location = 1) out highp vec2 v_normalizedCoord;\n";
   1435 
   1436 	vert << "\n"
   1437 			"void main (void)\n"
   1438 			"{\n"
   1439 			"	gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);\n"
   1440 			"	v_texCoord = a_texCoord;\n";
   1441 
   1442 	if (useNormalizedCoordInput)
   1443 		vert << "	v_normalizedCoord = a_normalizedCoord;\n";
   1444 
   1445 	vert << "}\n";
   1446 
   1447 	return glu::VertexSource(vert.str());
   1448 }
   1449 
   1450 glu::FragmentSource genFragmentShaderSource (bool					requireGpuShader5,
   1451 											 int					numTexCoordComponents,
   1452 											 glu::DataType			samplerType,
   1453 											 const string&			funcCall,
   1454 											 bool					useNormalizedCoordInput,
   1455 											 bool					usePixCoord,
   1456 											 OffsetSize				offsetSize,
   1457 											 const ImageBackingMode	sparseCase)
   1458 {
   1459 	DE_ASSERT(glu::isDataTypeSampler(samplerType));
   1460 	DE_ASSERT(de::inRange(numTexCoordComponents, 2, 3));
   1461 	DE_ASSERT(!usePixCoord || useNormalizedCoordInput);
   1462 
   1463 	const string		texCoordType	= "vec" + de::toString(numTexCoordComponents);
   1464 	deUint32			binding			= 0;
   1465 	std::ostringstream	frag;
   1466 	const string		outType			= glu::getDataTypeName(getSamplerGatherResultType(samplerType));
   1467 
   1468 	if (sparseCase == ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE)
   1469 		frag	<< "#version 450\n"
   1470 				<< "#extension GL_ARB_sparse_texture2 : require\n";
   1471 	else
   1472 		frag << "#version 310 es\n";
   1473 
   1474 	if (requireGpuShader5)
   1475 		frag << "#extension GL_EXT_gpu_shader5 : require\n";
   1476 
   1477 	frag << "\n"
   1478 			"layout (location = 0) out mediump " << outType << " o_color;\n"
   1479 			"\n"
   1480 			"layout (location = 0) in highp " << texCoordType << " v_texCoord;\n";
   1481 
   1482 	if (useNormalizedCoordInput)
   1483 		frag << "layout (location = 1) in highp vec2 v_normalizedCoord;\n";
   1484 
   1485 	frag << "\n"
   1486 			"layout (binding = " << binding++ << ") uniform highp " << glu::getDataTypeName(samplerType) << " u_sampler;\n";
   1487 
   1488 	if (usePixCoord)
   1489 		frag << "layout (binding = " << binding++ << ") uniform viewportSize { highp vec2 u_viewportSize; };\n";
   1490 
   1491 	if (offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1492 		frag << "layout (binding = " << binding++ << ") uniform offset { highp ivec2 u_offset; };\n";
   1493 
   1494 	frag << "\n"
   1495 			"void main(void)\n"
   1496 			"{\n";
   1497 
   1498 	if (usePixCoord)
   1499 		frag << "	ivec2 pixCoord = ivec2(v_normalizedCoord*u_viewportSize);\n";
   1500 
   1501 	if (sparseCase == ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE)
   1502 	{
   1503 		// Texel declaration
   1504 		frag << "\t" << outType << " texel;\n";
   1505 		frag << "\tint success = " << funcCall << ";\n";
   1506 
   1507 		// Check sparse validity, and handle each case
   1508 		frag << "\tif (sparseTexelsResidentARB(success))\n"
   1509 			 << "\t\to_color = texel;\n"
   1510 			 <<	"\telse\n"
   1511 			 << "\t\to_color = " << outType << "(0.0, 0.0, 0.0, 1.0);\n";
   1512 	}
   1513 	else
   1514 	{
   1515 		frag << "\t\to_color = " << funcCall << ";\n";
   1516 	}
   1517 
   1518 	frag << "}\n";
   1519 
   1520 	return glu::FragmentSource(frag.str());
   1521 }
   1522 
   1523 string genGatherFuncCall (GatherType				gatherType,
   1524 						  const tcu::TextureFormat&	textureFormat,
   1525 						  const GatherArgs&			gatherArgs,
   1526 						  const string&				refZExpr,
   1527 						  const IVec2&				offsetRange,
   1528 						  int						indentationDepth,
   1529 						  OffsetSize				offsetSize,
   1530 						  const ImageBackingMode	sparseCase)
   1531 {
   1532 	string result;
   1533 
   1534 	if (sparseCase == ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE)
   1535 	{
   1536 		switch (gatherType)
   1537 		{
   1538 			case GATHERTYPE_BASIC:
   1539 				result += "sparseTextureGatherARB";
   1540 				break;
   1541 			case GATHERTYPE_OFFSET: // \note Fallthrough.
   1542 			case GATHERTYPE_OFFSET_DYNAMIC:
   1543 				result += "sparseTextureGatherOffsetARB";
   1544 				break;
   1545 			case GATHERTYPE_OFFSETS:
   1546 				result += "sparseTextureGatherOffsetsARB";
   1547 				break;
   1548 			default:
   1549 				DE_ASSERT(false);
   1550 		}
   1551 	}
   1552 	else
   1553 	{
   1554 		switch (gatherType)
   1555 		{
   1556 			case GATHERTYPE_BASIC:
   1557 				result += "textureGather";
   1558 				break;
   1559 			case GATHERTYPE_OFFSET: // \note Fallthrough.
   1560 			case GATHERTYPE_OFFSET_DYNAMIC:
   1561 				result += "textureGatherOffset";
   1562 				break;
   1563 			case GATHERTYPE_OFFSETS:
   1564 				result += "textureGatherOffsets";
   1565 				break;
   1566 			default:
   1567 				DE_ASSERT(false);
   1568 		}
   1569 	}
   1570 
   1571 	result += "(u_sampler, v_texCoord";
   1572 
   1573 	if (isDepthFormat(textureFormat))
   1574 	{
   1575 		DE_ASSERT(gatherArgs.componentNdx < 0);
   1576 		result += ", " + refZExpr;
   1577 	}
   1578 
   1579 	if (gatherType == GATHERTYPE_OFFSET ||
   1580 		gatherType == GATHERTYPE_OFFSET_DYNAMIC ||
   1581 		gatherType == GATHERTYPE_OFFSETS)
   1582 	{
   1583 		result += ", ";
   1584 		switch (gatherType)
   1585 		{
   1586 			case GATHERTYPE_OFFSET:
   1587 				if (offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1588 					result += "u_offset";
   1589 				else
   1590 					result += "ivec2" + de::toString(gatherArgs.offsets[0]);
   1591 				break;
   1592 
   1593 			case GATHERTYPE_OFFSET_DYNAMIC:
   1594 				if (offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1595 					result += "pixCoord.yx % ivec2(u_offset.y - u_offset.x + 1) + u_offset.x";
   1596 				else
   1597 					result += "pixCoord.yx % ivec2(" + de::toString(offsetRange.y() - offsetRange.x() + 1) + ") + " + de::toString(offsetRange.x());
   1598 				break;
   1599 
   1600 			case GATHERTYPE_OFFSETS:
   1601 				DE_ASSERT(offsetSize != OFFSETSIZE_IMPLEMENTATION_MAXIMUM);
   1602 				result += "ivec2[4](\n"
   1603 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[0]) + ",\n"
   1604 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[1]) + ",\n"
   1605 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[2]) + ",\n"
   1606 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[3]) + ")\n"
   1607 						  + string(indentationDepth, '\t') + "\t";
   1608 				break;
   1609 
   1610 			default:
   1611 				DE_ASSERT(false);
   1612 		}
   1613 	}
   1614 
   1615 	if (sparseCase == ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE)
   1616 		result += ", texel";
   1617 
   1618 	if (gatherArgs.componentNdx >= 0)
   1619 	{
   1620 		DE_ASSERT(gatherArgs.componentNdx < 4);
   1621 		result += ", " + de::toString(gatherArgs.componentNdx);
   1622 	}
   1623 
   1624 	result += ")";
   1625 
   1626 	return result;
   1627 }
   1628 
   1629 // \todo [2016-07-08 pyry] Re-use programs if sources are identical
   1630 
   1631 void genGatherPrograms (vk::SourceCollections& programCollection, const GatherCaseBaseParams& baseParams, const vector<GatherArgs>& iterations)
   1632 {
   1633 	const int					numIterations		= (int)iterations.size();
   1634 	const string				refZExpr			= "v_normalizedCoord.x";
   1635 	const IVec2&				offsetRange			= baseParams.offsetSize != OFFSETSIZE_IMPLEMENTATION_MAXIMUM ? getOffsetRange(baseParams.offsetSize) : IVec2(0);
   1636 	const bool					usePixCoord			= baseParams.gatherType == GATHERTYPE_OFFSET_DYNAMIC;
   1637 	const bool					useNormalizedCoord	= usePixCoord || isDepthFormat(baseParams.textureFormat);
   1638 	const bool					isDynamicOffset		= baseParams.gatherType == GATHERTYPE_OFFSET_DYNAMIC;
   1639 	const bool					isShadow			= isDepthFormat(baseParams.textureFormat);
   1640 	const glu::DataType			samplerType			= getSamplerType(baseParams.textureType, baseParams.textureFormat);
   1641 	const int					numDims				= getNumTextureSamplingDimensions(baseParams.textureType);
   1642 	glu::VertexSource			vert				= genVertexShaderSource(requireGpuShader5(baseParams.gatherType, baseParams.offsetSize), numDims, isDynamicOffset || isShadow);
   1643 
   1644 	programCollection.glslSources.add("vert") << vert;
   1645 
   1646 	for (int iterNdx = 0; iterNdx < numIterations; iterNdx++)
   1647 	{
   1648 		const GatherArgs&		gatherArgs			= iterations[iterNdx];
   1649 		const string			funcCall			= genGatherFuncCall(baseParams.gatherType, baseParams.textureFormat, gatherArgs, refZExpr, offsetRange, 1, baseParams.offsetSize, baseParams.sparseCase);
   1650 		glu::FragmentSource		frag				= genFragmentShaderSource(requireGpuShader5(baseParams.gatherType, baseParams.offsetSize), numDims, samplerType, funcCall, useNormalizedCoord, usePixCoord, baseParams.offsetSize, baseParams.sparseCase);
   1651 
   1652 		programCollection.glslSources.add("frag_" + de::toString(iterNdx)) << frag;
   1653 	}
   1654 }
   1655 
   1656 // 2D
   1657 
   1658 class TextureGather2DInstance : public TextureGatherInstance
   1659 {
   1660 public:
   1661 									TextureGather2DInstance				(Context&						context,
   1662 																		 const GatherCaseBaseParams&	baseParams,
   1663 																		 const IVec2&					textureSize,
   1664 																		 const vector<GatherArgs>&		iterations);
   1665 	virtual							~TextureGather2DInstance			(void);
   1666 
   1667 protected:
   1668 	virtual int						getNumIterations					(void) const				{ return (int)m_iterations.size();	}
   1669 	virtual GatherArgs				getGatherArgs						(int iterationNdx) const	{ return m_iterations[iterationNdx];}
   1670 
   1671 	virtual TextureBindingSp		createTexture						(void);
   1672 	virtual vector<float>			computeQuadTexCoord					(int iterationNdx) const;
   1673 	virtual bool					verify								(int iterationNdx, const ConstPixelBufferAccess& rendered) const;
   1674 
   1675 private:
   1676 	const IVec2						m_textureSize;
   1677 	const vector<GatherArgs>		m_iterations;
   1678 
   1679 	tcu::Texture2D					m_swizzledTexture;
   1680 };
   1681 
   1682 TextureGather2DInstance::TextureGather2DInstance (Context&						context,
   1683 												  const GatherCaseBaseParams&	baseParams,
   1684 												  const IVec2&					textureSize,
   1685 												  const vector<GatherArgs>&		iterations)
   1686 	: TextureGatherInstance		(context, baseParams)
   1687 	, m_textureSize				(textureSize)
   1688 	, m_iterations				(iterations)
   1689 	, m_swizzledTexture			(tcu::TextureFormat(), 1, 1)
   1690 {
   1691 	init();
   1692 }
   1693 
   1694 TextureGather2DInstance::~TextureGather2DInstance (void)
   1695 {
   1696 }
   1697 
   1698 vector<float> TextureGather2DInstance::computeQuadTexCoord (int /* iterationNdx */) const
   1699 {
   1700 	vector<float> res;
   1701 	TextureTestUtil::computeQuadTexCoord2D(res, Vec2(-0.3f, -0.4f), Vec2(1.5f, 1.6f));
   1702 	return res;
   1703 }
   1704 
   1705 TextureBindingSp TextureGather2DInstance::createTexture (void)
   1706 {
   1707 	TestLog&						log			= m_context.getTestContext().getLog();
   1708 	const tcu::TextureFormatInfo	texFmtInfo	= tcu::getTextureFormatInfo(m_baseParams.textureFormat);
   1709 	MovePtr<tcu::Texture2D>			texture		= MovePtr<tcu::Texture2D>(new tcu::Texture2D(m_baseParams.textureFormat, m_textureSize.x(), m_textureSize.y()));
   1710 	const tcu::Sampler				sampler		(m_baseParams.wrapS, m_baseParams.wrapT, tcu::Sampler::REPEAT_GL,
   1711 												 m_baseParams.minFilter, m_baseParams.magFilter,
   1712 												 0.0f /* LOD threshold */, true /* normalized coords */, m_baseParams.shadowCompareMode);
   1713 
   1714 	{
   1715 		const int	levelBegin	= m_baseParams.baseLevel;
   1716 		const int	levelEnd	= texture->getNumLevels();
   1717 		DE_ASSERT(m_baseParams.baseLevel < texture->getNumLevels());
   1718 
   1719 		for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
   1720 		{
   1721 			texture->allocLevel(levelNdx);
   1722 			const PixelBufferAccess& level = texture->getLevel(levelNdx);
   1723 			fillWithRandomColorTiles(level, texFmtInfo.valueMin, texFmtInfo.valueMax, (deUint32)m_context.getTestContext().getCommandLine().getBaseSeed());
   1724 			log << TestLog::Image("InputTextureLevel" + de::toString(levelNdx), "Input texture, level " + de::toString(levelNdx), level)
   1725 				<< TestLog::Message << "Note: texture level's size is " << IVec2(level.getWidth(), level.getHeight()) << TestLog::EndMessage;
   1726 		}
   1727 
   1728 		swizzleTexture(m_swizzledTexture, *texture, m_baseParams.textureSwizzle);
   1729 	}
   1730 
   1731 	return TextureBindingSp(new TextureBinding(texture.release(), sampler));
   1732 }
   1733 
   1734 bool TextureGather2DInstance::verify (int iterationNdx, const ConstPixelBufferAccess& rendered) const
   1735 {
   1736 	Vec2 texCoords[4];
   1737 	computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
   1738 	return TextureGatherInstance::verify(rendered, getOneLevelSubView(tcu::Texture2DView(m_swizzledTexture), m_baseParams.baseLevel), texCoords, m_iterations[iterationNdx]);
   1739 }
   1740 
   1741 class TextureGather2DCase : public TestCase
   1742 {
   1743 public:
   1744 									TextureGather2DCase					(tcu::TestContext&					testCtx,
   1745 																		 const string&						name,
   1746 																		 const string&						description,
   1747 																		 const GatherType					gatherType,
   1748 																		 const OffsetSize					offsetSize,
   1749 																		 const tcu::TextureFormat			textureFormat,
   1750 																		 const tcu::Sampler::CompareMode	shadowCompareMode,
   1751 																		 const tcu::Sampler::WrapMode		wrapS,
   1752 																		 const tcu::Sampler::WrapMode		wrapT,
   1753 																		 const MaybeTextureSwizzle&			textureSwizzle,
   1754 																		 const tcu::Sampler::FilterMode		minFilter,
   1755 																		 const tcu::Sampler::FilterMode		magFilter,
   1756 																		 const int							baseLevel,
   1757 																		 const deUint32						flags,
   1758 																		 const IVec2&						textureSize,
   1759 																		 const ImageBackingMode				sparseCase);
   1760 	virtual							~TextureGather2DCase				(void);
   1761 
   1762 	virtual void					initPrograms						(vk::SourceCollections& dst) const;
   1763 	virtual	TestInstance*			createInstance						(Context& context) const;
   1764 
   1765 private:
   1766 	const GatherCaseBaseParams		m_baseParams;
   1767 	const IVec2						m_textureSize;
   1768 };
   1769 
   1770 TextureGather2DCase::TextureGather2DCase (tcu::TestContext&						testCtx,
   1771 										  const string&							name,
   1772 										  const string&							description,
   1773 										  const GatherType						gatherType,
   1774 										  const OffsetSize						offsetSize,
   1775 										  const tcu::TextureFormat				textureFormat,
   1776 										  const tcu::Sampler::CompareMode		shadowCompareMode,
   1777 										  const tcu::Sampler::WrapMode			wrapS,
   1778 										  const tcu::Sampler::WrapMode			wrapT,
   1779 										  const MaybeTextureSwizzle&			textureSwizzle,
   1780 										  const tcu::Sampler::FilterMode		minFilter,
   1781 										  const tcu::Sampler::FilterMode		magFilter,
   1782 										  const int								baseLevel,
   1783 										  const deUint32						flags,
   1784 										  const IVec2&							textureSize,
   1785 										  const ImageBackingMode				sparseCase)
   1786 	: TestCase		(testCtx, name, description)
   1787 	, m_baseParams	(TEXTURETYPE_2D, gatherType, offsetSize, textureFormat, shadowCompareMode, wrapS, wrapT, textureSwizzle, minFilter, magFilter, baseLevel, flags, sparseCase)
   1788 	, m_textureSize	(textureSize)
   1789 {
   1790 }
   1791 
   1792 TextureGather2DCase::~TextureGather2DCase (void)
   1793 {
   1794 }
   1795 
   1796 void TextureGather2DCase::initPrograms (vk::SourceCollections& dst) const
   1797 {
   1798 	const vector<GatherArgs>	iterations	= generateBasic2DCaseIterations(m_baseParams.gatherType,
   1799 																			m_baseParams.textureFormat,
   1800 																			m_baseParams.offsetSize != OFFSETSIZE_IMPLEMENTATION_MAXIMUM ? getOffsetRange(m_baseParams.offsetSize) : IVec2(0));
   1801 
   1802 	genGatherPrograms(dst, m_baseParams, iterations);
   1803 }
   1804 
   1805 TestInstance* TextureGather2DCase::createInstance (Context& context) const
   1806 {
   1807 	const vector<GatherArgs>	iterations	= generateBasic2DCaseIterations(m_baseParams.gatherType,
   1808 																			m_baseParams.textureFormat,
   1809 																			getOffsetRange(m_baseParams.offsetSize, context.getDeviceProperties().limits));
   1810 
   1811 	return new TextureGather2DInstance(context, m_baseParams, m_textureSize, iterations);
   1812 }
   1813 
   1814 // 2D array
   1815 
   1816 struct Gather2DArrayArgs
   1817 {
   1818 	GatherArgs	gatherArgs;
   1819 	int			layerNdx;
   1820 
   1821 	operator GatherArgs() const { return gatherArgs; }
   1822 };
   1823 
   1824 vector<Gather2DArrayArgs> generate2DArrayCaseIterations (GatherType					gatherType,
   1825 														 const tcu::TextureFormat&	textureFormat,
   1826 														 const IVec2&				offsetRange,
   1827 														 const IVec3&				textureSize)
   1828 {
   1829 	const vector<GatherArgs>	basicIterations	= generateBasic2DCaseIterations(gatherType, textureFormat, offsetRange);
   1830 	vector<Gather2DArrayArgs>	iterations;
   1831 
   1832 	// \note Out-of-bounds layer indices are tested too.
   1833 	for (int layerNdx = -1; layerNdx < textureSize.z()+1; layerNdx++)
   1834 	{
   1835 		// Don't duplicate all cases for all layers.
   1836 		if (layerNdx == 0)
   1837 		{
   1838 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   1839 			{
   1840 				iterations.push_back(Gather2DArrayArgs());
   1841 				iterations.back().gatherArgs = basicIterations[basicNdx];
   1842 				iterations.back().layerNdx = layerNdx;
   1843 			}
   1844 		}
   1845 		else
   1846 		{
   1847 			// For other layers than 0, only test one component and one set of offsets per layer.
   1848 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   1849 			{
   1850 				if (isDepthFormat(textureFormat) || basicIterations[basicNdx].componentNdx == (layerNdx + 2) % 4)
   1851 				{
   1852 					iterations.push_back(Gather2DArrayArgs());
   1853 					iterations.back().gatherArgs = basicIterations[basicNdx];
   1854 					iterations.back().layerNdx = layerNdx;
   1855 					break;
   1856 				}
   1857 			}
   1858 		}
   1859 	}
   1860 
   1861 	return iterations;
   1862 }
   1863 
   1864 class TextureGather2DArrayInstance : public TextureGatherInstance
   1865 {
   1866 public:
   1867 									TextureGather2DArrayInstance		(Context&							context,
   1868 																		 const GatherCaseBaseParams&		baseParams,
   1869 																		 const IVec3&						textureSize,
   1870 																		 const vector<Gather2DArrayArgs>&	iterations);
   1871 	virtual							~TextureGather2DArrayInstance		(void);
   1872 
   1873 protected:
   1874 	virtual int						getNumIterations					(void) const				{ return (int)m_iterations.size();				}
   1875 	virtual GatherArgs				getGatherArgs						(int iterationNdx) const	{ return m_iterations[iterationNdx].gatherArgs;	}
   1876 
   1877 	virtual TextureBindingSp		createTexture						(void);
   1878 	virtual vector<float>			computeQuadTexCoord					(int iterationNdx) const;
   1879 	virtual bool					verify								(int iterationNdx, const ConstPixelBufferAccess& rendered) const;
   1880 
   1881 private:
   1882 	const IVec3						m_textureSize;
   1883 	const vector<Gather2DArrayArgs>	m_iterations;
   1884 
   1885 	tcu::Texture2DArray				m_swizzledTexture;
   1886 };
   1887 
   1888 TextureGather2DArrayInstance::TextureGather2DArrayInstance (Context&							context,
   1889 															const GatherCaseBaseParams&			baseParams,
   1890 															const IVec3&						textureSize,
   1891 															const vector<Gather2DArrayArgs>&	iterations)
   1892 	: TextureGatherInstance		(context, baseParams)
   1893 	, m_textureSize				(textureSize)
   1894 	, m_iterations				(iterations)
   1895 	, m_swizzledTexture			(tcu::TextureFormat(), 1, 1, 1)
   1896 {
   1897 	init();
   1898 }
   1899 
   1900 TextureGather2DArrayInstance::~TextureGather2DArrayInstance (void)
   1901 {
   1902 }
   1903 
   1904 vector<float> TextureGather2DArrayInstance::computeQuadTexCoord (int iterationNdx) const
   1905 {
   1906 	vector<float> res;
   1907 	TextureTestUtil::computeQuadTexCoord2DArray(res, m_iterations[iterationNdx].layerNdx, Vec2(-0.3f, -0.4f), Vec2(1.5f, 1.6f));
   1908 	return res;
   1909 }
   1910 
   1911 TextureBindingSp TextureGather2DArrayInstance::createTexture (void)
   1912 {
   1913 	TestLog&						log			= m_context.getTestContext().getLog();
   1914 	const tcu::TextureFormatInfo	texFmtInfo	= tcu::getTextureFormatInfo(m_baseParams.textureFormat);
   1915 	MovePtr<tcu::Texture2DArray>	texture		= MovePtr<tcu::Texture2DArray>(new tcu::Texture2DArray(m_baseParams.textureFormat, m_textureSize.x(), m_textureSize.y(), m_textureSize.z()));
   1916 	const tcu::Sampler				sampler		(m_baseParams.wrapS, m_baseParams.wrapT, tcu::Sampler::REPEAT_GL,
   1917 												 m_baseParams.minFilter, m_baseParams.magFilter,
   1918 												 0.0f /* LOD threshold */, true /* normalized coords */, m_baseParams.shadowCompareMode);
   1919 
   1920 	{
   1921 		const int	levelBegin	= m_baseParams.baseLevel;
   1922 		const int	levelEnd	= texture->getNumLevels();
   1923 		DE_ASSERT(m_baseParams.baseLevel < texture->getNumLevels());
   1924 
   1925 		for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
   1926 		{
   1927 			texture->allocLevel(levelNdx);
   1928 			const PixelBufferAccess& level = texture->getLevel(levelNdx);
   1929 			fillWithRandomColorTiles(level, texFmtInfo.valueMin, texFmtInfo.valueMax, (deUint32)m_context.getTestContext().getCommandLine().getBaseSeed());
   1930 
   1931 			log << TestLog::ImageSet("InputTextureLevel", "Input texture, level " + de::toString(levelNdx));
   1932 			for (int layerNdx = 0; layerNdx < m_textureSize.z(); layerNdx++)
   1933 				log << TestLog::Image("InputTextureLevel" + de::toString(layerNdx) + "Layer" + de::toString(layerNdx),
   1934 									  "Layer " + de::toString(layerNdx),
   1935 									  tcu::getSubregion(level, 0, 0, layerNdx, level.getWidth(), level.getHeight(), 1));
   1936 			log << TestLog::EndImageSet
   1937 				<< TestLog::Message << "Note: texture level's size is " << IVec3(level.getWidth(), level.getHeight(), level.getDepth()) << TestLog::EndMessage;
   1938 		}
   1939 
   1940 		swizzleTexture(m_swizzledTexture, *texture, m_baseParams.textureSwizzle);
   1941 	}
   1942 
   1943 	return TextureBindingSp(new TextureBinding(texture.release(), sampler));
   1944 }
   1945 
   1946 bool TextureGather2DArrayInstance::verify (int iterationNdx, const ConstPixelBufferAccess& rendered) const
   1947 {
   1948 	Vec3 texCoords[4];
   1949 	computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
   1950 	return TextureGatherInstance::verify(rendered, getOneLevelSubView(tcu::Texture2DArrayView(m_swizzledTexture), m_baseParams.baseLevel), texCoords, m_iterations[iterationNdx].gatherArgs);
   1951 }
   1952 
   1953 class TextureGather2DArrayCase : public TestCase
   1954 {
   1955 public:
   1956 									TextureGather2DArrayCase			(tcu::TestContext&					testCtx,
   1957 																		 const string&						name,
   1958 																		 const string&						description,
   1959 																		 const GatherType					gatherType,
   1960 																		 const OffsetSize					offsetSize,
   1961 																		 const tcu::TextureFormat			textureFormat,
   1962 																		 const tcu::Sampler::CompareMode	shadowCompareMode,
   1963 																		 const tcu::Sampler::WrapMode		wrapS,
   1964 																		 const tcu::Sampler::WrapMode		wrapT,
   1965 																		 const MaybeTextureSwizzle&			textureSwizzle,
   1966 																		 const tcu::Sampler::FilterMode		minFilter,
   1967 																		 const tcu::Sampler::FilterMode		magFilter,
   1968 																		 const int							baseLevel,
   1969 																		 const deUint32						flags,
   1970 																		 const IVec3&						textureSize,
   1971 																		 const ImageBackingMode				sparseCase);
   1972 	virtual							~TextureGather2DArrayCase			(void);
   1973 
   1974 	virtual void					initPrograms						(vk::SourceCollections& dst) const;
   1975 	virtual	TestInstance*			createInstance						(Context& context) const;
   1976 
   1977 private:
   1978 	const GatherCaseBaseParams		m_baseParams;
   1979 	const IVec3						m_textureSize;
   1980 };
   1981 
   1982 TextureGather2DArrayCase::TextureGather2DArrayCase (tcu::TestContext&					testCtx,
   1983 													const string&						name,
   1984 													const string&						description,
   1985 													const GatherType					gatherType,
   1986 													const OffsetSize					offsetSize,
   1987 													const tcu::TextureFormat			textureFormat,
   1988 													const tcu::Sampler::CompareMode		shadowCompareMode,
   1989 													const tcu::Sampler::WrapMode		wrapS,
   1990 													const tcu::Sampler::WrapMode		wrapT,
   1991 													const MaybeTextureSwizzle&			textureSwizzle,
   1992 													const tcu::Sampler::FilterMode		minFilter,
   1993 													const tcu::Sampler::FilterMode		magFilter,
   1994 													const int							baseLevel,
   1995 													const deUint32						flags,
   1996 													const IVec3&						textureSize,
   1997 													const ImageBackingMode				sparseCase)
   1998 	: TestCase			(testCtx, name, description)
   1999 	, m_baseParams		(TEXTURETYPE_2D_ARRAY, gatherType, offsetSize, textureFormat, shadowCompareMode, wrapS, wrapT, textureSwizzle, minFilter, magFilter, baseLevel, flags, sparseCase)
   2000 	, m_textureSize		(textureSize)
   2001 {
   2002 }
   2003 
   2004 TextureGather2DArrayCase::~TextureGather2DArrayCase (void)
   2005 {
   2006 }
   2007 
   2008 void TextureGather2DArrayCase::initPrograms (vk::SourceCollections& dst) const
   2009 {
   2010 	const vector<Gather2DArrayArgs>		iterations	= generate2DArrayCaseIterations(m_baseParams.gatherType,
   2011 																					m_baseParams.textureFormat,
   2012 																					m_baseParams.offsetSize != OFFSETSIZE_IMPLEMENTATION_MAXIMUM ? getOffsetRange(m_baseParams.offsetSize) : IVec2(0),
   2013 																					m_textureSize);
   2014 
   2015 	genGatherPrograms(dst, m_baseParams, vector<GatherArgs>(iterations.begin(), iterations.end()));
   2016 }
   2017 
   2018 TestInstance* TextureGather2DArrayCase::createInstance (Context& context) const
   2019 {
   2020 	const vector<Gather2DArrayArgs>		iterations	= generate2DArrayCaseIterations(m_baseParams.gatherType,
   2021 																					m_baseParams.textureFormat,
   2022 																					getOffsetRange(m_baseParams.offsetSize, context.getDeviceProperties().limits),
   2023 																					m_textureSize);
   2024 
   2025 	return new TextureGather2DArrayInstance(context, m_baseParams, m_textureSize, iterations);
   2026 }
   2027 
   2028 // Cube
   2029 
   2030 struct GatherCubeArgs
   2031 {
   2032 	GatherArgs		gatherArgs;
   2033 	tcu::CubeFace	face;
   2034 
   2035 	operator GatherArgs() const { return gatherArgs; }
   2036 };
   2037 
   2038 vector<GatherCubeArgs> generateCubeCaseIterations (GatherType gatherType, const tcu::TextureFormat& textureFormat, const IVec2& offsetRange)
   2039 {
   2040 	const vector<GatherArgs>	basicIterations = generateBasic2DCaseIterations(gatherType, textureFormat, offsetRange);
   2041 	vector<GatherCubeArgs>		iterations;
   2042 
   2043 	for (int cubeFaceI = 0; cubeFaceI < tcu::CUBEFACE_LAST; cubeFaceI++)
   2044 	{
   2045 		const tcu::CubeFace cubeFace = (tcu::CubeFace)cubeFaceI;
   2046 
   2047 		// Don't duplicate all cases for all faces.
   2048 		if (cubeFaceI == 0)
   2049 		{
   2050 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   2051 			{
   2052 				iterations.push_back(GatherCubeArgs());
   2053 				iterations.back().gatherArgs = basicIterations[basicNdx];
   2054 				iterations.back().face = cubeFace;
   2055 			}
   2056 		}
   2057 		else
   2058 		{
   2059 			// For other faces than first, only test one component per face.
   2060 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   2061 			{
   2062 				if (isDepthFormat(textureFormat) || basicIterations[basicNdx].componentNdx == cubeFaceI % 4)
   2063 				{
   2064 					iterations.push_back(GatherCubeArgs());
   2065 					iterations.back().gatherArgs = basicIterations[basicNdx];
   2066 					iterations.back().face = cubeFace;
   2067 					break;
   2068 				}
   2069 			}
   2070 		}
   2071 	}
   2072 
   2073 	return iterations;
   2074 }
   2075 
   2076 class TextureGatherCubeInstance : public TextureGatherInstance
   2077 {
   2078 public:
   2079 									TextureGatherCubeInstance			(Context&							context,
   2080 																		 const GatherCaseBaseParams&		baseParams,
   2081 																		 const int							textureSize,
   2082 																		 const vector<GatherCubeArgs>&		iterations);
   2083 	virtual							~TextureGatherCubeInstance			(void);
   2084 
   2085 protected:
   2086 	virtual int						getNumIterations					(void) const				{ return (int)m_iterations.size();				}
   2087 	virtual GatherArgs				getGatherArgs						(int iterationNdx) const	{ return m_iterations[iterationNdx].gatherArgs;	}
   2088 
   2089 	virtual TextureBindingSp		createTexture						(void);
   2090 	virtual vector<float>			computeQuadTexCoord					(int iterationNdx) const;
   2091 	virtual bool					verify								(int iterationNdx, const ConstPixelBufferAccess& rendered) const;
   2092 
   2093 private:
   2094 	const int						m_textureSize;
   2095 	const vector<GatherCubeArgs>	m_iterations;
   2096 
   2097 	tcu::TextureCube				m_swizzledTexture;
   2098 };
   2099 
   2100 TextureGatherCubeInstance::TextureGatherCubeInstance (Context&							context,
   2101 													  const GatherCaseBaseParams&		baseParams,
   2102 													  const int							textureSize,
   2103 													  const vector<GatherCubeArgs>&		iterations)
   2104 	: TextureGatherInstance		(context, baseParams)
   2105 	, m_textureSize				(textureSize)
   2106 	, m_iterations				(iterations)
   2107 	, m_swizzledTexture			(tcu::TextureFormat(), 1)
   2108 {
   2109 	init();
   2110 }
   2111 
   2112 TextureGatherCubeInstance::~TextureGatherCubeInstance (void)
   2113 {
   2114 }
   2115 
   2116 vector<float> TextureGatherCubeInstance::computeQuadTexCoord (int iterationNdx) const
   2117 {
   2118 	const bool		corners	= (m_baseParams.flags & GATHERCASE_DONT_SAMPLE_CUBE_CORNERS) == 0;
   2119 	const Vec2		minC	= corners ? Vec2(-1.2f) : Vec2(-0.6f, -1.2f);
   2120 	const Vec2		maxC	= corners ? Vec2( 1.2f) : Vec2( 0.6f,  1.2f);
   2121 	vector<float>	res;
   2122 	TextureTestUtil::computeQuadTexCoordCube(res, m_iterations[iterationNdx].face, minC, maxC);
   2123 	return res;
   2124 }
   2125 
   2126 TextureBindingSp TextureGatherCubeInstance::createTexture (void)
   2127 {
   2128 	TestLog&						log			= m_context.getTestContext().getLog();
   2129 	const tcu::TextureFormatInfo	texFmtInfo	= tcu::getTextureFormatInfo(m_baseParams.textureFormat);
   2130 	MovePtr<tcu::TextureCube>		texture		= MovePtr<tcu::TextureCube>(new tcu::TextureCube(m_baseParams.textureFormat, m_textureSize));
   2131 	const tcu::Sampler				sampler		(m_baseParams.wrapS, m_baseParams.wrapT, tcu::Sampler::REPEAT_GL,
   2132 												 m_baseParams.minFilter, m_baseParams.magFilter,
   2133 												 0.0f /* LOD threshold */, true /* normalized coords */, m_baseParams.shadowCompareMode,
   2134 												 0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
   2135 
   2136 	{
   2137 		const int	levelBegin	= m_baseParams.baseLevel;
   2138 		const int	levelEnd	= texture->getNumLevels();
   2139 		DE_ASSERT(m_baseParams.baseLevel < texture->getNumLevels());
   2140 
   2141 		for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
   2142 		{
   2143 			log << TestLog::ImageSet("InputTextureLevel" + de::toString(levelNdx), "Input texture, level " + de::toString(levelNdx));
   2144 
   2145 			for (int cubeFaceI = 0; cubeFaceI < tcu::CUBEFACE_LAST; cubeFaceI++)
   2146 			{
   2147 				const tcu::CubeFace			cubeFace	= (tcu::CubeFace)cubeFaceI;
   2148 				texture->allocLevel(cubeFace, levelNdx);
   2149 				const PixelBufferAccess&	levelFace	= texture->getLevelFace(levelNdx, cubeFace);
   2150 				fillWithRandomColorTiles(levelFace, texFmtInfo.valueMin, texFmtInfo.valueMax, (deUint32)m_context.getTestContext().getCommandLine().getBaseSeed() ^ (deUint32)cubeFaceI);
   2151 
   2152 				log << TestLog::Image("InputTextureLevel" + de::toString(levelNdx) + "Face" + de::toString((int)cubeFace), de::toString(cubeFace), levelFace);
   2153 			}
   2154 
   2155 			log << TestLog::EndImageSet
   2156 				<< TestLog::Message << "Note: texture level's size is " << texture->getLevelFace(levelNdx, tcu::CUBEFACE_NEGATIVE_X).getWidth() << TestLog::EndMessage;
   2157 		}
   2158 
   2159 		swizzleTexture(m_swizzledTexture, *texture, m_baseParams.textureSwizzle);
   2160 	}
   2161 
   2162 	return TextureBindingSp(new TextureBinding(texture.release(), sampler));
   2163 }
   2164 
   2165 bool TextureGatherCubeInstance::verify (int iterationNdx, const ConstPixelBufferAccess& rendered) const
   2166 {
   2167 	Vec3 texCoords[4];
   2168 	computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
   2169 	return TextureGatherInstance::verify(rendered, getOneLevelSubView(tcu::TextureCubeView(m_swizzledTexture), m_baseParams.baseLevel), texCoords, m_iterations[iterationNdx].gatherArgs);
   2170 }
   2171 
   2172 // \note Cube case always uses just basic textureGather(); offset versions are not defined for cube maps.
   2173 class TextureGatherCubeCase : public TestCase
   2174 {
   2175 public:
   2176 									TextureGatherCubeCase				(tcu::TestContext&					testCtx,
   2177 																		 const string&						name,
   2178 																		 const string&						description,
   2179 																		 const tcu::TextureFormat			textureFormat,
   2180 																		 const tcu::Sampler::CompareMode	shadowCompareMode,
   2181 																		 const tcu::Sampler::WrapMode		wrapS,
   2182 																		 const tcu::Sampler::WrapMode		wrapT,
   2183 																		 const MaybeTextureSwizzle&			textureSwizzle,
   2184 																		 const tcu::Sampler::FilterMode		minFilter,
   2185 																		 const tcu::Sampler::FilterMode		magFilter,
   2186 																		 const int							baseLevel,
   2187 																		 const deUint32						flags,
   2188 																		 const int							textureSize,
   2189 																		 const ImageBackingMode				sparseCase);
   2190 	virtual							~TextureGatherCubeCase				(void);
   2191 
   2192 	virtual void					initPrograms						(vk::SourceCollections& dst) const;
   2193 	virtual	TestInstance*			createInstance						(Context& context) const;
   2194 
   2195 private:
   2196 	const GatherCaseBaseParams		m_baseParams;
   2197 	const int						m_textureSize;
   2198 };
   2199 
   2200 TextureGatherCubeCase::TextureGatherCubeCase (tcu::TestContext&						testCtx,
   2201 											  const string&							name,
   2202 											  const string&							description,
   2203 											  const tcu::TextureFormat				textureFormat,
   2204 											  const tcu::Sampler::CompareMode		shadowCompareMode,
   2205 											  const tcu::Sampler::WrapMode			wrapS,
   2206 											  const tcu::Sampler::WrapMode			wrapT,
   2207 											  const MaybeTextureSwizzle&			textureSwizzle,
   2208 											  const tcu::Sampler::FilterMode		minFilter,
   2209 											  const tcu::Sampler::FilterMode		magFilter,
   2210 											  const int								baseLevel,
   2211 											  const deUint32						flags,
   2212 											  const int								textureSize,
   2213 											  const ImageBackingMode				sparseCase)
   2214 	: TestCase			(testCtx, name, description)
   2215 	, m_baseParams		(TEXTURETYPE_CUBE, GATHERTYPE_BASIC, OFFSETSIZE_NONE, textureFormat, shadowCompareMode, wrapS, wrapT, textureSwizzle, minFilter, magFilter, baseLevel, flags, sparseCase)
   2216 	, m_textureSize		(textureSize)
   2217 {
   2218 }
   2219 
   2220 TextureGatherCubeCase::~TextureGatherCubeCase (void)
   2221 {
   2222 }
   2223 
   2224 void TextureGatherCubeCase::initPrograms (vk::SourceCollections& dst) const
   2225 {
   2226 	const vector<GatherCubeArgs>	iterations	= generateCubeCaseIterations(m_baseParams.gatherType,
   2227 																			 m_baseParams.textureFormat,
   2228 																			 m_baseParams.offsetSize != OFFSETSIZE_IMPLEMENTATION_MAXIMUM ? getOffsetRange(m_baseParams.offsetSize) : IVec2(0));
   2229 
   2230 	genGatherPrograms(dst, m_baseParams, vector<GatherArgs>(iterations.begin(), iterations.end()));
   2231 }
   2232 
   2233 TestInstance* TextureGatherCubeCase::createInstance (Context& context) const
   2234 {
   2235 	const vector<GatherCubeArgs>	iterations	= generateCubeCaseIterations(m_baseParams.gatherType,
   2236 																			 m_baseParams.textureFormat,
   2237 																			 getOffsetRange(m_baseParams.offsetSize, context.getDeviceProperties().limits));
   2238 
   2239 	return new TextureGatherCubeInstance(context, m_baseParams, m_textureSize, iterations);
   2240 }
   2241 
   2242 class TextureGatherTests : public tcu::TestCaseGroup
   2243 {
   2244 public:
   2245 								TextureGatherTests				(tcu::TestContext& context);
   2246 	virtual						~TextureGatherTests				(void);
   2247 	virtual void				init							(void);
   2248 
   2249 private:
   2250 								TextureGatherTests				(const TextureGatherTests&);		// not allowed!
   2251 	TextureGatherTests&			operator=						(const TextureGatherTests&);		// not allowed!
   2252 };
   2253 
   2254 TextureGatherTests::TextureGatherTests (tcu::TestContext& context)
   2255 	: TestCaseGroup(context, "texture_gather", "textureGather* tests")
   2256 {
   2257 }
   2258 
   2259 TextureGatherTests::~TextureGatherTests (void)
   2260 {
   2261 }
   2262 
   2263 static inline TestCase* makeTextureGatherCase (TextureType					textureType,
   2264 											   tcu::TestContext&			testCtx,
   2265 											   const string&				name,
   2266 											   const string&				description,
   2267 											   GatherType					gatherType,
   2268 											   OffsetSize					offsetSize,
   2269 											   tcu::TextureFormat			textureFormat,
   2270 											   tcu::Sampler::CompareMode	shadowCompareMode,
   2271 											   tcu::Sampler::WrapMode		wrapS,
   2272 											   tcu::Sampler::WrapMode		wrapT,
   2273 											   const MaybeTextureSwizzle&	texSwizzle,
   2274 											   tcu::Sampler::FilterMode		minFilter,
   2275 											   tcu::Sampler::FilterMode		magFilter,
   2276 											   int							baseLevel,
   2277 											   const IVec3&					textureSize,
   2278 											   deUint32						flags = 0,
   2279 											   const ImageBackingMode		sparseCase = ShaderRenderCaseInstance::IMAGE_BACKING_MODE_REGULAR)
   2280 {
   2281 	switch (textureType)
   2282 	{
   2283 		case TEXTURETYPE_2D:
   2284 			return new TextureGather2DCase(testCtx, name, description, gatherType, offsetSize, textureFormat, shadowCompareMode,
   2285 										   wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, flags, textureSize.swizzle(0, 1), sparseCase);
   2286 
   2287 		case TEXTURETYPE_2D_ARRAY:
   2288 			return new TextureGather2DArrayCase(testCtx, name, description, gatherType, offsetSize, textureFormat, shadowCompareMode,
   2289 												wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, flags, textureSize, sparseCase);
   2290 
   2291 		case TEXTURETYPE_CUBE:
   2292 			DE_ASSERT(gatherType == GATHERTYPE_BASIC);
   2293 			DE_ASSERT(offsetSize == OFFSETSIZE_NONE);
   2294 			return new TextureGatherCubeCase(testCtx, name, description, textureFormat, shadowCompareMode,
   2295 											 wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, flags, textureSize.x(), sparseCase);
   2296 
   2297 		default:
   2298 			DE_ASSERT(false);
   2299 			return DE_NULL;
   2300 	}
   2301 }
   2302 
   2303 static inline const char* compareModeName (tcu::Sampler::CompareMode mode)
   2304 {
   2305 	switch (mode)
   2306 	{
   2307 		case tcu::Sampler::COMPAREMODE_LESS:				return "less";
   2308 		case tcu::Sampler::COMPAREMODE_LESS_OR_EQUAL:		return "less_or_equal";
   2309 		case tcu::Sampler::COMPAREMODE_GREATER:				return "greater";
   2310 		case tcu::Sampler::COMPAREMODE_GREATER_OR_EQUAL:	return "greater_or_equal";
   2311 		case tcu::Sampler::COMPAREMODE_EQUAL:				return "equal";
   2312 		case tcu::Sampler::COMPAREMODE_NOT_EQUAL:			return "not_equal";
   2313 		case tcu::Sampler::COMPAREMODE_ALWAYS:				return "always";
   2314 		case tcu::Sampler::COMPAREMODE_NEVER:				return "never";
   2315 		default: DE_ASSERT(false); return DE_NULL;
   2316 	}
   2317 }
   2318 
   2319 void TextureGatherTests::init (void)
   2320 {
   2321 	const struct
   2322 	{
   2323 		const char* name;
   2324 		TextureType type;
   2325 	} textureTypes[] =
   2326 	{
   2327 		{ "2d",			TEXTURETYPE_2D			},
   2328 		{ "2d_array",	TEXTURETYPE_2D_ARRAY	},
   2329 		{ "cube",		TEXTURETYPE_CUBE		}
   2330 	};
   2331 
   2332 	const struct
   2333 	{
   2334 		const char*			name;
   2335 		tcu::TextureFormat	format;
   2336 	} formats[] =
   2337 	{
   2338 		{ "rgba8",		tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNORM_INT8)		},
   2339 		{ "rgba8ui",	tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNSIGNED_INT8)	},
   2340 		{ "rgba8i",		tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::SIGNED_INT8)	},
   2341 		{ "depth32f",	tcu::TextureFormat(tcu::TextureFormat::D,		tcu::TextureFormat::FLOAT)			}
   2342 	};
   2343 
   2344 	const struct
   2345 	{
   2346 		const char*		name;
   2347 		IVec3			size;
   2348 	} textureSizes[] =
   2349 	{
   2350 		{ "size_pot",	IVec3(64, 64, 3) },
   2351 		{ "size_npot",	IVec3(17, 23, 3) }
   2352 	};
   2353 
   2354 	const struct
   2355 	{
   2356 		const char*				name;
   2357 		tcu::Sampler::WrapMode	mode;
   2358 	} wrapModes[] =
   2359 	{
   2360 		{ "clamp_to_edge",		tcu::Sampler::CLAMP_TO_EDGE			},
   2361 		{ "repeat",				tcu::Sampler::REPEAT_GL				},
   2362 		{ "mirrored_repeat",	tcu::Sampler::MIRRORED_REPEAT_GL	}
   2363 	};
   2364 
   2365 	for (int gatherTypeI = 0; gatherTypeI < GATHERTYPE_LAST; gatherTypeI++)
   2366 	{
   2367 		const GatherType		gatherType			= (GatherType)gatherTypeI;
   2368 		TestCaseGroup* const	gatherTypeGroup		= new TestCaseGroup(m_testCtx, gatherTypeName(gatherType), gatherTypeDescription(gatherType));
   2369 		addChild(gatherTypeGroup);
   2370 
   2371 		for (int offsetSizeI = 0; offsetSizeI < OFFSETSIZE_LAST; offsetSizeI++)
   2372 		{
   2373 			const OffsetSize offsetSize = (OffsetSize)offsetSizeI;
   2374 			if ((gatherType == GATHERTYPE_BASIC) != (offsetSize == OFFSETSIZE_NONE))
   2375 				continue;
   2376 
   2377 			if (gatherType == GATHERTYPE_OFFSETS && offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM) // \note offsets argument must be compile-time constant
   2378 				continue;
   2379 
   2380 			TestCaseGroup* const offsetSizeGroup = offsetSize == OFFSETSIZE_NONE ?
   2381 													gatherTypeGroup :
   2382 													new TestCaseGroup(m_testCtx,
   2383 																	  offsetSize == OFFSETSIZE_MINIMUM_REQUIRED				? "min_required_offset"
   2384 																	  : offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM		? "implementation_offset"
   2385 																	  : DE_NULL,
   2386 																	  offsetSize == OFFSETSIZE_MINIMUM_REQUIRED				? "Use offsets within Vulkan minimum required range"
   2387 																	  : offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM		? "Use offsets within the implementation range"
   2388 																	  : DE_NULL);
   2389 
   2390 			if (offsetSizeGroup != gatherTypeGroup)
   2391 				gatherTypeGroup->addChild(offsetSizeGroup);
   2392 
   2393 			for (int textureTypeNdx = 0; textureTypeNdx < DE_LENGTH_OF_ARRAY(textureTypes); textureTypeNdx++)
   2394 			{
   2395 				const TextureType textureType = textureTypes[textureTypeNdx].type;
   2396 
   2397 				if (textureType == TEXTURETYPE_CUBE && gatherType != GATHERTYPE_BASIC)
   2398 					continue;
   2399 
   2400 				TestCaseGroup* const textureTypeGroup = new TestCaseGroup(m_testCtx, textureTypes[textureTypeNdx].name, "");
   2401 				offsetSizeGroup->addChild(textureTypeGroup);
   2402 
   2403 				for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
   2404 				{
   2405 					const tcu::TextureFormat&	format			= formats[formatNdx].format;
   2406 					TestCaseGroup* const		formatGroup		= new TestCaseGroup(m_testCtx, formats[formatNdx].name, "");
   2407 					textureTypeGroup->addChild(formatGroup);
   2408 
   2409 					for (int noCornersI = 0; noCornersI <= ((textureType == TEXTURETYPE_CUBE)?1:0); noCornersI++)
   2410 					{
   2411 						const bool				noCorners		= noCornersI!= 0;
   2412 						TestCaseGroup* const	cornersGroup	= noCorners
   2413 																? new TestCaseGroup(m_testCtx, "no_corners", "Test case variants that don't sample around cube map corners")
   2414 																: formatGroup;
   2415 
   2416 						if (formatGroup != cornersGroup)
   2417 							formatGroup->addChild(cornersGroup);
   2418 
   2419 						for (int textureSizeNdx = 0; textureSizeNdx < DE_LENGTH_OF_ARRAY(textureSizes); textureSizeNdx++)
   2420 						{
   2421 							const IVec3&			textureSize			= textureSizes[textureSizeNdx].size;
   2422 							TestCaseGroup* const	textureSizeGroup	= new TestCaseGroup(m_testCtx, textureSizes[textureSizeNdx].name, "");
   2423 							cornersGroup->addChild(textureSizeGroup);
   2424 
   2425 							for (int compareModeI = 0; compareModeI < tcu::Sampler::COMPAREMODE_LAST; compareModeI++)
   2426 							{
   2427 								const tcu::Sampler::CompareMode compareMode = (tcu::Sampler::CompareMode)compareModeI;
   2428 
   2429 								if ((compareMode != tcu::Sampler::COMPAREMODE_NONE) != isDepthFormat(format))
   2430 									continue;
   2431 
   2432 								if (compareMode != tcu::Sampler::COMPAREMODE_NONE &&
   2433 									compareMode != tcu::Sampler::COMPAREMODE_LESS &&
   2434 									compareMode != tcu::Sampler::COMPAREMODE_GREATER)
   2435 									continue;
   2436 
   2437 								TestCaseGroup* const compareModeGroup = compareMode == tcu::Sampler::COMPAREMODE_NONE ?
   2438 																			textureSizeGroup :
   2439 																			new TestCaseGroup(m_testCtx,
   2440 																							  (string() + "compare_" + compareModeName(compareMode)).c_str(),
   2441 																							  "");
   2442 								if (compareModeGroup != textureSizeGroup)
   2443 									textureSizeGroup->addChild(compareModeGroup);
   2444 
   2445 								for (int wrapCaseNdx = 0; wrapCaseNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapCaseNdx++)
   2446 								{
   2447 									const int						wrapSNdx	= wrapCaseNdx;
   2448 									const int						wrapTNdx	= (wrapCaseNdx + 1) % DE_LENGTH_OF_ARRAY(wrapModes);
   2449 									const tcu::Sampler::WrapMode	wrapS		= wrapModes[wrapSNdx].mode;
   2450 									const tcu::Sampler::WrapMode	wrapT		= wrapModes[wrapTNdx].mode;
   2451 
   2452 									const string caseName = string() + wrapModes[wrapSNdx].name + "_" + wrapModes[wrapTNdx].name;
   2453 
   2454 									compareModeGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, caseName.c_str(), "", gatherType, offsetSize, format, compareMode, wrapS, wrapT,
   2455 																					 MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, textureSize,
   2456 																					 noCorners ? GATHERCASE_DONT_SAMPLE_CUBE_CORNERS : 0));
   2457 									compareModeGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, "sparse_" + caseName, "", gatherType, offsetSize, format, compareMode, wrapS, wrapT,
   2458 																					 MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, textureSize,
   2459 																					 noCorners ? GATHERCASE_DONT_SAMPLE_CUBE_CORNERS : 0, ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE));
   2460 								}
   2461 							}
   2462 						}
   2463 					}
   2464 
   2465 					if (offsetSize != OFFSETSIZE_MINIMUM_REQUIRED || gatherType == GATHERTYPE_OFFSETS) // Don't test all features for both offset size types, as they should be rather orthogonal.
   2466 					{
   2467 						if (!isDepthFormat(format))
   2468 						{
   2469 							TestCaseGroup* const swizzleGroup = new TestCaseGroup(m_testCtx, "texture_swizzle", "");
   2470 							formatGroup->addChild(swizzleGroup);
   2471 
   2472 							DE_STATIC_ASSERT(TEXTURESWIZZLECOMPONENT_R == 0);
   2473 							for (int swizzleCaseNdx = 0; swizzleCaseNdx < TEXTURESWIZZLECOMPONENT_LAST; swizzleCaseNdx++)
   2474 							{
   2475 								MaybeTextureSwizzle	swizzle	= MaybeTextureSwizzle::createSomeTextureSwizzle();
   2476 								string				caseName;
   2477 
   2478 								for (int i = 0; i < 4; i++)
   2479 								{
   2480 									swizzle.getSwizzle()[i] = (TextureSwizzleComponent)((swizzleCaseNdx + i) % (int)TEXTURESWIZZLECOMPONENT_LAST);
   2481 									caseName += (i > 0 ? "_" : "") + de::toLower(de::toString(swizzle.getSwizzle()[i]));
   2482 								}
   2483 
   2484 								swizzleGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, caseName.c_str(), "", gatherType, offsetSize, format,
   2485 																			 tcu::Sampler::COMPAREMODE_NONE, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2486 																			 swizzle, tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, IVec3(64, 64, 3)));
   2487 								swizzleGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, "sparse_" + caseName, "", gatherType, offsetSize, format,
   2488 																			 tcu::Sampler::COMPAREMODE_NONE, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2489 																			 swizzle, tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, IVec3(64, 64, 3), 0, ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE));
   2490 							}
   2491 						}
   2492 
   2493 						{
   2494 							TestCaseGroup* const filterModeGroup = new TestCaseGroup(m_testCtx, "filter_mode", "Test that filter modes have no effect");
   2495 							formatGroup->addChild(filterModeGroup);
   2496 
   2497 							const struct
   2498 							{
   2499 								const char*					name;
   2500 								tcu::Sampler::FilterMode	filter;
   2501 							} magFilters[] =
   2502 							{
   2503 								{ "linear",		tcu::Sampler::LINEAR	},
   2504 								{ "nearest",	tcu::Sampler::NEAREST	}
   2505 							};
   2506 
   2507 							const struct
   2508 							{
   2509 								const char*					name;
   2510 								tcu::Sampler::FilterMode	filter;
   2511 							} minFilters[] =
   2512 							{
   2513 								// \note Don't test NEAREST here, as it's covered by other cases.
   2514 								{ "linear",						tcu::Sampler::LINEAR					},
   2515 								{ "nearest_mipmap_nearest",		tcu::Sampler::NEAREST_MIPMAP_NEAREST	},
   2516 								{ "nearest_mipmap_linear",		tcu::Sampler::NEAREST_MIPMAP_LINEAR		},
   2517 								{ "linear_mipmap_nearest",		tcu::Sampler::LINEAR_MIPMAP_NEAREST		},
   2518 								{ "linear_mipmap_linear",		tcu::Sampler::LINEAR_MIPMAP_LINEAR		},
   2519 							};
   2520 
   2521 							for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilters); minFilterNdx++)
   2522 							for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilters); magFilterNdx++)
   2523 							{
   2524 								const tcu::Sampler::FilterMode		minFilter		= minFilters[minFilterNdx].filter;
   2525 								const tcu::Sampler::FilterMode		magFilter		= magFilters[magFilterNdx].filter;
   2526 								const tcu::Sampler::CompareMode		compareMode		= isDepthFormat(format) ? tcu::Sampler::COMPAREMODE_LESS : tcu::Sampler::COMPAREMODE_NONE;
   2527 
   2528 								if ((isUnormFormatType(format.type) || isDepthFormat(format)) && magFilter == tcu::Sampler::NEAREST)
   2529 									continue; // Covered by other cases.
   2530 								if ((isUIntFormatType(format.type) || isSIntFormatType(format.type)) &&
   2531 									(magFilter != tcu::Sampler::NEAREST || minFilter != tcu::Sampler::NEAREST_MIPMAP_NEAREST))
   2532 									continue;
   2533 
   2534 								const string caseName = string() + "min_" + minFilters[minFilterNdx].name + "_mag_" + magFilters[magFilterNdx].name;
   2535 
   2536 								filterModeGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, caseName.c_str(), "", gatherType, offsetSize, format, compareMode,
   2537 																				tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, MaybeTextureSwizzle::createNoneTextureSwizzle(),
   2538 																				minFilter, magFilter, 0, IVec3(64, 64, 3)));
   2539 								filterModeGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, "sparse_" + caseName, "", gatherType, offsetSize, format, compareMode,
   2540 																				tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, MaybeTextureSwizzle::createNoneTextureSwizzle(),
   2541 																				minFilter, magFilter, 0, IVec3(64, 64, 3), 0, ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE));
   2542 							}
   2543 						}
   2544 
   2545 						{
   2546 							TestCaseGroup* const baseLevelGroup = new TestCaseGroup(m_testCtx, "base_level", "");
   2547 							formatGroup->addChild(baseLevelGroup);
   2548 
   2549 							for (int baseLevel = 1; baseLevel <= 2; baseLevel++)
   2550 							{
   2551 								const string						caseName		= "level_" + de::toString(baseLevel);
   2552 								const tcu::Sampler::CompareMode		compareMode		= isDepthFormat(format) ? tcu::Sampler::COMPAREMODE_LESS : tcu::Sampler::COMPAREMODE_NONE;
   2553 								baseLevelGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, caseName.c_str(), "", gatherType, offsetSize, format,
   2554 																			   compareMode, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2555 																			   MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST, tcu::Sampler::NEAREST,
   2556 																			   baseLevel, IVec3(64, 64, 3)));
   2557 								baseLevelGroup->addChild(makeTextureGatherCase(textureType, m_testCtx, "sparse_" + caseName, "", gatherType, offsetSize, format,
   2558 																			   compareMode, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2559 																			   MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST, tcu::Sampler::NEAREST,
   2560 																			   baseLevel, IVec3(64, 64, 3), 0, ShaderRenderCaseInstance::IMAGE_BACKING_MODE_SPARSE));
   2561 							}
   2562 						}
   2563 					}
   2564 				}
   2565 			}
   2566 		}
   2567 	}
   2568 }
   2569 
   2570 } // anonymous
   2571 
   2572 tcu::TestCaseGroup* createTextureGatherTests (tcu::TestContext& testCtx)
   2573 {
   2574 	return new TextureGatherTests(testCtx);
   2575 }
   2576 
   2577 } // sr
   2578 } // vkt
   2579