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