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 Shader Image Load & Store Tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es31fShaderImageLoadStoreTests.hpp"
25#include "glsTextureTestUtil.hpp"
26#include "gluContextInfo.hpp"
27#include "gluRenderContext.hpp"
28#include "gluShaderProgram.hpp"
29#include "gluObjectWrapper.hpp"
30#include "gluPixelTransfer.hpp"
31#include "gluTextureUtil.hpp"
32#include "gluStrUtil.hpp"
33#include "gluCallLogWrapper.hpp"
34#include "gluProgramInterfaceQuery.hpp"
35#include "gluDrawUtil.hpp"
36#include "tcuTestLog.hpp"
37#include "tcuTextureUtil.hpp"
38#include "tcuVector.hpp"
39#include "tcuImageCompare.hpp"
40#include "tcuFloat.hpp"
41#include "tcuVectorUtil.hpp"
42#include "deStringUtil.hpp"
43#include "deSharedPtr.hpp"
44#include "deUniquePtr.hpp"
45#include "deRandom.hpp"
46#include "deMemory.h"
47#include "glwFunctions.hpp"
48#include "glwDefs.hpp"
49#include "glwEnums.hpp"
50
51#include <vector>
52#include <string>
53#include <algorithm>
54#include <map>
55
56using glu::RenderContext;
57using tcu::TestLog;
58using tcu::Vec2;
59using tcu::Vec3;
60using tcu::Vec4;
61using tcu::IVec2;
62using tcu::IVec3;
63using tcu::IVec4;
64using tcu::UVec2;
65using tcu::UVec3;
66using tcu::UVec4;
67using tcu::TextureFormat;
68using tcu::ConstPixelBufferAccess;
69using tcu::PixelBufferAccess;
70using de::toString;
71using de::SharedPtr;
72using de::UniquePtr;
73
74using std::vector;
75using std::string;
76
77namespace deqp
78{
79
80using namespace gls::TextureTestUtil;
81using namespace glu::TextureTestUtil;
82
83namespace gles31
84{
85namespace Functional
86{
87
88//! Default image sizes used in most test cases.
89static inline IVec3 defaultImageSize (TextureType type)
90{
91	switch (type)
92	{
93		case TEXTURETYPE_BUFFER:	return IVec3(64,	1,		1);
94		case TEXTURETYPE_2D:		return IVec3(64,	64,		1);
95		case TEXTURETYPE_CUBE:		return IVec3(64,	64,		1);
96		case TEXTURETYPE_3D:		return IVec3(64,	64,		8);
97		case TEXTURETYPE_2D_ARRAY:	return IVec3(64,	64,		8);
98		default:
99			DE_ASSERT(false);
100			return IVec3(-1);
101	}
102}
103
104template <typename T>
105static string arrayStr (const std::vector<T> (&arr))
106{
107	string result = "{ ";
108	for (size_t i = 0; i < arr.size(); i++)
109		result += (i > 0 ? ", " : "") + toString(arr[i]);
110	result += " }";
111	return result;
112}
113
114template <typename T>
115static int arrayIndexOf (const std::vector<T> (&arr), const T& e)
116{
117	return (int)(std::find(arr.begin(), arr.end(), e) - arr.begin());
118}
119
120static const char* getTextureTypeName (TextureType type)
121{
122	switch (type)
123	{
124		case TEXTURETYPE_BUFFER:	return "buffer";
125		case TEXTURETYPE_2D:		return "2d";
126		case TEXTURETYPE_CUBE:		return "cube";
127		case TEXTURETYPE_3D:		return "3d";
128		case TEXTURETYPE_2D_ARRAY:	return "2d_array";
129		default:
130			DE_ASSERT(false);
131			return DE_NULL;
132	}
133}
134
135static inline bool isFormatTypeUnsignedInteger (TextureFormat::ChannelType type)
136{
137	return type == TextureFormat::UNSIGNED_INT8		||
138		   type == TextureFormat::UNSIGNED_INT16	||
139		   type == TextureFormat::UNSIGNED_INT32;
140}
141
142static inline bool isFormatTypeSignedInteger (TextureFormat::ChannelType type)
143{
144	return type == TextureFormat::SIGNED_INT8	||
145		   type == TextureFormat::SIGNED_INT16	||
146		   type == TextureFormat::SIGNED_INT32;
147}
148
149static inline bool isFormatTypeInteger (TextureFormat::ChannelType type)
150{
151	return isFormatTypeUnsignedInteger(type) || isFormatTypeSignedInteger(type);
152}
153
154static inline bool isFormatTypeUnorm (TextureFormat::ChannelType type)
155{
156	return type == TextureFormat::UNORM_INT8	||
157		   type == TextureFormat::UNORM_INT16	||
158		   type == TextureFormat::UNORM_INT32;
159}
160
161static inline bool isFormatTypeSnorm (TextureFormat::ChannelType type)
162{
163	return type == TextureFormat::SNORM_INT8	||
164		   type == TextureFormat::SNORM_INT16	||
165		   type == TextureFormat::SNORM_INT32;
166}
167
168static inline bool isFormatSupportedForTextureBuffer (const TextureFormat& format)
169{
170	switch (format.order)
171	{
172		case TextureFormat::RGB:
173			return format.type == TextureFormat::FLOAT				||
174				   format.type == TextureFormat::SIGNED_INT32		||
175				   format.type == TextureFormat::UNSIGNED_INT32;
176
177		// \note Fallthroughs.
178		case TextureFormat::R:
179		case TextureFormat::RG:
180		case TextureFormat::RGBA:
181			return format.type == TextureFormat::UNORM_INT8			||
182				   format.type == TextureFormat::HALF_FLOAT			||
183				   format.type == TextureFormat::FLOAT				||
184				   format.type == TextureFormat::SIGNED_INT8		||
185				   format.type == TextureFormat::SIGNED_INT16		||
186				   format.type == TextureFormat::SIGNED_INT32		||
187				   format.type == TextureFormat::UNSIGNED_INT8		||
188				   format.type == TextureFormat::UNSIGNED_INT16		||
189				   format.type == TextureFormat::UNSIGNED_INT32;
190
191		default:
192			return false;
193	}
194}
195
196static inline string getShaderImageFormatQualifier (const TextureFormat& format)
197{
198	const char* orderPart;
199	const char* typePart;
200
201	switch (format.order)
202	{
203		case TextureFormat::R:		orderPart = "r";		break;
204		case TextureFormat::RGBA:	orderPart = "rgba";		break;
205		default:
206			DE_ASSERT(false);
207			orderPart = DE_NULL;
208	}
209
210	switch (format.type)
211	{
212		case TextureFormat::FLOAT:				typePart = "32f";			break;
213		case TextureFormat::HALF_FLOAT:			typePart = "16f";			break;
214
215		case TextureFormat::UNSIGNED_INT32:		typePart = "32ui";			break;
216		case TextureFormat::UNSIGNED_INT16:		typePart = "16ui";			break;
217		case TextureFormat::UNSIGNED_INT8:		typePart = "8ui";			break;
218
219		case TextureFormat::SIGNED_INT32:		typePart = "32i";			break;
220		case TextureFormat::SIGNED_INT16:		typePart = "16i";			break;
221		case TextureFormat::SIGNED_INT8:		typePart = "8i";			break;
222
223		case TextureFormat::UNORM_INT16:		typePart = "16";			break;
224		case TextureFormat::UNORM_INT8:			typePart = "8";				break;
225
226		case TextureFormat::SNORM_INT16:		typePart = "16_snorm";		break;
227		case TextureFormat::SNORM_INT8:			typePart = "8_snorm";		break;
228
229		default:
230			DE_ASSERT(false);
231			typePart = DE_NULL;
232	}
233
234	return string() + orderPart + typePart;
235}
236
237static inline string getShaderSamplerOrImageType (TextureFormat::ChannelType formatType, TextureType textureType, bool isSampler)
238{
239	const char* const formatPart		= isFormatTypeUnsignedInteger(formatType)	? "u"
240										: isFormatTypeSignedInteger(formatType)		? "i"
241										: "";
242
243	const char* const imageTypePart		= textureType == TEXTURETYPE_BUFFER		? "Buffer"
244										: textureType == TEXTURETYPE_2D			? "2D"
245										: textureType == TEXTURETYPE_3D			? "3D"
246										: textureType == TEXTURETYPE_CUBE		? "Cube"
247										: textureType == TEXTURETYPE_2D_ARRAY	? "2DArray"
248										: DE_NULL;
249
250	return string() + formatPart + (isSampler ? "sampler" : "image") + imageTypePart;
251}
252
253static inline string getShaderImageType (TextureFormat::ChannelType formatType, TextureType imageType)
254{
255	return getShaderSamplerOrImageType(formatType, imageType, false);
256}
257
258static inline string getShaderSamplerType (TextureFormat::ChannelType formatType, TextureType imageType)
259{
260	return getShaderSamplerOrImageType(formatType, imageType, true);
261}
262
263static inline deUint32 getGLTextureTarget (TextureType texType)
264{
265	switch (texType)
266	{
267		case TEXTURETYPE_BUFFER:	return GL_TEXTURE_BUFFER;
268		case TEXTURETYPE_2D:		return GL_TEXTURE_2D;
269		case TEXTURETYPE_3D:		return GL_TEXTURE_3D;
270		case TEXTURETYPE_CUBE:		return GL_TEXTURE_CUBE_MAP;
271		case TEXTURETYPE_2D_ARRAY:	return GL_TEXTURE_2D_ARRAY;
272		default:
273			DE_ASSERT(false);
274			return (deUint32)-1;
275	}
276}
277
278static inline int getGLTextureMaxSize (glu::CallLogWrapper glLog, TextureType texType)
279{
280	int maxSize;
281	deUint32 macroValue;
282	switch (texType)
283	{
284		case TEXTURETYPE_BUFFER:
285			macroValue = GL_MAX_TEXTURE_BUFFER_SIZE;
286			break;
287		case TEXTURETYPE_3D:
288		case TEXTURETYPE_2D_ARRAY:
289			macroValue = GL_MAX_3D_TEXTURE_SIZE;
290			break;
291		case TEXTURETYPE_2D:
292		case TEXTURETYPE_CUBE:
293			macroValue = GL_MAX_TEXTURE_SIZE;
294			break;
295		default:
296			DE_ASSERT(false);
297			return (deUint32)-1;
298	}
299
300	glLog.glGetIntegerv(macroValue, &maxSize);
301
302	return maxSize;
303}
304
305static deUint32 cubeFaceToGLFace (tcu::CubeFace face)
306{
307	switch (face)
308	{
309		case tcu::CUBEFACE_NEGATIVE_X: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
310		case tcu::CUBEFACE_POSITIVE_X: return GL_TEXTURE_CUBE_MAP_POSITIVE_X;
311		case tcu::CUBEFACE_NEGATIVE_Y: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
312		case tcu::CUBEFACE_POSITIVE_Y: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
313		case tcu::CUBEFACE_NEGATIVE_Z: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
314		case tcu::CUBEFACE_POSITIVE_Z: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
315		default:
316			DE_ASSERT(false);
317			return GL_NONE;
318	}
319}
320
321static inline tcu::Texture1D* newOneLevelTexture1D (const tcu::TextureFormat& format, int w)
322{
323	tcu::Texture1D* const res = new tcu::Texture1D(format, w);
324	res->allocLevel(0);
325	return res;
326}
327
328static inline tcu::Texture2D* newOneLevelTexture2D (const tcu::TextureFormat& format, int w, int h)
329{
330	tcu::Texture2D* const res = new tcu::Texture2D(format, w, h);
331	res->allocLevel(0);
332	return res;
333}
334
335static inline tcu::TextureCube* newOneLevelTextureCube (const tcu::TextureFormat& format, int size)
336{
337	tcu::TextureCube* const res = new tcu::TextureCube(format, size);
338	for (int i = 0; i < tcu::CUBEFACE_LAST; i++)
339		res->allocLevel((tcu::CubeFace)i, 0);
340	return res;
341}
342
343static inline tcu::Texture3D* newOneLevelTexture3D (const tcu::TextureFormat& format, int w, int h, int d)
344{
345	tcu::Texture3D* const res = new tcu::Texture3D(format, w, h, d);
346	res->allocLevel(0);
347	return res;
348}
349
350static inline tcu::Texture2DArray* newOneLevelTexture2DArray (const tcu::TextureFormat& format, int w, int h, int d)
351{
352	tcu::Texture2DArray* const res = new tcu::Texture2DArray(format, w, h, d);
353	res->allocLevel(0);
354	return res;
355}
356
357static inline TextureType textureLayerType (TextureType entireTextureType)
358{
359	switch (entireTextureType)
360	{
361		// Single-layer types.
362		// \note Fallthrough.
363		case TEXTURETYPE_BUFFER:
364		case TEXTURETYPE_2D:
365			return entireTextureType;
366
367		// Multi-layer types with 2d layers.
368		case TEXTURETYPE_3D:
369		case TEXTURETYPE_CUBE:
370		case TEXTURETYPE_2D_ARRAY:
371			return TEXTURETYPE_2D;
372
373		default:
374			DE_ASSERT(false);
375			return TEXTURETYPE_LAST;
376	}
377}
378
379static const char* const s_texBufExtString = "GL_EXT_texture_buffer";
380
381static bool supportsES32orGL45(const RenderContext& renderContext)
382{
383	glu::ContextType contextType = renderContext.getType();
384	return glu::contextSupports(contextType, glu::ApiType::es(3, 2)) ||
385		   glu::contextSupports(contextType, glu::ApiType::core(4, 5));
386}
387
388static inline void checkTextureTypeExtensions (const glu::ContextInfo& contextInfo, TextureType type, const RenderContext& renderContext)
389{
390	if (type == TEXTURETYPE_BUFFER && !contextInfo.isExtensionSupported(s_texBufExtString) && !supportsES32orGL45(renderContext))
391		throw tcu::NotSupportedError("Test requires " + string(s_texBufExtString) + " extension");
392}
393
394static inline string textureTypeExtensionShaderRequires (TextureType type, const RenderContext& renderContext)
395{
396	if (!supportsES32orGL45(renderContext) && (type == TEXTURETYPE_BUFFER))
397		return "#extension " + string(s_texBufExtString) + " : require\n";
398	else
399		return "";
400}
401
402static const char* const s_imageAtomicExtString = "GL_OES_shader_image_atomic";
403
404static inline string imageAtomicExtensionShaderRequires (const RenderContext& renderContext)
405{
406	if (!supportsES32orGL45(renderContext))
407		return "#extension " + string(s_imageAtomicExtString) + " : require\n";
408	else
409		return "";
410}
411
412namespace
413{
414
415enum AtomicOperation
416{
417	ATOMIC_OPERATION_ADD = 0,
418	ATOMIC_OPERATION_MIN,
419	ATOMIC_OPERATION_MAX,
420	ATOMIC_OPERATION_AND,
421	ATOMIC_OPERATION_OR,
422	ATOMIC_OPERATION_XOR,
423	ATOMIC_OPERATION_EXCHANGE,
424	ATOMIC_OPERATION_COMP_SWAP,
425
426	ATOMIC_OPERATION_LAST
427};
428
429//! An order-independent operation is one for which the end result doesn't depend on the order in which the operations are carried (i.e. is both commutative and associative).
430static bool isOrderIndependentAtomicOperation (AtomicOperation op)
431{
432	return op == ATOMIC_OPERATION_ADD	||
433		   op == ATOMIC_OPERATION_MIN	||
434		   op == ATOMIC_OPERATION_MAX	||
435		   op == ATOMIC_OPERATION_AND	||
436		   op == ATOMIC_OPERATION_OR	||
437		   op == ATOMIC_OPERATION_XOR;
438}
439
440//! Computes the result of an atomic operation where "a" is the data operated on and "b" is the parameter to the atomic function.
441int computeBinaryAtomicOperationResult (AtomicOperation op, int a, int b)
442{
443	switch (op)
444	{
445		case ATOMIC_OPERATION_ADD:			return a + b;
446		case ATOMIC_OPERATION_MIN:			return de::min(a, b);
447		case ATOMIC_OPERATION_MAX:			return de::max(a, b);
448		case ATOMIC_OPERATION_AND:			return a & b;
449		case ATOMIC_OPERATION_OR:			return a | b;
450		case ATOMIC_OPERATION_XOR:			return a ^ b;
451		case ATOMIC_OPERATION_EXCHANGE:		return b;
452		default:
453			DE_ASSERT(false);
454			return -1;
455	}
456}
457
458//! \note For floats, only the exchange operation is supported.
459float computeBinaryAtomicOperationResult (AtomicOperation op, float /*a*/, float b)
460{
461	switch (op)
462	{
463		case ATOMIC_OPERATION_EXCHANGE: return b;
464		default:
465			DE_ASSERT(false);
466			return -1;
467	}
468}
469
470static const char* getAtomicOperationCaseName (AtomicOperation op)
471{
472	switch (op)
473	{
474		case ATOMIC_OPERATION_ADD:			return "add";
475		case ATOMIC_OPERATION_MIN:			return "min";
476		case ATOMIC_OPERATION_MAX:			return "max";
477		case ATOMIC_OPERATION_AND:			return "and";
478		case ATOMIC_OPERATION_OR:			return "or";
479		case ATOMIC_OPERATION_XOR:			return "xor";
480		case ATOMIC_OPERATION_EXCHANGE:		return "exchange";
481		case ATOMIC_OPERATION_COMP_SWAP:	return "comp_swap";
482		default:
483			DE_ASSERT(false);
484			return DE_NULL;
485	}
486}
487
488static const char* getAtomicOperationShaderFuncName (AtomicOperation op)
489{
490	switch (op)
491	{
492		case ATOMIC_OPERATION_ADD:			return "imageAtomicAdd";
493		case ATOMIC_OPERATION_MIN:			return "imageAtomicMin";
494		case ATOMIC_OPERATION_MAX:			return "imageAtomicMax";
495		case ATOMIC_OPERATION_AND:			return "imageAtomicAnd";
496		case ATOMIC_OPERATION_OR:			return "imageAtomicOr";
497		case ATOMIC_OPERATION_XOR:			return "imageAtomicXor";
498		case ATOMIC_OPERATION_EXCHANGE:		return "imageAtomicExchange";
499		case ATOMIC_OPERATION_COMP_SWAP:	return "imageAtomicCompSwap";
500		default:
501			DE_ASSERT(false);
502			return DE_NULL;
503	}
504}
505
506//! In GLSL, when accessing cube images, the z coordinate is mapped to a cube face.
507//! \note This is _not_ the same as casting the z to a tcu::CubeFace.
508static inline tcu::CubeFace glslImageFuncZToCubeFace (int z)
509{
510	static const tcu::CubeFace faces[6] =
511	{
512		tcu::CUBEFACE_POSITIVE_X,
513		tcu::CUBEFACE_NEGATIVE_X,
514		tcu::CUBEFACE_POSITIVE_Y,
515		tcu::CUBEFACE_NEGATIVE_Y,
516		tcu::CUBEFACE_POSITIVE_Z,
517		tcu::CUBEFACE_NEGATIVE_Z
518	};
519
520	DE_ASSERT(de::inBounds(z, 0, DE_LENGTH_OF_ARRAY(faces)));
521	return faces[z];
522}
523
524class BufferMemMap
525{
526public:
527	BufferMemMap (const glw::Functions& gl, deUint32 target, int offset, int size, deUint32 access)
528		: m_gl		(gl)
529		, m_target	(target)
530		, m_ptr		(DE_NULL)
531	{
532		m_ptr = gl.mapBufferRange(target, offset, size, access);
533		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
534		TCU_CHECK(m_ptr);
535	}
536
537	~BufferMemMap (void)
538	{
539		m_gl.unmapBuffer(m_target);
540	}
541
542	void*	getPtr		(void) const { return m_ptr; }
543	void*	operator*	(void) const { return m_ptr; }
544
545private:
546							BufferMemMap			(const BufferMemMap& other);
547	BufferMemMap&			operator=				(const BufferMemMap& other);
548
549	const glw::Functions&	m_gl;
550	const deUint32			m_target;
551	void*					m_ptr;
552};
553
554//! Utility for more readable uniform assignment logging; logs the name of the uniform when assigning. Handles the locations, querying them the first time they're assigned
555//  \note Assumes that the appropriate program is in use when assigning uniforms.
556class UniformAccessLogger
557{
558public:
559	UniformAccessLogger (const glw::Functions& gl, TestLog& log, deUint32 programGL)
560		: m_gl			(gl)
561		, m_log			(log)
562		, m_programGL	(programGL)
563	{
564	}
565
566	void						assign1i (const string& name, int x);
567	void						assign3f (const string& name, float x, float y, float z);
568
569private:
570	int							getLocation (const string& name);
571
572	const glw::Functions&		m_gl;
573	TestLog&					m_log;
574	const deUint32				m_programGL;
575
576	std::map<string, int>		m_uniformLocations;
577};
578
579int UniformAccessLogger::getLocation (const string& name)
580{
581	if (m_uniformLocations.find(name) == m_uniformLocations.end())
582	{
583		const int loc = m_gl.getUniformLocation(m_programGL, name.c_str());
584		TCU_CHECK(loc != -1);
585		m_uniformLocations[name] = loc;
586	}
587	return m_uniformLocations[name];
588}
589
590void UniformAccessLogger::assign1i (const string& name, int x)
591{
592	const int loc = getLocation(name);
593	m_log << TestLog::Message << "// Assigning to uniform " << name << ": " << x << TestLog::EndMessage;
594	m_gl.uniform1i(loc, x);
595}
596
597void UniformAccessLogger::assign3f (const string& name, float x, float y, float z)
598{
599	const int loc = getLocation(name);
600	m_log << TestLog::Message << "// Assigning to uniform " << name << ": " << Vec3(x, y, z) << TestLog::EndMessage;
601	m_gl.uniform3f(loc, x, y, z);
602}
603
604//! Class containing a (single-level) texture of a given type. Supports accessing pixels with coordinate convention similar to that in imageStore() and imageLoad() in shaders; useful especially for cube maps.
605class LayeredImage
606{
607public:
608												LayeredImage				(TextureType type, const TextureFormat& format, int w, int h, int d);
609
610	TextureType									getImageType				(void) const { return m_type; }
611	const IVec3&								getSize						(void) const { return m_size; }
612	const TextureFormat&						getFormat					(void) const { return m_format; }
613
614	// \note For cube maps, set/getPixel's z parameter specifies the cube face in the same manner as in imageStore/imageLoad in GL shaders (see glslImageFuncZToCubeFace), instead of directly as a tcu::CubeFace.
615
616	template <typename ColorT>
617	void										setPixel					(int x, int y, int z, const ColorT& color) const;
618
619	Vec4										getPixel					(int x, int y, int z) const;
620	IVec4										getPixelInt					(int x, int y, int z) const;
621	UVec4										getPixelUint				(int x, int y, int z) const { return getPixelInt(x, y, z).asUint(); }
622
623	PixelBufferAccess							getAccess					(void)							{ return getAccessInternal();				}
624	PixelBufferAccess							getSliceAccess				(int slice)						{ return getSliceAccessInternal(slice);		}
625	PixelBufferAccess							getCubeFaceAccess			(tcu::CubeFace face)			{ return getCubeFaceAccessInternal(face);	}
626
627	ConstPixelBufferAccess						getAccess					(void)					const	{ return getAccessInternal();				}
628	ConstPixelBufferAccess						getSliceAccess				(int slice)				const	{ return getSliceAccessInternal(slice);		}
629	ConstPixelBufferAccess						getCubeFaceAccess			(tcu::CubeFace face)	const	{ return getCubeFaceAccessInternal(face);	}
630
631private:
632												LayeredImage				(const LayeredImage&);
633	LayeredImage&								operator=					(const LayeredImage&);
634
635	// Some helpers to reduce code duplication between const/non-const versions of getAccess and others.
636	PixelBufferAccess							getAccessInternal			(void) const;
637	PixelBufferAccess							getSliceAccessInternal		(int slice) const;
638	PixelBufferAccess							getCubeFaceAccessInternal	(tcu::CubeFace face) const;
639
640	const TextureType							m_type;
641	const IVec3									m_size;
642	const TextureFormat							m_format;
643
644	// \note Depending on m_type, exactly one of the following will contain non-null.
645	const SharedPtr<tcu::Texture1D>				m_texBuffer;
646	const SharedPtr<tcu::Texture2D>				m_tex2D;
647	const SharedPtr<tcu::TextureCube>			m_texCube;
648	const SharedPtr<tcu::Texture3D>				m_tex3D;
649	const SharedPtr<tcu::Texture2DArray>		m_tex2DArray;
650};
651
652LayeredImage::LayeredImage (TextureType type, const TextureFormat& format, int w, int h, int d)
653	: m_type		(type)
654	, m_size		(w, h, d)
655	, m_format		(format)
656	, m_texBuffer	(type == TEXTURETYPE_BUFFER		? SharedPtr<tcu::Texture1D>			(newOneLevelTexture1D		(format, w))		: SharedPtr<tcu::Texture1D>())
657	, m_tex2D		(type == TEXTURETYPE_2D			? SharedPtr<tcu::Texture2D>			(newOneLevelTexture2D		(format, w, h))		: SharedPtr<tcu::Texture2D>())
658	, m_texCube		(type == TEXTURETYPE_CUBE		? SharedPtr<tcu::TextureCube>		(newOneLevelTextureCube		(format, w))		: SharedPtr<tcu::TextureCube>())
659	, m_tex3D		(type == TEXTURETYPE_3D			? SharedPtr<tcu::Texture3D>			(newOneLevelTexture3D		(format, w, h, d))	: SharedPtr<tcu::Texture3D>())
660	, m_tex2DArray	(type == TEXTURETYPE_2D_ARRAY	? SharedPtr<tcu::Texture2DArray>	(newOneLevelTexture2DArray	(format, w, h, d))	: SharedPtr<tcu::Texture2DArray>())
661{
662	DE_ASSERT(m_size.z() == 1					||
663			  m_type == TEXTURETYPE_3D			||
664			  m_type == TEXTURETYPE_2D_ARRAY);
665
666	DE_ASSERT(m_size.y() == 1					||
667			  m_type == TEXTURETYPE_2D			||
668			  m_type == TEXTURETYPE_CUBE		||
669			  m_type == TEXTURETYPE_3D			||
670			  m_type == TEXTURETYPE_2D_ARRAY);
671
672	DE_ASSERT(w == h || type != TEXTURETYPE_CUBE);
673
674	DE_ASSERT(m_texBuffer	!= DE_NULL ||
675			  m_tex2D		!= DE_NULL ||
676			  m_texCube		!= DE_NULL ||
677			  m_tex3D		!= DE_NULL ||
678			  m_tex2DArray	!= DE_NULL);
679}
680
681template <typename ColorT>
682void LayeredImage::setPixel (int x, int y, int z, const ColorT& color) const
683{
684	const PixelBufferAccess access = m_type == TEXTURETYPE_BUFFER		? m_texBuffer->getLevel(0)
685								   : m_type == TEXTURETYPE_2D			? m_tex2D->getLevel(0)
686								   : m_type == TEXTURETYPE_CUBE			? m_texCube->getLevelFace(0, glslImageFuncZToCubeFace(z))
687								   : m_type == TEXTURETYPE_3D			? m_tex3D->getLevel(0)
688								   : m_type == TEXTURETYPE_2D_ARRAY		? m_tex2DArray->getLevel(0)
689								   : PixelBufferAccess();
690
691	access.setPixel(color, x, y, m_type == TEXTURETYPE_CUBE ? 0 : z);
692}
693
694Vec4 LayeredImage::getPixel (int x, int y, int z) const
695{
696	const ConstPixelBufferAccess access = m_type == TEXTURETYPE_CUBE ? getCubeFaceAccess(glslImageFuncZToCubeFace(z)) : getAccess();
697	return access.getPixel(x, y, m_type == TEXTURETYPE_CUBE ? 0 : z);
698}
699
700IVec4 LayeredImage::getPixelInt (int x, int y, int z) const
701{
702	const ConstPixelBufferAccess access = m_type == TEXTURETYPE_CUBE ? getCubeFaceAccess(glslImageFuncZToCubeFace(z)) : getAccess();
703	return access.getPixelInt(x, y, m_type == TEXTURETYPE_CUBE ? 0 : z);
704}
705
706PixelBufferAccess LayeredImage::getAccessInternal (void) const
707{
708	DE_ASSERT(m_type == TEXTURETYPE_BUFFER || m_type == TEXTURETYPE_2D || m_type == TEXTURETYPE_3D || m_type == TEXTURETYPE_2D_ARRAY);
709
710	return m_type == TEXTURETYPE_BUFFER		? m_texBuffer->getLevel(0)
711		 : m_type == TEXTURETYPE_2D			? m_tex2D->getLevel(0)
712		 : m_type == TEXTURETYPE_3D			? m_tex3D->getLevel(0)
713		 : m_type == TEXTURETYPE_2D_ARRAY	? m_tex2DArray->getLevel(0)
714		 : PixelBufferAccess();
715}
716
717PixelBufferAccess LayeredImage::getSliceAccessInternal (int slice) const
718{
719	const PixelBufferAccess srcAccess = getAccessInternal();
720	return tcu::getSubregion(srcAccess, 0, 0, slice, srcAccess.getWidth(), srcAccess.getHeight(), 1);
721}
722
723PixelBufferAccess LayeredImage::getCubeFaceAccessInternal (tcu::CubeFace face) const
724{
725	DE_ASSERT(m_type == TEXTURETYPE_CUBE);
726	return m_texCube->getLevelFace(0, face);
727}
728
729//! Set texture storage or, if using buffer texture, setup buffer and attach to texture.
730static void setTextureStorage (glu::CallLogWrapper& glLog, TextureType imageType, deUint32 internalFormat, const IVec3& imageSize, deUint32 textureBufGL)
731{
732	const deUint32 textureTarget = getGLTextureTarget(imageType);
733
734	switch (imageType)
735	{
736		case TEXTURETYPE_BUFFER:
737		{
738			const TextureFormat		format		= glu::mapGLInternalFormat(internalFormat);
739			const int				numBytes	= format.getPixelSize() * imageSize.x();
740			DE_ASSERT(isFormatSupportedForTextureBuffer(format));
741			glLog.glBindBuffer(GL_TEXTURE_BUFFER, textureBufGL);
742			glLog.glBufferData(GL_TEXTURE_BUFFER, numBytes, DE_NULL, GL_STATIC_DRAW);
743			glLog.glTexBuffer(GL_TEXTURE_BUFFER, internalFormat, textureBufGL);
744			DE_ASSERT(imageSize.y() == 1 && imageSize.z() == 1);
745			break;
746		}
747
748		// \note Fall-throughs.
749
750		case TEXTURETYPE_2D:
751		case TEXTURETYPE_CUBE:
752			glLog.glTexStorage2D(textureTarget, 1, internalFormat, imageSize.x(), imageSize.y());
753			DE_ASSERT(imageSize.z() == 1);
754			break;
755
756		case TEXTURETYPE_3D:
757		case TEXTURETYPE_2D_ARRAY:
758			glLog.glTexStorage3D(textureTarget, 1, internalFormat, imageSize.x(), imageSize.y(), imageSize.z());
759			break;
760
761		default:
762			DE_ASSERT(false);
763	}
764}
765
766static void uploadTexture (glu::CallLogWrapper& glLog, const LayeredImage& src, deUint32 textureBufGL)
767{
768	const deUint32				internalFormat	= glu::getInternalFormat(src.getFormat());
769	const glu::TransferFormat	transferFormat	= glu::getTransferFormat(src.getFormat());
770	const IVec3&				imageSize		= src.getSize();
771
772	setTextureStorage(glLog, src.getImageType(), internalFormat, imageSize, textureBufGL);
773
774	{
775		const int	pixelSize = src.getFormat().getPixelSize();
776		int			unpackAlignment;
777
778		if (deIsPowerOfTwo32(pixelSize))
779			unpackAlignment = 8;
780		else
781			unpackAlignment = 1;
782
783		glLog.glPixelStorei(GL_UNPACK_ALIGNMENT, unpackAlignment);
784	}
785
786	if (src.getImageType() == TEXTURETYPE_BUFFER)
787	{
788		glLog.glBindBuffer(GL_TEXTURE_BUFFER, textureBufGL);
789		glLog.glBufferData(GL_TEXTURE_BUFFER, src.getFormat().getPixelSize() * imageSize.x(), src.getAccess().getDataPtr(), GL_STATIC_DRAW);
790	}
791	else if (src.getImageType() == TEXTURETYPE_2D)
792		glLog.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, imageSize.x(), imageSize.y(), transferFormat.format, transferFormat.dataType, src.getAccess().getDataPtr());
793	else if (src.getImageType() == TEXTURETYPE_CUBE)
794	{
795		for (int faceI = 0; faceI < tcu::CUBEFACE_LAST; faceI++)
796		{
797			const tcu::CubeFace face = (tcu::CubeFace)faceI;
798			glLog.glTexSubImage2D(cubeFaceToGLFace(face), 0, 0, 0, imageSize.x(), imageSize.y(), transferFormat.format, transferFormat.dataType, src.getCubeFaceAccess(face).getDataPtr());
799		}
800	}
801	else
802	{
803		DE_ASSERT(src.getImageType() == TEXTURETYPE_3D || src.getImageType() == TEXTURETYPE_2D_ARRAY);
804		const deUint32 textureTarget = getGLTextureTarget(src.getImageType());
805		glLog.glTexSubImage3D(textureTarget, 0, 0, 0, 0, imageSize.x(), imageSize.y(), imageSize.z(), transferFormat.format, transferFormat.dataType, src.getAccess().getDataPtr());
806	}
807}
808
809static void readPixelsRGBAInteger32 (const PixelBufferAccess& dst, int originX, int originY, glu::CallLogWrapper& glLog)
810{
811	DE_ASSERT(dst.getDepth() == 1);
812
813	if (isFormatTypeUnsignedInteger(dst.getFormat().type))
814	{
815		vector<UVec4> data(dst.getWidth()*dst.getHeight());
816
817		glLog.glReadPixels(originX, originY, dst.getWidth(), dst.getHeight(), GL_RGBA_INTEGER, GL_UNSIGNED_INT, &data[0]);
818
819		for (int y = 0; y < dst.getHeight(); y++)
820		for (int x = 0; x < dst.getWidth(); x++)
821			dst.setPixel(data[y*dst.getWidth() + x], x, y);
822	}
823	else if (isFormatTypeSignedInteger(dst.getFormat().type))
824	{
825		vector<IVec4> data(dst.getWidth()*dst.getHeight());
826
827		glLog.glReadPixels(originX, originY, dst.getWidth(), dst.getHeight(), GL_RGBA_INTEGER, GL_INT, &data[0]);
828
829		for (int y = 0; y < dst.getHeight(); y++)
830		for (int x = 0; x < dst.getWidth(); x++)
831			dst.setPixel(data[y*dst.getWidth() + x], x, y);
832	}
833	else
834		DE_ASSERT(false);
835}
836
837//! Base for a functor for verifying and logging a 2d texture layer (2d image, cube face, 3d slice, 2d layer).
838class ImageLayerVerifier
839{
840public:
841	virtual bool	operator()				(TestLog&, const ConstPixelBufferAccess&, int sliceOrFaceNdx) const = 0;
842	virtual			~ImageLayerVerifier		(void) {}
843};
844
845static void setTexParameteri (glu::CallLogWrapper& glLog, deUint32 target)
846{
847	if (target != GL_TEXTURE_BUFFER)
848	{
849		glLog.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
850		glLog.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
851	}
852}
853
854//! Binds texture (one layer at a time) to color attachment of FBO and does glReadPixels(). Calls the verifier for each layer.
855//! \note Not for buffer textures.
856static bool readIntegerTextureViaFBOAndVerify (const RenderContext&			renderCtx,
857											   glu::CallLogWrapper&			glLog,
858											   deUint32						textureGL,
859											   TextureType					textureType,
860											   const TextureFormat&			textureFormat,
861											   const IVec3&					textureSize,
862											   const ImageLayerVerifier&	verifyLayer)
863{
864	DE_ASSERT(isFormatTypeInteger(textureFormat.type));
865	DE_ASSERT(textureType != TEXTURETYPE_BUFFER);
866
867	TestLog& log = glLog.getLog();
868
869	const tcu::ScopedLogSection section(log, "Verification", "Result verification (bind texture layer-by-layer to FBO, read with glReadPixels())");
870
871	const int			numSlicesOrFaces	= textureType == TEXTURETYPE_CUBE ? 6 : textureSize.z();
872	const deUint32		textureTargetGL		= getGLTextureTarget(textureType);
873	glu::Framebuffer	fbo					(renderCtx);
874	tcu::TextureLevel	resultSlice			(textureFormat, textureSize.x(), textureSize.y());
875
876	glLog.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
877	GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "Bind FBO");
878
879	glLog.glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
880	GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glMemoryBarrier");
881
882	glLog.glActiveTexture(GL_TEXTURE0);
883	glLog.glBindTexture(textureTargetGL, textureGL);
884	setTexParameteri(glLog, textureTargetGL);
885
886	for (int sliceOrFaceNdx = 0; sliceOrFaceNdx < numSlicesOrFaces; sliceOrFaceNdx++)
887	{
888		if (textureType == TEXTURETYPE_CUBE)
889			glLog.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cubeFaceToGLFace(glslImageFuncZToCubeFace(sliceOrFaceNdx)), textureGL, 0);
890		else if (textureType == TEXTURETYPE_2D)
891			glLog.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureGL, 0);
892		else if (textureType == TEXTURETYPE_3D || textureType == TEXTURETYPE_2D_ARRAY)
893			glLog.glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureGL, 0, sliceOrFaceNdx);
894		else
895			DE_ASSERT(false);
896
897		GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "Bind texture to framebuffer color attachment 0");
898
899		TCU_CHECK(glLog.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
900
901		readPixelsRGBAInteger32(resultSlice.getAccess(), 0, 0, glLog);
902		GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glReadPixels");
903
904		if (!verifyLayer(log, resultSlice, sliceOrFaceNdx))
905			return false;
906	}
907
908	return true;
909}
910
911//! Reads texture with texture() in compute shader, one layer at a time, putting values into a SSBO and reading with a mapping. Calls the verifier for each layer.
912//! \note Not for buffer textures.
913static bool readFloatOrNormTextureWithLookupsAndVerify (const RenderContext&		renderCtx,
914														glu::CallLogWrapper&		glLog,
915														deUint32					textureGL,
916														TextureType					textureType,
917														const TextureFormat&		textureFormat,
918														const IVec3&				textureSize,
919														const ImageLayerVerifier&	verifyLayer)
920{
921	DE_ASSERT(!isFormatTypeInteger(textureFormat.type));
922	DE_ASSERT(textureType != TEXTURETYPE_BUFFER);
923
924	TestLog& log = glLog.getLog();
925
926	const tcu::ScopedLogSection section(log, "Verification", "Result verification (read texture layer-by-layer in compute shader with texture() into SSBO)");
927	const std::string			glslVersionDeclaration = getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
928
929	const glu::ShaderProgram program(renderCtx,
930		glu::ProgramSources() << glu::ComputeSource(glslVersionDeclaration + "\n"
931													"layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
932													"layout (binding = 0) buffer Output\n"
933													"{\n"
934													"	vec4 color[" + toString(textureSize.x()*textureSize.y()) + "];\n"
935													"} sb_out;\n"
936													"\n"
937													"precision highp " + getShaderSamplerType(textureFormat.type, textureType) + ";\n"
938													"\n"
939													"uniform highp " + getShaderSamplerType(textureFormat.type, textureType) + " u_texture;\n"
940													"uniform highp vec3 u_texCoordLD;\n"
941													"uniform highp vec3 u_texCoordRD;\n"
942													"uniform highp vec3 u_texCoordLU;\n"
943													"uniform highp vec3 u_texCoordRU;\n"
944													"\n"
945													"void main (void)\n"
946													"{\n"
947													"	int gx = int(gl_GlobalInvocationID.x);\n"
948													"	int gy = int(gl_GlobalInvocationID.y);\n"
949													"	highp float s = (float(gx) + 0.5) / float(" + toString(textureSize.x()) + ");\n"
950													"	highp float t = (float(gy) + 0.5) / float(" + toString(textureType == TEXTURETYPE_CUBE ? textureSize.x() : textureSize.y()) + ");\n"
951													"	highp vec3 texCoord = u_texCoordLD*(1.0-s)*(1.0-t)\n"
952													"	                    + u_texCoordRD*(    s)*(1.0-t)\n"
953													"	                    + u_texCoordLU*(1.0-s)*(    t)\n"
954													"	                    + u_texCoordRU*(    s)*(    t);\n"
955													"	int ndx = gy*" + toString(textureSize.x()) + " + gx;\n"
956													"	sb_out.color[ndx] = texture(u_texture, texCoord" + (textureType == TEXTURETYPE_2D ? ".xy" : "") + ");\n"
957													"}\n"));
958
959	glLog.glUseProgram(program.getProgram());
960
961	log << program;
962
963	if (!program.isOk())
964	{
965		log << TestLog::Message << "// Failure: failed to compile program" << TestLog::EndMessage;
966		TCU_FAIL("Program compilation failed");
967	}
968
969	{
970		const deUint32			textureTargetGL		= getGLTextureTarget(textureType);
971		const glu::Buffer		outputBuffer		(renderCtx);
972		UniformAccessLogger		uniforms			(renderCtx.getFunctions(), log, program.getProgram());
973
974		// Setup texture.
975
976		glLog.glActiveTexture(GL_TEXTURE0);
977		glLog.glBindTexture(textureTargetGL, textureGL);
978		setTexParameteri(glLog, textureTargetGL);
979
980		uniforms.assign1i("u_texture", 0);
981
982		// Setup output buffer.
983		{
984			const deUint32		blockIndex		= glLog.glGetProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
985			const int			blockSize		= glu::getProgramResourceInt(renderCtx.getFunctions(), program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
986
987			log << TestLog::Message << "// Got buffer data size = " << blockSize << TestLog::EndMessage;
988			TCU_CHECK(blockSize > 0);
989
990			glLog.glBindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
991			glLog.glBufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_READ);
992			glLog.glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
993			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "SSB setup failed");
994		}
995
996		// Dispatch one layer at a time, read back and verify.
997		{
998			const int							numSlicesOrFaces	= textureType == TEXTURETYPE_CUBE ? 6 : textureSize.z();
999			tcu::TextureLevel					resultSlice			(textureFormat, textureSize.x(), textureSize.y());
1000			const PixelBufferAccess				resultSliceAccess	= resultSlice.getAccess();
1001			const deUint32						blockIndex			= glLog.glGetProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
1002			const int							blockSize			= glu::getProgramResourceInt(renderCtx.getFunctions(), program.getProgram(), GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
1003			const deUint32						valueIndex			= glLog.glGetProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Output.color");
1004			const glu::InterfaceVariableInfo	valueInfo			= glu::getProgramInterfaceVariableInfo(renderCtx.getFunctions(), program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
1005
1006			TCU_CHECK(valueInfo.arraySize == (deUint32)(textureSize.x()*textureSize.y()));
1007
1008			glLog.glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
1009
1010			for (int sliceOrFaceNdx = 0; sliceOrFaceNdx < numSlicesOrFaces; sliceOrFaceNdx++)
1011			{
1012				if (textureType == TEXTURETYPE_CUBE)
1013				{
1014					vector<float> coords;
1015					computeQuadTexCoordCube(coords, glslImageFuncZToCubeFace(sliceOrFaceNdx));
1016					uniforms.assign3f("u_texCoordLD", coords[3*0 + 0], coords[3*0 + 1], coords[3*0 + 2]);
1017					uniforms.assign3f("u_texCoordRD", coords[3*2 + 0], coords[3*2 + 1], coords[3*2 + 2]);
1018					uniforms.assign3f("u_texCoordLU", coords[3*1 + 0], coords[3*1 + 1], coords[3*1 + 2]);
1019					uniforms.assign3f("u_texCoordRU", coords[3*3 + 0], coords[3*3 + 1], coords[3*3 + 2]);
1020				}
1021				else
1022				{
1023					const float z = textureType == TEXTURETYPE_3D ?
1024										((float)sliceOrFaceNdx + 0.5f) / (float)numSlicesOrFaces :
1025										(float)sliceOrFaceNdx;
1026					uniforms.assign3f("u_texCoordLD", 0.0f, 0.0f, z);
1027					uniforms.assign3f("u_texCoordRD", 1.0f, 0.0f, z);
1028					uniforms.assign3f("u_texCoordLU", 0.0f, 1.0f, z);
1029					uniforms.assign3f("u_texCoordRU", 1.0f, 1.0f, z);
1030				}
1031
1032				glLog.glDispatchCompute(textureSize.x(), textureSize.y(), 1);
1033
1034				{
1035					log << TestLog::Message << "// Note: mapping buffer and reading color values written" << TestLog::EndMessage;
1036
1037					const BufferMemMap bufMap(renderCtx.getFunctions(), GL_SHADER_STORAGE_BUFFER, 0, blockSize, GL_MAP_READ_BIT);
1038
1039					for (int y = 0; y < textureSize.y(); y++)
1040					for (int x = 0; x < textureSize.x(); x++)
1041					{
1042						const int				ndx			= y*textureSize.x() + x;
1043						const float* const		clrData		= (const float*)((const deUint8*)bufMap.getPtr() + valueInfo.offset + valueInfo.arrayStride*ndx);
1044
1045						switch (textureFormat.order)
1046						{
1047							case TextureFormat::R:		resultSliceAccess.setPixel(Vec4(clrData[0]),											x, y); break;
1048							case TextureFormat::RGBA:	resultSliceAccess.setPixel(Vec4(clrData[0], clrData[1], clrData[2], clrData[3]),		x, y); break;
1049							default:
1050								DE_ASSERT(false);
1051						}
1052					}
1053				}
1054
1055				if (!verifyLayer(log, resultSliceAccess, sliceOrFaceNdx))
1056					return false;
1057			}
1058		}
1059
1060		return true;
1061	}
1062}
1063
1064//! Read buffer texture by reading the corresponding buffer with a mapping.
1065static bool readBufferTextureWithMappingAndVerify (const RenderContext&			renderCtx,
1066												   glu::CallLogWrapper&			glLog,
1067												   deUint32						bufferGL,
1068												   const TextureFormat&			textureFormat,
1069												   int							imageSize,
1070												   const ImageLayerVerifier&	verifyLayer)
1071{
1072	tcu::TextureLevel			result			(textureFormat, imageSize, 1);
1073	const PixelBufferAccess		resultAccess	= result.getAccess();
1074	const int					dataSize		= imageSize * textureFormat.getPixelSize();
1075
1076	const tcu::ScopedLogSection section(glLog.getLog(), "Verification", "Result verification (read texture's buffer with a mapping)");
1077	glLog.glBindBuffer(GL_TEXTURE_BUFFER, bufferGL);
1078
1079	{
1080		const BufferMemMap bufMap(renderCtx.getFunctions(), GL_TEXTURE_BUFFER, 0, dataSize, GL_MAP_READ_BIT);
1081		deMemcpy(resultAccess.getDataPtr(), bufMap.getPtr(), dataSize);
1082	}
1083
1084	return verifyLayer(glLog.getLog(), resultAccess, 0);
1085}
1086
1087//! Calls the appropriate texture verification function depending on texture format or type.
1088static bool readTextureAndVerify (const RenderContext&			renderCtx,
1089								  glu::CallLogWrapper&			glLog,
1090								  deUint32						textureGL,
1091								  deUint32						bufferGL,
1092								  TextureType					textureType,
1093								  const TextureFormat&			textureFormat,
1094								  const IVec3&					imageSize,
1095								  const ImageLayerVerifier&		verifyLayer)
1096{
1097	if (textureType == TEXTURETYPE_BUFFER)
1098		return readBufferTextureWithMappingAndVerify(renderCtx, glLog, bufferGL, textureFormat, imageSize.x(), verifyLayer);
1099	else
1100		return isFormatTypeInteger(textureFormat.type) ? readIntegerTextureViaFBOAndVerify				(renderCtx, glLog, textureGL, textureType, textureFormat, imageSize, verifyLayer)
1101													   : readFloatOrNormTextureWithLookupsAndVerify		(renderCtx, glLog, textureGL, textureType, textureFormat, imageSize, verifyLayer);
1102}
1103
1104//! An ImageLayerVerifier that simply compares the result slice to a slice in a reference image.
1105//! \note Holds the reference image as a reference (no pun intended) instead of a copy; caller must be aware of lifetime issues.
1106class ImageLayerComparer : public ImageLayerVerifier
1107{
1108public:
1109	ImageLayerComparer (const LayeredImage& reference,
1110						const IVec2& relevantRegion = IVec2(0) /* If given, only check this region of each slice. */)
1111		: m_reference		(reference)
1112		, m_relevantRegion	(relevantRegion.x() > 0 && relevantRegion.y() > 0 ? relevantRegion : reference.getSize().swizzle(0, 1))
1113	{
1114	}
1115
1116	bool operator() (TestLog& log, const tcu::ConstPixelBufferAccess& resultSlice, int sliceOrFaceNdx) const
1117	{
1118		const bool						isCube				= m_reference.getImageType() == TEXTURETYPE_CUBE;
1119		const ConstPixelBufferAccess	referenceSlice		= tcu::getSubregion(isCube ? m_reference.getCubeFaceAccess(glslImageFuncZToCubeFace(sliceOrFaceNdx))
1120																					   : m_reference.getSliceAccess(sliceOrFaceNdx),
1121																				0, 0, m_relevantRegion.x(), m_relevantRegion.y());
1122
1123		const string comparisonName = "Comparison" + toString(sliceOrFaceNdx);
1124		const string comparisonDesc = "Image Comparison, "
1125									+ (isCube ? "face " + string(glu::getCubeMapFaceName(cubeFaceToGLFace(glslImageFuncZToCubeFace(sliceOrFaceNdx))))
1126											  : "slice " + toString(sliceOrFaceNdx));
1127
1128		if (isFormatTypeInteger(m_reference.getFormat().type))
1129			return tcu::intThresholdCompare(log, comparisonName.c_str(), comparisonDesc.c_str(), referenceSlice, resultSlice, UVec4(0), tcu::COMPARE_LOG_RESULT);
1130		else
1131			return tcu::floatThresholdCompare(log, comparisonName.c_str(), comparisonDesc.c_str(), referenceSlice, resultSlice, Vec4(0.01f), tcu::COMPARE_LOG_RESULT);
1132	}
1133
1134private:
1135	const LayeredImage&		m_reference;
1136	const IVec2				m_relevantRegion;
1137};
1138
1139//! Case that just stores some computation results into an image.
1140class ImageStoreCase : public TestCase
1141{
1142public:
1143	enum CaseFlag
1144	{
1145		CASEFLAG_SINGLE_LAYER_BIND = 1 << 0 //!< If given, glBindImageTexture() is called with GL_FALSE <layered> argument, and for each layer the compute shader is separately dispatched.
1146	};
1147
1148	ImageStoreCase (Context& context, const char* name, const char* description, const TextureFormat& format, TextureType textureType, deUint32 caseFlags = 0)
1149		: TestCase				(context, name, description)
1150		, m_format				(format)
1151		, m_textureType			(textureType)
1152		, m_singleLayerBind		((caseFlags & CASEFLAG_SINGLE_LAYER_BIND) != 0)
1153	{
1154	}
1155
1156	void			init		(void) { checkTextureTypeExtensions(m_context.getContextInfo(), m_textureType, m_context.getRenderContext()); }
1157	IterateResult	iterate		(void);
1158
1159private:
1160	const TextureFormat		m_format;
1161	const TextureType		m_textureType;
1162	const bool				m_singleLayerBind;
1163};
1164
1165ImageStoreCase::IterateResult ImageStoreCase::iterate (void)
1166{
1167	const RenderContext&		renderCtx				= m_context.getRenderContext();
1168	TestLog&					log						(m_testCtx.getLog());
1169	glu::CallLogWrapper			glLog					(renderCtx.getFunctions(), log);
1170	const deUint32				internalFormatGL		= glu::getInternalFormat(m_format);
1171	const deUint32				textureTargetGL			= getGLTextureTarget(m_textureType);
1172	const IVec3&				imageSize				= defaultImageSize(m_textureType);
1173	const int					numSlicesOrFaces		= m_textureType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
1174	const int					maxImageDimension		= de::max(imageSize.x(), de::max(imageSize.y(), imageSize.z()));
1175	const float					storeColorScale			= isFormatTypeUnorm(m_format.type) ? 1.0f / (float)(maxImageDimension - 1)
1176														: isFormatTypeSnorm(m_format.type) ? 2.0f / (float)(maxImageDimension - 1)
1177														: 1.0f;
1178	const float					storeColorBias			= isFormatTypeSnorm(m_format.type) ? -1.0f : 0.0f;
1179	const glu::Buffer			textureBuf				(renderCtx); // \note Only really used if using buffer texture.
1180	const glu::Texture			texture					(renderCtx);
1181
1182	glLog.enableLogging(true);
1183
1184	// Setup texture.
1185
1186	log << TestLog::Message << "// Created a texture (name " << *texture << ")" << TestLog::EndMessage;
1187	if (m_textureType == TEXTURETYPE_BUFFER)
1188		log << TestLog::Message << "// Created a buffer for the texture (name " << *textureBuf << ")" << TestLog::EndMessage;
1189
1190	glLog.glActiveTexture(GL_TEXTURE0);
1191	glLog.glBindTexture(textureTargetGL, *texture);
1192	setTexParameteri(glLog, textureTargetGL);
1193	setTextureStorage(glLog, m_textureType, internalFormatGL, imageSize, *textureBuf);
1194
1195	// Perform image stores in compute shader.
1196
1197	{
1198		// Generate compute shader.
1199
1200		const string		shaderImageFormatStr	= getShaderImageFormatQualifier(m_format);
1201		const TextureType	shaderImageType			= m_singleLayerBind ? textureLayerType(m_textureType) : m_textureType;
1202		const string		shaderImageTypeStr		= getShaderImageType(m_format.type, shaderImageType);
1203		const bool			isUintFormat			= isFormatTypeUnsignedInteger(m_format.type);
1204		const bool			isIntFormat				= isFormatTypeSignedInteger(m_format.type);
1205		const string		colorBaseExpr			= string(isUintFormat ? "u" : isIntFormat ? "i" : "") + "vec4(gx^gy^gz, "
1206																												 "(" + toString(imageSize.x()-1) + "-gx)^gy^gz, "
1207																												 "gx^(" + toString(imageSize.y()-1) + "-gy)^gz, "
1208																												 "(" + toString(imageSize.x()-1) + "-gx)^(" + toString(imageSize.y()-1) + "-gy)^gz)";
1209		const string		colorExpr				= colorBaseExpr + (storeColorScale == 1.0f ? "" : "*" + toString(storeColorScale))
1210																	+ (storeColorBias == 0.0f ? "" : " + float(" + toString(storeColorBias) + ")");
1211		const std::string	glslVersionDeclaration	= glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
1212
1213		const glu::ShaderProgram program(renderCtx,
1214			glu::ProgramSources() << glu::ComputeSource(glslVersionDeclaration + "\n"
1215														+ textureTypeExtensionShaderRequires(shaderImageType, renderCtx) +
1216														"\n"
1217														"precision highp " + shaderImageTypeStr + ";\n"
1218														"\n"
1219														"layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
1220														"layout (" + shaderImageFormatStr + ", binding=0) writeonly uniform " + shaderImageTypeStr + " u_image;\n"
1221														+ (m_singleLayerBind ? "uniform int u_layerNdx;\n" : "") +
1222														"\n"
1223														"void main (void)\n"
1224														"{\n"
1225														"	int gx = int(gl_GlobalInvocationID.x);\n"
1226														"	int gy = int(gl_GlobalInvocationID.y);\n"
1227														"	int gz = " + (m_singleLayerBind ? "u_layerNdx" : "int(gl_GlobalInvocationID.z)") + ";\n"
1228														+ (shaderImageType == TEXTURETYPE_BUFFER ?
1229															"	imageStore(u_image, gx, " + colorExpr + ");\n"
1230														 : shaderImageType == TEXTURETYPE_2D ?
1231															"	imageStore(u_image, ivec2(gx, gy), " + colorExpr + ");\n"
1232														 : shaderImageType == TEXTURETYPE_3D || shaderImageType == TEXTURETYPE_CUBE || shaderImageType == TEXTURETYPE_2D_ARRAY ?
1233															"	imageStore(u_image, ivec3(gx, gy, gz), " + colorExpr + ");\n"
1234														 : deFatalStr("Invalid TextureType")) +
1235														"}\n"));
1236
1237		UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
1238
1239		log << program;
1240
1241		if (!program.isOk())
1242		{
1243			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
1244			return STOP;
1245		}
1246
1247		// Setup and dispatch.
1248
1249		glLog.glUseProgram(program.getProgram());
1250
1251		if (m_singleLayerBind)
1252		{
1253			for (int layerNdx = 0; layerNdx < numSlicesOrFaces; layerNdx++)
1254			{
1255				if (layerNdx > 0)
1256					glLog.glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
1257
1258				uniforms.assign1i("u_layerNdx", layerNdx);
1259
1260				glLog.glBindImageTexture(0, *texture, 0, GL_FALSE, layerNdx, GL_WRITE_ONLY, internalFormatGL);
1261				GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1262
1263				glLog.glDispatchCompute(imageSize.x(), imageSize.y(), 1);
1264				GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
1265			}
1266		}
1267		else
1268		{
1269			glLog.glBindImageTexture(0, *texture, 0, GL_TRUE, 0, GL_WRITE_ONLY, internalFormatGL);
1270			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1271
1272			glLog.glDispatchCompute(imageSize.x(), imageSize.y(), numSlicesOrFaces);
1273			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
1274		}
1275	}
1276
1277	// Create reference, read texture and compare to reference.
1278	{
1279		const int		isIntegerFormat		= isFormatTypeInteger(m_format.type);
1280		LayeredImage	reference			(m_textureType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
1281
1282		DE_ASSERT(!isIntegerFormat || (storeColorScale == 1.0f && storeColorBias == 0.0f));
1283
1284		for (int z = 0; z < numSlicesOrFaces; z++)
1285		for (int y = 0; y < imageSize.y(); y++)
1286		for (int x = 0; x < imageSize.x(); x++)
1287		{
1288			const IVec4 color(x^y^z, (imageSize.x()-1-x)^y^z, x^(imageSize.y()-1-y)^z, (imageSize.x()-1-x)^(imageSize.y()-1-y)^z);
1289
1290			if (isIntegerFormat)
1291				reference.setPixel(x, y, z, color);
1292			else
1293				reference.setPixel(x, y, z, color.asFloat()*storeColorScale + storeColorBias);
1294		}
1295
1296		const bool compareOk = readTextureAndVerify(renderCtx, glLog, *texture, *textureBuf, m_textureType, m_format, imageSize, ImageLayerComparer(reference));
1297
1298		m_testCtx.setTestResult(compareOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, compareOk ? "Pass" : "Image comparison failed");
1299		return STOP;
1300	}
1301}
1302
1303//! Case that copies an image to another, using imageLoad() and imageStore(). Texture formats don't necessarily match image formats.
1304class ImageLoadAndStoreCase : public TestCase
1305{
1306public:
1307	enum CaseFlag
1308	{
1309		CASEFLAG_SINGLE_LAYER_BIND	= 1 << 0,	//!< If given, glBindImageTexture() is called with GL_FALSE <layered> argument, and for each layer the compute shader is separately dispatched.
1310		CASEFLAG_RESTRICT_IMAGES	= 1 << 1	//!< If given, images in shader will be qualified with "restrict".
1311	};
1312
1313	ImageLoadAndStoreCase (Context& context, const char* name, const char* description, const TextureFormat& format, TextureType textureType, deUint32 caseFlags = 0)
1314		: TestCase				(context, name, description)
1315		, m_textureFormat		(format)
1316		, m_imageFormat			(format)
1317		, m_textureType			(textureType)
1318		, m_restrictImages		((caseFlags & CASEFLAG_RESTRICT_IMAGES)		!= 0)
1319		, m_singleLayerBind		((caseFlags & CASEFLAG_SINGLE_LAYER_BIND)	!= 0)
1320	{
1321	}
1322
1323	ImageLoadAndStoreCase (Context& context, const char* name, const char* description, const TextureFormat& textureFormat, const TextureFormat& imageFormat, TextureType textureType, deUint32 caseFlags = 0)
1324		: TestCase				(context, name, description)
1325		, m_textureFormat		(textureFormat)
1326		, m_imageFormat			(imageFormat)
1327		, m_textureType			(textureType)
1328		, m_restrictImages		((caseFlags & CASEFLAG_RESTRICT_IMAGES)		!= 0)
1329		, m_singleLayerBind		((caseFlags & CASEFLAG_SINGLE_LAYER_BIND)	!= 0)
1330	{
1331		DE_ASSERT(textureFormat.getPixelSize() == imageFormat.getPixelSize());
1332	}
1333
1334	void			init		(void) { checkTextureTypeExtensions(m_context.getContextInfo(), m_textureType, m_context.getRenderContext()); }
1335	IterateResult	iterate		(void);
1336
1337private:
1338	template <TextureFormat::ChannelType ImageFormatType, typename TcuFloatType, typename TcuFloatStorageType>
1339	static void					replaceBadFloatReinterpretValues (LayeredImage& image, const TextureFormat& imageFormat);
1340
1341	const TextureFormat			m_textureFormat;
1342	const TextureFormat			m_imageFormat;
1343	const TextureType			m_textureType;
1344	const bool					m_restrictImages;
1345	const bool					m_singleLayerBind;
1346};
1347
1348template <TextureFormat::ChannelType ImageFormatType, typename TcuFloatType, typename TcuFloatTypeStorageType>
1349void ImageLoadAndStoreCase::replaceBadFloatReinterpretValues (LayeredImage& image, const TextureFormat& imageFormat)
1350{
1351	// Find potential bad values, such as nan or inf, and replace with something else.
1352	const int		pixelSize			= imageFormat.getPixelSize();
1353	const int		imageNumChannels	= imageFormat.order == tcu::TextureFormat::R	? 1
1354										: imageFormat.order == tcu::TextureFormat::RGBA	? 4
1355										: 0;
1356	const IVec3		imageSize			= image.getSize();
1357	const int		numSlicesOrFaces	= image.getImageType() == TEXTURETYPE_CUBE ? 6 : imageSize.z();
1358
1359	DE_ASSERT(pixelSize % imageNumChannels == 0);
1360
1361	for (int z = 0; z < numSlicesOrFaces; z++)
1362	{
1363		const PixelBufferAccess		sliceAccess		= image.getImageType() == TEXTURETYPE_CUBE ? image.getCubeFaceAccess((tcu::CubeFace)z) : image.getSliceAccess(z);
1364		const int					rowPitch		= sliceAccess.getRowPitch();
1365		void *const					data			= sliceAccess.getDataPtr();
1366
1367		for (int y = 0; y < imageSize.y(); y++)
1368		for (int x = 0; x < imageSize.x(); x++)
1369		{
1370			void *const pixelData = (deUint8*)data + y*rowPitch + x*pixelSize;
1371
1372			for (int c = 0; c < imageNumChannels; c++)
1373			{
1374				void *const			channelData		= (deUint8*)pixelData + c*pixelSize/imageNumChannels;
1375				const TcuFloatType	f				(*(TcuFloatTypeStorageType*)channelData);
1376
1377				if (f.isDenorm() || f.isInf() || f.isNaN())
1378					*(TcuFloatTypeStorageType*)channelData = TcuFloatType(0.0f).bits();
1379			}
1380		}
1381	}
1382}
1383
1384ImageLoadAndStoreCase::IterateResult ImageLoadAndStoreCase::iterate (void)
1385{
1386	const RenderContext&		renderCtx					= m_context.getRenderContext();
1387	TestLog&					log							(m_testCtx.getLog());
1388	glu::CallLogWrapper			glLog						(renderCtx.getFunctions(), log);
1389	const deUint32				textureInternalFormatGL		= glu::getInternalFormat(m_textureFormat);
1390	const deUint32				imageInternalFormatGL		= glu::getInternalFormat(m_imageFormat);
1391	const deUint32				textureTargetGL				= getGLTextureTarget(m_textureType);
1392	const IVec3&				imageSize					= defaultImageSize(m_textureType);
1393	const int					maxImageDimension			= de::max(imageSize.x(), de::max(imageSize.y(), imageSize.z()));
1394	const float					storeColorScale				= isFormatTypeUnorm(m_textureFormat.type) ? 1.0f / (float)(maxImageDimension - 1)
1395															: isFormatTypeSnorm(m_textureFormat.type) ? 2.0f / (float)(maxImageDimension - 1)
1396															: 1.0f;
1397	const float					storeColorBias				= isFormatTypeSnorm(m_textureFormat.type) ? -1.0f : 0.0f;
1398	const int					numSlicesOrFaces			= m_textureType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
1399	const bool					isIntegerTextureFormat		= isFormatTypeInteger(m_textureFormat.type);
1400	const glu::Buffer			texture0Buf					(renderCtx);
1401	const glu::Buffer			texture1Buf					(renderCtx);
1402	const glu::Texture			texture0					(renderCtx);
1403	const glu::Texture			texture1					(renderCtx);
1404	LayeredImage				reference					(m_textureType, m_textureFormat, imageSize.x(), imageSize.y(), imageSize.z());
1405
1406	glLog.enableLogging(true);
1407
1408	// Setup textures.
1409
1410	log << TestLog::Message << "// Created 2 textures (names " << *texture0 << " and " << *texture1 << ")" << TestLog::EndMessage;
1411	if (m_textureType == TEXTURETYPE_BUFFER)
1412		log << TestLog::Message << "// Created buffers for the textures (names " << *texture0Buf << " and " << *texture1Buf << ")" << TestLog::EndMessage;
1413
1414	// First, fill reference with (a fairly arbitrary) initial pattern. This will be used as texture upload source data as well as for actual reference computation later on.
1415
1416	DE_ASSERT(!isIntegerTextureFormat || (storeColorScale == 1.0f && storeColorBias == 0.0f));
1417
1418	for (int z = 0; z < numSlicesOrFaces; z++)
1419	for (int y = 0; y < imageSize.y(); y++)
1420	for (int x = 0; x < imageSize.x(); x++)
1421	{
1422		const IVec4 color(x^y^z, (imageSize.x()-1-x)^y^z, x^(imageSize.y()-1-y)^z, (imageSize.x()-1-x)^(imageSize.y()-1-y)^z);
1423
1424		if (isIntegerTextureFormat)
1425			reference.setPixel(x, y, z, color);
1426		else
1427			reference.setPixel(x, y, z, color.asFloat()*storeColorScale + storeColorBias);
1428	}
1429
1430	// If re-interpreting the texture contents as floating point values, need to get rid of inf, nan etc.
1431	if (m_imageFormat.type == TextureFormat::HALF_FLOAT && m_textureFormat.type != TextureFormat::HALF_FLOAT)
1432		replaceBadFloatReinterpretValues<TextureFormat::HALF_FLOAT, tcu::Float16, deUint16>(reference, m_imageFormat);
1433	else if (m_imageFormat.type == TextureFormat::FLOAT && m_textureFormat.type != TextureFormat::FLOAT)
1434		replaceBadFloatReinterpretValues<TextureFormat::FLOAT, tcu::Float32, deUint32>(reference, m_imageFormat);
1435
1436	// Upload initial pattern to texture 0.
1437
1438	glLog.glActiveTexture(GL_TEXTURE0);
1439	glLog.glBindTexture(textureTargetGL, *texture0);
1440	setTexParameteri(glLog, textureTargetGL);
1441
1442	log << TestLog::Message << "// Filling texture " << *texture0 << " with xor pattern" << TestLog::EndMessage;
1443
1444	uploadTexture(glLog, reference, *texture0Buf);
1445
1446	// Set storage for texture 1.
1447
1448	glLog.glActiveTexture(GL_TEXTURE1);
1449	glLog.glBindTexture(textureTargetGL, *texture1);
1450	setTexParameteri(glLog, textureTargetGL);
1451	setTextureStorage(glLog, m_textureType, textureInternalFormatGL, imageSize, *texture1Buf);
1452
1453	// Perform image loads and stores in compute shader and finalize reference computation.
1454
1455	{
1456		// Generate compute shader.
1457
1458		const char* const		maybeRestrict			= m_restrictImages ? "restrict" : "";
1459		const string			shaderImageFormatStr	= getShaderImageFormatQualifier(m_imageFormat);
1460		const TextureType		shaderImageType			= m_singleLayerBind ? textureLayerType(m_textureType) : m_textureType;
1461		const string			shaderImageTypeStr		= getShaderImageType(m_imageFormat.type, shaderImageType);
1462		const std::string		glslVersionDeclaration	= glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
1463
1464		const glu::ShaderProgram program(renderCtx,
1465			glu::ProgramSources() << glu::ComputeSource(glslVersionDeclaration + "\n"
1466														+ textureTypeExtensionShaderRequires(shaderImageType, renderCtx) +
1467														"\n"
1468														"precision highp " + shaderImageTypeStr + ";\n"
1469														"\n"
1470														"layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
1471														"layout (" + shaderImageFormatStr + ", binding=0) " + maybeRestrict + " readonly uniform " + shaderImageTypeStr + " u_image0;\n"
1472														"layout (" + shaderImageFormatStr + ", binding=1) " + maybeRestrict + " writeonly uniform " + shaderImageTypeStr + " u_image1;\n"
1473														"\n"
1474														"void main (void)\n"
1475														"{\n"
1476														+ (shaderImageType == TEXTURETYPE_BUFFER ?
1477															"	int pos = int(gl_GlobalInvocationID.x);\n"
1478															"	imageStore(u_image1, pos, imageLoad(u_image0, " + toString(imageSize.x()-1) + "-pos));\n"
1479														 : shaderImageType == TEXTURETYPE_2D ?
1480															"	ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n"
1481															"	imageStore(u_image1, pos, imageLoad(u_image0, ivec2(" + toString(imageSize.x()-1) + "-pos.x, pos.y)));\n"
1482														 : shaderImageType == TEXTURETYPE_3D || shaderImageType == TEXTURETYPE_CUBE || shaderImageType == TEXTURETYPE_2D_ARRAY ?
1483															"	ivec3 pos = ivec3(gl_GlobalInvocationID);\n"
1484															"	imageStore(u_image1, pos, imageLoad(u_image0, ivec3(" + toString(imageSize.x()-1) + "-pos.x, pos.y, pos.z)));\n"
1485														 : deFatalStr("Invalid TextureType")) +
1486														"}\n"));
1487
1488		UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
1489
1490		log << program;
1491
1492		if (!program.isOk())
1493		{
1494			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
1495			return STOP;
1496		}
1497
1498		// Setup and dispatch.
1499
1500		glLog.glUseProgram(program.getProgram());
1501
1502		if (m_singleLayerBind)
1503		{
1504			for (int layerNdx = 0; layerNdx < numSlicesOrFaces; layerNdx++)
1505			{
1506				if (layerNdx > 0)
1507					glLog.glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
1508
1509				glLog.glBindImageTexture(0, *texture0, 0, GL_FALSE, layerNdx, GL_READ_ONLY, imageInternalFormatGL);
1510				GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1511
1512				glLog.glBindImageTexture(1, *texture1, 0, GL_FALSE, layerNdx, GL_WRITE_ONLY, imageInternalFormatGL);
1513				GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1514
1515				glLog.glDispatchCompute(imageSize.x(), imageSize.y(), 1);
1516				GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
1517			}
1518		}
1519		else
1520		{
1521			glLog.glBindImageTexture(0, *texture0, 0, GL_TRUE, 0, GL_READ_ONLY, imageInternalFormatGL);
1522			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1523
1524			glLog.glBindImageTexture(1, *texture1, 0, GL_TRUE, 0, GL_WRITE_ONLY, imageInternalFormatGL);
1525			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1526
1527			glLog.glDispatchCompute(imageSize.x(), imageSize.y(), numSlicesOrFaces);
1528			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
1529		}
1530
1531		// Finalize reference.
1532
1533		if (m_textureFormat != m_imageFormat)
1534		{
1535			// Format re-interpretation case. Read data with image format and write back, with the same image format.
1536			// We do this because the data may change a little during lookups (e.g. unorm8 -> float; not all unorms can be exactly represented as floats).
1537
1538			const int					pixelSize		= m_imageFormat.getPixelSize();
1539			tcu::TextureLevel			scratch			(m_imageFormat, 1, 1);
1540			const PixelBufferAccess		scratchAccess	= scratch.getAccess();
1541
1542			for (int z = 0; z < numSlicesOrFaces; z++)
1543			{
1544				const PixelBufferAccess		sliceAccess		= m_textureType == TEXTURETYPE_CUBE ? reference.getCubeFaceAccess((tcu::CubeFace)z) : reference.getSliceAccess(z);
1545				const int					rowPitch		= sliceAccess.getRowPitch();
1546				void *const					data			= sliceAccess.getDataPtr();
1547
1548				for (int y = 0; y < imageSize.y(); y++)
1549				for (int x = 0; x < imageSize.x(); x++)
1550				{
1551					void *const pixelData = (deUint8*)data + y*rowPitch + x*pixelSize;
1552
1553					deMemcpy(scratchAccess.getDataPtr(), pixelData, pixelSize);
1554
1555					if (isFormatTypeInteger(m_imageFormat.type))
1556						scratchAccess.setPixel(scratchAccess.getPixelUint(0, 0), 0, 0);
1557					else
1558						scratchAccess.setPixel(scratchAccess.getPixel(0, 0), 0, 0);
1559
1560					deMemcpy(pixelData, scratchAccess.getDataPtr(), pixelSize);
1561				}
1562			}
1563		}
1564
1565		for (int z = 0; z < numSlicesOrFaces; z++)
1566		for (int y = 0; y < imageSize.y(); y++)
1567		for (int x = 0; x < imageSize.x()/2; x++)
1568		{
1569			if (isIntegerTextureFormat)
1570			{
1571				const UVec4 temp = reference.getPixelUint(imageSize.x()-1-x, y, z);
1572				reference.setPixel(imageSize.x()-1-x, y, z, reference.getPixelUint(x, y, z));
1573				reference.setPixel(x, y, z, temp);
1574			}
1575			else
1576			{
1577				const Vec4 temp = reference.getPixel(imageSize.x()-1-x, y, z);
1578				reference.setPixel(imageSize.x()-1-x, y, z, reference.getPixel(x, y, z));
1579				reference.setPixel(x, y, z, temp);
1580			}
1581		}
1582	}
1583
1584	// Read texture 1 and compare to reference.
1585
1586	const bool compareOk = readTextureAndVerify(renderCtx, glLog, *texture1, *texture1Buf, m_textureType, m_textureFormat, imageSize, ImageLayerComparer(reference));
1587
1588	m_testCtx.setTestResult(compareOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, compareOk ? "Pass" : "Image comparison failed");
1589	return STOP;
1590}
1591
1592enum AtomicOperationCaseType
1593{
1594	ATOMIC_OPERATION_CASE_TYPE_END_RESULT = 0,	//!< Atomic case checks the end result of the operations, and not the return values.
1595	ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES,	//!< Atomic case checks the return values of the atomic function, and not the end result.
1596
1597	ATOMIC_OPERATION_CASE_TYPE_LAST
1598};
1599
1600/*--------------------------------------------------------------------*//*!
1601 * \brief Binary atomic operation case.
1602 *
1603 * Case that performs binary atomic operations (i.e. any but compSwap) and
1604 * verifies according to the given AtomicOperationCaseType.
1605 *
1606 * For the "end result" case type, a single texture (and image) is created,
1607 * upon which the atomic operations operate. A compute shader is dispatched
1608 * with dimensions equal to the image size, except with a bigger X size
1609 * so that every pixel is operated on by multiple invocations. The end
1610 * results are verified in BinaryAtomicOperationCase::EndResultVerifier.
1611 * The return values of the atomic function calls are ignored.
1612 *
1613 * For the "return value" case type, the case does much the same operations
1614 * as in the "end result" case, but also creates an additional texture,
1615 * of size equal to the dispatch size, into which the return values of the
1616 * atomic functions are stored (with imageStore()). The return values are
1617 * verified in BinaryAtomicOperationCase::ReturnValueVerifier.
1618 * The end result values are not checked.
1619 *
1620 * The compute shader invocations contributing to a pixel (X, Y, Z) in the
1621 * end result image are the invocations with global IDs
1622 * (X, Y, Z), (X+W, Y, Z), (X+2*W, Y, Z), ..., (X+(N-1)*W, Y, W), where W
1623 * is the width of the end result image and N is m_numInvocationsPerPixel.
1624 *//*--------------------------------------------------------------------*/
1625class BinaryAtomicOperationCase : public TestCase
1626{
1627public:
1628									BinaryAtomicOperationCase		(Context& context, const char* name, const char* description, const TextureFormat& format, TextureType imageType, AtomicOperation operation, AtomicOperationCaseType caseType)
1629		: TestCase		(context, name, description)
1630		, m_format		(format)
1631		, m_imageType	(imageType)
1632		, m_operation	(operation)
1633		, m_caseType	(caseType)
1634	{
1635		DE_ASSERT(m_format == TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32)	||
1636				  m_format == TextureFormat(TextureFormat::R, TextureFormat::SIGNED_INT32)		||
1637				  (m_format == TextureFormat(TextureFormat::R, TextureFormat::FLOAT) && m_operation == ATOMIC_OPERATION_EXCHANGE));
1638
1639		DE_ASSERT(m_operation != ATOMIC_OPERATION_COMP_SWAP);
1640	}
1641
1642	void							init							(void);
1643	IterateResult					iterate							(void);
1644
1645private:
1646	class EndResultVerifier;
1647	class ReturnValueVerifier;
1648
1649	static int						getOperationInitialValue		(AtomicOperation op); //!< Appropriate value with which to initialize the texture.
1650	//! Compute the argument given to the atomic function at the given invocation ID, when the entire dispatch has the given width and height.
1651	static int						getAtomicFuncArgument			(AtomicOperation op, const IVec3& invocationID, const IVec2& dispatchSizeXY);
1652	//! Generate the shader expression for the argument given to the atomic function. x, y and z are the identifiers for the invocation ID components.
1653	static string					getAtomicFuncArgumentShaderStr	(AtomicOperation op, const string& x, const string& y, const string& z, const IVec2& dispatchSizeXY);
1654
1655	const int						m_numInvocationsPerPixel = 5;
1656	const TextureFormat				m_format;
1657	const TextureType				m_imageType;
1658	const AtomicOperation			m_operation;
1659	const AtomicOperationCaseType	m_caseType;
1660};
1661
1662int BinaryAtomicOperationCase::getOperationInitialValue (AtomicOperation op)
1663{
1664	switch (op)
1665	{
1666		// \note 18 is just an arbitrary small nonzero value.
1667		case ATOMIC_OPERATION_ADD:			return 18;
1668		case ATOMIC_OPERATION_MIN:			return (1<<15) - 1;
1669		case ATOMIC_OPERATION_MAX:			return 18;
1670		case ATOMIC_OPERATION_AND:			return (1<<15) - 1;
1671		case ATOMIC_OPERATION_OR:			return 18;
1672		case ATOMIC_OPERATION_XOR:			return 18;
1673		case ATOMIC_OPERATION_EXCHANGE:		return 18;
1674		default:
1675			DE_ASSERT(false);
1676			return -1;
1677	}
1678}
1679
1680int BinaryAtomicOperationCase::getAtomicFuncArgument (AtomicOperation op, const IVec3& invocationID, const IVec2& dispatchSizeXY)
1681{
1682	const int x		= invocationID.x();
1683	const int y		= invocationID.y();
1684	const int z		= invocationID.z();
1685	const int wid	= dispatchSizeXY.x();
1686	const int hei	= dispatchSizeXY.y();
1687
1688	switch (op)
1689	{
1690		// \note Fall-throughs.
1691		case ATOMIC_OPERATION_ADD:
1692		case ATOMIC_OPERATION_MIN:
1693		case ATOMIC_OPERATION_MAX:
1694		case ATOMIC_OPERATION_AND:
1695		case ATOMIC_OPERATION_OR:
1696		case ATOMIC_OPERATION_XOR:
1697			return x*x + y*y + z*z;
1698
1699		case ATOMIC_OPERATION_EXCHANGE:
1700			return (z*wid + x)*hei + y;
1701
1702		default:
1703			DE_ASSERT(false);
1704			return -1;
1705	}
1706}
1707
1708string BinaryAtomicOperationCase::getAtomicFuncArgumentShaderStr (AtomicOperation op, const string& x, const string& y, const string& z, const IVec2& dispatchSizeXY)
1709{
1710	switch (op)
1711	{
1712		// \note Fall-throughs.
1713		case ATOMIC_OPERATION_ADD:
1714		case ATOMIC_OPERATION_MIN:
1715		case ATOMIC_OPERATION_MAX:
1716		case ATOMIC_OPERATION_AND:
1717		case ATOMIC_OPERATION_OR:
1718		case ATOMIC_OPERATION_XOR:
1719			return "("+ x+"*"+x +" + "+ y+"*"+y +" + "+ z+"*"+z +")";
1720
1721		case ATOMIC_OPERATION_EXCHANGE:
1722			return "((" + z + "*" + toString(dispatchSizeXY.x()) + " + " + x + ")*" + toString(dispatchSizeXY.y()) + " + " + y + ")";
1723
1724		default:
1725			DE_ASSERT(false);
1726			return "";
1727	}
1728}
1729
1730class BinaryAtomicOperationCase::EndResultVerifier : public ImageLayerVerifier
1731{
1732public:
1733	EndResultVerifier (AtomicOperation operation, TextureType imageType, int numInvocationsPerPixel) : m_operation(operation), m_imageType(imageType), m_numInvocationsPerPixel(numInvocationsPerPixel) {}
1734
1735	bool operator() (TestLog& log, const ConstPixelBufferAccess& resultSlice, int sliceOrFaceNdx) const
1736	{
1737		const bool		isIntegerFormat		= isFormatTypeInteger(resultSlice.getFormat().type);
1738		const IVec2		dispatchSizeXY		(m_numInvocationsPerPixel*resultSlice.getWidth(), resultSlice.getHeight());
1739
1740		log << TestLog::Image("EndResults" + toString(sliceOrFaceNdx),
1741							  "Result Values, " + (m_imageType == TEXTURETYPE_CUBE ? "face " + string(glu::getCubeMapFaceName(cubeFaceToGLFace(glslImageFuncZToCubeFace(sliceOrFaceNdx))))
1742																				   : "slice " + toString(sliceOrFaceNdx)),
1743							  resultSlice);
1744
1745		for (int y = 0; y < resultSlice.getHeight(); y++)
1746		for (int x = 0; x < resultSlice.getWidth(); x++)
1747		{
1748			union
1749			{
1750				int		i;
1751				float	f;
1752			} result;
1753
1754			if (isIntegerFormat)
1755				result.i = resultSlice.getPixelInt(x, y).x();
1756			else
1757				result.f = resultSlice.getPixel(x, y).x();
1758
1759			// Compute the arguments that were given to the atomic function in the invocations that contribute to this pixel.
1760
1761			std::vector<IVec3>	invocationGlobalIDs(m_numInvocationsPerPixel);
1762			std::vector<int>	atomicArgs(m_numInvocationsPerPixel);
1763
1764			for (int i = 0; i < m_numInvocationsPerPixel; i++)
1765			{
1766				const IVec3 gid(x + i*resultSlice.getWidth(), y, sliceOrFaceNdx);
1767
1768				invocationGlobalIDs[i]	= gid;
1769				atomicArgs[i]			= getAtomicFuncArgument(m_operation, gid, dispatchSizeXY);
1770			}
1771
1772			if (isOrderIndependentAtomicOperation(m_operation))
1773			{
1774				// Just accumulate the atomic args (and the initial value) according to the operation, and compare.
1775
1776				DE_ASSERT(isIntegerFormat);
1777
1778				int reference = getOperationInitialValue(m_operation);
1779
1780				for (int i = 0; i < m_numInvocationsPerPixel; i++)
1781					reference = computeBinaryAtomicOperationResult(m_operation, reference, atomicArgs[i]);
1782
1783				if (result.i != reference)
1784				{
1785					log << TestLog::Message << "// Failure: end result at pixel " << IVec2(x, y) << " of current layer is " << result.i << TestLog::EndMessage
1786						<< TestLog::Message << "// Note: relevant shader invocation global IDs are " << arrayStr(invocationGlobalIDs) << TestLog::EndMessage
1787						<< TestLog::Message << "// Note: data expression values for the IDs are " << arrayStr(atomicArgs) << TestLog::EndMessage
1788						<< TestLog::Message << "// Note: reference value is " << reference << TestLog::EndMessage;
1789					return false;
1790				}
1791			}
1792			else if (m_operation == ATOMIC_OPERATION_EXCHANGE)
1793			{
1794				// Check that the end result equals one of the atomic args.
1795
1796				bool matchFound = false;
1797
1798				for (int i = 0; i < m_numInvocationsPerPixel && !matchFound; i++)
1799					matchFound = isIntegerFormat ? result.i == atomicArgs[i]
1800												 : de::abs(result.f - (float)atomicArgs[i]) <= 0.01f;
1801
1802				if (!matchFound)
1803				{
1804					log << TestLog::Message << "// Failure: invalid value at pixel " << IVec2(x, y) << ": got " << (isIntegerFormat ? toString(result.i) : toString(result.f)) << TestLog::EndMessage
1805											<< TestLog::Message << "// Note: expected one of " << arrayStr(atomicArgs) << TestLog::EndMessage;
1806
1807					return false;
1808				}
1809			}
1810			else
1811				DE_ASSERT(false);
1812		}
1813
1814		return true;
1815	}
1816
1817private:
1818	const AtomicOperation	m_operation;
1819	const TextureType		m_imageType;
1820	const int				m_numInvocationsPerPixel;
1821};
1822
1823
1824template <typename T>
1825T getPixelValueX(const ConstPixelBufferAccess& resultSlice, int x, int y)
1826{
1827	return resultSlice.getPixelInt(x, y).x();
1828}
1829
1830template <>
1831float getPixelValueX<float>(const ConstPixelBufferAccess& resultSlice, int x, int y)
1832{
1833	return resultSlice.getPixel(x, y).x();
1834}
1835
1836class BinaryAtomicOperationCase::ReturnValueVerifier : public ImageLayerVerifier
1837{
1838public:
1839	//! \note endResultImageLayerSize is (width, height) of the image operated on by the atomic ops, and not the size of the image where the return values are stored.
1840	ReturnValueVerifier (AtomicOperation operation, TextureType imageType, const IVec2& endResultImageLayerSize, int numInvocationsPerPixel) : m_operation(operation), m_imageType(imageType), m_endResultImageLayerSize(endResultImageLayerSize), m_numInvocationsPerPixel(numInvocationsPerPixel) {}
1841
1842	bool operator() (TestLog& log, const ConstPixelBufferAccess& resultSlice, int sliceOrFaceNdx) const
1843	{
1844		const bool		isIntegerFormat		(isFormatTypeInteger(resultSlice.getFormat().type));
1845		const IVec2		dispatchSizeXY	(resultSlice.getWidth(), resultSlice.getHeight());
1846
1847		DE_ASSERT(resultSlice.getWidth()	== m_numInvocationsPerPixel*m_endResultImageLayerSize.x()	&&
1848				  resultSlice.getHeight()	== m_endResultImageLayerSize.y()							&&
1849				  resultSlice.getDepth()	== 1);
1850
1851		log << TestLog::Image("ReturnValues" + toString(sliceOrFaceNdx),
1852							  "Per-Invocation Return Values, "
1853								   + (m_imageType == TEXTURETYPE_CUBE ? "face " + string(glu::getCubeMapFaceName(cubeFaceToGLFace(glslImageFuncZToCubeFace(sliceOrFaceNdx))))
1854																	  : "slice " + toString(sliceOrFaceNdx)),
1855							  resultSlice);
1856
1857		for (int y = 0; y < m_endResultImageLayerSize.y(); y++)
1858		for (int x = 0; x < m_endResultImageLayerSize.x(); x++)
1859		{
1860			if (isIntegerFormat)
1861			{
1862				if (!checkPixel<int>(log, resultSlice, x, y, sliceOrFaceNdx, dispatchSizeXY))
1863					return false;
1864			}
1865			else
1866			{
1867				if (!checkPixel<float>(log, resultSlice, x, y, sliceOrFaceNdx, dispatchSizeXY))
1868					return false;
1869			}
1870		}
1871
1872		return true;
1873	}
1874
1875private:
1876	const AtomicOperation	m_operation;
1877	const TextureType		m_imageType;
1878	const IVec2				m_endResultImageLayerSize;
1879	const int				m_numInvocationsPerPixel;
1880
1881	template <typename T>
1882	bool checkPixel(TestLog& log, const ConstPixelBufferAccess& resultSlice, int x, int y, int sliceOrFaceNdx, const IVec2 &dispatchSizeXY) const
1883	{
1884		// Get the atomic function args and return values for all the invocations that contribute to the pixel (x, y) in the current end result slice.
1885
1886		std::vector<T>		returnValues(m_numInvocationsPerPixel);
1887		std::vector<T>		atomicArgs(m_numInvocationsPerPixel);
1888		std::vector<IVec3>	invocationGlobalIDs(m_numInvocationsPerPixel);
1889		std::vector<IVec2>	pixelCoords(m_numInvocationsPerPixel);
1890
1891		for (int i = 0; i < m_numInvocationsPerPixel; i++)
1892		{
1893			const IVec2 pixCoord	(x + i*m_endResultImageLayerSize.x(), y);
1894			const IVec3 gid			(pixCoord.x(), pixCoord.y(), sliceOrFaceNdx);
1895
1896			invocationGlobalIDs[i]	= gid;
1897			pixelCoords[i]			= pixCoord;
1898
1899			returnValues[i]			= getPixelValueX<T>(resultSlice, gid.x(), y);
1900			atomicArgs[i]			= static_cast<T>(getAtomicFuncArgument(m_operation, gid, dispatchSizeXY));
1901		}
1902
1903		// Verify that the return values form a valid sequence.
1904		{
1905			const bool success = verifyOperationAccumulationIntermediateValues(m_operation,
1906																			   static_cast<T>(getOperationInitialValue(m_operation)),
1907																			   atomicArgs,
1908																			   returnValues);
1909
1910			if (!success)
1911			{
1912				log << TestLog::Message << "// Failure: intermediate return values at pixels " << arrayStr(pixelCoords) << " of current layer are "
1913										<< arrayStr(returnValues) << TestLog::EndMessage
1914					<< TestLog::Message << "// Note: relevant shader invocation global IDs are " << arrayStr(invocationGlobalIDs) << TestLog::EndMessage
1915					<< TestLog::Message << "// Note: data expression values for the IDs are "
1916										<< arrayStr(atomicArgs) << "; return values are not a valid result for any order of operations" << TestLog::EndMessage;
1917				return false;
1918			}
1919		}
1920
1921		return true;
1922	}
1923
1924	//! Check whether there exists an ordering of args such that { init*A", init*A*B, ..., init*A*B*...*LAST } is the "returnValues" sequence, where { A, B, ..., LAST } is args, and * denotes the operation.
1925	//	That is, whether "returnValues" is a valid sequence of intermediate return values when "operation" has been accumulated on "args" (and "init") in some arbitrary order.
1926	template <typename T>
1927	bool verifyOperationAccumulationIntermediateValues (AtomicOperation operation, T init, const std::vector<T> (&args), const std::vector<T> (&returnValues)) const
1928	{
1929		std::vector<bool> argsUsed(m_numInvocationsPerPixel, false);
1930
1931		return verifyRecursive(operation, 0, init, argsUsed, args, returnValues);
1932	}
1933
1934	static bool compare (int a, int b)		{ return a == b; }
1935	static bool compare (float a, float b)	{ return de::abs(a - b) <= 0.01f; }
1936
1937	//! Depth-first search for verifying the return value sequence.
1938	template <typename T>
1939	bool verifyRecursive (AtomicOperation operation, int index, T valueSoFar, std::vector<bool> (&argsUsed), const std::vector<T> (&args), const std::vector<T> (&returnValues)) const
1940	{
1941		if (index < m_numInvocationsPerPixel)
1942		{
1943			for (int i = 0; i < m_numInvocationsPerPixel; i++)
1944			{
1945				if (!argsUsed[i] && compare(returnValues[i], valueSoFar))
1946				{
1947					argsUsed[i] = true;
1948					if (verifyRecursive(operation, index+1, computeBinaryAtomicOperationResult(operation, valueSoFar, args[i]), argsUsed, args, returnValues))
1949						return true;
1950					argsUsed[i] = false;
1951				}
1952			}
1953
1954			return false;
1955		}
1956		else
1957			return true;
1958	}
1959};
1960
1961void BinaryAtomicOperationCase::init (void)
1962{
1963	const glu::RenderContext& renderContext = m_context.getRenderContext();
1964	if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic") && !supportsES32orGL45(renderContext))
1965		throw tcu::NotSupportedError("Test requires OES_shader_image_atomic extension");
1966
1967	checkTextureTypeExtensions(m_context.getContextInfo(), m_imageType, renderContext);
1968}
1969
1970BinaryAtomicOperationCase::IterateResult BinaryAtomicOperationCase::iterate (void)
1971{
1972	const RenderContext&		renderCtx				= m_context.getRenderContext();
1973	TestLog&					log						(m_testCtx.getLog());
1974	glu::CallLogWrapper			glLog					(renderCtx.getFunctions(), log);
1975	const deUint32				internalFormatGL		= glu::getInternalFormat(m_format);
1976	const deUint32				textureTargetGL			= getGLTextureTarget(m_imageType);
1977	const IVec3&				imageSize				= defaultImageSize(m_imageType);
1978	const int					numSlicesOrFaces		= m_imageType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
1979	const bool					isUintFormat			= isFormatTypeUnsignedInteger(m_format.type);
1980	const bool					isIntFormat				= isFormatTypeSignedInteger(m_format.type);
1981	const glu::Buffer			endResultTextureBuf		(renderCtx);
1982	const glu::Buffer			returnValueTextureBuf	(renderCtx);
1983	const glu::Texture			endResultTexture		(renderCtx); //!< Texture for the final result; i.e. the texture on which the atomic operations are done. Size imageSize.
1984	const glu::Texture			returnValueTexture		(renderCtx); //!< Texture into which the return values are stored if m_caseType == CASETYPE_RETURN_VALUES.
1985																	 //	  Size imageSize*IVec3(N, 1, 1) or, for cube maps, imageSize*IVec3(N, N, 1) where N is m_numInvocationsPerPixel.
1986
1987	glLog.enableLogging(true);
1988
1989	// Adjust result image size for result image
1990	if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
1991	{
1992		int maxWidth = getGLTextureMaxSize(glLog, m_imageType);
1993
1994		while (maxWidth < m_numInvocationsPerPixel * imageSize.x())
1995		{
1996			int* numInvocationsPerPixel = const_cast<int*>(&m_numInvocationsPerPixel);
1997			(*numInvocationsPerPixel) -= 1;
1998		}
1999	}
2000
2001	// Setup textures.
2002
2003	log << TestLog::Message << "// Created a texture (name " << *endResultTexture << ") to act as the target of atomic operations" << TestLog::EndMessage;
2004	if (m_imageType == TEXTURETYPE_BUFFER)
2005		log << TestLog::Message << "// Created a buffer for the texture (name " << *endResultTextureBuf << ")" << TestLog::EndMessage;
2006
2007	if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2008	{
2009		log << TestLog::Message << "// Created a texture (name " << *returnValueTexture << ") to which the intermediate return values of the atomic operation are stored" << TestLog::EndMessage;
2010		if (m_imageType == TEXTURETYPE_BUFFER)
2011			log << TestLog::Message << "// Created a buffer for the texture (name " << *returnValueTextureBuf << ")" << TestLog::EndMessage;
2012	}
2013
2014	// Fill endResultTexture with initial pattern.
2015
2016	{
2017		const LayeredImage imageData(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
2018
2019		{
2020			const IVec4 initial(getOperationInitialValue(m_operation));
2021
2022			for (int z = 0; z < numSlicesOrFaces; z++)
2023			for (int y = 0; y < imageSize.y(); y++)
2024			for (int x = 0; x < imageSize.x(); x++)
2025				imageData.setPixel(x, y, z, initial);
2026		}
2027
2028		// Upload initial pattern to endResultTexture and bind to image.
2029
2030		glLog.glActiveTexture(GL_TEXTURE0);
2031		glLog.glBindTexture(textureTargetGL, *endResultTexture);
2032		setTexParameteri(glLog, textureTargetGL);
2033
2034		log << TestLog::Message << "// Filling end-result texture with initial pattern (initial value " << getOperationInitialValue(m_operation) << ")" << TestLog::EndMessage;
2035
2036		uploadTexture(glLog, imageData, *endResultTextureBuf);
2037	}
2038
2039	glLog.glBindImageTexture(0, *endResultTexture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
2040	GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2041
2042	if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2043	{
2044		// Set storage for returnValueTexture and bind to image.
2045
2046		glLog.glActiveTexture(GL_TEXTURE1);
2047		glLog.glBindTexture(textureTargetGL, *returnValueTexture);
2048		setTexParameteri(glLog, textureTargetGL);
2049
2050		log << TestLog::Message << "// Setting storage of return-value texture" << TestLog::EndMessage;
2051		setTextureStorage(glLog, m_imageType, internalFormatGL, imageSize * (m_imageType == TEXTURETYPE_CUBE ? IVec3(m_numInvocationsPerPixel, m_numInvocationsPerPixel,	1)
2052																											 : IVec3(m_numInvocationsPerPixel, 1,							1)),
2053						  *returnValueTextureBuf);
2054
2055		glLog.glBindImageTexture(1, *returnValueTexture, 0, GL_TRUE, 0, GL_WRITE_ONLY, internalFormatGL);
2056		GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2057	}
2058
2059	// Perform image stores in compute shader and finalize reference computation.
2060
2061	{
2062		// Generate compute shader.
2063
2064		const string colorVecTypeName		= string(isUintFormat ? "u" : isIntFormat ? "i" : "") + "vec4";
2065		const string atomicCoord			= m_imageType == TEXTURETYPE_BUFFER		? "gx % " + toString(imageSize.x())
2066											: m_imageType == TEXTURETYPE_2D			? "ivec2(gx % " + toString(imageSize.x()) + ", gy)"
2067											: "ivec3(gx % " + toString(imageSize.x()) + ", gy, gz)";
2068		const string invocationCoord		= m_imageType == TEXTURETYPE_BUFFER		? "gx"
2069											: m_imageType == TEXTURETYPE_2D			? "ivec2(gx, gy)"
2070											: "ivec3(gx, gy, gz)";
2071		const string atomicArgExpr			= (isUintFormat		? "uint"
2072											 : isIntFormat		? ""
2073											 : "float")
2074												+ getAtomicFuncArgumentShaderStr(m_operation, "gx", "gy", "gz", IVec2(m_numInvocationsPerPixel*imageSize.x(), imageSize.y()));
2075		const string atomicInvocation		= string() + getAtomicOperationShaderFuncName(m_operation) + "(u_results, " + atomicCoord + ", " + atomicArgExpr + ")";
2076		const string shaderImageFormatStr	= getShaderImageFormatQualifier(m_format);
2077		const string shaderImageTypeStr		= getShaderImageType(m_format.type, m_imageType);
2078		const std::string		glslVersionDeclaration	= glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
2079
2080		const glu::ShaderProgram program(renderCtx,
2081			glu::ProgramSources() << glu::ComputeSource(glslVersionDeclaration + "\n"
2082														+ imageAtomicExtensionShaderRequires(renderCtx)
2083														+ textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
2084														"\n"
2085														"precision highp " + shaderImageTypeStr + ";\n"
2086														"\n"
2087														"layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2088														"layout (" + shaderImageFormatStr + ", binding=0) coherent uniform " + shaderImageTypeStr + " u_results;\n"
2089														+ (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2090															  "layout (" + shaderImageFormatStr + ", binding=1) writeonly uniform " + shaderImageTypeStr + " u_returnValues;\n"
2091															: "") +
2092														"\n"
2093														"void main (void)\n"
2094														"{\n"
2095														"	int gx = int(gl_GlobalInvocationID.x);\n"
2096														"	int gy = int(gl_GlobalInvocationID.y);\n"
2097														"	int gz = int(gl_GlobalInvocationID.z);\n"
2098														+ (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2099															"	imageStore(u_returnValues, " + invocationCoord + ", " + colorVecTypeName + "(" + atomicInvocation + "));\n"
2100														 : m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ?
2101															"	" + atomicInvocation + ";\n"
2102														 : deFatalStr("Invalid AtomicOperationCaseType")) +
2103														"}\n"));
2104
2105		UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
2106
2107		log << program;
2108
2109		if (!program.isOk())
2110		{
2111			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
2112			return STOP;
2113		}
2114
2115		// Setup and dispatch.
2116
2117		glLog.glUseProgram(program.getProgram());
2118
2119		glLog.glDispatchCompute(m_numInvocationsPerPixel*imageSize.x(), imageSize.y(), numSlicesOrFaces);
2120		GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
2121	}
2122
2123	// Read texture and check.
2124
2125	{
2126		const deUint32								textureToCheckGL	= m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT		? *endResultTexture
2127																		: m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES	? *returnValueTexture
2128																		: (deUint32)-1;
2129		const deUint32								textureToCheckBufGL	= m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT		? *endResultTextureBuf
2130																		: m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES	? *returnValueTextureBuf
2131																		: (deUint32)-1;
2132
2133		const IVec3									textureToCheckSize	= imageSize * IVec3(m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ? 1 : m_numInvocationsPerPixel, 1, 1);
2134		const UniquePtr<const ImageLayerVerifier>	verifier			(m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT		? new EndResultVerifier(m_operation, m_imageType, m_numInvocationsPerPixel)
2135																	   : m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES		? new ReturnValueVerifier(m_operation, m_imageType, imageSize.swizzle(0, 1), m_numInvocationsPerPixel)
2136																	   : (ImageLayerVerifier*)DE_NULL);
2137
2138		if (readTextureAndVerify(renderCtx, glLog, textureToCheckGL, textureToCheckBufGL, m_imageType, m_format, textureToCheckSize, *verifier))
2139			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2140		else
2141			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2142
2143		return STOP;
2144	}
2145}
2146
2147/*--------------------------------------------------------------------*//*!
2148 * \brief Atomic compSwap operation case.
2149 *
2150 * Similar in principle to BinaryAtomicOperationCase, but separated for
2151 * convenience, since the atomic function is somewhat different. Like
2152 * BinaryAtomicOperationCase, this has separate cases for checking end
2153 * result and return values.
2154 *//*--------------------------------------------------------------------*/
2155class AtomicCompSwapCase : public TestCase
2156{
2157public:
2158									AtomicCompSwapCase		(Context& context, const char* name, const char* description, const TextureFormat& format, TextureType imageType, AtomicOperationCaseType caseType)
2159		: TestCase		(context, name, description)
2160		, m_format		(format)
2161		, m_imageType	(imageType)
2162		, m_caseType	(caseType)
2163	{
2164		DE_ASSERT(m_format == TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32)	||
2165				  m_format == TextureFormat(TextureFormat::R, TextureFormat::SIGNED_INT32));
2166	}
2167
2168	void							init					(void);
2169	IterateResult					iterate					(void);
2170
2171private:
2172	class EndResultVerifier;
2173	class ReturnValueVerifier;
2174
2175	static int						getCompareArg			(const IVec3& invocationID, int imageWidth);
2176	static int						getAssignArg			(const IVec3& invocationID, int imageWidth);
2177	static string					getCompareArgShaderStr	(const string& x, const string& y, const string& z, int imageWidth);
2178	static string					getAssignArgShaderStr	(const string& x, const string& y, const string& z, int imageWidth);
2179
2180	const int						m_numInvocationsPerPixel = 5;
2181	const TextureFormat				m_format;
2182	const TextureType				m_imageType;
2183	const AtomicOperationCaseType	m_caseType;
2184};
2185
2186int AtomicCompSwapCase::getCompareArg (const IVec3& invocationID, int imageWidth)
2187{
2188	const int x							= invocationID.x();
2189	const int y							= invocationID.y();
2190	const int z							= invocationID.z();
2191	const int wrapX						= x % imageWidth;
2192	const int curPixelInvocationNdx		= x / imageWidth;
2193
2194	return wrapX*wrapX + y*y + z*z + curPixelInvocationNdx*42;
2195}
2196
2197int AtomicCompSwapCase::getAssignArg (const IVec3& invocationID, int imageWidth)
2198{
2199	return getCompareArg(IVec3(invocationID.x() + imageWidth, invocationID.y(), invocationID.z()), imageWidth);
2200}
2201
2202string AtomicCompSwapCase::getCompareArgShaderStr (const string& x, const string& y, const string& z, int imageWidth)
2203{
2204	const string wrapX					= "(" + x + "%" + toString(imageWidth) + ")";
2205	const string curPixelInvocationNdx	= "(" + x + "/" + toString(imageWidth) + ")";
2206
2207	return "(" +wrapX+"*"+wrapX+ " + " +y+"*"+y+ " + " +z+"*"+z+ " + " + curPixelInvocationNdx + "*42)";
2208}
2209
2210string AtomicCompSwapCase::getAssignArgShaderStr (const string& x, const string& y, const string& z, int imageWidth)
2211{
2212	const string wrapX					= "(" + x + "%" + toString(imageWidth) + ")";
2213	const string curPixelInvocationNdx	= "(" + x + "/" + toString(imageWidth) + " + 1)";
2214
2215	return "(" +wrapX+"*"+wrapX+ " + " +y+"*"+y+ " + " +z+"*"+z+ " + " + curPixelInvocationNdx + "*42)";
2216}
2217
2218void AtomicCompSwapCase::init (void)
2219{
2220	const glu::RenderContext& renderContext = m_context.getRenderContext();
2221	if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic") && !supportsES32orGL45(renderContext))
2222		throw tcu::NotSupportedError("Test requires OES_shader_image_atomic extension");
2223
2224	checkTextureTypeExtensions(m_context.getContextInfo(), m_imageType, renderContext);
2225}
2226
2227class AtomicCompSwapCase::EndResultVerifier : public ImageLayerVerifier
2228{
2229public:
2230	EndResultVerifier (TextureType imageType, int imageWidth, int numInvocationsPerPixel) : m_imageType(imageType), m_imageWidth(imageWidth), m_numInvocationsPerPixel(numInvocationsPerPixel) {}
2231
2232	bool operator() (TestLog& log, const ConstPixelBufferAccess& resultSlice, int sliceOrFaceNdx) const
2233	{
2234		DE_ASSERT(isFormatTypeInteger(resultSlice.getFormat().type));
2235		DE_ASSERT(resultSlice.getWidth() == m_imageWidth);
2236
2237		log << TestLog::Image("EndResults" + toString(sliceOrFaceNdx),
2238							  "Result Values, " + (m_imageType == TEXTURETYPE_CUBE ? "face " + string(glu::getCubeMapFaceName(cubeFaceToGLFace(glslImageFuncZToCubeFace(sliceOrFaceNdx))))
2239																				   : "slice " + toString(sliceOrFaceNdx)),
2240							  resultSlice);
2241
2242		for (int y = 0; y < resultSlice.getHeight(); y++)
2243		for (int x = 0; x < resultSlice.getWidth(); x++)
2244		{
2245			// Compute the value-to-assign arguments that were given to the atomic function in the invocations that contribute to this pixel.
2246			// One of those should be the result.
2247
2248			const int			result = resultSlice.getPixelInt(x, y).x();
2249			std::vector<IVec3>	invocationGlobalIDs(m_numInvocationsPerPixel);
2250			std::vector<int>	assignArgs(m_numInvocationsPerPixel);
2251
2252			for (int i = 0; i < m_numInvocationsPerPixel; i++)
2253			{
2254				const IVec3 gid(x + i*resultSlice.getWidth(), y, sliceOrFaceNdx);
2255
2256				invocationGlobalIDs[i]	= gid;
2257				assignArgs[i]			= getAssignArg(gid, m_imageWidth);
2258			}
2259
2260			{
2261				bool matchFound = false;
2262				for (int i = 0; i < m_numInvocationsPerPixel && !matchFound; i++)
2263					matchFound = result == assignArgs[i];
2264
2265				if (!matchFound)
2266				{
2267					log << TestLog::Message << "// Failure: invalid value at pixel " << IVec2(x, y) << ": got " << result << TestLog::EndMessage
2268						<< TestLog::Message << "// Note: relevant shader invocation global IDs are " << arrayStr(invocationGlobalIDs) << TestLog::EndMessage
2269						<< TestLog::Message << "// Note: expected one of " << arrayStr(assignArgs)
2270											<< " (those are the values given as the 'data' argument in the invocations that contribute to this pixel)"
2271											<< TestLog::EndMessage;
2272					return false;
2273				}
2274			}
2275		}
2276
2277		return true;
2278	}
2279
2280private:
2281	const TextureType	m_imageType;
2282	const int			m_imageWidth;
2283	const int			m_numInvocationsPerPixel;
2284};
2285
2286class AtomicCompSwapCase::ReturnValueVerifier : public ImageLayerVerifier
2287{
2288public:
2289	//! \note endResultImageLayerSize is (width, height) of the image operated on by the atomic ops, and not the size of the image where the return values are stored.
2290	ReturnValueVerifier (TextureType imageType, int endResultImageWidth, int numInvocationsPerPixel) : m_imageType(imageType), m_endResultImageWidth(endResultImageWidth), m_numInvocationsPerPixel(numInvocationsPerPixel) {}
2291
2292	bool operator() (TestLog& log, const ConstPixelBufferAccess& resultSlice, int sliceOrFaceNdx) const
2293	{
2294		DE_ASSERT(isFormatTypeInteger(resultSlice.getFormat().type));
2295		DE_ASSERT(resultSlice.getWidth() == m_numInvocationsPerPixel*m_endResultImageWidth);
2296
2297		log << TestLog::Image("ReturnValues" + toString(sliceOrFaceNdx),
2298							  "Per-Invocation Return Values, "
2299								   + (m_imageType == TEXTURETYPE_CUBE ? "face " + string(glu::getCubeMapFaceName(cubeFaceToGLFace(glslImageFuncZToCubeFace(sliceOrFaceNdx))))
2300																	  : "slice " + toString(sliceOrFaceNdx)),
2301							  resultSlice);
2302
2303		for (int y = 0; y < resultSlice.getHeight(); y++)
2304		for (int x = 0; x < m_endResultImageWidth; x++)
2305		{
2306			// Get the atomic function args and return values for all the invocations that contribute to the pixel (x, y) in the current end result slice.
2307
2308			std::vector<int>	returnValues(m_numInvocationsPerPixel);
2309			std::vector<int>	compareArgs(m_numInvocationsPerPixel);
2310			std::vector<IVec3>	invocationGlobalIDs(m_numInvocationsPerPixel);
2311			std::vector<IVec2>	pixelCoords(m_numInvocationsPerPixel);
2312
2313			for (int i = 0; i < m_numInvocationsPerPixel; i++)
2314			{
2315				const IVec2 pixCoord	(x + i*m_endResultImageWidth, y);
2316				const IVec3 gid			(pixCoord.x(), pixCoord.y(), sliceOrFaceNdx);
2317
2318				pixelCoords[i]			= pixCoord;
2319				invocationGlobalIDs[i]	= gid;
2320				returnValues[i]			= resultSlice.getPixelInt(gid.x(), y).x();
2321				compareArgs[i]			= getCompareArg(gid, m_endResultImageWidth);
2322			}
2323
2324			// Verify that the return values form a valid sequence.
2325			// Due to the way the compare and assign arguments to the atomic calls are organized
2326			// among the different invocations contributing to the same pixel -- i.e. one invocation
2327			// compares to A and assigns B, another compares to B and assigns C, and so on, where
2328			// A<B<C etc -- the first value in the return value sequence must be A, and each following
2329			// value must be the smallest value (among A, B, C, ...) bigger than the one just before it.
2330			// The only valid sequence being: A B C D E F G H
2331
2332			{
2333				int failingNdx = -1;
2334
2335				{
2336					int currentAtomicValueNdx = 0;
2337					for (int i = 0; i < m_numInvocationsPerPixel; i++)
2338					{
2339						if (returnValues[i] == compareArgs[currentAtomicValueNdx])
2340							continue;
2341						if (i > 0 && returnValues[i] == compareArgs[currentAtomicValueNdx+1])
2342						{
2343							currentAtomicValueNdx++;
2344							continue;
2345						}
2346						failingNdx = i;
2347						break;
2348					}
2349				}
2350
2351				if (failingNdx >= 0)
2352				{
2353					log << TestLog::Message << "// Failure: intermediate return values at pixels " << arrayStr(pixelCoords) << " of current layer are "
2354											<< arrayStr(returnValues) << TestLog::EndMessage
2355						<< TestLog::Message << "// Note: relevant shader invocation global IDs are " << arrayStr(invocationGlobalIDs) << TestLog::EndMessage
2356						<< TestLog::Message << "// Note: 'compare' argument values for the IDs are " << arrayStr(compareArgs) << TestLog::EndMessage
2357						<< TestLog::Message << "// Note: expected the return value sequence to fulfill the following conditions:\n"
2358											<< "// - first value is " << compareArgs[0] << "\n"
2359											<< "// - each value other than the first is either the same as the one just before it, or the smallest value (in the sequence "
2360											<< arrayStr(compareArgs) << ") bigger than the one just before it" << TestLog::EndMessage;
2361					if (failingNdx == 0)
2362						log << TestLog::Message << "// Note: the first return value (" << returnValues[0] << ") isn't " << compareArgs[0] << TestLog::EndMessage;
2363					else
2364						log << TestLog::Message << "// Note: the return value at index " << failingNdx << " (value " << returnValues[failingNdx] << ") "
2365												<< "is neither " << returnValues[failingNdx-1] << " (the one just before it) "
2366												<< "nor " << compareArgs[arrayIndexOf(compareArgs, returnValues[failingNdx-1])+1] << " (the smallest value bigger than the one just before it)"
2367												<< TestLog::EndMessage;
2368
2369					return false;
2370				}
2371			}
2372		}
2373
2374		return true;
2375	}
2376
2377private:
2378	const TextureType	m_imageType;
2379	const int			m_endResultImageWidth;
2380	const int			m_numInvocationsPerPixel;
2381};
2382
2383AtomicCompSwapCase::IterateResult AtomicCompSwapCase::iterate (void)
2384{
2385	const RenderContext&		renderCtx				= m_context.getRenderContext();
2386	TestLog&					log						(m_testCtx.getLog());
2387	glu::CallLogWrapper			glLog					(renderCtx.getFunctions(), log);
2388	const deUint32				internalFormatGL		= glu::getInternalFormat(m_format);
2389	const deUint32				textureTargetGL			= getGLTextureTarget(m_imageType);
2390	const IVec3&				imageSize				= defaultImageSize(m_imageType);
2391	const int					numSlicesOrFaces		= m_imageType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
2392	const bool					isUintFormat			= isFormatTypeUnsignedInteger(m_format.type);
2393	const bool					isIntFormat				= isFormatTypeSignedInteger(m_format.type);
2394	const glu::Buffer			endResultTextureBuf		(renderCtx);
2395	const glu::Buffer			returnValueTextureBuf	(renderCtx);
2396	const glu::Texture			endResultTexture		(renderCtx); //!< Texture for the final result; i.e. the texture on which the atomic operations are done. Size imageSize.
2397	const glu::Texture			returnValueTexture		(renderCtx); //!< Texture into which the return values are stored if m_caseType == CASETYPE_RETURN_VALUES.
2398																	 //	  Size imageSize*IVec3(N, 1, 1) or, for cube maps, imageSize*IVec3(N, N, 1) where N is NUM_INVOCATIONS_PER_PIXEL.
2399
2400	DE_ASSERT(isUintFormat || isIntFormat);
2401
2402	glLog.enableLogging(true);
2403
2404	// Adjust result image size for result image
2405	if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2406	{
2407		int maxWidth = getGLTextureMaxSize(glLog, m_imageType);
2408
2409		while (maxWidth < m_numInvocationsPerPixel * imageSize.x())
2410		{
2411			int* numInvocationsPerPixel = const_cast<int*>(&m_numInvocationsPerPixel);
2412			(*numInvocationsPerPixel) -= 1;
2413		}
2414	}
2415
2416	// Setup textures.
2417
2418	log << TestLog::Message << "// Created a texture (name " << *endResultTexture << ") to act as the target of atomic operations" << TestLog::EndMessage;
2419	if (m_imageType == TEXTURETYPE_BUFFER)
2420		log << TestLog::Message << "// Created a buffer for the texture (name " << *endResultTextureBuf << ")" << TestLog::EndMessage;
2421
2422	if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2423	{
2424		log << TestLog::Message << "// Created a texture (name " << *returnValueTexture << ") to which the intermediate return values of the atomic operation are stored" << TestLog::EndMessage;
2425		if (m_imageType == TEXTURETYPE_BUFFER)
2426			log << TestLog::Message << "// Created a buffer for the texture (name " << *returnValueTextureBuf << ")" << TestLog::EndMessage;
2427	}
2428
2429	// Fill endResultTexture with initial pattern.
2430
2431	{
2432		const LayeredImage imageData(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
2433
2434		{
2435			for (int z = 0; z < numSlicesOrFaces; z++)
2436			for (int y = 0; y < imageSize.y(); y++)
2437			for (int x = 0; x < imageSize.x(); x++)
2438				imageData.setPixel(x, y, z, IVec4(getCompareArg(IVec3(x, y, z), imageSize.x())));
2439		}
2440
2441		// Upload initial pattern to endResultTexture and bind to image.
2442
2443		glLog.glActiveTexture(GL_TEXTURE0);
2444		glLog.glBindTexture(textureTargetGL, *endResultTexture);
2445		setTexParameteri(glLog, textureTargetGL);
2446
2447		log << TestLog::Message << "// Filling end-result texture with initial pattern" << TestLog::EndMessage;
2448
2449		uploadTexture(glLog, imageData, *endResultTextureBuf);
2450	}
2451
2452	glLog.glBindImageTexture(0, *endResultTexture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
2453	GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2454
2455	if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2456	{
2457		// Set storage for returnValueTexture and bind to image.
2458
2459		glLog.glActiveTexture(GL_TEXTURE1);
2460		glLog.glBindTexture(textureTargetGL, *returnValueTexture);
2461		setTexParameteri(glLog, textureTargetGL);
2462
2463		log << TestLog::Message << "// Setting storage of return-value texture" << TestLog::EndMessage;
2464		setTextureStorage(glLog, m_imageType, internalFormatGL, imageSize * (m_imageType == TEXTURETYPE_CUBE ? IVec3(m_numInvocationsPerPixel, m_numInvocationsPerPixel,	1)
2465																											 : IVec3(m_numInvocationsPerPixel, 1,							1)),
2466						  *returnValueTextureBuf);
2467
2468		glLog.glBindImageTexture(1, *returnValueTexture, 0, GL_TRUE, 0, GL_WRITE_ONLY, internalFormatGL);
2469		GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2470	}
2471
2472	// Perform atomics in compute shader.
2473
2474	{
2475		// Generate compute shader.
2476
2477		const string colorScalarTypeName	= isUintFormat ? "uint" : isIntFormat ? "int" : DE_NULL;
2478		const string colorVecTypeName		= string(isUintFormat ? "u" : isIntFormat ? "i" : DE_NULL) + "vec4";
2479		const string atomicCoord			= m_imageType == TEXTURETYPE_BUFFER		? "gx % " + toString(imageSize.x())
2480											: m_imageType == TEXTURETYPE_2D			? "ivec2(gx % " + toString(imageSize.x()) + ", gy)"
2481											: "ivec3(gx % " + toString(imageSize.x()) + ", gy, gz)";
2482		const string invocationCoord		= m_imageType == TEXTURETYPE_BUFFER		? "gx"
2483											: m_imageType == TEXTURETYPE_2D			? "ivec2(gx, gy)"
2484											: "ivec3(gx, gy, gz)";
2485		const string shaderImageFormatStr	= getShaderImageFormatQualifier(m_format);
2486		const string shaderImageTypeStr		= getShaderImageType(m_format.type, m_imageType);
2487		const string glslVersionDeclaration	= glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
2488
2489		const glu::ShaderProgram program(renderCtx,
2490			glu::ProgramSources() << glu::ComputeSource(glslVersionDeclaration + "\n"
2491														+ imageAtomicExtensionShaderRequires(renderCtx)
2492														+ textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
2493														"\n"
2494														"precision highp " + shaderImageTypeStr + ";\n"
2495														"\n"
2496														"layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2497														"layout (" + shaderImageFormatStr + ", binding=0) coherent uniform " + shaderImageTypeStr + " u_results;\n"
2498														+ (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2499															  "layout (" + shaderImageFormatStr + ", binding=1) writeonly uniform " + shaderImageTypeStr + " u_returnValues;\n"
2500															: "") +
2501														"uniform int offset;"
2502														"\n"
2503														"void main (void)\n"
2504														"{\n"
2505														"	int gx = int(gl_GlobalInvocationID.x) + offset * " + std::to_string(imageSize.x()) + ";\n"
2506														"	int gy = int(gl_GlobalInvocationID.y);\n"
2507														"	int gz = int(gl_GlobalInvocationID.z);\n"
2508														"	" + colorScalarTypeName + " compare = " + colorScalarTypeName + getCompareArgShaderStr("gx", "gy", "gz", imageSize.x()) + ";\n"
2509														"	" + colorScalarTypeName + " data    = " + colorScalarTypeName + getAssignArgShaderStr("gx", "gy", "gz", imageSize.x()) + ";\n"
2510														"	" + colorScalarTypeName + " status  = " + colorScalarTypeName + "(-1);\n"
2511														"	status = imageAtomicCompSwap(u_results, " + atomicCoord + ", compare, data);\n"
2512														+ (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2513															// Ensure there's an ordered ascending pattern to correctly check values are being stored in order
2514															"	if(compare == status) imageStore(u_returnValues, " + invocationCoord + ", " + colorVecTypeName + "(status));\n" :
2515															"") +
2516														"}\n"));
2517
2518		UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
2519
2520		log << program;
2521
2522		if (!program.isOk())
2523		{
2524			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
2525			return STOP;
2526		}
2527
2528		// Setup and dispatch.
2529
2530		glLog.glUseProgram(program.getProgram());
2531
2532		{
2533			deUint32 offsetLocation = glLog.glGetUniformLocation(program.getProgram(), "offset");
2534			int dispatchCount = m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? m_numInvocationsPerPixel : 1u;
2535
2536			for (int i = 0; i < dispatchCount; ++i)
2537			{
2538				// Ensure we get correct values for output
2539				glLog.glUniform1i(offsetLocation, i);
2540				glLog.glDispatchCompute((dispatchCount - i)*imageSize.x(), imageSize.y(), numSlicesOrFaces);
2541				GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
2542			}
2543		}
2544	}
2545
2546	// Create reference, read texture and compare.
2547
2548	{
2549		const deUint32								textureToCheckGL	= m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT		? *endResultTexture
2550																		: m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES	? *returnValueTexture
2551																		: (deUint32)-1;
2552
2553		const deUint32								textureToCheckBufGL	= m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT		? *endResultTextureBuf
2554																		: m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES	? *returnValueTextureBuf
2555																		: (deUint32)-1;
2556
2557		// The relevant region of the texture being checked (potentially
2558		// different from actual texture size for cube maps, because cube maps
2559		// may have unused pixels due to square size restriction).
2560		const IVec3									relevantRegion		= imageSize * (m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT	? IVec3(1,							1,							1)
2561																					 :														  IVec3(m_numInvocationsPerPixel,	1,							1));
2562
2563		const UniquePtr<const ImageLayerVerifier>	verifier			(m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT		? new EndResultVerifier(m_imageType, imageSize.x(), m_numInvocationsPerPixel)
2564																	   : m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES		? new ReturnValueVerifier(m_imageType, imageSize.x(), m_numInvocationsPerPixel)
2565																	   : (ImageLayerVerifier*)DE_NULL);
2566
2567		if (readTextureAndVerify(renderCtx, glLog, textureToCheckGL, textureToCheckBufGL, m_imageType, m_format, relevantRegion, *verifier))
2568			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2569		else
2570			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2571
2572		return STOP;
2573	}
2574}
2575
2576//! Case testing the "coherent" or "volatile" qualifier, along with memoryBarrier() and barrier().
2577class CoherenceCase : public TestCase
2578{
2579public:
2580	enum Qualifier
2581	{
2582		QUALIFIER_COHERENT = 0,
2583		QUALIFIER_VOLATILE,
2584
2585		QUALIFIER_LAST
2586	};
2587
2588	CoherenceCase (Context& context, const char* name, const char* description, const TextureFormat& format, TextureType imageType, Qualifier qualifier)
2589		: TestCase		(context, name, description)
2590		, m_format		(format)
2591		, m_imageType	(imageType)
2592		, m_qualifier	(qualifier)
2593	{
2594		DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_Y) == DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_X) &&
2595						 DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_Z) == DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_X));
2596
2597		DE_ASSERT(qualifier == QUALIFIER_COHERENT || qualifier == QUALIFIER_VOLATILE);
2598
2599		DE_ASSERT(m_format == TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32)	||
2600				  m_format == TextureFormat(TextureFormat::R, TextureFormat::SIGNED_INT32)		||
2601				  m_format == TextureFormat(TextureFormat::R, TextureFormat::FLOAT));
2602	}
2603
2604	void			init		(void) { checkTextureTypeExtensions(m_context.getContextInfo(), m_imageType, m_context.getRenderContext()); }
2605	IterateResult	iterate		(void);
2606
2607private:
2608	static const int			SHADER_READ_OFFSETS_X[4];
2609	static const int			SHADER_READ_OFFSETS_Y[4];
2610	static const int			SHADER_READ_OFFSETS_Z[4];
2611	static const char* const	SHADER_READ_OFFSETS_X_STR;
2612	static const char* const	SHADER_READ_OFFSETS_Y_STR;
2613	static const char* const	SHADER_READ_OFFSETS_Z_STR;
2614
2615	const TextureFormat		m_format;
2616	const TextureType		m_imageType;
2617	const Qualifier			m_qualifier;
2618};
2619
2620const int			CoherenceCase::SHADER_READ_OFFSETS_X[4]		=		{ 1, 4, 7, 10 };
2621const int			CoherenceCase::SHADER_READ_OFFSETS_Y[4]		=		{ 2, 5, 8, 11 };
2622const int			CoherenceCase::SHADER_READ_OFFSETS_Z[4]		=		{ 3, 6, 9, 12 };
2623const char* const	CoherenceCase::SHADER_READ_OFFSETS_X_STR	= "int[]( 1, 4, 7, 10 )";
2624const char* const	CoherenceCase::SHADER_READ_OFFSETS_Y_STR	= "int[]( 2, 5, 8, 11 )";
2625const char* const	CoherenceCase::SHADER_READ_OFFSETS_Z_STR	= "int[]( 3, 6, 9, 12 )";
2626
2627CoherenceCase::IterateResult CoherenceCase::iterate (void)
2628{
2629	const RenderContext&		renderCtx					= m_context.getRenderContext();
2630	TestLog&					log							(m_testCtx.getLog());
2631	glu::CallLogWrapper			glLog						(renderCtx.getFunctions(), log);
2632	const deUint32				internalFormatGL			= glu::getInternalFormat(m_format);
2633	const deUint32				textureTargetGL				= getGLTextureTarget(m_imageType);
2634	const IVec3&				imageSize					= defaultImageSize(m_imageType);
2635	const int					numSlicesOrFaces			= m_imageType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
2636	const bool					isUintFormat				= isFormatTypeUnsignedInteger(m_format.type);
2637	const bool					isIntFormat					= isFormatTypeSignedInteger(m_format.type);
2638	const char* const			qualifierName				= m_qualifier == QUALIFIER_COHERENT ? "coherent"
2639															: m_qualifier == QUALIFIER_VOLATILE ? "volatile"
2640															: DE_NULL;
2641	const glu::Buffer			textureBuf					(renderCtx);
2642	const glu::Texture			texture						(renderCtx);
2643	const IVec3					numGroups					= IVec3(16, de::min(16, imageSize.y()), de::min(2, numSlicesOrFaces));
2644	const IVec3					workItemSize				= IVec3(imageSize.x(), imageSize.y(), numSlicesOrFaces);
2645	const IVec3					localSize					= workItemSize / numGroups;
2646	const IVec3					minReqMaxLocalSize			= IVec3(128, 128, 64);
2647	const int					minReqMaxLocalInvocations	= 128;
2648
2649	DE_ASSERT(workItemSize == localSize*numGroups);
2650	DE_ASSERT(tcu::boolAll(tcu::lessThanEqual(localSize, minReqMaxLocalSize)));
2651	DE_ASSERT(localSize.x()*localSize.y()*localSize.z() <= minReqMaxLocalInvocations);
2652	DE_UNREF(minReqMaxLocalSize);
2653	DE_UNREF(minReqMaxLocalInvocations);
2654
2655	glLog.enableLogging(true);
2656
2657	// Setup texture.
2658
2659	log << TestLog::Message << "// Created a texture (name " << *texture << ")" << TestLog::EndMessage;
2660	if (m_imageType == TEXTURETYPE_BUFFER)
2661		log << TestLog::Message << "// Created a buffer for the texture (name " << *textureBuf << ")" << TestLog::EndMessage;
2662
2663	glLog.glActiveTexture(GL_TEXTURE0);
2664	glLog.glBindTexture(textureTargetGL, *texture);
2665	setTexParameteri(glLog, textureTargetGL);
2666	setTextureStorage(glLog, m_imageType, internalFormatGL, imageSize, *textureBuf);
2667	glLog.glBindImageTexture(0, *texture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
2668	GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2669
2670	// Perform computations in compute shader.
2671
2672	{
2673		// Generate compute shader.
2674
2675		const string		colorVecTypeName		= string(isUintFormat ? "u" : isIntFormat ? "i" : "") + "vec4";
2676		const char* const	colorScalarTypeName		= isUintFormat ? "uint" : isIntFormat ? "int" : "float";
2677		const string		invocationCoord			= m_imageType == TEXTURETYPE_BUFFER		? "gx"
2678													: m_imageType == TEXTURETYPE_2D			? "ivec2(gx, gy)"
2679													: "ivec3(gx, gy, gz)";
2680		const string		shaderImageFormatStr	= getShaderImageFormatQualifier(m_format);
2681		const string		shaderImageTypeStr		= getShaderImageType(m_format.type, m_imageType);
2682		const string		localSizeX				= de::toString(localSize.x());
2683		const string		localSizeY				= de::toString(localSize.y());
2684		const string		localSizeZ				= de::toString(localSize.z());
2685		const std::string	glslVersionDeclaration	= glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
2686
2687
2688		const glu::ShaderProgram program(renderCtx,
2689			glu::ProgramSources() << glu::ComputeSource(glslVersionDeclaration + "\n"
2690														+ textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
2691														"\n"
2692														"precision highp " + shaderImageTypeStr + ";\n"
2693														"\n"
2694														"layout (local_size_x = " + localSizeX
2695															+ ", local_size_y = " + localSizeY
2696															+ ", local_size_z = " + localSizeZ
2697															+ ") in;\n"
2698														"layout (" + shaderImageFormatStr + ", binding=0) " + qualifierName + " uniform " + shaderImageTypeStr + " u_image;\n"
2699														"void main (void)\n"
2700														"{\n"
2701														"	int gx = int(gl_GlobalInvocationID.x);\n"
2702														"	int gy = int(gl_GlobalInvocationID.y);\n"
2703														"	int gz = int(gl_GlobalInvocationID.z);\n"
2704														"	imageStore(u_image, " + invocationCoord + ", " + colorVecTypeName + "(gx^gy^gz));\n"
2705														"\n"
2706														"	memoryBarrier();\n"
2707														"	barrier();\n"
2708														"\n"
2709														"	" + colorScalarTypeName + " sum = " + colorScalarTypeName + "(0);\n"
2710														"	int groupBaseX = gx/" + localSizeX + "*" + localSizeX + ";\n"
2711														"	int groupBaseY = gy/" + localSizeY + "*" + localSizeY + ";\n"
2712														"	int groupBaseZ = gz/" + localSizeZ + "*" + localSizeZ + ";\n"
2713														"	int xOffsets[] = " + SHADER_READ_OFFSETS_X_STR + ";\n"
2714														"	int yOffsets[] = " + SHADER_READ_OFFSETS_Y_STR + ";\n"
2715														"	int zOffsets[] = " + SHADER_READ_OFFSETS_Z_STR + ";\n"
2716														"	for (int i = 0; i < " + toString(DE_LENGTH_OF_ARRAY(SHADER_READ_OFFSETS_X)) + "; i++)\n"
2717														"	{\n"
2718														"		int readX = groupBaseX + (gx + xOffsets[i]) % " + localSizeX + ";\n"
2719														"		int readY = groupBaseY + (gy + yOffsets[i]) % " + localSizeY + ";\n"
2720														"		int readZ = groupBaseZ + (gz + zOffsets[i]) % " + localSizeZ + ";\n"
2721														"		sum += imageLoad(u_image, " + (m_imageType == TEXTURETYPE_BUFFER	? "readX"
2722																							 : m_imageType == TEXTURETYPE_2D		? "ivec2(readX, readY)"
2723																							 : "ivec3(readX, readY, readZ)") + ").x;\n"
2724														"	}\n"
2725														"\n"
2726														"	memoryBarrier();\n"
2727														"	barrier();\n"
2728														"\n"
2729														"	imageStore(u_image, " + invocationCoord + ", " + colorVecTypeName + "(sum));\n"
2730														"}\n"));
2731
2732		UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
2733
2734		log << program;
2735
2736		if (!program.isOk())
2737		{
2738			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
2739			return STOP;
2740		}
2741
2742		// Setup and dispatch.
2743
2744		glLog.glUseProgram(program.getProgram());
2745
2746		glLog.glDispatchCompute(numGroups.x(), numGroups.y(), numGroups.z());
2747		GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
2748	}
2749
2750	// Create reference, read texture and compare.
2751
2752	{
2753		LayeredImage reference(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
2754
2755		{
2756			LayeredImage base(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
2757			for (int z = 0; z < numSlicesOrFaces; z++)
2758			for (int y = 0; y < imageSize.y(); y++)
2759			for (int x = 0; x < imageSize.x(); x++)
2760				base.setPixel(x, y, z, IVec4(x^y^z));
2761
2762			for (int z = 0; z < numSlicesOrFaces; z++)
2763			for (int y = 0; y < imageSize.y(); y++)
2764			for (int x = 0; x < imageSize.x(); x++)
2765			{
2766				const int	groupBaseX	= x / localSize.x() * localSize.x();
2767				const int	groupBaseY	= y / localSize.y() * localSize.y();
2768				const int	groupBaseZ	= z / localSize.z() * localSize.z();
2769				int			sum			= 0;
2770				for (int i = 0; i < DE_LENGTH_OF_ARRAY(SHADER_READ_OFFSETS_X); i++)
2771					sum += base.getPixelInt(groupBaseX + (x + SHADER_READ_OFFSETS_X[i]) % localSize.x(),
2772											groupBaseY + (y + SHADER_READ_OFFSETS_Y[i]) % localSize.y(),
2773											groupBaseZ + (z + SHADER_READ_OFFSETS_Z[i]) % localSize.z()).x();
2774
2775				reference.setPixel(x, y, z, IVec4(sum));
2776			}
2777		}
2778
2779		if (readTextureAndVerify(renderCtx, glLog, *texture, *textureBuf, m_imageType, m_format, imageSize, ImageLayerComparer(reference)))
2780			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2781		else
2782			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
2783
2784		return STOP;
2785	}
2786}
2787
2788class R32UIImageSingleValueVerifier : public ImageLayerVerifier
2789{
2790public:
2791	R32UIImageSingleValueVerifier (const deUint32 value)					: m_min(value),	m_max(value)	{}
2792	R32UIImageSingleValueVerifier (const deUint32 min, const deUint32 max)	: m_min(min),	m_max(max)		{}
2793
2794	bool operator() (TestLog& log, const ConstPixelBufferAccess& resultSlice, int) const
2795	{
2796		DE_ASSERT(resultSlice.getWidth() == 1 && resultSlice.getHeight() == 1 && resultSlice.getDepth() == 1);
2797		DE_ASSERT(resultSlice.getFormat() == TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32));
2798
2799		log << TestLog::Message << "// Note: expecting to get value " << (m_min == m_max ? toString(m_min) : "in range [" + toString(m_min) + ", " + toString(m_max) + "]") << TestLog::EndMessage;
2800
2801		const deUint32 resultValue = resultSlice.getPixelUint(0, 0).x();
2802		if (!de::inRange(resultValue, m_min, m_max))
2803		{
2804			log << TestLog::Message << "// Failure: got value " << resultValue << TestLog::EndMessage;
2805			return false;
2806		}
2807		else
2808		{
2809			log << TestLog::Message << "// Success: got value " << resultValue << TestLog::EndMessage;
2810			return true;
2811		}
2812	}
2813
2814private:
2815	const deUint32 m_min;
2816	const deUint32 m_max;
2817};
2818
2819//! Tests the imageSize() GLSL function. Stores result in a 1x1 R32UI image. The image with which imageSize() is called isn't read or written, and
2820//  can thus be qualifier readonly, writeonly, or both.
2821class ImageSizeCase : public TestCase
2822{
2823public:
2824	enum ImageAccess
2825	{
2826		IMAGEACCESS_READ_ONLY = 0,
2827		IMAGEACCESS_WRITE_ONLY,
2828		IMAGEACCESS_READ_ONLY_WRITE_ONLY,
2829
2830		IMAGEACCESS_LAST
2831	};
2832
2833	ImageSizeCase (Context& context, const char* name, const char* description, const TextureFormat& format, TextureType imageType, const IVec3& size, ImageAccess imageAccess)
2834		: TestCase			(context, name, description)
2835		, m_format			(format)
2836		, m_imageType		(imageType)
2837		, m_imageSize		(size)
2838		, m_imageAccess		(imageAccess)
2839	{
2840	}
2841
2842	void			init		(void) { checkTextureTypeExtensions(m_context.getContextInfo(), m_imageType, m_context.getRenderContext()); }
2843	IterateResult	iterate		(void);
2844
2845private:
2846	const TextureFormat		m_format;
2847	const TextureType		m_imageType;
2848	const IVec3				m_imageSize;
2849	const ImageAccess		m_imageAccess;
2850};
2851
2852ImageSizeCase::IterateResult ImageSizeCase::iterate (void)
2853{
2854	const RenderContext&		renderCtx				= m_context.getRenderContext();
2855	TestLog&					log						(m_testCtx.getLog());
2856	glu::CallLogWrapper			glLog					(renderCtx.getFunctions(), log);
2857	const deUint32				internalFormatGL		= glu::getInternalFormat(m_format);
2858	const deUint32				textureTargetGL			= getGLTextureTarget(m_imageType);
2859	const glu::Buffer			mainTextureBuf			(renderCtx);
2860	const glu::Texture			mainTexture				(renderCtx);
2861	const glu::Texture			shaderOutResultTexture	(renderCtx);
2862
2863	glLog.enableLogging(true);
2864
2865	// Setup textures.
2866
2867	log << TestLog::Message << "// Created a texture (name " << *mainTexture << ")" << TestLog::EndMessage;
2868	if (m_imageType == TEXTURETYPE_BUFFER)
2869		log << TestLog::Message << "// Created a buffer for the texture (name " << *mainTextureBuf << ")" << TestLog::EndMessage;
2870	log << TestLog::Message << "// Created a texture (name " << *shaderOutResultTexture << ") for storing the shader output" << TestLog::EndMessage;
2871
2872	glLog.glActiveTexture(GL_TEXTURE0);
2873	glLog.glBindTexture(textureTargetGL, *mainTexture);
2874	setTexParameteri(glLog, textureTargetGL);
2875	setTextureStorage(glLog, m_imageType, internalFormatGL, m_imageSize, *mainTextureBuf);
2876	glLog.glBindImageTexture(0, *mainTexture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
2877	GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2878
2879	glLog.glActiveTexture(GL_TEXTURE1);
2880	glLog.glBindTexture(GL_TEXTURE_2D, *shaderOutResultTexture);
2881	setTexParameteri(glLog, GL_TEXTURE_2D);
2882	setTextureStorage(glLog, TEXTURETYPE_2D, GL_R32UI, IVec3(1, 1, 1), 0 /* always 2d texture, no buffer needed */);
2883	glLog.glBindImageTexture(1, *shaderOutResultTexture, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_R32UI);
2884	GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2885
2886	// Read texture size in compute shader.
2887
2888	{
2889		// Generate compute shader.
2890
2891		const char* const	shaderImageAccessStr	= m_imageAccess == IMAGEACCESS_READ_ONLY			? "readonly"
2892													: m_imageAccess == IMAGEACCESS_WRITE_ONLY			? "writeonly"
2893													: m_imageAccess == IMAGEACCESS_READ_ONLY_WRITE_ONLY	? "readonly writeonly"
2894													: DE_NULL;
2895		const string		shaderImageFormatStr	= getShaderImageFormatQualifier(m_format);
2896		const string		shaderImageTypeStr		= getShaderImageType(m_format.type, m_imageType);
2897		const string		glslVersionDeclaration	= glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
2898
2899		const glu::ShaderProgram program(renderCtx,
2900			glu::ProgramSources() << glu::ComputeSource(glslVersionDeclaration + "\n"
2901														+ textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
2902														"\n"
2903														"precision highp " + shaderImageTypeStr + ";\n"
2904														"precision highp uimage2D;\n"
2905														"\n"
2906														"layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2907														"layout (" + shaderImageFormatStr + ", binding=0) " + shaderImageAccessStr + " uniform " + shaderImageTypeStr + " u_image;\n"
2908														"layout (r32ui, binding=1) writeonly uniform uimage2D u_result;\n"
2909														"void main (void)\n"
2910														"{\n"
2911														+ (m_imageType == TEXTURETYPE_BUFFER ?
2912															"	int result = imageSize(u_image);\n"
2913														 : m_imageType == TEXTURETYPE_2D || m_imageType == TEXTURETYPE_CUBE ?
2914															"	ivec2 size = imageSize(u_image);\n"
2915															"	int result = size.y*1000 + size.x;\n"
2916														 : m_imageType == TEXTURETYPE_3D || m_imageType == TEXTURETYPE_2D_ARRAY ?
2917															"	ivec3 size = imageSize(u_image);\n"
2918															"	int result = size.z*1000000 + size.y*1000 + size.x;\n"
2919														 : DE_NULL) +
2920														"	imageStore(u_result, ivec2(0, 0), uvec4(result));\n"
2921														"}\n"));
2922
2923		UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
2924
2925		log << program;
2926
2927		if (!program.isOk())
2928		{
2929			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
2930			return STOP;
2931		}
2932
2933		// Setup and dispatch.
2934
2935		glLog.glUseProgram(program.getProgram());
2936
2937		glLog.glDispatchCompute(1, 1, 1);
2938		GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
2939	}
2940
2941	// Read texture and compare to reference.
2942
2943	{
2944		const deUint32	referenceOutput		= m_imageType == TEXTURETYPE_BUFFER										? (deUint32)(												  m_imageSize.x())
2945											: m_imageType == TEXTURETYPE_2D || m_imageType == TEXTURETYPE_CUBE		? (deUint32)(						   m_imageSize.y()*1000 + m_imageSize.x())
2946											: m_imageType == TEXTURETYPE_3D || m_imageType == TEXTURETYPE_2D_ARRAY	? (deUint32)(m_imageSize.z()*1000000 + m_imageSize.y()*1000 + m_imageSize.x())
2947											: (deUint32)-1;
2948
2949		if (readIntegerTextureViaFBOAndVerify(renderCtx, glLog, *shaderOutResultTexture, TEXTURETYPE_2D, TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32),
2950											  IVec3(1, 1, 1), R32UIImageSingleValueVerifier(referenceOutput)))
2951			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2952		else
2953			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got wrong value");
2954
2955		return STOP;
2956	}
2957}
2958
2959//! Case testing the control over early/late fragment tests.
2960class EarlyFragmentTestsCase : public TestCase
2961{
2962public:
2963	enum TestType
2964	{
2965		TESTTYPE_DEPTH = 0,
2966		TESTTYPE_STENCIL,
2967
2968		TESTTYPE_LAST
2969	};
2970
2971	enum RenderTargetType
2972	{
2973		RENDERTARGET_DEFAULT = 0,
2974		RENDERTARGET_FBO,
2975		RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT,
2976
2977		RENDERTARGET_LAST
2978	};
2979
2980
2981	EarlyFragmentTestsCase (Context& context, const char* name, const char* description, TestType type, bool useEarlyTests, RenderTargetType renderTarget)
2982		: TestCase			(context, name, description)
2983		, m_type			(type)
2984		, m_useEarlyTests	(useEarlyTests)
2985		, m_renderTarget	(renderTarget)
2986	{
2987	}
2988
2989	void init (void)
2990	{
2991		if (m_context.getContextInfo().getInt(GL_MAX_FRAGMENT_IMAGE_UNIFORMS) == 0)
2992			throw tcu::NotSupportedError("GL_MAX_FRAGMENT_IMAGE_UNIFORMS is zero");
2993
2994		if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic") && !supportsES32orGL45(m_context.getRenderContext()))
2995			throw tcu::NotSupportedError("Test requires OES_shader_image_atomic extension");
2996
2997		if (m_type == TESTTYPE_DEPTH				&&
2998			m_renderTarget == RENDERTARGET_DEFAULT	&&
2999			m_context.getRenderTarget().getDepthBits() == 0)
3000		{
3001			throw tcu::NotSupportedError("Test requires depth buffer");
3002		}
3003
3004		if (m_type == TESTTYPE_STENCIL				&&
3005			m_renderTarget == RENDERTARGET_DEFAULT	&&
3006			m_context.getRenderTarget().getStencilBits() == 0)
3007		{
3008			throw tcu::NotSupportedError("Test requires stencil buffer");
3009		}
3010
3011		if (m_renderTarget == RENDERTARGET_DEFAULT	&&
3012			(m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE))
3013			throw tcu::NotSupportedError("Render target must have at least " + toString(RENDER_SIZE) + " width and height");
3014	}
3015
3016	IterateResult iterate (void);
3017
3018private:
3019	static const int		RENDER_SIZE;
3020
3021	const TestType			m_type;
3022	const bool				m_useEarlyTests;
3023	const RenderTargetType	m_renderTarget;
3024};
3025
3026const int EarlyFragmentTestsCase::RENDER_SIZE = 32;
3027
3028EarlyFragmentTestsCase::IterateResult EarlyFragmentTestsCase::iterate (void)
3029{
3030	const RenderContext&			renderCtx			= m_context.getRenderContext();
3031	TestLog&						log					(m_testCtx.getLog());
3032	glu::CallLogWrapper				glLog				(renderCtx.getFunctions(), log);
3033	de::Random						rnd					(deStringHash(getName()));
3034	const bool						expectPartialResult	= m_useEarlyTests && m_renderTarget != RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT;
3035	const int						viewportWidth		= RENDER_SIZE;
3036	const int						viewportHeight		= RENDER_SIZE;
3037	const int						viewportX			= (m_renderTarget == RENDERTARGET_DEFAULT) ? (rnd.getInt(0, renderCtx.getRenderTarget().getWidth() - viewportWidth))	: (0);
3038	const int						viewportY			= (m_renderTarget == RENDERTARGET_DEFAULT) ? (rnd.getInt(0, renderCtx.getRenderTarget().getHeight() - viewportHeight))	: (0);
3039	const glu::Texture				texture				(renderCtx);
3040	de::MovePtr<glu::Framebuffer>	fbo;
3041	de::MovePtr<glu::Renderbuffer>	colorAttachment;
3042	de::MovePtr<glu::Renderbuffer>	testAttachment;
3043
3044	glLog.enableLogging(true);
3045
3046	// Setup texture.
3047
3048	log << TestLog::Message << "// Created a texture (name " << *texture << ")" << TestLog::EndMessage;
3049
3050	glLog.glActiveTexture(GL_TEXTURE0);
3051	glLog.glBindTexture(GL_TEXTURE_2D, *texture);
3052	setTexParameteri(glLog, GL_TEXTURE_2D);
3053	{
3054		LayeredImage src(TEXTURETYPE_2D, TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32), 1, 1, 1);
3055		src.setPixel(0, 0, 0, IVec4(0));
3056		uploadTexture(glLog, src, 0 /* always 2d texture, no buffer needed */);
3057	}
3058	glLog.glBindImageTexture(0, *texture, 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32UI);
3059	GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
3060
3061	// Set up framebuffer
3062	if (m_renderTarget == RENDERTARGET_FBO ||
3063		m_renderTarget == RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT)
3064	{
3065		fbo				= de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(renderCtx));
3066		colorAttachment	= de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(renderCtx));
3067		testAttachment	= de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(renderCtx));
3068
3069		glLog.glBindRenderbuffer(GL_RENDERBUFFER, **colorAttachment);
3070		glLog.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, RENDER_SIZE, RENDER_SIZE);
3071		GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "gen color attachment rb");
3072
3073		glLog.glBindFramebuffer(GL_FRAMEBUFFER, **fbo);
3074		glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **colorAttachment);
3075		GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "set fbo color attachment");
3076
3077		if (m_renderTarget == RENDERTARGET_FBO && m_type == TESTTYPE_DEPTH)
3078		{
3079			glLog.glBindRenderbuffer(GL_RENDERBUFFER, **testAttachment);
3080			glLog.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, RENDER_SIZE, RENDER_SIZE);
3081			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "gen depth attachment rb");
3082
3083			glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, **testAttachment);
3084			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "set fbo depth attachment");
3085		}
3086		else if (m_renderTarget == RENDERTARGET_FBO && m_type == TESTTYPE_STENCIL)
3087		{
3088			glLog.glBindRenderbuffer(GL_RENDERBUFFER, **testAttachment);
3089			glLog.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, RENDER_SIZE, RENDER_SIZE);
3090			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "gen stencil attachment rb");
3091
3092			glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, **testAttachment);
3093			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "set fbo stencil attachment");
3094		}
3095
3096		glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **colorAttachment);
3097		GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "setup fbo");
3098		TCU_CHECK(glLog.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
3099	}
3100
3101	// Set up appropriate conditions for the test.
3102
3103	glLog.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
3104	glLog.glClear(GL_COLOR_BUFFER_BIT);
3105
3106	if (m_type == TESTTYPE_DEPTH)
3107	{
3108		glLog.glClearDepthf(0.5f);
3109		glLog.glClear(GL_DEPTH_BUFFER_BIT);
3110		glLog.glEnable(GL_DEPTH_TEST);
3111	}
3112	else if (m_type == TESTTYPE_STENCIL)
3113	{
3114		glLog.glClearStencil(0);
3115		glLog.glClear(GL_STENCIL_BUFFER_BIT);
3116		glLog.glScissor(viewportX, viewportY, viewportWidth/2, viewportHeight);
3117		glLog.glEnable(GL_SCISSOR_TEST);
3118		glLog.glClearStencil(1);
3119		glLog.glClear(GL_STENCIL_BUFFER_BIT);
3120		glLog.glDisable(GL_SCISSOR_TEST);
3121		glLog.glStencilFunc(GL_EQUAL, 1, 1);
3122		glLog.glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
3123		glLog.glEnable(GL_STENCIL_TEST);
3124	}
3125	else
3126		DE_ASSERT(false);
3127
3128	// Perform image stores in fragment shader.
3129
3130	{
3131		const std::string glslVersionDeclaration = glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
3132
3133		// Generate fragment shader.
3134
3135		const glu::ShaderProgram program(renderCtx,
3136			glu::ProgramSources() << glu::VertexSource(		glslVersionDeclaration + "\n"
3137															"\n"
3138															"highp in vec3 a_position;\n"
3139															"\n"
3140															"void main (void)\n"
3141															"{\n"
3142															"	gl_Position = vec4(a_position, 1.0);\n"
3143															"}\n")
3144
3145								  << glu::FragmentSource(	glslVersionDeclaration + "\n"
3146															+ imageAtomicExtensionShaderRequires(renderCtx) +
3147															"\n"
3148															+ string(m_useEarlyTests ? "layout (early_fragment_tests) in;\n\n" : "") +
3149															"layout (location = 0) out highp vec4 o_color;\n"
3150															"\n"
3151															"precision highp uimage2D;\n"
3152															"\n"
3153															"layout (r32ui, binding=0) coherent uniform uimage2D u_image;\n"
3154															"\n"
3155															"void main (void)\n"
3156															"{\n"
3157															"	imageAtomicAdd(u_image, ivec2(0, 0), uint(1));\n"
3158															"	o_color = vec4(1.0);\n"
3159															"}\n"));
3160
3161		UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
3162
3163		log << program;
3164
3165		if (!program.isOk())
3166		{
3167			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
3168			return STOP;
3169		}
3170
3171		// Setup and draw full-viewport quad.
3172
3173		glLog.glUseProgram(program.getProgram());
3174
3175		{
3176			static const float vertexPositions[4*3] =
3177			{
3178				-1.0, -1.0, -1.0f,
3179				 1.0, -1.0,  0.0f,
3180				-1.0,  1.0,  0.0f,
3181				 1.0,  1.0,  1.0f,
3182			};
3183
3184			static const deUint16 indices[6] = { 0, 1, 2, 2, 1, 3 };
3185
3186			const glu::VertexArrayBinding attrBindings[] =
3187			{
3188				glu::va::Float("a_position", 3, 4, 0, &vertexPositions[0])
3189			};
3190
3191			glLog.glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
3192
3193			glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3194				glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
3195			GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "Draw failed");
3196		}
3197	}
3198
3199	// Log rendered result for convenience.
3200	{
3201		tcu::Surface rendered(viewportWidth, viewportHeight);
3202		glu::readPixels(renderCtx, viewportX, viewportY, rendered.getAccess());
3203		log << TestLog::Image("Rendered", "Rendered image", rendered);
3204	}
3205
3206	// Read counter value and check.
3207	{
3208		const int numSamples		= de::max(1, renderCtx.getRenderTarget().getNumSamples());
3209		const int expectedCounter	= expectPartialResult ? viewportWidth*viewportHeight/2				: viewportWidth*viewportHeight;
3210		const int tolerance			= expectPartialResult ? de::max(viewportWidth, viewportHeight)*3	: 0;
3211		const int expectedMin		= de::max(0, expectedCounter - tolerance);
3212		const int expectedMax		= (expectedCounter + tolerance) * numSamples;
3213
3214		if (readIntegerTextureViaFBOAndVerify(renderCtx, glLog, *texture, TEXTURETYPE_2D, TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32),
3215											  IVec3(1, 1, 1), R32UIImageSingleValueVerifier(expectedMin, expectedMax)))
3216			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3217		else
3218			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got wrong value");
3219
3220		return STOP;
3221	}
3222}
3223
3224} // anonymous
3225
3226ShaderImageLoadStoreTests::ShaderImageLoadStoreTests (Context& context)
3227	: TestCaseGroup(context, "image_load_store", "Shader Image Load & Store Tests")
3228{
3229}
3230
3231ShaderImageLoadStoreTests::~ShaderImageLoadStoreTests (void)
3232{
3233}
3234
3235void ShaderImageLoadStoreTests::init (void)
3236{
3237	// Per-image-type tests.
3238
3239	{
3240		static const TextureType imageTypes[] =
3241		{
3242			TEXTURETYPE_2D,
3243			TEXTURETYPE_CUBE,
3244			TEXTURETYPE_3D,
3245			TEXTURETYPE_2D_ARRAY,
3246			TEXTURETYPE_BUFFER
3247		};
3248
3249		static const TextureFormat formats[] =
3250		{
3251			TextureFormat(TextureFormat::RGBA,	TextureFormat::FLOAT),
3252			TextureFormat(TextureFormat::RGBA,	TextureFormat::HALF_FLOAT),
3253			TextureFormat(TextureFormat::R,		TextureFormat::FLOAT),
3254
3255			TextureFormat(TextureFormat::RGBA,	TextureFormat::UNSIGNED_INT32),
3256			TextureFormat(TextureFormat::RGBA,	TextureFormat::UNSIGNED_INT16),
3257			TextureFormat(TextureFormat::RGBA,	TextureFormat::UNSIGNED_INT8),
3258			TextureFormat(TextureFormat::R,		TextureFormat::UNSIGNED_INT32),
3259
3260			TextureFormat(TextureFormat::RGBA,	TextureFormat::SIGNED_INT32),
3261			TextureFormat(TextureFormat::RGBA,	TextureFormat::SIGNED_INT16),
3262			TextureFormat(TextureFormat::RGBA,	TextureFormat::SIGNED_INT8),
3263			TextureFormat(TextureFormat::R,		TextureFormat::SIGNED_INT32),
3264
3265			TextureFormat(TextureFormat::RGBA,	TextureFormat::UNORM_INT8),
3266
3267			TextureFormat(TextureFormat::RGBA,	TextureFormat::SNORM_INT8)
3268		};
3269
3270		for (int imageTypeNdx = 0; imageTypeNdx < DE_LENGTH_OF_ARRAY(imageTypes); imageTypeNdx++)
3271		{
3272			const TextureType		imageType			= imageTypes[imageTypeNdx];
3273			TestCaseGroup* const	imageTypeGroup		= new TestCaseGroup(m_context, getTextureTypeName(imageType), "");
3274			addChild(imageTypeGroup);
3275
3276			TestCaseGroup* const	storeGroup			= new TestCaseGroup(m_context, "store",					"Plain imageStore() cases");
3277			TestCaseGroup* const	loadStoreGroup		= new TestCaseGroup(m_context, "load_store",			"Cases with imageLoad() followed by imageStore()");
3278			TestCaseGroup* const	atomicGroup			= new TestCaseGroup(m_context, "atomic",				"Atomic image operation cases");
3279			TestCaseGroup* const	qualifierGroup		= new TestCaseGroup(m_context, "qualifiers",			"Coherent, volatile and restrict");
3280			TestCaseGroup* const	reinterpretGroup	= new TestCaseGroup(m_context, "format_reinterpret",	"Cases with differing texture and image formats");
3281			TestCaseGroup* const	imageSizeGroup		= new TestCaseGroup(m_context, "image_size",			"imageSize() cases");
3282			imageTypeGroup->addChild(storeGroup);
3283			imageTypeGroup->addChild(loadStoreGroup);
3284			imageTypeGroup->addChild(atomicGroup);
3285			imageTypeGroup->addChild(qualifierGroup);
3286			imageTypeGroup->addChild(reinterpretGroup);
3287			imageTypeGroup->addChild(imageSizeGroup);
3288
3289			for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
3290			{
3291				const TextureFormat&	format		= formats[formatNdx];
3292				const string			formatName	= getShaderImageFormatQualifier(formats[formatNdx]);
3293
3294				if (imageType == TEXTURETYPE_BUFFER && !isFormatSupportedForTextureBuffer(format))
3295					continue;
3296
3297				// Store cases.
3298
3299				storeGroup->addChild(new ImageStoreCase(m_context, formatName.c_str(), "", format, imageType));
3300				if (textureLayerType(imageType) != imageType)
3301					storeGroup->addChild(new ImageStoreCase(m_context, (formatName + "_single_layer").c_str(), "", format, imageType, ImageStoreCase::CASEFLAG_SINGLE_LAYER_BIND));
3302
3303				// Load & store.
3304
3305				loadStoreGroup->addChild(new ImageLoadAndStoreCase(m_context, formatName.c_str(), "", format, imageType));
3306				if (textureLayerType(imageType) != imageType)
3307					loadStoreGroup->addChild(new ImageLoadAndStoreCase(m_context, (formatName + "_single_layer").c_str(), "", format, imageType, ImageLoadAndStoreCase::CASEFLAG_SINGLE_LAYER_BIND));
3308
3309				if (format.order == TextureFormat::R)
3310				{
3311					// Atomic operations.
3312
3313					for (int operationI = 0; operationI < ATOMIC_OPERATION_LAST; operationI++)
3314					{
3315						for (int atomicCaseTypeI = 0; atomicCaseTypeI < ATOMIC_OPERATION_CASE_TYPE_LAST; atomicCaseTypeI++)
3316						{
3317							const AtomicOperation operation = (AtomicOperation)operationI;
3318
3319							if (format.type == TextureFormat::FLOAT && operation != ATOMIC_OPERATION_EXCHANGE)
3320								continue;
3321
3322							const AtomicOperationCaseType	caseType		= (AtomicOperationCaseType)atomicCaseTypeI;
3323							const string					caseTypeName	= caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT		? "result"
3324																			: caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES	? "return_value"
3325																			: DE_NULL;
3326							const string					caseName		= string() + getAtomicOperationCaseName(operation) + "_" + formatName + "_" + caseTypeName;
3327
3328							if (operation == ATOMIC_OPERATION_COMP_SWAP)
3329								atomicGroup->addChild(new AtomicCompSwapCase(m_context, caseName.c_str(), "", format, imageType, caseType));
3330							else
3331								atomicGroup->addChild(new BinaryAtomicOperationCase(m_context, caseName.c_str(), "", format, imageType, operation, caseType));
3332						}
3333					}
3334
3335					// Coherence.
3336
3337					for (int coherenceQualifierI = 0; coherenceQualifierI < CoherenceCase::QUALIFIER_LAST; coherenceQualifierI++)
3338					{
3339						const CoherenceCase::Qualifier	coherenceQualifier		= (CoherenceCase::Qualifier)coherenceQualifierI;
3340						const char* const				coherenceQualifierName	= coherenceQualifier == CoherenceCase::QUALIFIER_COHERENT ? "coherent"
3341																				: coherenceQualifier == CoherenceCase::QUALIFIER_VOLATILE ? "volatile"
3342																				: DE_NULL;
3343						const string					caseName				= string() + coherenceQualifierName + "_" + formatName;
3344
3345						qualifierGroup->addChild(new CoherenceCase(m_context, caseName.c_str(), "", format, imageType, coherenceQualifier));
3346					}
3347				}
3348			}
3349
3350			// Restrict.
3351			qualifierGroup->addChild(new ImageLoadAndStoreCase(m_context, "restrict", "", TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32), imageType, ImageLoadAndStoreCase::CASEFLAG_RESTRICT_IMAGES));
3352
3353			// Format re-interpretation.
3354
3355			for (int texFmtNdx = 0; texFmtNdx < DE_LENGTH_OF_ARRAY(formats); texFmtNdx++)
3356			for (int imgFmtNdx = 0; imgFmtNdx < DE_LENGTH_OF_ARRAY(formats); imgFmtNdx++)
3357			{
3358				const TextureFormat& texFmt = formats[texFmtNdx];
3359				const TextureFormat& imgFmt = formats[imgFmtNdx];
3360
3361				if (imageType == TEXTURETYPE_BUFFER && !isFormatSupportedForTextureBuffer(texFmt))
3362					continue;
3363
3364				if (texFmt != imgFmt && texFmt.getPixelSize() == imgFmt.getPixelSize())
3365					reinterpretGroup->addChild(new ImageLoadAndStoreCase(m_context,
3366																		 (getShaderImageFormatQualifier(texFmt) + "_" + getShaderImageFormatQualifier(imgFmt)).c_str(), "",
3367																		 texFmt, imgFmt, imageType));
3368			}
3369
3370			// imageSize().
3371
3372			{
3373				static const IVec3 baseImageSizes[] =
3374				{
3375					IVec3(32, 32, 32),
3376					IVec3(12, 34, 56),
3377					IVec3(1,   1,  1),
3378					IVec3(7,   1,  1)
3379				};
3380
3381				for (int imageAccessI = 0; imageAccessI < ImageSizeCase::IMAGEACCESS_LAST; imageAccessI++)
3382				{
3383					const ImageSizeCase::ImageAccess	imageAccess		= (ImageSizeCase::ImageAccess)imageAccessI;
3384					const char* const					imageAccessStr	= imageAccess == ImageSizeCase::IMAGEACCESS_READ_ONLY				? "readonly"
3385																		: imageAccess == ImageSizeCase::IMAGEACCESS_WRITE_ONLY				? "writeonly"
3386																		: imageAccess == ImageSizeCase::IMAGEACCESS_READ_ONLY_WRITE_ONLY	? "readonly_writeonly"
3387																		: deFatalStr("Invalid ImageAccess");
3388
3389					for (int imageSizeNdx = 0; imageSizeNdx < DE_LENGTH_OF_ARRAY(baseImageSizes); imageSizeNdx++)
3390					{
3391						const IVec3&	baseSize	= baseImageSizes[imageSizeNdx];
3392						const IVec3		imageSize	= imageType == TEXTURETYPE_BUFFER		? IVec3(baseSize.x(), 1, 1)
3393													: imageType == TEXTURETYPE_2D			? IVec3(baseSize.x(), baseSize.y(), 1)
3394													: imageType == TEXTURETYPE_CUBE			? IVec3(baseSize.x(), baseSize.x(), 1)
3395													: imageType == TEXTURETYPE_3D			? baseSize
3396													: imageType == TEXTURETYPE_2D_ARRAY		? baseSize
3397													: IVec3(-1, -1, -1);
3398
3399						const string	sizeStr		= imageType == TEXTURETYPE_BUFFER		? toString(imageSize.x())
3400													: imageType == TEXTURETYPE_2D			? toString(imageSize.x()) + "x" + toString(imageSize.y())
3401													: imageType == TEXTURETYPE_CUBE			? toString(imageSize.x()) + "x" + toString(imageSize.y())
3402													: imageType == TEXTURETYPE_3D			? toString(imageSize.x()) + "x" + toString(imageSize.y()) + "x" + toString(imageSize.z())
3403													: imageType == TEXTURETYPE_2D_ARRAY		? toString(imageSize.x()) + "x" + toString(imageSize.y()) + "x" + toString(imageSize.z())
3404													: deFatalStr("Invalid TextureType");
3405
3406						const string	caseName	= string() + imageAccessStr + "_" + sizeStr;
3407
3408						imageSizeGroup->addChild(new ImageSizeCase(m_context, caseName.c_str(), "", TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT), imageType, imageSize, imageAccess));
3409					}
3410				}
3411			}
3412		}
3413	}
3414
3415	// early_fragment_tests cases.
3416
3417	{
3418		TestCaseGroup* const earlyTestsGroup = new TestCaseGroup(m_context, "early_fragment_tests", "");
3419		addChild(earlyTestsGroup);
3420
3421		for (int testRenderTargetI = 0; testRenderTargetI < EarlyFragmentTestsCase::RENDERTARGET_LAST; testRenderTargetI++)
3422		for (int useEarlyTestsI = 0; useEarlyTestsI <= 1; useEarlyTestsI++)
3423		for (int testTypeI = 0; testTypeI < EarlyFragmentTestsCase::TESTTYPE_LAST; testTypeI++)
3424		{
3425			const EarlyFragmentTestsCase::RenderTargetType	targetType		= (EarlyFragmentTestsCase::RenderTargetType)testRenderTargetI;
3426			const bool										useEarlyTests	= useEarlyTestsI != 0;
3427			const EarlyFragmentTestsCase::TestType			testType		= (EarlyFragmentTestsCase::TestType)testTypeI;
3428
3429			const string									testTypeName	= testType == EarlyFragmentTestsCase::TESTTYPE_DEPTH	? "depth"
3430																			: testType == EarlyFragmentTestsCase::TESTTYPE_STENCIL	? "stencil"
3431																			: DE_NULL;
3432
3433			const string									targetName		= targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO							? (std::string("_fbo"))
3434																			: targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT	? (std::string("_fbo_with_no_") + testTypeName)
3435																			: std::string("");
3436
3437			const string									caseName		= string(useEarlyTests ? "" : "no_") + "early_fragment_tests_" + testTypeName + targetName;
3438
3439			const string									caseDesc		= string(useEarlyTests ? "Specify" : "Don't specify")
3440																			+ " early_fragment_tests, use the " + testTypeName + " test"
3441																			+ ((targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO)								? (", render to fbo")
3442																			   : (targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT)	? (", render to fbo without relevant buffer")
3443																			   : (""));
3444
3445			earlyTestsGroup->addChild(new EarlyFragmentTestsCase(m_context, caseName.c_str(), caseDesc.c_str(), testType, useEarlyTests, targetType));
3446		}
3447	}
3448}
3449
3450} // Functional
3451} // gles31
3452} // deqp
3453