Home | History | Annotate | Download | only in functional
      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 
     48 using glu::ShaderProgram;
     49 using tcu::ConstPixelBufferAccess;
     50 using tcu::PixelBufferAccess;
     51 using tcu::TestLog;
     52 using tcu::IVec2;
     53 using tcu::IVec3;
     54 using tcu::IVec4;
     55 using tcu::UVec4;
     56 using tcu::Vec2;
     57 using tcu::Vec3;
     58 using tcu::Vec4;
     59 using de::MovePtr;
     60 
     61 using std::string;
     62 using std::vector;
     63 
     64 namespace deqp
     65 {
     66 
     67 using gls::TextureTestUtil::TextureType;
     68 using gls::TextureTestUtil::TEXTURETYPE_2D;
     69 using gls::TextureTestUtil::TEXTURETYPE_2D_ARRAY;
     70 using gls::TextureTestUtil::TEXTURETYPE_CUBE;
     71 
     72 namespace gles31
     73 {
     74 namespace Functional
     75 {
     76 
     77 namespace
     78 {
     79 
     80 // Round-to-zero int division, because pre-c++11 it's somewhat implementation-defined for negative values.
     81 static inline int divRoundToZero (int a, int b)
     82 {
     83 	return de::abs(a) / de::abs(b) * deSign32(a) * deSign32(b);
     84 }
     85 
     86 static 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 
    107 static inline bool isDepthFormat (const tcu::TextureFormat& fmt)
    108 {
    109 	return fmt.order == tcu::TextureFormat::D || fmt.order == tcu::TextureFormat::DS;
    110 }
    111 
    112 static 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 
    119 static 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 
    126 static 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 
    133 static 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 
    169 enum 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 
    181 static 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 
    195 struct MaybeTextureSwizzle
    196 {
    197 public:
    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 
    208 private:
    209 													MaybeTextureSwizzle			(void);
    210 
    211 	tcu::Vector<TextureSwizzleComponent, 4>			m_swizzle;
    212 	bool											m_isSome;
    213 };
    214 
    215 static 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 
    229 MaybeTextureSwizzle 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 
    242 MaybeTextureSwizzle 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 
    255 bool MaybeTextureSwizzle::isSome (void) const
    256 {
    257 	return m_isSome;
    258 }
    259 
    260 bool MaybeTextureSwizzle::isNone (void) const
    261 {
    262 	return !m_isSome;
    263 }
    264 
    265 bool 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 
    274 tcu::Vector<TextureSwizzleComponent, 4>& MaybeTextureSwizzle::getSwizzle (void)
    275 {
    276 	return m_swizzle;
    277 }
    278 
    279 const tcu::Vector<TextureSwizzleComponent, 4>& MaybeTextureSwizzle::getSwizzle (void) const
    280 {
    281 	return m_swizzle;
    282 }
    283 
    284 MaybeTextureSwizzle::MaybeTextureSwizzle (void)
    285 	: m_swizzle	(TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST)
    286 	, m_isSome	(false)
    287 {
    288 }
    289 
    290 static 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 
    304 template <typename T>
    305 static 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 
    319 template <typename T>
    320 static 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 
    330 template <typename T>
    331 static 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 
    342 static 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 
    359 static 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 
    371 static 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 
    383 static 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 
    399 static tcu::Texture2DView getOneLevelSubView (const tcu::Texture2DView& view, int level)
    400 {
    401 	return tcu::Texture2DView(1, view.getLevels() + level);
    402 }
    403 
    404 static tcu::Texture2DArrayView getOneLevelSubView (const tcu::Texture2DArrayView& view, int level)
    405 {
    406 	return tcu::Texture2DArrayView(1, view.getLevels() + level);
    407 }
    408 
    409 static 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 
    419 class PixelOffsets
    420 {
    421 public:
    422 	virtual void operator() (const IVec2& pixCoord, IVec2 (&dst)[4]) const = 0;
    423 	virtual ~PixelOffsets (void) {}
    424 };
    425 
    426 class MultiplePixelOffsets : public PixelOffsets
    427 {
    428 public:
    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 
    446 private:
    447 	IVec2 m_offsets[4];
    448 };
    449 
    450 class SinglePixelOffsets : public MultiplePixelOffsets
    451 {
    452 public:
    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 
    462 class DynamicSinglePixelOffsets : public PixelOffsets
    463 {
    464 public:
    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 
    473 private:
    474 	IVec2 m_offsetRange;
    475 };
    476 
    477 template <typename T>
    478 static 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 
    486 template <int N>
    487 static 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().
    497 static 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 
    507 template <typename ColorScalarType>
    508 static 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 
    513 template <typename ColorScalarType>
    514 static 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 
    519 template <typename ColorScalarType>
    520 static 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 
    527 static 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 
    532 static 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 
    537 static 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 
    544 template <typename PrecType, typename ColorScalarT>
    545 static 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 
    558 static 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 
    571 template <typename ColorScalarType, typename PrecType, typename TexViewT, typename TexCoordT>
    572 static 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 
    631 class PixelCompareRefZ
    632 {
    633 public:
    634 	virtual float operator() (const IVec2& pixCoord) const = 0;
    635 };
    636 
    637 class PixelCompareRefZDefault : public PixelCompareRefZ
    638 {
    639 public:
    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 
    647 private:
    648 	IVec2 m_renderSize;
    649 };
    650 
    651 template <typename TexViewT, typename TexCoordT>
    652 static 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 
    708 static 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 
    744 enum GatherType
    745 {
    746 	GATHERTYPE_BASIC = 0,
    747 	GATHERTYPE_OFFSET,
    748 	GATHERTYPE_OFFSET_DYNAMIC,
    749 	GATHERTYPE_OFFSETS,
    750 
    751 	GATHERTYPE_LAST
    752 };
    753 
    754 static inline const char* gatherTypeName (GatherType type)
    755 {
    756 	switch (type)
    757 	{
    758 		case GATHERTYPE_BASIC:				return "basic";
    759 		case GATHERTYPE_OFFSET:				return "offset";
    760 		case GATHERTYPE_OFFSET_DYNAMIC:		return "offset_dynamic";
    761 		case GATHERTYPE_OFFSETS:			return "offsets";
    762 		default: DE_ASSERT(false); return DE_NULL;
    763 	}
    764 }
    765 
    766 static inline const char* gatherTypeDescription (GatherType type)
    767 {
    768 	switch (type)
    769 	{
    770 		case GATHERTYPE_BASIC:				return "textureGather";
    771 		case GATHERTYPE_OFFSET:				return "textureGatherOffset";
    772 		case GATHERTYPE_OFFSET_DYNAMIC:		return "textureGatherOffset with dynamic offsets";
    773 		case GATHERTYPE_OFFSETS:			return "textureGatherOffsets";
    774 		default: DE_ASSERT(false); return DE_NULL;
    775 	}
    776 }
    777 
    778 static inline bool requireGpuShader5 (GatherType gatherType)
    779 {
    780 	return gatherType == GATHERTYPE_OFFSET_DYNAMIC || gatherType == GATHERTYPE_OFFSETS;
    781 }
    782 
    783 struct GatherArgs
    784 {
    785 	int		componentNdx;	// If negative, implicit component index 0 is used (i.e. the parameter is not given).
    786 	IVec2	offsets[4];		// \note Unless GATHERTYPE_OFFSETS is used, only offsets[0] is relevant; also, for GATHERTYPE_OFFSET_DYNAMIC, none are relevant.
    787 
    788 	GatherArgs (void)
    789 		: componentNdx(-1)
    790 	{
    791 		std::fill(DE_ARRAY_BEGIN(offsets), DE_ARRAY_END(offsets), IVec2());
    792 	}
    793 
    794 	GatherArgs (int comp,
    795 				const IVec2& off0 = IVec2(),
    796 				const IVec2& off1 = IVec2(),
    797 				const IVec2& off2 = IVec2(),
    798 				const IVec2& off3 = IVec2())
    799 		: componentNdx(comp)
    800 	{
    801 		offsets[0] = off0;
    802 		offsets[1] = off1;
    803 		offsets[2] = off2;
    804 		offsets[3] = off3;
    805 	}
    806 };
    807 
    808 static MovePtr<PixelOffsets> makePixelOffsetsFunctor (GatherType gatherType, const GatherArgs& gatherArgs, const IVec2& offsetRange)
    809 {
    810 	if (gatherType == GATHERTYPE_BASIC || gatherType == GATHERTYPE_OFFSET)
    811 	{
    812 		const IVec2 offset = gatherType == GATHERTYPE_BASIC ? IVec2(0) : gatherArgs.offsets[0];
    813 		return MovePtr<PixelOffsets>(new SinglePixelOffsets(offset));
    814 	}
    815 	else if (gatherType == GATHERTYPE_OFFSET_DYNAMIC)
    816 	{
    817 		return MovePtr<PixelOffsets>(new DynamicSinglePixelOffsets(offsetRange));
    818 	}
    819 	else if (gatherType == GATHERTYPE_OFFSETS)
    820 		return MovePtr<PixelOffsets>(new MultiplePixelOffsets(gatherArgs.offsets[0],
    821 															  gatherArgs.offsets[1],
    822 															  gatherArgs.offsets[2],
    823 															  gatherArgs.offsets[3]));
    824 	else
    825 	{
    826 		DE_ASSERT(false);
    827 		return MovePtr<PixelOffsets>(DE_NULL);
    828 	}
    829 }
    830 
    831 static inline glu::DataType getSamplerType (TextureType textureType, const tcu::TextureFormat& format)
    832 {
    833 	if (isDepthFormat(format))
    834 	{
    835 		switch (textureType)
    836 		{
    837 			case TEXTURETYPE_2D:		return glu::TYPE_SAMPLER_2D_SHADOW;
    838 			case TEXTURETYPE_2D_ARRAY:	return glu::TYPE_SAMPLER_2D_ARRAY_SHADOW;
    839 			case TEXTURETYPE_CUBE:		return glu::TYPE_SAMPLER_CUBE_SHADOW;
    840 			default: DE_ASSERT(false); return glu::TYPE_LAST;
    841 		}
    842 	}
    843 	else
    844 	{
    845 		switch (textureType)
    846 		{
    847 			case TEXTURETYPE_2D:		return glu::getSampler2DType(format);
    848 			case TEXTURETYPE_2D_ARRAY:	return glu::getSampler2DArrayType(format);
    849 			case TEXTURETYPE_CUBE:		return glu::getSamplerCubeType(format);
    850 			default: DE_ASSERT(false); return glu::TYPE_LAST;
    851 		}
    852 	}
    853 }
    854 
    855 static inline glu::DataType getSamplerGatherResultType (glu::DataType samplerType)
    856 {
    857 	switch (samplerType)
    858 	{
    859 		case glu::TYPE_SAMPLER_2D_SHADOW:
    860 		case glu::TYPE_SAMPLER_2D_ARRAY_SHADOW:
    861 		case glu::TYPE_SAMPLER_CUBE_SHADOW:
    862 		case glu::TYPE_SAMPLER_2D:
    863 		case glu::TYPE_SAMPLER_2D_ARRAY:
    864 		case glu::TYPE_SAMPLER_CUBE:
    865 			return glu::TYPE_FLOAT_VEC4;
    866 
    867 		case glu::TYPE_INT_SAMPLER_2D:
    868 		case glu::TYPE_INT_SAMPLER_2D_ARRAY:
    869 		case glu::TYPE_INT_SAMPLER_CUBE:
    870 			return glu::TYPE_INT_VEC4;
    871 
    872 		case glu::TYPE_UINT_SAMPLER_2D:
    873 		case glu::TYPE_UINT_SAMPLER_2D_ARRAY:
    874 		case glu::TYPE_UINT_SAMPLER_CUBE:
    875 			return glu::TYPE_UINT_VEC4;
    876 
    877 		default:
    878 			DE_ASSERT(false);
    879 			return glu::TYPE_LAST;
    880 	}
    881 }
    882 
    883 static inline int getNumTextureSamplingDimensions (TextureType type)
    884 {
    885 	switch (type)
    886 	{
    887 		case TEXTURETYPE_2D:		return 2;
    888 		case TEXTURETYPE_2D_ARRAY:	return 3;
    889 		case TEXTURETYPE_CUBE:		return 3;
    890 		default: DE_ASSERT(false); return -1;
    891 	}
    892 }
    893 
    894 static deUint32 getGLTextureType (TextureType type)
    895 {
    896 	switch (type)
    897 	{
    898 		case TEXTURETYPE_2D:		return GL_TEXTURE_2D;
    899 		case TEXTURETYPE_2D_ARRAY:	return GL_TEXTURE_2D_ARRAY;
    900 		case TEXTURETYPE_CUBE:		return GL_TEXTURE_CUBE_MAP;
    901 		default: DE_ASSERT(false); return (deUint32)-1;
    902 	}
    903 }
    904 
    905 enum OffsetSize
    906 {
    907 	OFFSETSIZE_NONE = 0,
    908 	OFFSETSIZE_MINIMUM_REQUIRED,
    909 	OFFSETSIZE_IMPLEMENTATION_MAXIMUM,
    910 
    911 	OFFSETSIZE_LAST
    912 };
    913 
    914 static inline bool isMipmapFilter (tcu::Sampler::FilterMode filter)
    915 {
    916 	switch (filter)
    917 	{
    918 		case tcu::Sampler::NEAREST:
    919 		case tcu::Sampler::LINEAR:
    920 			return false;
    921 
    922 		case tcu::Sampler::NEAREST_MIPMAP_NEAREST:
    923 		case tcu::Sampler::NEAREST_MIPMAP_LINEAR:
    924 		case tcu::Sampler::LINEAR_MIPMAP_NEAREST:
    925 		case tcu::Sampler::LINEAR_MIPMAP_LINEAR:
    926 			return true;
    927 
    928 		default:
    929 			DE_ASSERT(false);
    930 			return false;
    931 	}
    932 }
    933 
    934 class TextureGatherCase : public TestCase
    935 {
    936 public:
    937 										TextureGatherCase		(Context&					context,
    938 																 const char*				name,
    939 																 const char*				description,
    940 																 TextureType				textureType,
    941 																 GatherType					gatherType,
    942 																 OffsetSize					offsetSize,
    943 																 tcu::TextureFormat			textureFormat,
    944 																 tcu::Sampler::CompareMode	shadowCompareMode, //!< Should be COMPAREMODE_NONE iff textureFormat is a depth format.
    945 																 tcu::Sampler::WrapMode		wrapS,
    946 																 tcu::Sampler::WrapMode		wrapT,
    947 																 const MaybeTextureSwizzle&	texSwizzle,
    948 																 // \note Filter modes have no effect on gather (except when it comes to
    949 																 //		  texture completeness); these are supposed to test just that.
    950 																 tcu::Sampler::FilterMode	minFilter,
    951 																 tcu::Sampler::FilterMode	magFilter,
    952 																 int						baseLevel,
    953 																 bool						mipmapIncomplete);
    954 
    955 	void								init					(void);
    956 	void								deinit					(void);
    957 	IterateResult						iterate					(void);
    958 
    959 protected:
    960 	IVec2								getOffsetRange			(void) const;
    961 
    962 	template <typename TexViewT, typename TexCoordT>
    963 	bool								verify					(const ConstPixelBufferAccess&	rendered,
    964 																 const TexViewT&				texture,
    965 																 const TexCoordT				(&bottomLeft)[4],
    966 																 const GatherArgs&				gatherArgs) const;
    967 
    968 	virtual void						generateIterations		(void) = 0;
    969 	virtual void						createAndUploadTexture	(void) = 0;
    970 	virtual int							getNumIterations		(void) const = 0;
    971 	virtual GatherArgs					getGatherArgs			(int iterationNdx) const = 0;
    972 	virtual vector<float>				computeQuadTexCoord		(int iterationNdx) const = 0;
    973 	virtual bool						verify					(int iterationNdx, const ConstPixelBufferAccess& rendered) const = 0;
    974 
    975 	const GatherType					m_gatherType;
    976 	const OffsetSize					m_offsetSize;
    977 	const tcu::TextureFormat			m_textureFormat;
    978 	const tcu::Sampler::CompareMode		m_shadowCompareMode;
    979 	const tcu::Sampler::WrapMode		m_wrapS;
    980 	const tcu::Sampler::WrapMode		m_wrapT;
    981 	const MaybeTextureSwizzle			m_textureSwizzle;
    982 	const tcu::Sampler::FilterMode		m_minFilter;
    983 	const tcu::Sampler::FilterMode		m_magFilter;
    984 	const int							m_baseLevel;
    985 	const bool							m_mipmapIncomplete;
    986 
    987 private:
    988 	enum
    989 	{
    990 		SPEC_MAX_MIN_OFFSET = -8,
    991 		SPEC_MIN_MAX_OFFSET = 7
    992 	};
    993 
    994 	static const IVec2					RENDER_SIZE;
    995 
    996 	static glu::VertexSource			genVertexShaderSource		(bool requireGpuShader5, int numTexCoordComponents, bool useNormalizedCoordInput);
    997 	static glu::FragmentSource			genFragmentShaderSource		(bool requireGpuShader5, int numTexCoordComponents, glu::DataType samplerType, const string& funcCall, bool useNormalizedCoordInput, bool usePixCoord);
    998 	static string						genGatherFuncCall			(GatherType, const tcu::TextureFormat&, const GatherArgs&, const string& refZExpr, const IVec2& offsetRange, int indentationDepth);
    999 	static glu::ProgramSources			genProgramSources			(GatherType, TextureType, const tcu::TextureFormat&, const GatherArgs&, const string& refZExpr, const IVec2& offsetRange);
   1000 
   1001 	const TextureType					m_textureType;
   1002 
   1003 	const tcu::TextureFormat			m_colorBufferFormat;
   1004 	MovePtr<glu::Renderbuffer>			m_colorBuffer;
   1005 	MovePtr<glu::Framebuffer>			m_fbo;
   1006 
   1007 	int									m_currentIteration;
   1008 	MovePtr<ShaderProgram>				m_program;
   1009 };
   1010 
   1011 const IVec2 TextureGatherCase::RENDER_SIZE = IVec2(64, 64);
   1012 
   1013 TextureGatherCase::TextureGatherCase (Context&						context,
   1014 									  const char*					name,
   1015 									  const char*					description,
   1016 									  TextureType					textureType,
   1017 									  GatherType					gatherType,
   1018 									  OffsetSize					offsetSize,
   1019 									  tcu::TextureFormat			textureFormat,
   1020 									  tcu::Sampler::CompareMode		shadowCompareMode, //!< Should be COMPAREMODE_NONE iff textureType == TEXTURETYPE_NORMAL.
   1021 									  tcu::Sampler::WrapMode		wrapS,
   1022 									  tcu::Sampler::WrapMode		wrapT,
   1023 									  const MaybeTextureSwizzle&	textureSwizzle,
   1024 									  tcu::Sampler::FilterMode		minFilter,
   1025 									  tcu::Sampler::FilterMode		magFilter,
   1026 									  int							baseLevel,
   1027 									  bool							mipmapIncomplete)
   1028 	: TestCase				(context, name, description)
   1029 	, m_gatherType			(gatherType)
   1030 	, m_offsetSize			(offsetSize)
   1031 	, m_textureFormat		(textureFormat)
   1032 	, m_shadowCompareMode	(shadowCompareMode)
   1033 	, m_wrapS				(wrapS)
   1034 	, m_wrapT				(wrapT)
   1035 	, m_textureSwizzle		(textureSwizzle)
   1036 	, m_minFilter			(minFilter)
   1037 	, m_magFilter			(magFilter)
   1038 	, m_baseLevel			(baseLevel)
   1039 	, m_mipmapIncomplete	(mipmapIncomplete)
   1040 	, m_textureType			(textureType)
   1041 	, m_colorBufferFormat	(tcu::TextureFormat(tcu::TextureFormat::RGBA,
   1042 												isDepthFormat(textureFormat) ? tcu::TextureFormat::UNORM_INT8 : textureFormat.type))
   1043 	, m_currentIteration	(0)
   1044 {
   1045 	DE_ASSERT((m_gatherType == GATHERTYPE_BASIC) == (m_offsetSize == OFFSETSIZE_NONE));
   1046 	DE_ASSERT((m_shadowCompareMode != tcu::Sampler::COMPAREMODE_NONE) == isDepthFormat(m_textureFormat));
   1047 	DE_ASSERT(isUnormFormatType(m_colorBufferFormat.type)						||
   1048 			  m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT8		||
   1049 			  m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT16	||
   1050 			  m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT8		||
   1051 			  m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT16);
   1052 	DE_ASSERT(glu::isGLInternalColorFormatFilterable(glu::getInternalFormat(m_colorBufferFormat)) ||
   1053 			  (m_magFilter == tcu::Sampler::NEAREST && (m_minFilter == tcu::Sampler::NEAREST || m_minFilter == tcu::Sampler::NEAREST_MIPMAP_NEAREST)));
   1054 	DE_ASSERT(isMipmapFilter(m_minFilter) || !m_mipmapIncomplete);
   1055 	DE_ASSERT(!(m_mipmapIncomplete && isDepthFormat(m_textureFormat))); // It's not clear what shadow textures should return when incomplete.
   1056 }
   1057 
   1058 IVec2 TextureGatherCase::getOffsetRange (void) const
   1059 {
   1060 	switch (m_offsetSize)
   1061 	{
   1062 		case OFFSETSIZE_NONE:
   1063 			return IVec2(0);
   1064 			break;
   1065 
   1066 		case OFFSETSIZE_MINIMUM_REQUIRED:
   1067 			// \note Defined by spec.
   1068 			return IVec2(SPEC_MAX_MIN_OFFSET,
   1069 						 SPEC_MIN_MAX_OFFSET);
   1070 			break;
   1071 
   1072 		case OFFSETSIZE_IMPLEMENTATION_MAXIMUM:
   1073 			return IVec2(m_context.getContextInfo().getInt(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET),
   1074 						 m_context.getContextInfo().getInt(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET));
   1075 			break;
   1076 
   1077 		default:
   1078 			DE_ASSERT(false);
   1079 			return IVec2(-1);
   1080 	}
   1081 }
   1082 
   1083 glu::VertexSource TextureGatherCase::genVertexShaderSource (bool requireGpuShader5, int numTexCoordComponents, bool useNormalizedCoordInput)
   1084 {
   1085 	DE_ASSERT(numTexCoordComponents == 2 || numTexCoordComponents == 3);
   1086 	const string texCoordType = "vec" + de::toString(numTexCoordComponents);
   1087 	return glu::VertexSource("#version 310 es\n"
   1088 							 + string(requireGpuShader5 ? "#extension GL_EXT_gpu_shader5 : require\n" : "") +
   1089 							 "\n"
   1090 							 "in highp vec2 a_position;\n"
   1091 							 "in highp " + texCoordType + " a_texCoord;\n"
   1092 							 + (useNormalizedCoordInput ? "in highp vec2 a_normalizedCoord; // (0,0) to (1,1)\n" : "") +
   1093 							 "\n"
   1094 							 "out highp " + texCoordType + " v_texCoord;\n"
   1095 							 + (useNormalizedCoordInput ? "out highp vec2 v_normalizedCoord;\n" : "") +
   1096 							 "\n"
   1097 							 "void main (void)\n"
   1098 							 "{\n"
   1099 							 "	gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);\n"
   1100 							 "	v_texCoord = a_texCoord;\n"
   1101 							 + (useNormalizedCoordInput ? "\tv_normalizedCoord = a_normalizedCoord;\n" : "") +
   1102 							 "}\n");
   1103 }
   1104 
   1105 glu::FragmentSource TextureGatherCase::genFragmentShaderSource (bool			requireGpuShader5,
   1106 																int				numTexCoordComponents,
   1107 																glu::DataType	samplerType,
   1108 																const string&	funcCall,
   1109 																bool			useNormalizedCoordInput,
   1110 																bool			usePixCoord)
   1111 {
   1112 	DE_ASSERT(glu::isDataTypeSampler(samplerType));
   1113 	DE_ASSERT(de::inRange(numTexCoordComponents, 2, 3));
   1114 	DE_ASSERT(!usePixCoord || useNormalizedCoordInput);
   1115 
   1116 	const string texCoordType = "vec" + de::toString(numTexCoordComponents);
   1117 
   1118 	return glu::FragmentSource("#version 310 es\n"
   1119 							   + string(requireGpuShader5 ? "#extension GL_EXT_gpu_shader5 : require\n" : "") +
   1120 							   "\n"
   1121 							   "layout (location = 0) out mediump " + glu::getDataTypeName(getSamplerGatherResultType(samplerType)) + " o_color;\n"
   1122 							   "\n"
   1123 							   "in highp " + texCoordType + " v_texCoord;\n"
   1124 							   + (useNormalizedCoordInput ? "in highp vec2 v_normalizedCoord;\n" : "") +
   1125 							   "\n"
   1126 							   "uniform highp " + string(glu::getDataTypeName(samplerType)) + " u_sampler;\n"
   1127 							   + (useNormalizedCoordInput ? "uniform highp vec2 u_viewportSize;\n" : "") +
   1128 							   "\n"
   1129 							   "void main(void)\n"
   1130 							   "{\n"
   1131 							   + (usePixCoord ? "\tivec2 pixCoord = ivec2(v_normalizedCoord*u_viewportSize);\n" : "") +
   1132 							   "	o_color = " + funcCall + ";\n"
   1133 							   "}\n");
   1134 }
   1135 
   1136 string TextureGatherCase::genGatherFuncCall (GatherType gatherType, const tcu::TextureFormat& textureFormat, const GatherArgs& gatherArgs, const string& refZExpr, const IVec2& offsetRange, int indentationDepth)
   1137 {
   1138 	string result;
   1139 
   1140 	switch (gatherType)
   1141 	{
   1142 		case GATHERTYPE_BASIC:
   1143 			result += "textureGather";
   1144 			break;
   1145 		case GATHERTYPE_OFFSET: // \note Fallthrough.
   1146 		case GATHERTYPE_OFFSET_DYNAMIC:
   1147 			result += "textureGatherOffset";
   1148 			break;
   1149 		case GATHERTYPE_OFFSETS:
   1150 			result += "textureGatherOffsets";
   1151 			break;
   1152 		default:
   1153 			DE_ASSERT(false);
   1154 	}
   1155 
   1156 	result += "(u_sampler, v_texCoord";
   1157 
   1158 	if (isDepthFormat(textureFormat))
   1159 	{
   1160 		DE_ASSERT(gatherArgs.componentNdx < 0);
   1161 		result += ", " + refZExpr;
   1162 	}
   1163 
   1164 	if (gatherType == GATHERTYPE_OFFSET ||
   1165 		gatherType == GATHERTYPE_OFFSET_DYNAMIC ||
   1166 		gatherType == GATHERTYPE_OFFSETS)
   1167 	{
   1168 		result += ", ";
   1169 		switch (gatherType)
   1170 		{
   1171 			case GATHERTYPE_OFFSET:
   1172 				result += "ivec2" + de::toString(gatherArgs.offsets[0]);
   1173 				break;
   1174 
   1175 			case GATHERTYPE_OFFSET_DYNAMIC:
   1176 				result += "pixCoord.yx % ivec2(" + de::toString(offsetRange.y() - offsetRange.x() + 1) + ") + " + de::toString(offsetRange.x());
   1177 				break;
   1178 
   1179 			case GATHERTYPE_OFFSETS:
   1180 				result += "ivec2[4](\n"
   1181 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[0]) + ",\n"
   1182 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[1]) + ",\n"
   1183 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[2]) + ",\n"
   1184 						  + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[3]) + ")\n"
   1185 						  + string(indentationDepth, '\t') + "\t";
   1186 				break;
   1187 
   1188 			default:
   1189 				DE_ASSERT(false);
   1190 		}
   1191 	}
   1192 
   1193 	if (gatherArgs.componentNdx >= 0)
   1194 	{
   1195 		DE_ASSERT(gatherArgs.componentNdx < 4);
   1196 		result += ", " + de::toString(gatherArgs.componentNdx);
   1197 	}
   1198 
   1199 	result += ")";
   1200 
   1201 	return result;
   1202 }
   1203 
   1204 // \note If componentNdx for genProgramSources() is -1, component index is not specified.
   1205 glu::ProgramSources TextureGatherCase::genProgramSources (GatherType					gatherType,
   1206 														  TextureType					textureType,
   1207 														  const tcu::TextureFormat&		textureFormat,
   1208 														  const GatherArgs&				gatherArgs,
   1209 														  const string&					refZExpr,
   1210 														  const IVec2&					offsetRange)
   1211 {
   1212 	const bool				usePixCoord			= gatherType == GATHERTYPE_OFFSET_DYNAMIC;
   1213 	const bool				useNormalizedCoord	= usePixCoord || isDepthFormat(textureFormat);
   1214 	const bool				isDynamicOffset		= gatherType == GATHERTYPE_OFFSET_DYNAMIC;
   1215 	const bool				isShadow			= isDepthFormat(textureFormat);
   1216 	const glu::DataType		samplerType			= getSamplerType(textureType, textureFormat);
   1217 	const int				numDims				= getNumTextureSamplingDimensions(textureType);
   1218 	const string			funcCall			= genGatherFuncCall(gatherType, textureFormat, gatherArgs, refZExpr, offsetRange, 1);
   1219 
   1220 	return glu::ProgramSources() << genVertexShaderSource(requireGpuShader5(gatherType), numDims, isDynamicOffset || isShadow)
   1221 								 << genFragmentShaderSource(requireGpuShader5(gatherType), numDims, samplerType, funcCall, useNormalizedCoord, usePixCoord);
   1222 }
   1223 
   1224 void TextureGatherCase::init (void)
   1225 {
   1226 	TestLog&					log			= m_testCtx.getLog();
   1227 	const glu::RenderContext&	renderCtx	= m_context.getRenderContext();
   1228 	const glw::Functions&		gl			= renderCtx.getFunctions();
   1229 	const deUint32				texTypeGL	= getGLTextureType(m_textureType);
   1230 
   1231 	// Check prerequisites.
   1232 	if (requireGpuShader5(m_gatherType) && !m_context.getContextInfo().isExtensionSupported("GL_EXT_gpu_shader5"))
   1233 		throw tcu::NotSupportedError("GL_EXT_gpu_shader5 required");
   1234 
   1235 	// Log and check implementation offset limits, if appropriate.
   1236 	if (m_offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
   1237 	{
   1238 		const IVec2 offsetRange = getOffsetRange();
   1239 		log << TestLog::Integer("ImplementationMinTextureGatherOffset", "Implementation's value for GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET", "", QP_KEY_TAG_NONE, offsetRange[0])
   1240 			<< TestLog::Integer("ImplementationMaxTextureGatherOffset", "Implementation's value for GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET", "", QP_KEY_TAG_NONE, offsetRange[1]);
   1241 		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());
   1242 		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());
   1243 	}
   1244 
   1245 	// Create rbo and fbo.
   1246 
   1247 	m_colorBuffer = MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(renderCtx));
   1248 	gl.bindRenderbuffer(GL_RENDERBUFFER, **m_colorBuffer);
   1249 	gl.renderbufferStorage(GL_RENDERBUFFER, glu::getInternalFormat(m_colorBufferFormat), RENDER_SIZE.x(), RENDER_SIZE.y());
   1250 	GLU_EXPECT_NO_ERROR(gl.getError(), "Create and setup renderbuffer object");
   1251 
   1252 	m_fbo = MovePtr<glu::Framebuffer>(new glu::Framebuffer(renderCtx));
   1253 	gl.bindFramebuffer(GL_FRAMEBUFFER, **m_fbo);
   1254 	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_colorBuffer);
   1255 	GLU_EXPECT_NO_ERROR(gl.getError(), "Create and setup framebuffer object");
   1256 
   1257 	log << TestLog::Message << "Using a framebuffer object with renderbuffer with format "
   1258 							<< glu::getPixelFormatName(glu::getInternalFormat(m_colorBufferFormat))
   1259 							<< " and size " << RENDER_SIZE << TestLog::EndMessage;
   1260 
   1261 	// Generate subclass-specific iterations.
   1262 
   1263 	generateIterations();
   1264 	m_currentIteration = 0;
   1265 
   1266 	// Initialize texture.
   1267 
   1268 	createAndUploadTexture();
   1269 	gl.texParameteri(texTypeGL, GL_TEXTURE_WRAP_S,		glu::getGLWrapMode(m_wrapS));
   1270 	gl.texParameteri(texTypeGL, GL_TEXTURE_WRAP_T,		glu::getGLWrapMode(m_wrapT));
   1271 	gl.texParameteri(texTypeGL, GL_TEXTURE_MIN_FILTER,	glu::getGLFilterMode(m_minFilter));
   1272 	gl.texParameteri(texTypeGL, GL_TEXTURE_MAG_FILTER,	glu::getGLFilterMode(m_magFilter));
   1273 
   1274 	if (m_baseLevel != 0)
   1275 		gl.texParameteri(texTypeGL, GL_TEXTURE_BASE_LEVEL, m_baseLevel);
   1276 
   1277 	if (isDepthFormat(m_textureFormat))
   1278 	{
   1279 		gl.texParameteri(texTypeGL, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
   1280 		gl.texParameteri(texTypeGL, GL_TEXTURE_COMPARE_FUNC, glu::getGLCompareFunc(m_shadowCompareMode));
   1281 	}
   1282 
   1283 	if (m_textureSwizzle.isSome())
   1284 	{
   1285 		const deUint32 swizzleNamesGL[4] =
   1286 		{
   1287 			GL_TEXTURE_SWIZZLE_R,
   1288 			GL_TEXTURE_SWIZZLE_G,
   1289 			GL_TEXTURE_SWIZZLE_B,
   1290 			GL_TEXTURE_SWIZZLE_A
   1291 		};
   1292 
   1293 		for (int i = 0; i < 4; i++)
   1294 		{
   1295 			const deUint32 curGLSwizzle = getGLTextureSwizzleComponent(m_textureSwizzle.getSwizzle()[i]);
   1296 			gl.texParameteri(texTypeGL, swizzleNamesGL[i], curGLSwizzle);
   1297 		}
   1298 	}
   1299 
   1300 	GLU_EXPECT_NO_ERROR(gl.getError(), "Set texture parameters");
   1301 
   1302 	log << TestLog::Message << "Texture base level is " << m_baseLevel << TestLog::EndMessage
   1303 		<< TestLog::Message << "s and t wrap modes are "
   1304 							<< glu::getTextureWrapModeName(glu::getGLWrapMode(m_wrapS)) << " and "
   1305 							<< glu::getTextureWrapModeName(glu::getGLWrapMode(m_wrapT)) << ", respectively" << TestLog::EndMessage
   1306 		<< TestLog::Message << "Minification and magnification filter modes are "
   1307 							<< glu::getTextureFilterName(glu::getGLFilterMode(m_minFilter)) << " and "
   1308 							<< glu::getTextureFilterName(glu::getGLFilterMode(m_magFilter)) << ", respectively "
   1309 							<< (m_mipmapIncomplete ?
   1310 								"(note that they cause the texture to be incomplete)" :
   1311 								"(note that they should have no effect on gather result)")
   1312 							<< TestLog::EndMessage
   1313 		<< TestLog::Message << "Using texture swizzle " << m_textureSwizzle << TestLog::EndMessage;
   1314 
   1315 	if (m_shadowCompareMode != tcu::Sampler::COMPAREMODE_NONE)
   1316 		log << TestLog::Message << "Using texture compare func " << glu::getCompareFuncName(glu::getGLCompareFunc(m_shadowCompareMode)) << TestLog::EndMessage;
   1317 }
   1318 
   1319 void TextureGatherCase::deinit (void)
   1320 {
   1321 	m_program		= MovePtr<ShaderProgram>(DE_NULL);
   1322 	m_fbo			= MovePtr<glu::Framebuffer>(DE_NULL);
   1323 	m_colorBuffer	= MovePtr<glu::Renderbuffer>(DE_NULL);
   1324 }
   1325 
   1326 TextureGatherCase::IterateResult TextureGatherCase::iterate (void)
   1327 {
   1328 	TestLog&						log								= m_testCtx.getLog();
   1329 	const tcu::ScopedLogSection		iterationSection				(log, "Iteration" + de::toString(m_currentIteration), "Iteration " + de::toString(m_currentIteration));
   1330 	const glu::RenderContext&		renderCtx						= m_context.getRenderContext();
   1331 	const tcu::IVec2				renderSize						= RENDER_SIZE;
   1332 	const glw::Functions&			gl								= renderCtx.getFunctions();
   1333 	const GatherArgs&				gatherArgs						= getGatherArgs(m_currentIteration);
   1334 	const string					refZExpr						= "v_normalizedCoord.x";
   1335 	const bool						needPixelCoordInShader			= m_gatherType == GATHERTYPE_OFFSET_DYNAMIC;
   1336 	const bool						needNormalizedCoordInShader		= needPixelCoordInShader || isDepthFormat(m_textureFormat);
   1337 
   1338 	// Generate a program appropriate for this iteration.
   1339 
   1340 	m_program = MovePtr<ShaderProgram>(new ShaderProgram(renderCtx, genProgramSources(m_gatherType, m_textureType, m_textureFormat, gatherArgs, refZExpr, getOffsetRange())));
   1341 	if (m_currentIteration == 0)
   1342 		m_testCtx.getLog() << *m_program;
   1343 	else
   1344 		m_testCtx.getLog() << TestLog::Message << "Using a program similar to the previous one, except with a gather function call as follows:\n"
   1345 											   << genGatherFuncCall(m_gatherType, m_textureFormat, gatherArgs, refZExpr, getOffsetRange(), 0)
   1346 											   << TestLog::EndMessage;
   1347 	if (!m_program->isOk())
   1348 	{
   1349 		if (m_currentIteration != 0)
   1350 			m_testCtx.getLog() << *m_program;
   1351 		TCU_FAIL("Failed to build program");
   1352 	}
   1353 
   1354 	// Render.
   1355 
   1356 	gl.viewport(0, 0, RENDER_SIZE.x(), RENDER_SIZE.y());
   1357 	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
   1358 	gl.clear(GL_COLOR_BUFFER_BIT);
   1359 
   1360 	{
   1361 		const float position[4*2] =
   1362 		{
   1363 			-1.0f, -1.0f,
   1364 			-1.0f, +1.0f,
   1365 			+1.0f, -1.0f,
   1366 			+1.0f, +1.0f,
   1367 		};
   1368 
   1369 		const float normalizedCoord[4*2] =
   1370 		{
   1371 			0.0f, 0.0f,
   1372 			0.0f, 1.0f,
   1373 			1.0f, 0.0f,
   1374 			1.0f, 1.0f,
   1375 		};
   1376 
   1377 		const vector<float> texCoord = computeQuadTexCoord(m_currentIteration);
   1378 
   1379 		vector<glu::VertexArrayBinding> attrBindings;
   1380 		attrBindings.push_back(glu::va::Float("a_position", 2, 4, 0, &position[0]));
   1381 		attrBindings.push_back(glu::va::Float("a_texCoord", (int)texCoord.size()/4, 4, 0, &texCoord[0]));
   1382 		if (needNormalizedCoordInShader)
   1383 			attrBindings.push_back(glu::va::Float("a_normalizedCoord", 2, 4, 0, &normalizedCoord[0]));
   1384 
   1385 		const deUint16 indices[6] = { 0, 1, 2, 2, 1, 3 };
   1386 
   1387 		gl.useProgram(m_program->getProgram());
   1388 
   1389 		{
   1390 			const int samplerUniformLocation = gl.getUniformLocation(m_program->getProgram(), "u_sampler");
   1391 			TCU_CHECK(samplerUniformLocation >= 0);
   1392 			gl.uniform1i(samplerUniformLocation, 0);
   1393 		}
   1394 
   1395 		if (needPixelCoordInShader)
   1396 		{
   1397 			const int viewportSizeUniformLocation = gl.getUniformLocation(m_program->getProgram(), "u_viewportSize");
   1398 			TCU_CHECK(viewportSizeUniformLocation >= 0);
   1399 			gl.uniform2f(viewportSizeUniformLocation, (float)RENDER_SIZE.x(), (float)RENDER_SIZE.y());
   1400 		}
   1401 
   1402 		if (texCoord.size() == 2*4)
   1403 		{
   1404 			Vec2 texCoordVec[4];
   1405 			computeTexCoordVecs(texCoord, texCoordVec);
   1406 			log << TestLog::Message << "Texture coordinates run from " << texCoordVec[0] << " to " << texCoordVec[3] << TestLog::EndMessage;
   1407 		}
   1408 		else if (texCoord.size() == 3*4)
   1409 		{
   1410 			Vec3 texCoordVec[4];
   1411 			computeTexCoordVecs(texCoord, texCoordVec);
   1412 			log << TestLog::Message << "Texture coordinates run from " << texCoordVec[0] << " to " << texCoordVec[3] << TestLog::EndMessage;
   1413 		}
   1414 		else
   1415 			DE_ASSERT(false);
   1416 
   1417 		glu::draw(renderCtx, m_program->getProgram(), (int)attrBindings.size(), &attrBindings[0],
   1418 			glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
   1419 	}
   1420 
   1421 	// Verify result.
   1422 
   1423 	{
   1424 		const tcu::TextureLevel rendered = getPixels(renderCtx, RENDER_SIZE, m_colorBufferFormat);
   1425 
   1426 		if (!verify(m_currentIteration, rendered.getAccess()))
   1427 		{
   1428 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed");
   1429 			return STOP;
   1430 		}
   1431 	}
   1432 
   1433 	m_currentIteration++;
   1434 	if (m_currentIteration == (int)getNumIterations())
   1435 	{
   1436 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
   1437 		return STOP;
   1438 	}
   1439 	else
   1440 		return CONTINUE;
   1441 }
   1442 
   1443 template <typename TexViewT, typename TexCoordT>
   1444 bool TextureGatherCase::verify (const ConstPixelBufferAccess&	rendered,
   1445 								const TexViewT&					texture,
   1446 								const TexCoordT					(&texCoords)[4],
   1447 								const GatherArgs&				gatherArgs) const
   1448 {
   1449 	TestLog& log = m_testCtx.getLog();
   1450 
   1451 	if (m_mipmapIncomplete)
   1452 	{
   1453 		const int	componentNdx		= de::max(0, gatherArgs.componentNdx);
   1454 		const Vec4	incompleteColor		(0.0f, 0.0f, 0.0f, 1.0f);
   1455 		const Vec4	refColor			(incompleteColor[componentNdx]);
   1456 		const bool	isOk				= verifySingleColored(log, rendered, refColor);
   1457 
   1458 		if (!isOk)
   1459 			log << TestLog::Message << "Note: expected color " << refColor << " for all pixels; "
   1460 									<< incompleteColor[componentNdx] << " is component at index " << componentNdx
   1461 									<< " in the color " << incompleteColor << ", which is used for incomplete textures" << TestLog::EndMessage;
   1462 
   1463 		return isOk;
   1464 	}
   1465 	else
   1466 	{
   1467 		DE_ASSERT(m_colorBufferFormat.order == tcu::TextureFormat::RGBA);
   1468 		DE_ASSERT(m_colorBufferFormat.type == tcu::TextureFormat::UNORM_INT8		||
   1469 				  m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT8		||
   1470 				  m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT8);
   1471 
   1472 		const MovePtr<PixelOffsets>		pixelOffsets	= makePixelOffsetsFunctor(m_gatherType, gatherArgs, getOffsetRange());
   1473 		const tcu::PixelFormat			pixelFormat		= tcu::PixelFormat(8,8,8,8);
   1474 		const IVec4						colorBits		= tcu::max(gls::TextureTestUtil::getBitsVec(pixelFormat) - 1, tcu::IVec4(0));
   1475 		const IVec3						coordBits		= m_textureType == TEXTURETYPE_2D			? IVec3(20,20,0)
   1476 														: m_textureType == TEXTURETYPE_CUBE			? IVec3(10,10,10)
   1477 														: m_textureType == TEXTURETYPE_2D_ARRAY		? IVec3(20,20,20)
   1478 														: IVec3(-1);
   1479 		const IVec3						uvwBits			= m_textureType == TEXTURETYPE_2D			? IVec3(7,7,0)
   1480 														: m_textureType == TEXTURETYPE_CUBE			? IVec3(6,6,0)
   1481 														: m_textureType == TEXTURETYPE_2D_ARRAY		? IVec3(7,7,7)
   1482 														: IVec3(-1);
   1483 		tcu::Sampler					sampler;
   1484 		sampler.wrapS		= m_wrapS;
   1485 		sampler.wrapT		= m_wrapT;
   1486 		sampler.compare		= m_shadowCompareMode;
   1487 
   1488 		if (isDepthFormat(m_textureFormat))
   1489 		{
   1490 			tcu::TexComparePrecision comparePrec;
   1491 			comparePrec.coordBits		= coordBits;
   1492 			comparePrec.uvwBits			= uvwBits;
   1493 			comparePrec.referenceBits	= 16;
   1494 			comparePrec.resultBits		= pixelFormat.redBits-1;
   1495 
   1496 			return verifyGatherOffsetsCompare(log, rendered, texture, texCoords, sampler, comparePrec, PixelCompareRefZDefault(RENDER_SIZE), *pixelOffsets);
   1497 		}
   1498 		else
   1499 		{
   1500 			const int componentNdx = de::max(0, gatherArgs.componentNdx);
   1501 
   1502 			if (isUnormFormatType(m_textureFormat.type))
   1503 			{
   1504 				tcu::LookupPrecision lookupPrec;
   1505 				lookupPrec.colorThreshold	= tcu::computeFixedPointThreshold(colorBits);
   1506 				lookupPrec.coordBits		= coordBits;
   1507 				lookupPrec.uvwBits			= uvwBits;
   1508 				lookupPrec.colorMask		= gls::TextureTestUtil::getCompareMask(pixelFormat);
   1509 				return verifyGatherOffsets<float>(log, rendered, texture, texCoords, sampler, lookupPrec, componentNdx, *pixelOffsets);
   1510 			}
   1511 			else if (isUIntFormatType(m_textureFormat.type) || isSIntFormatType(m_textureFormat.type))
   1512 			{
   1513 				tcu::IntLookupPrecision		lookupPrec;
   1514 				lookupPrec.colorThreshold	= UVec4(0);
   1515 				lookupPrec.coordBits		= coordBits;
   1516 				lookupPrec.uvwBits			= uvwBits;
   1517 				lookupPrec.colorMask		= gls::TextureTestUtil::getCompareMask(pixelFormat);
   1518 
   1519 				if (isUIntFormatType(m_textureFormat.type))
   1520 					return verifyGatherOffsets<deUint32>(log, rendered, texture, texCoords, sampler, lookupPrec, componentNdx, *pixelOffsets);
   1521 				else if (isSIntFormatType(m_textureFormat.type))
   1522 					return verifyGatherOffsets<deInt32>(log, rendered, texture, texCoords, sampler, lookupPrec, componentNdx, *pixelOffsets);
   1523 				else
   1524 				{
   1525 					DE_ASSERT(false);
   1526 					return false;
   1527 				}
   1528 			}
   1529 			else
   1530 			{
   1531 				DE_ASSERT(false);
   1532 				return false;
   1533 			}
   1534 		}
   1535 	}
   1536 }
   1537 
   1538 vector<GatherArgs> generateBasic2DCaseIterations (GatherType gatherType, const tcu::TextureFormat& textureFormat, const IVec2& offsetRange)
   1539 {
   1540 	const int			numComponentCases	= isDepthFormat(textureFormat) ? 1 : 4+1; // \note For non-depth textures, test explicit components 0 to 3 and implicit component 0.
   1541 	vector<GatherArgs>	result;
   1542 
   1543 	for (int componentCaseNdx = 0; componentCaseNdx < numComponentCases; componentCaseNdx++)
   1544 	{
   1545 		const int componentNdx = componentCaseNdx - 1;
   1546 
   1547 		switch (gatherType)
   1548 		{
   1549 			case GATHERTYPE_BASIC:
   1550 				result.push_back(GatherArgs(componentNdx));
   1551 				break;
   1552 
   1553 			case GATHERTYPE_OFFSET:
   1554 			{
   1555 				const int min	= offsetRange.x();
   1556 				const int max	= offsetRange.y();
   1557 				const int hmin	= divRoundToZero(min, 2);
   1558 				const int hmax	= divRoundToZero(max, 2);
   1559 
   1560 				result.push_back(GatherArgs(componentNdx, IVec2(min, max)));
   1561 
   1562 				if (componentCaseNdx == 0) // Don't test all offsets variants for all color components (they should be pretty orthogonal).
   1563 				{
   1564 					result.push_back(GatherArgs(componentNdx, IVec2(min,	min)));
   1565 					result.push_back(GatherArgs(componentNdx, IVec2(max,	min)));
   1566 					result.push_back(GatherArgs(componentNdx, IVec2(max,	max)));
   1567 
   1568 					result.push_back(GatherArgs(componentNdx, IVec2(0,		hmax)));
   1569 					result.push_back(GatherArgs(componentNdx, IVec2(hmin,	0)));
   1570 					result.push_back(GatherArgs(componentNdx, IVec2(0,		0)));
   1571 				}
   1572 
   1573 				break;
   1574 			}
   1575 
   1576 			case GATHERTYPE_OFFSET_DYNAMIC:
   1577 				result.push_back(GatherArgs(componentNdx));
   1578 				break;
   1579 
   1580 			case GATHERTYPE_OFFSETS:
   1581 			{
   1582 				const int min	= offsetRange.x();
   1583 				const int max	= offsetRange.y();
   1584 				const int hmin	= divRoundToZero(min, 2);
   1585 				const int hmax	= divRoundToZero(max, 2);
   1586 
   1587 				result.push_back(GatherArgs(componentNdx,
   1588 											IVec2(min,	min),
   1589 											IVec2(min,	max),
   1590 											IVec2(max,	min),
   1591 											IVec2(max,	max)));
   1592 
   1593 				if (componentCaseNdx == 0) // Don't test all offsets variants for all color components (they should be pretty orthogonal).
   1594 					result.push_back(GatherArgs(componentNdx,
   1595 												IVec2(min,	hmax),
   1596 												IVec2(hmin,	max),
   1597 												IVec2(0,	hmax),
   1598 												IVec2(hmax,	0)));
   1599 				break;
   1600 			}
   1601 
   1602 			default:
   1603 				DE_ASSERT(false);
   1604 		}
   1605 	}
   1606 
   1607 	return result;
   1608 }
   1609 
   1610 class TextureGather2DCase : public TextureGatherCase
   1611 {
   1612 public:
   1613 	TextureGather2DCase (Context&					context,
   1614 						 const char*				name,
   1615 						 const char*				description,
   1616 						 GatherType					gatherType,
   1617 						 OffsetSize					offsetSize,
   1618 						 tcu::TextureFormat			textureFormat,
   1619 						 tcu::Sampler::CompareMode	shadowCompareMode,
   1620 						 tcu::Sampler::WrapMode		wrapS,
   1621 						 tcu::Sampler::WrapMode		wrapT,
   1622 						 const MaybeTextureSwizzle&	texSwizzle,
   1623 						 tcu::Sampler::FilterMode	minFilter,
   1624 						 tcu::Sampler::FilterMode	magFilter,
   1625 						 int						baseLevel,
   1626 						 bool						mipmapIncomplete,
   1627 						 const IVec2&				textureSize)
   1628 		: TextureGatherCase		(context, name, description, TEXTURETYPE_2D, gatherType, offsetSize, textureFormat, shadowCompareMode, wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, mipmapIncomplete)
   1629 		, m_textureSize			(textureSize)
   1630 		, m_swizzledTexture		(tcu::TextureFormat(), 1, 1)
   1631 	{
   1632 	}
   1633 
   1634 protected:
   1635 	void						generateIterations		(void);
   1636 	void						createAndUploadTexture	(void);
   1637 	int							getNumIterations		(void) const { DE_ASSERT(!m_iterations.empty()); return (int)m_iterations.size(); }
   1638 	GatherArgs					getGatherArgs			(int iterationNdx) const { return m_iterations[iterationNdx]; }
   1639 	vector<float>				computeQuadTexCoord		(int iterationNdx) const;
   1640 	bool						verify					(int iterationNdx, const ConstPixelBufferAccess& rendered) const;
   1641 
   1642 private:
   1643 	const IVec2					m_textureSize;
   1644 
   1645 	MovePtr<glu::Texture2D>		m_texture;
   1646 	tcu::Texture2D				m_swizzledTexture;
   1647 	vector<GatherArgs>			m_iterations;
   1648 };
   1649 
   1650 vector<float> TextureGather2DCase::computeQuadTexCoord (int /* iterationNdx */) const
   1651 {
   1652 	vector<float> res;
   1653 	gls::TextureTestUtil::computeQuadTexCoord2D(res, Vec2(-0.3f, -0.4f), Vec2(1.5f, 1.6f));
   1654 	return res;
   1655 }
   1656 
   1657 void TextureGather2DCase::generateIterations (void)
   1658 {
   1659 	DE_ASSERT(m_iterations.empty());
   1660 	m_iterations = generateBasic2DCaseIterations(m_gatherType, m_textureFormat, getOffsetRange());
   1661 }
   1662 
   1663 void TextureGather2DCase::createAndUploadTexture (void)
   1664 {
   1665 	const glu::RenderContext&		renderCtx	= m_context.getRenderContext();
   1666 	const glw::Functions&			gl			= renderCtx.getFunctions();
   1667 	const tcu::TextureFormatInfo	texFmtInfo	= tcu::getTextureFormatInfo(m_textureFormat);
   1668 
   1669 	m_texture = MovePtr<glu::Texture2D>(new glu::Texture2D(renderCtx, glu::getInternalFormat(m_textureFormat), m_textureSize.x(), m_textureSize.y()));
   1670 
   1671 	{
   1672 		tcu::Texture2D&		refTexture	= m_texture->getRefTexture();
   1673 		const int			levelBegin	= m_baseLevel;
   1674 		const int			levelEnd	= isMipmapFilter(m_minFilter) && !m_mipmapIncomplete ? refTexture.getNumLevels() : m_baseLevel+1;
   1675 		DE_ASSERT(m_baseLevel < refTexture.getNumLevels());
   1676 
   1677 		for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
   1678 		{
   1679 			refTexture.allocLevel(levelNdx);
   1680 			const PixelBufferAccess& level = refTexture.getLevel(levelNdx);
   1681 			fillWithRandomColorTiles(level, texFmtInfo.valueMin, texFmtInfo.valueMax, (deUint32)m_testCtx.getCommandLine().getBaseSeed());
   1682 			m_testCtx.getLog() << TestLog::Image("InputTextureLevel" + de::toString(levelNdx), "Input texture, level " + de::toString(levelNdx), level)
   1683 							   << TestLog::Message << "Note: texture level's size is " << IVec2(level.getWidth(), level.getHeight()) << TestLog::EndMessage;
   1684 		}
   1685 
   1686 		swizzleTexture(m_swizzledTexture, refTexture, m_textureSwizzle);
   1687 	}
   1688 
   1689 	gl.activeTexture(GL_TEXTURE0);
   1690 	m_texture->upload();
   1691 }
   1692 
   1693 bool TextureGather2DCase::verify (int iterationNdx, const ConstPixelBufferAccess& rendered) const
   1694 {
   1695 	Vec2 texCoords[4];
   1696 	computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
   1697 	return TextureGatherCase::verify(rendered, getOneLevelSubView(tcu::Texture2DView(m_swizzledTexture), m_baseLevel), texCoords, m_iterations[iterationNdx]);
   1698 }
   1699 
   1700 class TextureGather2DArrayCase : public TextureGatherCase
   1701 {
   1702 public:
   1703 	TextureGather2DArrayCase (Context&						context,
   1704 							  const char*					name,
   1705 							  const char*					description,
   1706 							  GatherType					gatherType,
   1707 							  OffsetSize					offsetSize,
   1708 							  tcu::TextureFormat			textureFormat,
   1709 							  tcu::Sampler::CompareMode		shadowCompareMode,
   1710 							  tcu::Sampler::WrapMode		wrapS,
   1711 							  tcu::Sampler::WrapMode		wrapT,
   1712 							  const MaybeTextureSwizzle&	texSwizzle,
   1713 							  tcu::Sampler::FilterMode		minFilter,
   1714 							  tcu::Sampler::FilterMode		magFilter,
   1715 							  int							baseLevel,
   1716 							  bool							mipmapIncomplete,
   1717 							  const IVec3&					textureSize)
   1718 		: TextureGatherCase		(context, name, description, TEXTURETYPE_2D_ARRAY, gatherType, offsetSize, textureFormat, shadowCompareMode, wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, mipmapIncomplete)
   1719 		, m_textureSize			(textureSize)
   1720 		, m_swizzledTexture		(tcu::TextureFormat(), 1, 1, 1)
   1721 	{
   1722 	}
   1723 
   1724 protected:
   1725 	void							generateIterations		(void);
   1726 	void							createAndUploadTexture	(void);
   1727 	int								getNumIterations		(void) const { DE_ASSERT(!m_iterations.empty()); return (int)m_iterations.size(); }
   1728 	GatherArgs						getGatherArgs			(int iterationNdx) const { return m_iterations[iterationNdx].gatherArgs; }
   1729 	vector<float>					computeQuadTexCoord		(int iterationNdx) const;
   1730 	bool							verify					(int iterationNdx, const ConstPixelBufferAccess& rendered) const;
   1731 
   1732 private:
   1733 	struct Iteration
   1734 	{
   1735 		GatherArgs	gatherArgs;
   1736 		int			layerNdx;
   1737 	};
   1738 
   1739 	const IVec3						m_textureSize;
   1740 
   1741 	MovePtr<glu::Texture2DArray>	m_texture;
   1742 	tcu::Texture2DArray				m_swizzledTexture;
   1743 	vector<Iteration>				m_iterations;
   1744 };
   1745 
   1746 vector<float> TextureGather2DArrayCase::computeQuadTexCoord (int iterationNdx) const
   1747 {
   1748 	vector<float> res;
   1749 	gls::TextureTestUtil::computeQuadTexCoord2DArray(res, m_iterations[iterationNdx].layerNdx, Vec2(-0.3f, -0.4f), Vec2(1.5f, 1.6f));
   1750 	return res;
   1751 }
   1752 
   1753 void TextureGather2DArrayCase::generateIterations (void)
   1754 {
   1755 	DE_ASSERT(m_iterations.empty());
   1756 
   1757 	const vector<GatherArgs> basicIterations = generateBasic2DCaseIterations(m_gatherType, m_textureFormat, getOffsetRange());
   1758 
   1759 	// \note Out-of-bounds layer indices are tested too.
   1760 	for (int layerNdx = -1; layerNdx < m_textureSize.z()+1; layerNdx++)
   1761 	{
   1762 		// Don't duplicate all cases for all layers.
   1763 		if (layerNdx == 0)
   1764 		{
   1765 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   1766 			{
   1767 				m_iterations.push_back(Iteration());
   1768 				m_iterations.back().gatherArgs = basicIterations[basicNdx];
   1769 				m_iterations.back().layerNdx = layerNdx;
   1770 			}
   1771 		}
   1772 		else
   1773 		{
   1774 			// For other layers than 0, only test one component and one set of offsets per layer.
   1775 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   1776 			{
   1777 				if (isDepthFormat(m_textureFormat) || basicIterations[basicNdx].componentNdx == (layerNdx + 2) % 4)
   1778 				{
   1779 					m_iterations.push_back(Iteration());
   1780 					m_iterations.back().gatherArgs = basicIterations[basicNdx];
   1781 					m_iterations.back().layerNdx = layerNdx;
   1782 					break;
   1783 				}
   1784 			}
   1785 		}
   1786 	}
   1787 }
   1788 
   1789 void TextureGather2DArrayCase::createAndUploadTexture (void)
   1790 {
   1791 	TestLog&						log			= m_testCtx.getLog();
   1792 	const glu::RenderContext&		renderCtx	= m_context.getRenderContext();
   1793 	const glw::Functions&			gl			= renderCtx.getFunctions();
   1794 	const tcu::TextureFormatInfo	texFmtInfo	= tcu::getTextureFormatInfo(m_textureFormat);
   1795 
   1796 	m_texture = MovePtr<glu::Texture2DArray>(new glu::Texture2DArray(renderCtx, glu::getInternalFormat(m_textureFormat), m_textureSize.x(), m_textureSize.y(), m_textureSize.z()));
   1797 
   1798 	{
   1799 		tcu::Texture2DArray&	refTexture	= m_texture->getRefTexture();
   1800 		const int				levelBegin	= m_baseLevel;
   1801 		const int				levelEnd	= isMipmapFilter(m_minFilter) && !m_mipmapIncomplete ? refTexture.getNumLevels() : m_baseLevel+1;
   1802 		DE_ASSERT(m_baseLevel < refTexture.getNumLevels());
   1803 
   1804 		for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
   1805 		{
   1806 			refTexture.allocLevel(levelNdx);
   1807 			const PixelBufferAccess& level = refTexture.getLevel(levelNdx);
   1808 			fillWithRandomColorTiles(level, texFmtInfo.valueMin, texFmtInfo.valueMax, (deUint32)m_testCtx.getCommandLine().getBaseSeed());
   1809 
   1810 			log << TestLog::ImageSet("InputTextureLevel", "Input texture, level " + de::toString(levelNdx));
   1811 			for (int layerNdx = 0; layerNdx < m_textureSize.z(); layerNdx++)
   1812 				log << TestLog::Image("InputTextureLevel" + de::toString(layerNdx) + "Layer" + de::toString(layerNdx),
   1813 									  "Layer " + de::toString(layerNdx),
   1814 									  tcu::getSubregion(level, 0, 0, layerNdx, level.getWidth(), level.getHeight(), 1));
   1815 			log << TestLog::EndImageSet
   1816 				<< TestLog::Message << "Note: texture level's size is " << IVec3(level.getWidth(), level.getHeight(), level.getDepth()) << TestLog::EndMessage;
   1817 		}
   1818 
   1819 		swizzleTexture(m_swizzledTexture, refTexture, m_textureSwizzle);
   1820 	}
   1821 
   1822 	gl.activeTexture(GL_TEXTURE0);
   1823 	m_texture->upload();
   1824 }
   1825 
   1826 bool TextureGather2DArrayCase::verify (int iterationNdx, const ConstPixelBufferAccess& rendered) const
   1827 {
   1828 	Vec3 texCoords[4];
   1829 	computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
   1830 	return TextureGatherCase::verify(rendered, getOneLevelSubView(tcu::Texture2DArrayView(m_swizzledTexture), m_baseLevel), texCoords, m_iterations[iterationNdx].gatherArgs);
   1831 }
   1832 
   1833 // \note Cube case always uses just basic textureGather(); offset versions are not defined for cube maps.
   1834 class TextureGatherCubeCase : public TextureGatherCase
   1835 {
   1836 public:
   1837 	TextureGatherCubeCase (Context&						context,
   1838 						   const char*					name,
   1839 						   const char*					description,
   1840 						   tcu::TextureFormat			textureFormat,
   1841 						   tcu::Sampler::CompareMode	shadowCompareMode,
   1842 						   tcu::Sampler::WrapMode		wrapS,
   1843 						   tcu::Sampler::WrapMode		wrapT,
   1844 						   const MaybeTextureSwizzle&	texSwizzle,
   1845 						   tcu::Sampler::FilterMode		minFilter,
   1846 						   tcu::Sampler::FilterMode		magFilter,
   1847 						   int							baseLevel,
   1848 						   bool							mipmapIncomplete,
   1849 						   int							textureSize)
   1850 		: TextureGatherCase		(context, name, description, TEXTURETYPE_CUBE, GATHERTYPE_BASIC, OFFSETSIZE_NONE, textureFormat, shadowCompareMode, wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, mipmapIncomplete)
   1851 		, m_textureSize			(textureSize)
   1852 		, m_swizzledTexture		(tcu::TextureFormat(), 1)
   1853 	{
   1854 	}
   1855 
   1856 protected:
   1857 	void						generateIterations		(void);
   1858 	void						createAndUploadTexture	(void);
   1859 	int							getNumIterations		(void) const { DE_ASSERT(!m_iterations.empty()); return (int)m_iterations.size(); }
   1860 	GatherArgs					getGatherArgs			(int iterationNdx) const { return m_iterations[iterationNdx].gatherArgs; }
   1861 	vector<float>				computeQuadTexCoord		(int iterationNdx) const;
   1862 	bool						verify					(int iterationNdx, const ConstPixelBufferAccess& rendered) const;
   1863 
   1864 private:
   1865 	struct Iteration
   1866 	{
   1867 		GatherArgs		gatherArgs;
   1868 		tcu::CubeFace	face;
   1869 	};
   1870 
   1871 	const int					m_textureSize;
   1872 
   1873 	MovePtr<glu::TextureCube>	m_texture;
   1874 	tcu::TextureCube			m_swizzledTexture;
   1875 	vector<Iteration>			m_iterations;
   1876 };
   1877 
   1878 vector<float> TextureGatherCubeCase::computeQuadTexCoord (int iterationNdx) const
   1879 {
   1880 	vector<float> res;
   1881 	gls::TextureTestUtil::computeQuadTexCoordCube(res, m_iterations[iterationNdx].face, Vec2(-1.2f), Vec2(1.2f));
   1882 	return res;
   1883 }
   1884 
   1885 void TextureGatherCubeCase::generateIterations (void)
   1886 {
   1887 	DE_ASSERT(m_iterations.empty());
   1888 
   1889 	const vector<GatherArgs> basicIterations = generateBasic2DCaseIterations(m_gatherType, m_textureFormat, getOffsetRange());
   1890 
   1891 	for (int cubeFaceI = 0; cubeFaceI < tcu::CUBEFACE_LAST; cubeFaceI++)
   1892 	{
   1893 		const tcu::CubeFace cubeFace = (tcu::CubeFace)cubeFaceI;
   1894 
   1895 		// Don't duplicate all cases for all faces.
   1896 		if (cubeFaceI == 0)
   1897 		{
   1898 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   1899 			{
   1900 				m_iterations.push_back(Iteration());
   1901 				m_iterations.back().gatherArgs = basicIterations[basicNdx];
   1902 				m_iterations.back().face = cubeFace;
   1903 			}
   1904 		}
   1905 		else
   1906 		{
   1907 			// For other faces than first, only test one component per face.
   1908 			for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
   1909 			{
   1910 				if (isDepthFormat(m_textureFormat) || basicIterations[basicNdx].componentNdx == cubeFaceI % 4)
   1911 				{
   1912 					m_iterations.push_back(Iteration());
   1913 					m_iterations.back().gatherArgs = basicIterations[basicNdx];
   1914 					m_iterations.back().face = cubeFace;
   1915 					break;
   1916 				}
   1917 			}
   1918 		}
   1919 	}
   1920 }
   1921 
   1922 void TextureGatherCubeCase::createAndUploadTexture (void)
   1923 {
   1924 	TestLog&						log			= m_testCtx.getLog();
   1925 	const glu::RenderContext&		renderCtx	= m_context.getRenderContext();
   1926 	const glw::Functions&			gl			= renderCtx.getFunctions();
   1927 	const tcu::TextureFormatInfo	texFmtInfo	= tcu::getTextureFormatInfo(m_textureFormat);
   1928 
   1929 	m_texture = MovePtr<glu::TextureCube>(new glu::TextureCube(renderCtx, glu::getInternalFormat(m_textureFormat), m_textureSize));
   1930 
   1931 	{
   1932 		tcu::TextureCube&	refTexture	= m_texture->getRefTexture();
   1933 		const int			levelBegin	= m_baseLevel;
   1934 		const int			levelEnd	= isMipmapFilter(m_minFilter) && !m_mipmapIncomplete ? refTexture.getNumLevels() : m_baseLevel+1;
   1935 		DE_ASSERT(m_baseLevel < refTexture.getNumLevels());
   1936 
   1937 		for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
   1938 		{
   1939 			log << TestLog::ImageSet("InputTextureLevel" + de::toString(levelNdx), "Input texture, level " + de::toString(levelNdx));
   1940 
   1941 			for (int cubeFaceI = 0; cubeFaceI < tcu::CUBEFACE_LAST; cubeFaceI++)
   1942 			{
   1943 				const tcu::CubeFace			cubeFace	= (tcu::CubeFace)cubeFaceI;
   1944 				refTexture.allocLevel(cubeFace, levelNdx);
   1945 				const PixelBufferAccess&	levelFace	= refTexture.getLevelFace(levelNdx, cubeFace);
   1946 				fillWithRandomColorTiles(levelFace, texFmtInfo.valueMin, texFmtInfo.valueMax, (deUint32)m_testCtx.getCommandLine().getBaseSeed() ^ (deUint32)cubeFaceI);
   1947 
   1948 				m_testCtx.getLog() << TestLog::Image("InputTextureLevel" + de::toString(levelNdx) + "Face" + de::toString((int)cubeFace),
   1949 													 de::toString(cubeFace),
   1950 													 levelFace);
   1951 			}
   1952 
   1953 			log << TestLog::EndImageSet
   1954 				<< TestLog::Message << "Note: texture level's size is " << refTexture.getLevelFace(levelNdx, tcu::CUBEFACE_NEGATIVE_X).getWidth() << TestLog::EndMessage;
   1955 		}
   1956 
   1957 		swizzleTexture(m_swizzledTexture, refTexture, m_textureSwizzle);
   1958 	}
   1959 
   1960 	gl.activeTexture(GL_TEXTURE0);
   1961 	m_texture->upload();
   1962 }
   1963 
   1964 bool TextureGatherCubeCase::verify (int iterationNdx, const ConstPixelBufferAccess& rendered) const
   1965 {
   1966 	Vec3 texCoords[4];
   1967 	computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
   1968 	return TextureGatherCase::verify(rendered, getOneLevelSubView(tcu::TextureCubeView(m_swizzledTexture), m_baseLevel), texCoords, m_iterations[iterationNdx].gatherArgs);
   1969 }
   1970 
   1971 static inline TextureGatherCase* makeTextureGatherCase (TextureType					textureType,
   1972 														Context&					context,
   1973 														const char*					name,
   1974 														const char*					description,
   1975 														GatherType					gatherType,
   1976 														OffsetSize					offsetSize,
   1977 														tcu::TextureFormat			textureFormat,
   1978 														tcu::Sampler::CompareMode	shadowCompareMode,
   1979 														tcu::Sampler::WrapMode		wrapS,
   1980 														tcu::Sampler::WrapMode		wrapT,
   1981 														const MaybeTextureSwizzle&	texSwizzle,
   1982 														tcu::Sampler::FilterMode	minFilter,
   1983 														tcu::Sampler::FilterMode	magFilter,
   1984 														int							baseLevel,
   1985 														const IVec3&				textureSize,
   1986 														bool						mipmapIncomplete = false)
   1987 {
   1988 	switch (textureType)
   1989 	{
   1990 		case TEXTURETYPE_2D:
   1991 			return new TextureGather2DCase(context, name, description, gatherType, offsetSize, textureFormat, shadowCompareMode,
   1992 										   wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, mipmapIncomplete, textureSize.swizzle(0, 1));
   1993 
   1994 		case TEXTURETYPE_2D_ARRAY:
   1995 			return new TextureGather2DArrayCase(context, name, description, gatherType, offsetSize, textureFormat, shadowCompareMode,
   1996 												wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, mipmapIncomplete, textureSize);
   1997 
   1998 		case TEXTURETYPE_CUBE:
   1999 			DE_ASSERT(gatherType == GATHERTYPE_BASIC);
   2000 			DE_ASSERT(offsetSize == OFFSETSIZE_NONE);
   2001 			return new TextureGatherCubeCase(context, name, description, textureFormat, shadowCompareMode,
   2002 											 wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, mipmapIncomplete, textureSize.x());
   2003 
   2004 		default:
   2005 			DE_ASSERT(false);
   2006 			return DE_NULL;
   2007 	}
   2008 }
   2009 
   2010 } // anonymous
   2011 
   2012 TextureGatherTests::TextureGatherTests (Context& context)
   2013 	: TestCaseGroup(context, "gather", "textureGather* tests")
   2014 {
   2015 }
   2016 
   2017 static inline const char* compareModeName (tcu::Sampler::CompareMode mode)
   2018 {
   2019 	switch (mode)
   2020 	{
   2021 		case tcu::Sampler::COMPAREMODE_LESS:				return "less";
   2022 		case tcu::Sampler::COMPAREMODE_LESS_OR_EQUAL:		return "less_or_equal";
   2023 		case tcu::Sampler::COMPAREMODE_GREATER:				return "greater";
   2024 		case tcu::Sampler::COMPAREMODE_GREATER_OR_EQUAL:	return "greater_or_equal";
   2025 		case tcu::Sampler::COMPAREMODE_EQUAL:				return "equal";
   2026 		case tcu::Sampler::COMPAREMODE_NOT_EQUAL:			return "not_equal";
   2027 		case tcu::Sampler::COMPAREMODE_ALWAYS:				return "always";
   2028 		case tcu::Sampler::COMPAREMODE_NEVER:				return "never";
   2029 		default: DE_ASSERT(false); return DE_NULL;
   2030 	}
   2031 }
   2032 
   2033 void TextureGatherTests::init (void)
   2034 {
   2035 	const struct
   2036 	{
   2037 		const char* name;
   2038 		TextureType type;
   2039 	} textureTypes[] =
   2040 	{
   2041 		{ "2d",			TEXTURETYPE_2D			},
   2042 		{ "2d_array",	TEXTURETYPE_2D_ARRAY	},
   2043 		{ "cube",		TEXTURETYPE_CUBE		}
   2044 	};
   2045 
   2046 	const struct
   2047 	{
   2048 		const char*			name;
   2049 		tcu::TextureFormat	format;
   2050 	} formats[] =
   2051 	{
   2052 		{ "rgba8",		tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNORM_INT8)		},
   2053 		{ "rgba8ui",	tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::UNSIGNED_INT8)	},
   2054 		{ "rgba8i",		tcu::TextureFormat(tcu::TextureFormat::RGBA,	tcu::TextureFormat::SIGNED_INT8)	},
   2055 		{ "depth32f",	tcu::TextureFormat(tcu::TextureFormat::D,		tcu::TextureFormat::FLOAT)			}
   2056 	};
   2057 
   2058 	const struct
   2059 	{
   2060 		const char*		name;
   2061 		IVec3			size;
   2062 	} textureSizes[] =
   2063 	{
   2064 		{ "size_pot",	IVec3(64, 64, 3) },
   2065 		{ "size_npot",	IVec3(17, 23, 3) }
   2066 	};
   2067 
   2068 	const struct
   2069 	{
   2070 		const char*				name;
   2071 		tcu::Sampler::WrapMode	mode;
   2072 	} wrapModes[] =
   2073 	{
   2074 		{ "clamp_to_edge",		tcu::Sampler::CLAMP_TO_EDGE			},
   2075 		{ "repeat",				tcu::Sampler::REPEAT_GL				},
   2076 		{ "mirrored_repeat",	tcu::Sampler::MIRRORED_REPEAT_GL	}
   2077 	};
   2078 
   2079 	for (int gatherTypeI = 0; gatherTypeI < GATHERTYPE_LAST; gatherTypeI++)
   2080 	{
   2081 		const GatherType		gatherType			= (GatherType)gatherTypeI;
   2082 		TestCaseGroup* const	gatherTypeGroup		= new TestCaseGroup(m_context, gatherTypeName(gatherType), gatherTypeDescription(gatherType));
   2083 		addChild(gatherTypeGroup);
   2084 
   2085 		for (int offsetSizeI = 0; offsetSizeI < OFFSETSIZE_LAST; offsetSizeI++)
   2086 		{
   2087 			const OffsetSize offsetSize = (OffsetSize)offsetSizeI;
   2088 			if ((gatherType == GATHERTYPE_BASIC) != (offsetSize == OFFSETSIZE_NONE))
   2089 				continue;
   2090 
   2091 			TestCaseGroup* const offsetSizeGroup = offsetSize == OFFSETSIZE_NONE ?
   2092 													gatherTypeGroup :
   2093 													new TestCaseGroup(m_context,
   2094 																	  offsetSize == OFFSETSIZE_MINIMUM_REQUIRED				? "min_required_offset"
   2095 																	  : offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM		? "implementation_offset"
   2096 																	  : DE_NULL,
   2097 																	  offsetSize == OFFSETSIZE_MINIMUM_REQUIRED				? "Use offsets within GL minimum required range"
   2098 																	  : offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM		? "Use offsets within the implementation range"
   2099 																	  : DE_NULL);
   2100 			if (offsetSizeGroup != gatherTypeGroup)
   2101 				gatherTypeGroup->addChild(offsetSizeGroup);
   2102 
   2103 			for (int textureTypeNdx = 0; textureTypeNdx < DE_LENGTH_OF_ARRAY(textureTypes); textureTypeNdx++)
   2104 			{
   2105 				const TextureType textureType = textureTypes[textureTypeNdx].type;
   2106 
   2107 				if (textureType == TEXTURETYPE_CUBE && gatherType != GATHERTYPE_BASIC)
   2108 					continue;
   2109 
   2110 				TestCaseGroup* const textureTypeGroup = new TestCaseGroup(m_context, textureTypes[textureTypeNdx].name, "");
   2111 				offsetSizeGroup->addChild(textureTypeGroup);
   2112 
   2113 				for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
   2114 				{
   2115 					const tcu::TextureFormat&	format			= formats[formatNdx].format;
   2116 					TestCaseGroup* const		formatGroup		= new TestCaseGroup(m_context, formats[formatNdx].name, "");
   2117 					textureTypeGroup->addChild(formatGroup);
   2118 
   2119 					for (int textureSizeNdx = 0; textureSizeNdx < DE_LENGTH_OF_ARRAY(textureSizes); textureSizeNdx++)
   2120 					{
   2121 						const IVec3&			textureSize			= textureSizes[textureSizeNdx].size;
   2122 						TestCaseGroup* const	textureSizeGroup	= new TestCaseGroup(m_context, textureSizes[textureSizeNdx].name, "");
   2123 						formatGroup->addChild(textureSizeGroup);
   2124 
   2125 						for (int compareModeI = 0; compareModeI < tcu::Sampler::COMPAREMODE_LAST; compareModeI++)
   2126 						{
   2127 							const tcu::Sampler::CompareMode compareMode = (tcu::Sampler::CompareMode)compareModeI;
   2128 
   2129 							if ((compareMode != tcu::Sampler::COMPAREMODE_NONE) != isDepthFormat(format))
   2130 								continue;
   2131 
   2132 							if (compareMode != tcu::Sampler::COMPAREMODE_NONE &&
   2133 								compareMode != tcu::Sampler::COMPAREMODE_LESS &&
   2134 								compareMode != tcu::Sampler::COMPAREMODE_GREATER)
   2135 								continue;
   2136 
   2137 							TestCaseGroup* const compareModeGroup = compareMode == tcu::Sampler::COMPAREMODE_NONE ?
   2138 																		textureSizeGroup :
   2139 																		new TestCaseGroup(m_context,
   2140 																						  (string() + "compare_" + compareModeName(compareMode)).c_str(),
   2141 																						  "");
   2142 							if (compareModeGroup != textureSizeGroup)
   2143 								textureSizeGroup->addChild(compareModeGroup);
   2144 
   2145 							for (int wrapCaseNdx = 0; wrapCaseNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapCaseNdx++)
   2146 							{
   2147 								const int						wrapSNdx	= wrapCaseNdx;
   2148 								const int						wrapTNdx	= (wrapCaseNdx + 1) % DE_LENGTH_OF_ARRAY(wrapModes);
   2149 								const tcu::Sampler::WrapMode	wrapS		= wrapModes[wrapSNdx].mode;
   2150 								const tcu::Sampler::WrapMode	wrapT		= wrapModes[wrapTNdx].mode;
   2151 
   2152 								const string caseName = string() + wrapModes[wrapSNdx].name + "_" + wrapModes[wrapTNdx].name;
   2153 
   2154 								compareModeGroup->addChild(makeTextureGatherCase(textureType, m_context, caseName.c_str(), "", gatherType, offsetSize, format, compareMode, wrapS, wrapT,
   2155 																				 MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, textureSize));
   2156 							}
   2157 						}
   2158 					}
   2159 
   2160 					if (offsetSize != OFFSETSIZE_MINIMUM_REQUIRED) // Don't test all features for both offset size types, as they should be rather orthogonal.
   2161 					{
   2162 						if (!isDepthFormat(format))
   2163 						{
   2164 							TestCaseGroup* const swizzleGroup = new TestCaseGroup(m_context, "texture_swizzle", "");
   2165 							formatGroup->addChild(swizzleGroup);
   2166 
   2167 							DE_STATIC_ASSERT(TEXTURESWIZZLECOMPONENT_R == 0);
   2168 							for (int swizzleCaseNdx = 0; swizzleCaseNdx < TEXTURESWIZZLECOMPONENT_LAST; swizzleCaseNdx++)
   2169 							{
   2170 								MaybeTextureSwizzle	swizzle	= MaybeTextureSwizzle::createSomeTextureSwizzle();
   2171 								string				caseName;
   2172 
   2173 								for (int i = 0; i < 4; i++)
   2174 								{
   2175 									swizzle.getSwizzle()[i] = (TextureSwizzleComponent)((swizzleCaseNdx + i) % (int)TEXTURESWIZZLECOMPONENT_LAST);
   2176 									caseName += (i > 0 ? "_" : "") + de::toLower(de::toString(swizzle.getSwizzle()[i]));
   2177 								}
   2178 
   2179 								swizzleGroup->addChild(makeTextureGatherCase(textureType, m_context, caseName.c_str(), "", gatherType, offsetSize, format,
   2180 																			 tcu::Sampler::COMPAREMODE_NONE, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2181 																			 swizzle, tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, IVec3(64, 64, 3)));
   2182 							}
   2183 						}
   2184 
   2185 						{
   2186 							TestCaseGroup* const filterModeGroup = new TestCaseGroup(m_context, "filter_mode", "Test that filter modes have no effect");
   2187 							formatGroup->addChild(filterModeGroup);
   2188 
   2189 							const struct
   2190 							{
   2191 								const char*					name;
   2192 								tcu::Sampler::FilterMode	filter;
   2193 							} magFilters[] =
   2194 							{
   2195 								{ "linear",		tcu::Sampler::LINEAR	},
   2196 								{ "nearest",	tcu::Sampler::NEAREST	}
   2197 							};
   2198 
   2199 							const struct
   2200 							{
   2201 								const char*					name;
   2202 								tcu::Sampler::FilterMode	filter;
   2203 							} minFilters[] =
   2204 							{
   2205 								// \note Don't test NEAREST here, as it's covered by other cases.
   2206 								{ "linear",						tcu::Sampler::LINEAR					},
   2207 								{ "nearest_mipmap_nearest",		tcu::Sampler::NEAREST_MIPMAP_NEAREST	},
   2208 								{ "nearest_mipmap_linear",		tcu::Sampler::NEAREST_MIPMAP_LINEAR		},
   2209 								{ "linear_mipmap_nearest",		tcu::Sampler::LINEAR_MIPMAP_NEAREST		},
   2210 								{ "linear_mipmap_linear",		tcu::Sampler::LINEAR_MIPMAP_LINEAR		},
   2211 							};
   2212 
   2213 							for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilters); minFilterNdx++)
   2214 							for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilters); magFilterNdx++)
   2215 							{
   2216 								const tcu::Sampler::FilterMode		minFilter		= minFilters[minFilterNdx].filter;
   2217 								const tcu::Sampler::FilterMode		magFilter		= magFilters[magFilterNdx].filter;
   2218 								const tcu::Sampler::CompareMode		compareMode		= isDepthFormat(format) ? tcu::Sampler::COMPAREMODE_LESS : tcu::Sampler::COMPAREMODE_NONE;
   2219 
   2220 								if ((isUnormFormatType(format.type) || isDepthFormat(format)) && magFilter == tcu::Sampler::NEAREST)
   2221 									continue; // Covered by other cases.
   2222 								if ((isUIntFormatType(format.type) || isSIntFormatType(format.type)) &&
   2223 									(magFilter != tcu::Sampler::NEAREST || minFilter != tcu::Sampler::NEAREST_MIPMAP_NEAREST))
   2224 									continue;
   2225 
   2226 								const string caseName = string() + "min_" + minFilters[minFilterNdx].name + "_mag_" + magFilters[magFilterNdx].name;
   2227 
   2228 								filterModeGroup->addChild(makeTextureGatherCase(textureType, m_context, caseName.c_str(), "", gatherType, offsetSize, format, compareMode,
   2229 																				tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, MaybeTextureSwizzle::createNoneTextureSwizzle(),
   2230 																				minFilter, magFilter, 0, IVec3(64, 64, 3)));
   2231 							}
   2232 						}
   2233 
   2234 						{
   2235 							TestCaseGroup* const baseLevelGroup = new TestCaseGroup(m_context, "base_level", "");
   2236 							formatGroup->addChild(baseLevelGroup);
   2237 
   2238 							for (int baseLevel = 1; baseLevel <= 2; baseLevel++)
   2239 							{
   2240 								const string						caseName		= "level_" + de::toString(baseLevel);
   2241 								const tcu::Sampler::CompareMode		compareMode		= isDepthFormat(format) ? tcu::Sampler::COMPAREMODE_LESS : tcu::Sampler::COMPAREMODE_NONE;
   2242 								baseLevelGroup->addChild(makeTextureGatherCase(textureType, m_context, caseName.c_str(), "", gatherType, offsetSize, format,
   2243 																			   compareMode, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2244 																			   MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST, tcu::Sampler::NEAREST,
   2245 																			   baseLevel, IVec3(64, 64, 3)));
   2246 							}
   2247 						}
   2248 
   2249 						if (!isDepthFormat(format)) // What shadow textures should return for incomplete textures is unclear.
   2250 						{
   2251 							TestCaseGroup* const incompleteGroup = new TestCaseGroup(m_context, "incomplete", "Test that textureGather* takes components from (0,0,0,1) for incomplete textures");
   2252 							formatGroup->addChild(incompleteGroup);
   2253 
   2254 							const tcu::Sampler::CompareMode compareMode = isDepthFormat(format) ? tcu::Sampler::COMPAREMODE_LESS : tcu::Sampler::COMPAREMODE_NONE;
   2255 							incompleteGroup->addChild(makeTextureGatherCase(textureType, m_context, "mipmap_incomplete", "", gatherType, offsetSize, format,
   2256 																			compareMode, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
   2257 																			MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST_MIPMAP_NEAREST, tcu::Sampler::NEAREST,
   2258 																			0, IVec3(64, 64, 3), true /* Mipmap-incomplete */));
   2259 						}
   2260 					}
   2261 				}
   2262 			}
   2263 		}
   2264 	}
   2265 }
   2266 
   2267 } // Functional
   2268 } // gles31
   2269 } // deqp
   2270