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
43using tcu::ConstPixelBufferAccess;
44using tcu::PixelBufferAccess;
45using tcu::TestLog;
46using tcu::IVec2;
47using tcu::IVec3;
48using tcu::IVec4;
49using tcu::UVec4;
50using tcu::Vec2;
51using tcu::Vec3;
52using tcu::Vec4;
53using de::MovePtr;
54
55using std::string;
56using std::vector;
57
58namespace vkt
59{
60namespace sr
61{
62namespace
63{
64
65typedef ShaderRenderCaseInstance::ImageBackingMode ImageBackingMode;
66
67enum
68{
69	SPEC_MAX_MIN_OFFSET = -8,
70	SPEC_MIN_MAX_OFFSET = 7
71};
72
73enum 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
83namespace TextureTestUtil
84{
85
86inline tcu::IVec4 getBitsVec (const tcu::PixelFormat& format)
87{
88	return tcu::IVec4(format.redBits, format.greenBits, format.blueBits, format.alphaBits);
89}
90
91inline 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
99void 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
109void 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
119void 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.
162static inline int divRoundToZero (int a, int b)
163{
164	return de::abs(a) / de::abs(b) * deSign32(a) * deSign32(b);
165}
166
167static 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
188static inline bool isDepthFormat (const tcu::TextureFormat& fmt)
189{
190	return fmt.order == tcu::TextureFormat::D || fmt.order == tcu::TextureFormat::DS;
191}
192
193static 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
200static 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
207static 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
214enum 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
226static 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
240struct MaybeTextureSwizzle
241{
242public:
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
253private:
254													MaybeTextureSwizzle			(void);
255
256	tcu::Vector<TextureSwizzleComponent, 4>			m_swizzle;
257	bool											m_isSome;
258};
259
260static 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
274MaybeTextureSwizzle 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
287MaybeTextureSwizzle 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
300bool MaybeTextureSwizzle::isSome (void) const
301{
302	return m_isSome;
303}
304
305bool MaybeTextureSwizzle::isNone (void) const
306{
307	return !m_isSome;
308}
309
310bool 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
319tcu::Vector<TextureSwizzleComponent, 4>& MaybeTextureSwizzle::getSwizzle (void)
320{
321	return m_swizzle;
322}
323
324const tcu::Vector<TextureSwizzleComponent, 4>& MaybeTextureSwizzle::getSwizzle (void) const
325{
326	return m_swizzle;
327}
328
329MaybeTextureSwizzle::MaybeTextureSwizzle (void)
330	: m_swizzle	(TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST)
331	, m_isSome	(false)
332{
333}
334
335static 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
349template <typename T>
350static 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
364template <typename T>
365static 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
375template <typename T>
376static 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
387static 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
404static 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
416static 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
428static 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
444static tcu::Texture2DView getOneLevelSubView (const tcu::Texture2DView& view, int level)
445{
446	return tcu::Texture2DView(1, view.getLevels() + level);
447}
448
449static tcu::Texture2DArrayView getOneLevelSubView (const tcu::Texture2DArrayView& view, int level)
450{
451	return tcu::Texture2DArrayView(1, view.getLevels() + level);
452}
453
454static 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
464class PixelOffsets
465{
466public:
467	virtual void operator() (const IVec2& pixCoord, IVec2 (&dst)[4]) const = 0;
468	virtual ~PixelOffsets (void) {}
469};
470
471class MultiplePixelOffsets : public PixelOffsets
472{
473public:
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
491private:
492	IVec2 m_offsets[4];
493};
494
495class SinglePixelOffsets : public MultiplePixelOffsets
496{
497public:
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
507class DynamicSinglePixelOffsets : public PixelOffsets
508{
509public:
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
518private:
519	IVec2 m_offsetRange;
520};
521
522template <typename T>
523static 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
531template <int N>
532static 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().
542static 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
552template <typename ColorScalarType>
553static 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
558template <typename ColorScalarType>
559static 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
564template <typename ColorScalarType>
565static 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
572static 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
577static 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
582static 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
589template <typename PrecType, typename ColorScalarT>
590static 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
603static 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
616template <typename ColorScalarType, typename PrecType, typename TexViewT, typename TexCoordT>
617static 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
676class PixelCompareRefZ
677{
678public:
679	virtual float operator() (const IVec2& pixCoord) const = 0;
680};
681
682class PixelCompareRefZDefault : public PixelCompareRefZ
683{
684public:
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
692private:
693	IVec2 m_renderSize;
694};
695
696template <typename TexViewT, typename TexCoordT>
697static 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
753enum GatherType
754{
755	GATHERTYPE_BASIC = 0,
756	GATHERTYPE_OFFSET,
757	GATHERTYPE_OFFSET_DYNAMIC,
758	GATHERTYPE_OFFSETS,
759
760	GATHERTYPE_LAST
761};
762
763enum GatherCaseFlags
764{
765	GATHERCASE_DONT_SAMPLE_CUBE_CORNERS	= (1<<0)	//!< For cube map cases: do not sample cube corners
766};
767
768enum OffsetSize
769{
770	OFFSETSIZE_NONE = 0,
771	OFFSETSIZE_MINIMUM_REQUIRED,
772	OFFSETSIZE_IMPLEMENTATION_MAXIMUM,
773
774	OFFSETSIZE_LAST
775};
776
777static 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
789static 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
801static 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
807struct 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
832static 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
855static 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
879static 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
907static 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
918vector<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
990struct 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
1051IVec2 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
1072IVec2 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
1093class TextureGatherInstance : public ShaderRenderCaseInstance
1094{
1095public:
1096										TextureGatherInstance		(Context&						context,
1097																	 const GatherCaseBaseParams&	baseParams);
1098	virtual								~TextureGatherInstance		(void);
1099
1100	virtual tcu::TestStatus				iterate						(void);
1101
1102protected:
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
1121protected:
1122	static const IVec2					RENDER_SIZE;
1123
1124	const GatherCaseBaseParams			m_baseParams;
1125
1126private:
1127	const tcu::TextureFormat			m_colorBufferFormat;
1128	int									m_currentIteration;
1129};
1130
1131const IVec2 TextureGatherInstance::RENDER_SIZE = IVec2(64, 64);
1132
1133TextureGatherInstance::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
1156TextureGatherInstance::~TextureGatherInstance (void)
1157{
1158}
1159
1160void 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
1224void 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
1257tcu::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
1305void 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
1331template <typename TexViewT, typename TexCoordT>
1332bool 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
1411glu::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
1450glu::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
1523string 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
1631void 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
1658class TextureGather2DInstance : public TextureGatherInstance
1659{
1660public:
1661									TextureGather2DInstance				(Context&						context,
1662																		 const GatherCaseBaseParams&	baseParams,
1663																		 const IVec2&					textureSize,
1664																		 const vector<GatherArgs>&		iterations);
1665	virtual							~TextureGather2DInstance			(void);
1666
1667protected:
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
1675private:
1676	const IVec2						m_textureSize;
1677	const vector<GatherArgs>		m_iterations;
1678
1679	tcu::Texture2D					m_swizzledTexture;
1680};
1681
1682TextureGather2DInstance::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
1694TextureGather2DInstance::~TextureGather2DInstance (void)
1695{
1696}
1697
1698vector<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
1705TextureBindingSp 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
1734bool 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
1741class TextureGather2DCase : public TestCase
1742{
1743public:
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
1765private:
1766	const GatherCaseBaseParams		m_baseParams;
1767	const IVec2						m_textureSize;
1768};
1769
1770TextureGather2DCase::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
1792TextureGather2DCase::~TextureGather2DCase (void)
1793{
1794}
1795
1796void 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
1805TestInstance* 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
1816struct Gather2DArrayArgs
1817{
1818	GatherArgs	gatherArgs;
1819	int			layerNdx;
1820
1821	operator GatherArgs() const { return gatherArgs; }
1822};
1823
1824vector<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
1864class TextureGather2DArrayInstance : public TextureGatherInstance
1865{
1866public:
1867									TextureGather2DArrayInstance		(Context&							context,
1868																		 const GatherCaseBaseParams&		baseParams,
1869																		 const IVec3&						textureSize,
1870																		 const vector<Gather2DArrayArgs>&	iterations);
1871	virtual							~TextureGather2DArrayInstance		(void);
1872
1873protected:
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
1881private:
1882	const IVec3						m_textureSize;
1883	const vector<Gather2DArrayArgs>	m_iterations;
1884
1885	tcu::Texture2DArray				m_swizzledTexture;
1886};
1887
1888TextureGather2DArrayInstance::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
1900TextureGather2DArrayInstance::~TextureGather2DArrayInstance (void)
1901{
1902}
1903
1904vector<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
1911TextureBindingSp 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
1946bool 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
1953class TextureGather2DArrayCase : public TestCase
1954{
1955public:
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
1977private:
1978	const GatherCaseBaseParams		m_baseParams;
1979	const IVec3						m_textureSize;
1980};
1981
1982TextureGather2DArrayCase::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
2004TextureGather2DArrayCase::~TextureGather2DArrayCase (void)
2005{
2006}
2007
2008void 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
2018TestInstance* 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
2030struct GatherCubeArgs
2031{
2032	GatherArgs		gatherArgs;
2033	tcu::CubeFace	face;
2034
2035	operator GatherArgs() const { return gatherArgs; }
2036};
2037
2038vector<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
2076class TextureGatherCubeInstance : public TextureGatherInstance
2077{
2078public:
2079									TextureGatherCubeInstance			(Context&							context,
2080																		 const GatherCaseBaseParams&		baseParams,
2081																		 const int							textureSize,
2082																		 const vector<GatherCubeArgs>&		iterations);
2083	virtual							~TextureGatherCubeInstance			(void);
2084
2085protected:
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
2093private:
2094	const int						m_textureSize;
2095	const vector<GatherCubeArgs>	m_iterations;
2096
2097	tcu::TextureCube				m_swizzledTexture;
2098};
2099
2100TextureGatherCubeInstance::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
2112TextureGatherCubeInstance::~TextureGatherCubeInstance (void)
2113{
2114}
2115
2116vector<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
2126TextureBindingSp 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
2165bool 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.
2173class TextureGatherCubeCase : public TestCase
2174{
2175public:
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
2195private:
2196	const GatherCaseBaseParams		m_baseParams;
2197	const int						m_textureSize;
2198};
2199
2200TextureGatherCubeCase::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
2220TextureGatherCubeCase::~TextureGatherCubeCase (void)
2221{
2222}
2223
2224void 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
2233TestInstance* 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
2242class TextureGatherTests : public tcu::TestCaseGroup
2243{
2244public:
2245								TextureGatherTests				(tcu::TestContext& context);
2246	virtual						~TextureGatherTests				(void);
2247	virtual void				init							(void);
2248
2249private:
2250								TextureGatherTests				(const TextureGatherTests&);		// not allowed!
2251	TextureGatherTests&			operator=						(const TextureGatherTests&);		// not allowed!
2252};
2253
2254TextureGatherTests::TextureGatherTests (tcu::TestContext& context)
2255	: TestCaseGroup(context, "texture_gather", "textureGather* tests")
2256{
2257}
2258
2259TextureGatherTests::~TextureGatherTests (void)
2260{
2261}
2262
2263static 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
2303static 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
2319void 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
2572tcu::TestCaseGroup* createTextureGatherTests (tcu::TestContext& testCtx)
2573{
2574	return new TextureGatherTests(testCtx);
2575}
2576
2577} // sr
2578} // vkt
2579