1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 The Khronos Group Inc.
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  vktImageAtomicOperationTests.cpp
21 * \brief Image atomic operation tests
22 *//*--------------------------------------------------------------------*/
23
24#include "vktImageAtomicOperationTests.hpp"
25
26#include "deUniquePtr.hpp"
27#include "deStringUtil.hpp"
28
29#include "vktTestCaseUtil.hpp"
30#include "vkPrograms.hpp"
31#include "vkImageUtil.hpp"
32#include "vktImageTestsUtil.hpp"
33#include "vkBuilderUtil.hpp"
34#include "vkRef.hpp"
35#include "vkRefUtil.hpp"
36#include "vkTypeUtil.hpp"
37
38#include "tcuTextureUtil.hpp"
39#include "tcuTexture.hpp"
40#include "tcuVectorType.hpp"
41
42namespace vkt
43{
44namespace image
45{
46namespace
47{
48
49using namespace vk;
50using namespace std;
51using de::toString;
52
53using tcu::TextureFormat;
54using tcu::IVec2;
55using tcu::IVec3;
56using tcu::UVec3;
57using tcu::Vec4;
58using tcu::IVec4;
59using tcu::UVec4;
60using tcu::CubeFace;
61using tcu::Texture1D;
62using tcu::Texture2D;
63using tcu::Texture3D;
64using tcu::Texture2DArray;
65using tcu::TextureCube;
66using tcu::PixelBufferAccess;
67using tcu::ConstPixelBufferAccess;
68using tcu::Vector;
69using tcu::TestContext;
70
71enum
72{
73	NUM_INVOCATIONS_PER_PIXEL = 5u
74};
75
76enum AtomicOperation
77{
78	ATOMIC_OPERATION_ADD = 0,
79	ATOMIC_OPERATION_MIN,
80	ATOMIC_OPERATION_MAX,
81	ATOMIC_OPERATION_AND,
82	ATOMIC_OPERATION_OR,
83	ATOMIC_OPERATION_XOR,
84	ATOMIC_OPERATION_EXCHANGE,
85
86	ATOMIC_OPERATION_LAST
87};
88
89static string getCoordStr (const ImageType		imageType,
90						   const std::string&	x,
91						   const std::string&	y,
92						   const std::string&	z)
93{
94	switch (imageType)
95	{
96		case IMAGE_TYPE_1D:
97		case IMAGE_TYPE_BUFFER:
98			return x;
99		case IMAGE_TYPE_1D_ARRAY:
100		case IMAGE_TYPE_2D:
101			return string("ivec2(" + x + "," + y + ")");
102		case IMAGE_TYPE_2D_ARRAY:
103		case IMAGE_TYPE_3D:
104		case IMAGE_TYPE_CUBE:
105		case IMAGE_TYPE_CUBE_ARRAY:
106			return string("ivec3(" + x + "," + y + "," + z + ")");
107		default:
108			DE_ASSERT(false);
109			return DE_NULL;
110	}
111}
112
113static string getAtomicFuncArgumentShaderStr (const AtomicOperation	op,
114											  const string&			x,
115											  const string&			y,
116											  const string&			z,
117											  const IVec3&			gridSize)
118{
119	switch (op)
120	{
121		case ATOMIC_OPERATION_ADD:
122		case ATOMIC_OPERATION_MIN:
123		case ATOMIC_OPERATION_MAX:
124		case ATOMIC_OPERATION_AND:
125		case ATOMIC_OPERATION_OR:
126		case ATOMIC_OPERATION_XOR:
127			return string("(" + x + "*" + x + " + " + y + "*" + y + " + " + z + "*" + z + ")");
128		case ATOMIC_OPERATION_EXCHANGE:
129			return string("((" + z + "*" + toString(gridSize.x()) + " + " + x + ")*" + toString(gridSize.y()) + " + " + y + ")");
130		default:
131			DE_ASSERT(false);
132			return DE_NULL;
133	}
134}
135
136static string getAtomicOperationCaseName (const AtomicOperation op)
137{
138	switch (op)
139	{
140		case ATOMIC_OPERATION_ADD:			return string("add");
141		case ATOMIC_OPERATION_MIN:			return string("min");
142		case ATOMIC_OPERATION_MAX:			return string("max");
143		case ATOMIC_OPERATION_AND:			return string("and");
144		case ATOMIC_OPERATION_OR:			return string("or");
145		case ATOMIC_OPERATION_XOR:			return string("xor");
146		case ATOMIC_OPERATION_EXCHANGE:		return string("exchange");
147		default:
148			DE_ASSERT(false);
149			return DE_NULL;
150	}
151}
152
153static string getAtomicOperationShaderFuncName (const AtomicOperation op)
154{
155	switch (op)
156	{
157		case ATOMIC_OPERATION_ADD:			return string("imageAtomicAdd");
158		case ATOMIC_OPERATION_MIN:			return string("imageAtomicMin");
159		case ATOMIC_OPERATION_MAX:			return string("imageAtomicMax");
160		case ATOMIC_OPERATION_AND:			return string("imageAtomicAnd");
161		case ATOMIC_OPERATION_OR:			return string("imageAtomicOr");
162		case ATOMIC_OPERATION_XOR:			return string("imageAtomicXor");
163		case ATOMIC_OPERATION_EXCHANGE:		return string("imageAtomicExchange");
164		default:
165			DE_ASSERT(false);
166			return DE_NULL;
167	}
168}
169
170static deInt32 getOperationInitialValue (const AtomicOperation op)
171{
172	switch (op)
173	{
174		// \note 18 is just an arbitrary small nonzero value.
175		case ATOMIC_OPERATION_ADD:			return 18;
176		case ATOMIC_OPERATION_MIN:			return (1 << 15) - 1;
177		case ATOMIC_OPERATION_MAX:			return 18;
178		case ATOMIC_OPERATION_AND:			return (1 << 15) - 1;
179		case ATOMIC_OPERATION_OR:			return 18;
180		case ATOMIC_OPERATION_XOR:			return 18;
181		case ATOMIC_OPERATION_EXCHANGE:		return 18;
182		default:
183			DE_ASSERT(false);
184			return -1;
185	}
186}
187
188static deInt32 getAtomicFuncArgument (const AtomicOperation op, const IVec3& invocationID, const IVec3& gridSize)
189{
190	const int x = invocationID.x();
191	const int y = invocationID.y();
192	const int z = invocationID.z();
193
194	switch (op)
195	{
196		// \note Fall-throughs.
197		case ATOMIC_OPERATION_ADD:
198		case ATOMIC_OPERATION_MIN:
199		case ATOMIC_OPERATION_MAX:
200		case ATOMIC_OPERATION_AND:
201		case ATOMIC_OPERATION_OR:
202		case ATOMIC_OPERATION_XOR:
203			return x*x + y*y + z*z;
204		case ATOMIC_OPERATION_EXCHANGE:
205			return (z*gridSize.x() + x)*gridSize.y() + y;
206		default:
207			DE_ASSERT(false);
208			return -1;
209	}
210}
211
212//! 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).
213static bool isOrderIndependentAtomicOperation (const AtomicOperation op)
214{
215	return	op == ATOMIC_OPERATION_ADD ||
216			op == ATOMIC_OPERATION_MIN ||
217			op == ATOMIC_OPERATION_MAX ||
218			op == ATOMIC_OPERATION_AND ||
219			op == ATOMIC_OPERATION_OR ||
220			op == ATOMIC_OPERATION_XOR;
221}
222
223//! Computes the result of an atomic operation where "a" is the data operated on and "b" is the parameter to the atomic function.
224static deInt32 computeBinaryAtomicOperationResult (const AtomicOperation op, const deInt32 a, const deInt32 b)
225{
226	switch (op)
227	{
228		case ATOMIC_OPERATION_ADD:			return a + b;
229		case ATOMIC_OPERATION_MIN:			return de::min(a, b);
230		case ATOMIC_OPERATION_MAX:			return de::max(a, b);
231		case ATOMIC_OPERATION_AND:			return a & b;
232		case ATOMIC_OPERATION_OR:			return a | b;
233		case ATOMIC_OPERATION_XOR:			return a ^ b;
234		case ATOMIC_OPERATION_EXCHANGE:		return b;
235		default:
236			DE_ASSERT(false);
237			return -1;
238	}
239}
240
241class BinaryAtomicEndResultCase : public vkt::TestCase
242{
243public:
244								BinaryAtomicEndResultCase  (tcu::TestContext&			testCtx,
245															const string&				name,
246															const string&				description,
247															const ImageType				imageType,
248															const tcu::UVec3&			imageSize,
249															const tcu::TextureFormat&	format,
250															const AtomicOperation		operation,
251															const glu::GLSLVersion		glslVersion);
252
253	void						initPrograms			   (SourceCollections&			sourceCollections) const;
254	TestInstance*				createInstance			   (Context&					context) const;
255private:
256
257	const ImageType				m_imageType;
258	const tcu::UVec3			m_imageSize;
259	const tcu::TextureFormat	m_format;
260	const AtomicOperation		m_operation;
261	const glu::GLSLVersion		m_glslVersion;
262};
263
264BinaryAtomicEndResultCase::BinaryAtomicEndResultCase (tcu::TestContext&			testCtx,
265													  const string&				name,
266													  const string&				description,
267													  const ImageType			imageType,
268													  const tcu::UVec3&			imageSize,
269													  const tcu::TextureFormat&	format,
270													  const AtomicOperation		operation,
271													  const glu::GLSLVersion	glslVersion)
272	: TestCase		(testCtx, name, description)
273	, m_imageType	(imageType)
274	, m_imageSize	(imageSize)
275	, m_format		(format)
276	, m_operation	(operation)
277	, m_glslVersion	(glslVersion)
278{
279}
280
281void BinaryAtomicEndResultCase::initPrograms (SourceCollections& sourceCollections) const
282{
283	const string	versionDecl				= glu::getGLSLVersionDeclaration(m_glslVersion);
284
285	const bool		uintFormat				= isUintFormat(mapTextureFormat(m_format));
286	const bool		intFormat				= isIntFormat(mapTextureFormat(m_format));
287	const UVec3		gridSize				= getShaderGridSize(m_imageType, m_imageSize);
288	const string	atomicCoord				= getCoordStr(m_imageType, "gx % " + toString(gridSize.x()), "gy", "gz");
289
290	const string	atomicArgExpr			= (uintFormat ? "uint" : intFormat ? "int" : "float")
291											+ getAtomicFuncArgumentShaderStr(m_operation, "gx", "gy", "gz", IVec3(NUM_INVOCATIONS_PER_PIXEL*gridSize.x(), gridSize.y(), gridSize.z()));
292
293	const string	atomicInvocation		= getAtomicOperationShaderFuncName(m_operation) + "(u_resultImage, " + atomicCoord + ", " + atomicArgExpr + ")";
294	const string	shaderImageFormatStr	= getShaderImageFormatQualifier(m_format);
295	const string	shaderImageTypeStr		= getShaderImageType(m_format, m_imageType);
296
297	string source = versionDecl + "\n"
298					"precision highp " + shaderImageTypeStr + ";\n"
299					"\n"
300					"layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
301					"layout (" + shaderImageFormatStr + ", binding=0) coherent uniform " + shaderImageTypeStr + " u_resultImage;\n"
302					"\n"
303					"void main (void)\n"
304					"{\n"
305					"	int gx = int(gl_GlobalInvocationID.x);\n"
306					"	int gy = int(gl_GlobalInvocationID.y);\n"
307					"	int gz = int(gl_GlobalInvocationID.z);\n"
308					"	" + atomicInvocation + ";\n"
309					"}\n";
310
311	sourceCollections.glslSources.add(m_name) << glu::ComputeSource(source.c_str());
312}
313
314class BinaryAtomicIntermValuesCase : public vkt::TestCase
315{
316public:
317								BinaryAtomicIntermValuesCase   (tcu::TestContext&			testCtx,
318																const string&				name,
319																const string&				description,
320																const ImageType				imageType,
321																const tcu::UVec3&			imageSize,
322																const tcu::TextureFormat&	format,
323																const AtomicOperation		operation,
324																const glu::GLSLVersion		glslVersion);
325
326	void						initPrograms				   (SourceCollections&			sourceCollections) const;
327	TestInstance*				createInstance				   (Context&					context) const;
328private:
329
330	const ImageType				m_imageType;
331	const tcu::UVec3			m_imageSize;
332	const tcu::TextureFormat	m_format;
333	const AtomicOperation		m_operation;
334	const glu::GLSLVersion		m_glslVersion;
335};
336
337BinaryAtomicIntermValuesCase::BinaryAtomicIntermValuesCase (TestContext&			testCtx,
338															const string&			name,
339															const string&			description,
340															const ImageType			imageType,
341															const tcu::UVec3&		imageSize,
342															const TextureFormat&	format,
343															const AtomicOperation	operation,
344															const glu::GLSLVersion	glslVersion)
345	: TestCase		(testCtx, name, description)
346	, m_imageType	(imageType)
347	, m_imageSize	(imageSize)
348	, m_format		(format)
349	, m_operation	(operation)
350	, m_glslVersion	(glslVersion)
351{
352}
353
354void BinaryAtomicIntermValuesCase::initPrograms (SourceCollections& sourceCollections) const
355{
356	const string	versionDecl				= glu::getGLSLVersionDeclaration(m_glslVersion);
357
358	const bool		uintFormat				= isUintFormat(mapTextureFormat(m_format));
359	const bool		intFormat				= isIntFormat(mapTextureFormat(m_format));
360	const string	colorVecTypeName		= string(uintFormat ? "u" : intFormat ? "i" : "") + "vec4";
361	const UVec3		gridSize				= getShaderGridSize(m_imageType, m_imageSize);
362	const string	atomicCoord				= getCoordStr(m_imageType, "gx % " + toString(gridSize.x()), "gy", "gz");
363	const string	invocationCoord			= getCoordStr(m_imageType, "gx", "gy", "gz");
364	const string	atomicArgExpr			= (uintFormat ? "uint" : intFormat ? "int" : "float")
365											+ getAtomicFuncArgumentShaderStr(m_operation, "gx", "gy", "gz", IVec3(NUM_INVOCATIONS_PER_PIXEL*gridSize.x(), gridSize.y(), gridSize.z()));
366
367	const string	atomicInvocation		= getAtomicOperationShaderFuncName(m_operation) + "(u_resultImage, " + atomicCoord + ", " + atomicArgExpr + ")";
368	const string	shaderImageFormatStr	= getShaderImageFormatQualifier(m_format);
369	const string	shaderImageTypeStr		= getShaderImageType(m_format, m_imageType);
370
371	string source = versionDecl + "\n"
372					"precision highp " + shaderImageTypeStr + ";\n"
373					"\n"
374					"layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
375					"layout (" + shaderImageFormatStr + ", binding=0) coherent uniform " + shaderImageTypeStr + " u_resultImage;\n"
376					"layout (" + shaderImageFormatStr + ", binding=1) writeonly uniform " + shaderImageTypeStr + " u_intermValuesImage;\n"
377					"\n"
378					"void main (void)\n"
379					"{\n"
380					"	int gx = int(gl_GlobalInvocationID.x);\n"
381					"	int gy = int(gl_GlobalInvocationID.y);\n"
382					"	int gz = int(gl_GlobalInvocationID.z);\n"
383					"	imageStore(u_intermValuesImage, " + invocationCoord + ", " + colorVecTypeName + "(" + atomicInvocation + "));\n"
384					"}\n";
385
386	sourceCollections.glslSources.add(m_name) << glu::ComputeSource(source.c_str());
387}
388
389class BinaryAtomicInstanceBase : public vkt::TestInstance
390{
391public:
392
393								BinaryAtomicInstanceBase (Context&						context,
394														  const string&					name,
395														  const ImageType				imageType,
396														  const tcu::UVec3&				imageSize,
397														  const TextureFormat&			format,
398														  const AtomicOperation			operation);
399
400	tcu::TestStatus				iterate					 (void);
401
402	virtual deUint32			getOutputBufferSize		 (void) const = 0;
403
404	virtual void				prepareResources		 (void) = 0;
405	virtual void				prepareDescriptors		 (void) = 0;
406
407	virtual void				commandsBeforeCompute	 (const VkCommandBuffer			cmdBuffer) const = 0;
408	virtual void				commandsAfterCompute	 (const VkCommandBuffer			cmdBuffer) const = 0;
409
410	virtual bool				verifyResult			 (Allocation&					outputBufferAllocation) const = 0;
411
412protected:
413	const string				m_name;
414	const ImageType				m_imageType;
415	const tcu::UVec3			m_imageSize;
416	const TextureFormat			m_format;
417	const AtomicOperation		m_operation;
418
419	de::MovePtr<Buffer>			m_outputBuffer;
420	Move<VkDescriptorPool>		m_descriptorPool;
421	Move<VkDescriptorSetLayout>	m_descriptorSetLayout;
422	Move<VkDescriptorSet>		m_descriptorSet;
423	de::MovePtr<Image>			m_resultImage;
424	Move<VkImageView>			m_resultImageView;
425};
426
427BinaryAtomicInstanceBase::BinaryAtomicInstanceBase (Context&				context,
428													const string&			name,
429													const ImageType			imageType,
430													const tcu::UVec3&		imageSize,
431													const TextureFormat&	format,
432													const AtomicOperation	operation)
433	: vkt::TestInstance	(context)
434	, m_name			(name)
435	, m_imageType		(imageType)
436	, m_imageSize		(imageSize)
437	, m_format			(format)
438	, m_operation		(operation)
439{
440}
441
442tcu::TestStatus	BinaryAtomicInstanceBase::iterate (void)
443{
444	const VkDevice			device				= m_context.getDevice();
445	const DeviceInterface&	deviceInterface		= m_context.getDeviceInterface();
446	const VkQueue			queue				= m_context.getUniversalQueue();
447	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
448	Allocator&				allocator			= m_context.getDefaultAllocator();
449	const VkDeviceSize		imageSizeInBytes	= tcu::getPixelSize(m_format) * getNumPixels(m_imageType, m_imageSize);
450	const VkDeviceSize		outBuffSizeInBytes	= getOutputBufferSize();
451
452	const VkImageCreateInfo imageParams	=
453	{
454		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,					// VkStructureType			sType;
455		DE_NULL,												// const void*				pNext;
456		(m_imageType == IMAGE_TYPE_CUBE ||
457		 m_imageType == IMAGE_TYPE_CUBE_ARRAY ?
458		 (VkImageCreateFlags)VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT :
459		 (VkImageCreateFlags)0u),								// VkImageCreateFlags		flags;
460		mapImageType(m_imageType),								// VkImageType				imageType;
461		mapTextureFormat(m_format),								// VkFormat					format;
462		makeExtent3D(getLayerSize(m_imageType, m_imageSize)),	// VkExtent3D				extent;
463		1u,														// deUint32					mipLevels;
464		getNumLayers(m_imageType, m_imageSize),					// deUint32					arrayLayers;
465		VK_SAMPLE_COUNT_1_BIT,									// VkSampleCountFlagBits	samples;
466		VK_IMAGE_TILING_OPTIMAL,								// VkImageTiling			tiling;
467		VK_IMAGE_USAGE_STORAGE_BIT |
468		VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
469		VK_IMAGE_USAGE_TRANSFER_DST_BIT,						// VkImageUsageFlags		usage;
470		VK_SHARING_MODE_EXCLUSIVE,								// VkSharingMode			sharingMode;
471		0u,														// deUint32					queueFamilyIndexCount;
472		DE_NULL,												// const deUint32*			pQueueFamilyIndices;
473		VK_IMAGE_LAYOUT_UNDEFINED,								// VkImageLayout			initialLayout;
474	};
475
476	//Create the image that is going to store results of atomic operations
477	m_resultImage = de::MovePtr<Image>(new Image(deviceInterface, device, allocator, imageParams, MemoryRequirement::Any));
478
479	const VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, getNumLayers(m_imageType, m_imageSize));
480
481	m_resultImageView = makeImageView(deviceInterface, device, m_resultImage->get(), mapImageViewType(m_imageType), mapTextureFormat(m_format), subresourceRange);
482
483	//Prepare the buffer with the initial data for the image
484	const Buffer inputBuffer(deviceInterface, device, allocator, makeBufferCreateInfo(imageSizeInBytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT), MemoryRequirement::HostVisible);
485
486	Allocation& inputBufferAllocation = inputBuffer.getAllocation();
487
488	//Prepare the initial data for the image
489	const tcu::IVec4 initialValue(getOperationInitialValue(m_operation));
490
491	tcu::UVec3 gridSize = getShaderGridSize(m_imageType, m_imageSize);
492	tcu::PixelBufferAccess inputPixelBuffer(m_format, gridSize.x(), gridSize.y(), gridSize.z(), inputBufferAllocation.getHostPtr());
493
494	for (deUint32 z = 0; z < gridSize.z(); z++)
495	for (deUint32 y = 0; y < gridSize.y(); y++)
496	for (deUint32 x = 0; x < gridSize.x(); x++)
497	{
498		inputPixelBuffer.setPixel(initialValue, x, y, z);
499	}
500
501	flushMappedMemoryRange(deviceInterface, device, inputBufferAllocation.getMemory(), inputBufferAllocation.getOffset(), imageSizeInBytes);
502
503	// Create a buffer to store shader output copied from result image
504	m_outputBuffer = de::MovePtr<Buffer>(new Buffer(deviceInterface, device, allocator, makeBufferCreateInfo(outBuffSizeInBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible));
505
506	prepareResources();
507
508	prepareDescriptors();
509
510	// Create pipeline
511	const Unique<VkShaderModule>	shaderModule(createShaderModule(deviceInterface, device, m_context.getBinaryCollection().get(m_name), 0));
512	const Unique<VkPipelineLayout>	pipelineLayout(makePipelineLayout(deviceInterface, device, *m_descriptorSetLayout));
513	const Unique<VkPipeline>		pipeline(makeComputePipeline(deviceInterface, device, *pipelineLayout, *shaderModule));
514
515	// Create command buffer
516	const Unique<VkCommandPool>		cmdPool(makeCommandPool(deviceInterface, device, queueFamilyIndex));
517	const Unique<VkCommandBuffer>	cmdBuffer(makeCommandBuffer(deviceInterface, device, *cmdPool));
518
519	beginCommandBuffer(deviceInterface, *cmdBuffer);
520
521	deviceInterface.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
522	deviceInterface.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &m_descriptorSet.get(), 0u, DE_NULL);
523
524	const VkBufferMemoryBarrier inputBufferPostHostWriteBarrier	=
525		makeBufferMemoryBarrier(VK_ACCESS_HOST_WRITE_BIT,
526								VK_ACCESS_TRANSFER_READ_BIT,
527								*inputBuffer,
528								0ull,
529								imageSizeInBytes);
530
531	const VkImageMemoryBarrier	resultImagePreCopyBarrier =
532		makeImageMemoryBarrier(	0u,
533								VK_ACCESS_TRANSFER_WRITE_BIT,
534								VK_IMAGE_LAYOUT_UNDEFINED,
535								VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
536								m_resultImage->get(),
537								subresourceRange);
538
539	deviceInterface.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, DE_FALSE, 0u, DE_NULL, 1u, &inputBufferPostHostWriteBarrier, 1u, &resultImagePreCopyBarrier);
540
541	const VkBufferImageCopy		bufferImageCopyParams = makeBufferImageCopy(makeExtent3D(getLayerSize(m_imageType, m_imageSize)), getNumLayers(m_imageType, m_imageSize));
542
543	deviceInterface.cmdCopyBufferToImage(*cmdBuffer, *inputBuffer, m_resultImage->get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, &bufferImageCopyParams);
544
545	const VkImageMemoryBarrier	resultImagePostCopyBarrier	=
546		makeImageMemoryBarrier(	VK_ACCESS_TRANSFER_WRITE_BIT,
547								VK_ACCESS_SHADER_READ_BIT,
548								VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
549								VK_IMAGE_LAYOUT_GENERAL,
550								m_resultImage->get(),
551								subresourceRange);
552
553	deviceInterface.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, DE_FALSE, 0u, DE_NULL, 0u, DE_NULL, 1u, &resultImagePostCopyBarrier);
554
555	commandsBeforeCompute(*cmdBuffer);
556
557	deviceInterface.cmdDispatch(*cmdBuffer, NUM_INVOCATIONS_PER_PIXEL*gridSize.x(), gridSize.y(), gridSize.z());
558
559	commandsAfterCompute(*cmdBuffer);
560
561	const VkBufferMemoryBarrier	outputBufferPreHostReadBarrier
562		= makeBufferMemoryBarrier(	VK_ACCESS_TRANSFER_WRITE_BIT,
563									VK_ACCESS_HOST_READ_BIT,
564									m_outputBuffer->get(),
565									0ull,
566									outBuffSizeInBytes);
567
568	deviceInterface.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, DE_FALSE, 0u, DE_NULL, 1u, &outputBufferPreHostReadBarrier, 0u, DE_NULL);
569
570	endCommandBuffer(deviceInterface, *cmdBuffer);
571
572	submitCommandsAndWait(deviceInterface, device, queue, *cmdBuffer);
573
574	Allocation& outputBufferAllocation = m_outputBuffer->getAllocation();
575
576	invalidateMappedMemoryRange(deviceInterface, device, outputBufferAllocation.getMemory(), outputBufferAllocation.getOffset(), outBuffSizeInBytes);
577
578	if (verifyResult(outputBufferAllocation))
579		return tcu::TestStatus::pass("Comparison succeeded");
580	else
581		return tcu::TestStatus::fail("Comparison failed");
582}
583
584class BinaryAtomicEndResultInstance : public BinaryAtomicInstanceBase
585{
586public:
587
588						BinaryAtomicEndResultInstance  (Context&				context,
589														const string&			name,
590														const ImageType			imageType,
591														const tcu::UVec3&		imageSize,
592														const TextureFormat&	format,
593														const AtomicOperation	operation)
594							: BinaryAtomicInstanceBase(context, name, imageType, imageSize, format, operation) {}
595
596	virtual deUint32	getOutputBufferSize			   (void) const;
597
598	virtual void		prepareResources			   (void) {}
599	virtual void		prepareDescriptors			   (void);
600
601	virtual void		commandsBeforeCompute		   (const VkCommandBuffer) const {}
602	virtual void		commandsAfterCompute		   (const VkCommandBuffer	cmdBuffer) const;
603
604	virtual bool		verifyResult				   (Allocation&				outputBufferAllocation) const;
605};
606
607deUint32 BinaryAtomicEndResultInstance::getOutputBufferSize (void) const
608{
609	return tcu::getPixelSize(m_format) * getNumPixels(m_imageType, m_imageSize);
610}
611
612void BinaryAtomicEndResultInstance::prepareDescriptors (void)
613{
614	const VkDevice			device			= m_context.getDevice();
615	const DeviceInterface&	deviceInterface = m_context.getDeviceInterface();
616
617	m_descriptorSetLayout =
618		DescriptorSetLayoutBuilder()
619		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
620		.build(deviceInterface, device);
621
622	m_descriptorPool =
623		DescriptorPoolBuilder()
624		.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
625		.build(deviceInterface, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
626
627	m_descriptorSet = makeDescriptorSet(deviceInterface, device, *m_descriptorPool, *m_descriptorSetLayout);
628
629	const VkDescriptorImageInfo	descResultImageInfo = makeDescriptorImageInfo(DE_NULL, *m_resultImageView, VK_IMAGE_LAYOUT_GENERAL);
630
631	DescriptorSetUpdateBuilder()
632		.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descResultImageInfo)
633		.update(deviceInterface, device);
634}
635
636void BinaryAtomicEndResultInstance::commandsAfterCompute (const VkCommandBuffer	cmdBuffer) const
637{
638	const DeviceInterface&			deviceInterface		= m_context.getDeviceInterface();
639	const VkImageSubresourceRange	subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, getNumLayers(m_imageType, m_imageSize));
640
641	const VkImageMemoryBarrier	resultImagePostDispatchBarrier =
642		makeImageMemoryBarrier(	VK_ACCESS_SHADER_WRITE_BIT,
643								VK_ACCESS_TRANSFER_READ_BIT,
644								VK_IMAGE_LAYOUT_GENERAL,
645								VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
646								m_resultImage->get(),
647								subresourceRange);
648
649	deviceInterface.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, DE_FALSE, 0u, DE_NULL, 0u, DE_NULL, 1u, &resultImagePostDispatchBarrier);
650
651	const VkBufferImageCopy		bufferImageCopyParams = makeBufferImageCopy(makeExtent3D(getLayerSize(m_imageType, m_imageSize)), getNumLayers(m_imageType, m_imageSize));
652
653	deviceInterface.cmdCopyImageToBuffer(cmdBuffer, m_resultImage->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_outputBuffer->get(), 1u, &bufferImageCopyParams);
654}
655
656bool BinaryAtomicEndResultInstance::verifyResult (Allocation& outputBufferAllocation) const
657{
658	const UVec3	gridSize			= getShaderGridSize(m_imageType, m_imageSize);
659	const IVec3 extendedGridSize	= IVec3(NUM_INVOCATIONS_PER_PIXEL*gridSize.x(), gridSize.y(), gridSize.z());
660
661	tcu::ConstPixelBufferAccess resultBuffer(m_format, gridSize.x(), gridSize.y(), gridSize.z(), outputBufferAllocation.getHostPtr());
662
663	for (deInt32 z = 0; z < resultBuffer.getDepth();  z++)
664	for (deInt32 y = 0; y < resultBuffer.getHeight(); y++)
665	for (deInt32 x = 0; x < resultBuffer.getWidth();  x++)
666	{
667		deInt32 resultValue = resultBuffer.getPixelInt(x, y, z).x();
668
669		if (isOrderIndependentAtomicOperation(m_operation))
670		{
671			deInt32 reference = getOperationInitialValue(m_operation);
672
673			for (deInt32 i = 0; i < static_cast<deInt32>(NUM_INVOCATIONS_PER_PIXEL); i++)
674			{
675				const IVec3 gid(x + i*gridSize.x(), y, z);
676				reference = computeBinaryAtomicOperationResult(m_operation, reference, getAtomicFuncArgument(m_operation, gid, extendedGridSize));
677			}
678
679			if (resultValue != reference)
680				return false;
681		}
682		else if (m_operation == ATOMIC_OPERATION_EXCHANGE)
683		{
684			// Check if the end result equals one of the atomic args.
685			bool matchFound = false;
686
687			for (deInt32 i = 0; i < static_cast<deInt32>(NUM_INVOCATIONS_PER_PIXEL) && !matchFound; i++)
688			{
689				const IVec3 gid(x + i*gridSize.x(), y, z);
690				matchFound = (resultValue == getAtomicFuncArgument(m_operation, gid, extendedGridSize));
691			}
692
693			if (!matchFound)
694				return false;
695		}
696		else
697			DE_ASSERT(false);
698	}
699	return true;
700}
701
702TestInstance* BinaryAtomicEndResultCase::createInstance (Context& context) const
703{
704	return new BinaryAtomicEndResultInstance(context, m_name, m_imageType, m_imageSize, m_format, m_operation);
705}
706
707class BinaryAtomicIntermValuesInstance : public BinaryAtomicInstanceBase
708{
709public:
710
711						BinaryAtomicIntermValuesInstance   (Context&				context,
712															const string&			name,
713															const ImageType			imageType,
714															const tcu::UVec3&		imageSize,
715															const TextureFormat&	format,
716															const AtomicOperation	operation)
717							: BinaryAtomicInstanceBase(context, name, imageType, imageSize, format, operation) {}
718
719	virtual deUint32	getOutputBufferSize				   (void) const;
720
721	virtual void		prepareResources				   (void);
722	virtual void		prepareDescriptors				   (void);
723
724	virtual void		commandsBeforeCompute			   (const VkCommandBuffer	cmdBuffer) const;
725	virtual void		commandsAfterCompute			   (const VkCommandBuffer	cmdBuffer) const;
726
727	virtual bool		verifyResult					   (Allocation&				outputBufferAllocation) const;
728
729protected:
730
731	bool				verifyRecursive					   (const deInt32			index,
732															const deInt32			valueSoFar,
733															bool					argsUsed[NUM_INVOCATIONS_PER_PIXEL],
734															const deInt32			atomicArgs[NUM_INVOCATIONS_PER_PIXEL],
735															const deInt32			resultValues[NUM_INVOCATIONS_PER_PIXEL]) const;
736	de::MovePtr<Image>	m_intermResultsImage;
737	Move<VkImageView>	m_intermResultsImageView;
738};
739
740deUint32 BinaryAtomicIntermValuesInstance::getOutputBufferSize (void) const
741{
742	return NUM_INVOCATIONS_PER_PIXEL * tcu::getPixelSize(m_format) * getNumPixels(m_imageType, m_imageSize);
743}
744
745void BinaryAtomicIntermValuesInstance::prepareResources (void)
746{
747	const VkDevice			device			= m_context.getDevice();
748	const DeviceInterface&	deviceInterface = m_context.getDeviceInterface();
749	Allocator&				allocator		= m_context.getDefaultAllocator();
750
751	const UVec3 layerSize			= getLayerSize(m_imageType, m_imageSize);
752	const bool  isCubeBasedImage	= (m_imageType == IMAGE_TYPE_CUBE || m_imageType == IMAGE_TYPE_CUBE_ARRAY);
753	const UVec3 extendedLayerSize	= isCubeBasedImage	? UVec3(NUM_INVOCATIONS_PER_PIXEL * layerSize.x(), NUM_INVOCATIONS_PER_PIXEL * layerSize.y(), layerSize.z())
754														: UVec3(NUM_INVOCATIONS_PER_PIXEL * layerSize.x(), layerSize.y(), layerSize.z());
755
756	const VkImageCreateInfo imageParams =
757	{
758		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,		// VkStructureType			sType;
759		DE_NULL,									// const void*				pNext;
760		(m_imageType == IMAGE_TYPE_CUBE ||
761		 m_imageType == IMAGE_TYPE_CUBE_ARRAY ?
762		 (VkImageCreateFlags)VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT :
763		 (VkImageCreateFlags)0u),					// VkImageCreateFlags		flags;
764		mapImageType(m_imageType),					// VkImageType				imageType;
765		mapTextureFormat(m_format),					// VkFormat					format;
766		makeExtent3D(extendedLayerSize),			// VkExtent3D				extent;
767		1u,											// deUint32					mipLevels;
768		getNumLayers(m_imageType, m_imageSize),		// deUint32					arrayLayers;
769		VK_SAMPLE_COUNT_1_BIT,						// VkSampleCountFlagBits	samples;
770		VK_IMAGE_TILING_OPTIMAL,					// VkImageTiling			tiling;
771		VK_IMAGE_USAGE_STORAGE_BIT |
772		VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
773		VK_SHARING_MODE_EXCLUSIVE,					// VkSharingMode			sharingMode;
774		0u,											// deUint32					queueFamilyIndexCount;
775		DE_NULL,									// const deUint32*			pQueueFamilyIndices;
776		VK_IMAGE_LAYOUT_UNDEFINED,					// VkImageLayout			initialLayout;
777	};
778
779	m_intermResultsImage = de::MovePtr<Image>(new Image(deviceInterface, device, allocator, imageParams, MemoryRequirement::Any));
780
781	const VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, getNumLayers(m_imageType, m_imageSize));
782
783	m_intermResultsImageView = makeImageView(deviceInterface, device, m_intermResultsImage->get(), mapImageViewType(m_imageType), mapTextureFormat(m_format), subresourceRange);
784}
785
786void BinaryAtomicIntermValuesInstance::prepareDescriptors (void)
787{
788	const VkDevice			device			= m_context.getDevice();
789	const DeviceInterface&	deviceInterface = m_context.getDeviceInterface();
790
791	m_descriptorSetLayout =
792		DescriptorSetLayoutBuilder()
793		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
794		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
795		.build(deviceInterface, device);
796
797	m_descriptorPool =
798		DescriptorPoolBuilder()
799		.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2u)
800		.build(deviceInterface, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
801
802	m_descriptorSet = makeDescriptorSet(deviceInterface, device, *m_descriptorPool, *m_descriptorSetLayout);
803
804	const VkDescriptorImageInfo	descResultImageInfo			= makeDescriptorImageInfo(DE_NULL, *m_resultImageView, VK_IMAGE_LAYOUT_GENERAL);
805	const VkDescriptorImageInfo	descIntermResultsImageInfo	= makeDescriptorImageInfo(DE_NULL, *m_intermResultsImageView, VK_IMAGE_LAYOUT_GENERAL);
806
807	DescriptorSetUpdateBuilder()
808		.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descResultImageInfo)
809		.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descIntermResultsImageInfo)
810		.update(deviceInterface, device);
811}
812
813void BinaryAtomicIntermValuesInstance::commandsBeforeCompute (const VkCommandBuffer cmdBuffer) const
814{
815	const DeviceInterface&			deviceInterface		= m_context.getDeviceInterface();
816	const VkImageSubresourceRange	subresourceRange	= makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, getNumLayers(m_imageType, m_imageSize));
817
818	const VkImageMemoryBarrier	imagePreDispatchBarrier =
819		makeImageMemoryBarrier(	0u,
820								VK_ACCESS_SHADER_WRITE_BIT,
821								VK_IMAGE_LAYOUT_UNDEFINED,
822								VK_IMAGE_LAYOUT_GENERAL,
823								m_intermResultsImage->get(),
824								subresourceRange);
825
826	deviceInterface.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, DE_FALSE, 0u, DE_NULL, 0u, DE_NULL, 1u, &imagePreDispatchBarrier);
827}
828
829void BinaryAtomicIntermValuesInstance::commandsAfterCompute (const VkCommandBuffer cmdBuffer) const
830{
831	const DeviceInterface&			deviceInterface		= m_context.getDeviceInterface();
832	const VkImageSubresourceRange	subresourceRange	= makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, getNumLayers(m_imageType, m_imageSize));
833
834	const VkImageMemoryBarrier	imagePostDispatchBarrier =
835		makeImageMemoryBarrier(	VK_ACCESS_SHADER_WRITE_BIT,
836								VK_ACCESS_TRANSFER_READ_BIT,
837								VK_IMAGE_LAYOUT_GENERAL,
838								VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
839								m_intermResultsImage->get(),
840								subresourceRange);
841
842	deviceInterface.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, DE_FALSE, 0u, DE_NULL, 0u, DE_NULL, 1u, &imagePostDispatchBarrier);
843
844	const UVec3					layerSize				= getLayerSize(m_imageType, m_imageSize);
845	const UVec3					extendedLayerSize		= UVec3(NUM_INVOCATIONS_PER_PIXEL * layerSize.x(), layerSize.y(), layerSize.z());
846	const VkBufferImageCopy		bufferImageCopyParams	= makeBufferImageCopy(makeExtent3D(extendedLayerSize), getNumLayers(m_imageType, m_imageSize));
847
848	deviceInterface.cmdCopyImageToBuffer(cmdBuffer, m_intermResultsImage->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_outputBuffer->get(), 1u, &bufferImageCopyParams);
849}
850
851bool BinaryAtomicIntermValuesInstance::verifyResult (Allocation&	outputBufferAllocation) const
852{
853	const UVec3	gridSize		 = getShaderGridSize(m_imageType, m_imageSize);
854	const IVec3 extendedGridSize = IVec3(NUM_INVOCATIONS_PER_PIXEL*gridSize.x(), gridSize.y(), gridSize.z());
855
856	tcu::ConstPixelBufferAccess resultBuffer(m_format, extendedGridSize.x(), extendedGridSize.y(), extendedGridSize.z(), outputBufferAllocation.getHostPtr());
857
858	for (deInt32 z = 0; z < resultBuffer.getDepth(); z++)
859	for (deInt32 y = 0; y < resultBuffer.getHeight(); y++)
860	for (deUint32 x = 0; x < gridSize.x(); x++)
861	{
862		deInt32 resultValues[NUM_INVOCATIONS_PER_PIXEL];
863		deInt32 atomicArgs[NUM_INVOCATIONS_PER_PIXEL];
864		bool	argsUsed[NUM_INVOCATIONS_PER_PIXEL];
865
866		for (deInt32 i = 0; i < static_cast<deInt32>(NUM_INVOCATIONS_PER_PIXEL); i++)
867		{
868			IVec3 gid(x + i*gridSize.x(), y, z);
869
870			resultValues[i] = resultBuffer.getPixelInt(gid.x(), gid.y(), gid.z()).x();
871			atomicArgs[i]	= getAtomicFuncArgument(m_operation, gid, extendedGridSize);
872			argsUsed[i]		= false;
873		}
874
875		// Verify that the return values form a valid sequence.
876		if (!verifyRecursive(0, getOperationInitialValue(m_operation), argsUsed, atomicArgs, resultValues))
877		{
878			return false;
879		}
880	}
881
882	return true;
883}
884
885bool BinaryAtomicIntermValuesInstance::verifyRecursive (const deInt32	index,
886														const deInt32	valueSoFar,
887														bool			argsUsed[NUM_INVOCATIONS_PER_PIXEL],
888														const deInt32	atomicArgs[NUM_INVOCATIONS_PER_PIXEL],
889														const deInt32	resultValues[NUM_INVOCATIONS_PER_PIXEL]) const
890{
891	if (index >= static_cast<deInt32>(NUM_INVOCATIONS_PER_PIXEL))
892		return true;
893
894	for (deInt32 i = 0; i < static_cast<deInt32>(NUM_INVOCATIONS_PER_PIXEL); i++)
895	{
896		if (!argsUsed[i] && resultValues[i] == valueSoFar)
897		{
898			argsUsed[i] = true;
899
900			if (verifyRecursive(index + 1, computeBinaryAtomicOperationResult(m_operation, valueSoFar, atomicArgs[i]), argsUsed, atomicArgs, resultValues))
901			{
902				return true;
903			}
904
905			argsUsed[i] = false;
906		}
907	}
908
909	return false;
910}
911
912TestInstance* BinaryAtomicIntermValuesCase::createInstance (Context& context) const
913{
914	return new BinaryAtomicIntermValuesInstance(context, m_name, m_imageType, m_imageSize, m_format, m_operation);
915}
916
917} // anonymous ns
918
919tcu::TestCaseGroup* createImageAtomicOperationTests (tcu::TestContext& testCtx)
920{
921	de::MovePtr<tcu::TestCaseGroup> imageAtomicOperationsTests(new tcu::TestCaseGroup(testCtx, "atomic_operations", "Atomic image operations cases"));
922
923	struct ImageParams
924	{
925		ImageParams(const ImageType imageType, const tcu::UVec3& imageSize)
926			: m_imageType	(imageType)
927			, m_imageSize	(imageSize)
928		{
929		}
930		const ImageType		m_imageType;
931		const tcu::UVec3	m_imageSize;
932	};
933
934	static const ImageParams imageParamsArray[] =
935	{
936		ImageParams(IMAGE_TYPE_1D,			tcu::UVec3(64u, 1u, 1u)),
937		ImageParams(IMAGE_TYPE_1D_ARRAY,	tcu::UVec3(64u, 1u, 8u)),
938		ImageParams(IMAGE_TYPE_2D,			tcu::UVec3(64u, 64u, 1u)),
939		ImageParams(IMAGE_TYPE_2D_ARRAY,	tcu::UVec3(64u, 64u, 8u)),
940		ImageParams(IMAGE_TYPE_3D,			tcu::UVec3(64u, 64u, 8u)),
941		ImageParams(IMAGE_TYPE_CUBE,		tcu::UVec3(64u, 64u, 1u)),
942		ImageParams(IMAGE_TYPE_CUBE_ARRAY,	tcu::UVec3(64u, 64u, 2u))
943	};
944
945	static const tcu::TextureFormat formats[] =
946	{
947		tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT32),
948		tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::SIGNED_INT32)
949	};
950
951	for (deUint32 operationI = 0; operationI < ATOMIC_OPERATION_LAST; operationI++)
952	{
953		const AtomicOperation operation = (AtomicOperation)operationI;
954
955		de::MovePtr<tcu::TestCaseGroup> operationGroup(new tcu::TestCaseGroup(testCtx, getAtomicOperationCaseName(operation).c_str(), ""));
956
957		for (deUint32 imageTypeNdx = 0; imageTypeNdx < DE_LENGTH_OF_ARRAY(imageParamsArray); imageTypeNdx++)
958		{
959			const ImageType	 imageType = imageParamsArray[imageTypeNdx].m_imageType;
960			const tcu::UVec3 imageSize = imageParamsArray[imageTypeNdx].m_imageSize;
961
962			for (deUint32 formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
963			{
964				const TextureFormat&	format		= formats[formatNdx];
965				const std::string		formatName	= getShaderImageFormatQualifier(format);
966
967				//!< Atomic case checks the end result of the operations, and not the intermediate return values
968				const string caseEndResult = getImageTypeName(imageType) + "_" + formatName + "_end_result";
969				operationGroup->addChild(new BinaryAtomicEndResultCase(testCtx, caseEndResult, "", imageType, imageSize, format, operation, glu::GLSL_VERSION_440));
970
971				//!< Atomic case checks the return values of the atomic function and not the end result.
972				const string caseIntermValues = getImageTypeName(imageType) + "_" + formatName + "_intermediate_values";
973				operationGroup->addChild(new BinaryAtomicIntermValuesCase(testCtx, caseIntermValues, "", imageType, imageSize, format, operation, glu::GLSL_VERSION_440));
974			}
975		}
976
977		imageAtomicOperationsTests->addChild(operationGroup.release());
978	}
979
980	return imageAtomicOperationsTests.release();
981}
982
983} // image
984} // vkt
985