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