• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2015 The Khronos Group Inc.
6  * Copyright (c) 2017 Google Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Atomic operations (OpAtomic*) tests.
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktAtomicOperationTests.hpp"
26 #include "vktShaderExecutor.hpp"
27 
28 #include "vkRefUtil.hpp"
29 #include "vkMemUtil.hpp"
30 #include "vkQueryUtil.hpp"
31 #include "vktTestGroupUtil.hpp"
32 
33 #include "tcuTestLog.hpp"
34 #include "tcuStringTemplate.hpp"
35 #include "tcuResultCollector.hpp"
36 
37 #include "deStringUtil.hpp"
38 #include "deSharedPtr.hpp"
39 #include "deRandom.hpp"
40 #include "deArrayUtil.hpp"
41 
42 #include <string>
43 
44 namespace vkt
45 {
46 namespace shaderexecutor
47 {
48 
49 namespace
50 {
51 
52 using de::UniquePtr;
53 using de::MovePtr;
54 using std::vector;
55 
56 using namespace vk;
57 
58 // Buffer helper
59 class Buffer
60 {
61 public:
62 						Buffer				(Context& context, VkBufferUsageFlags usage, size_t size);
63 
getBuffer(void) const64 	VkBuffer			getBuffer			(void) const { return *m_buffer;					}
getHostPtr(void) const65 	void*				getHostPtr			(void) const { return m_allocation->getHostPtr();	}
66 	void				flush				(void);
67 	void				invalidate			(void);
68 
69 private:
70 	const DeviceInterface&		m_vkd;
71 	const VkDevice				m_device;
72 	const Unique<VkBuffer>		m_buffer;
73 	const UniquePtr<Allocation>	m_allocation;
74 };
75 
76 typedef de::SharedPtr<Buffer> BufferSp;
77 
createBuffer(const DeviceInterface & vkd,VkDevice device,VkDeviceSize size,VkBufferUsageFlags usageFlags)78 Move<VkBuffer> createBuffer (const DeviceInterface& vkd, VkDevice device, VkDeviceSize size, VkBufferUsageFlags usageFlags)
79 {
80 	const VkBufferCreateInfo createInfo	=
81 	{
82 		VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
83 		DE_NULL,
84 		(VkBufferCreateFlags)0,
85 		size,
86 		usageFlags,
87 		VK_SHARING_MODE_EXCLUSIVE,
88 		0u,
89 		DE_NULL
90 	};
91 	return createBuffer(vkd, device, &createInfo);
92 }
93 
allocateAndBindMemory(const DeviceInterface & vkd,VkDevice device,Allocator & allocator,VkBuffer buffer)94 MovePtr<Allocation> allocateAndBindMemory (const DeviceInterface& vkd, VkDevice device, Allocator& allocator, VkBuffer buffer)
95 {
96 	MovePtr<Allocation>	alloc(allocator.allocate(getBufferMemoryRequirements(vkd, device, buffer), MemoryRequirement::HostVisible));
97 
98 	VK_CHECK(vkd.bindBufferMemory(device, buffer, alloc->getMemory(), alloc->getOffset()));
99 
100 	return alloc;
101 }
102 
Buffer(Context & context,VkBufferUsageFlags usage,size_t size)103 Buffer::Buffer (Context& context, VkBufferUsageFlags usage, size_t size)
104 	: m_vkd			(context.getDeviceInterface())
105 	, m_device		(context.getDevice())
106 	, m_buffer		(createBuffer			(context.getDeviceInterface(),
107 											 context.getDevice(),
108 											 (VkDeviceSize)size,
109 											 usage))
110 	, m_allocation	(allocateAndBindMemory	(context.getDeviceInterface(),
111 											 context.getDevice(),
112 											 context.getDefaultAllocator(),
113 											 *m_buffer))
114 {
115 }
116 
flush(void)117 void Buffer::flush (void)
118 {
119 	flushMappedMemoryRange(m_vkd, m_device, m_allocation->getMemory(), m_allocation->getOffset(), VK_WHOLE_SIZE);
120 }
121 
invalidate(void)122 void Buffer::invalidate (void)
123 {
124 	invalidateMappedMemoryRange(m_vkd, m_device, m_allocation->getMemory(), m_allocation->getOffset(), VK_WHOLE_SIZE);
125 }
126 
127 // Tests
128 
129 enum AtomicOperation
130 {
131 	ATOMIC_OP_EXCHANGE = 0,
132 	ATOMIC_OP_COMP_SWAP,
133 	ATOMIC_OP_ADD,
134 	ATOMIC_OP_MIN,
135 	ATOMIC_OP_MAX,
136 	ATOMIC_OP_AND,
137 	ATOMIC_OP_OR,
138 	ATOMIC_OP_XOR,
139 
140 	ATOMIC_OP_LAST
141 };
142 
atomicOp2Str(AtomicOperation op)143 std::string atomicOp2Str (AtomicOperation op)
144 {
145 	static const char* const s_names[] =
146 	{
147 		"atomicExchange",
148 		"atomicCompSwap",
149 		"atomicAdd",
150 		"atomicMin",
151 		"atomicMax",
152 		"atomicAnd",
153 		"atomicOr",
154 		"atomicXor"
155 	};
156 	return de::getSizedArrayElement<ATOMIC_OP_LAST>(s_names, op);
157 }
158 
159 enum
160 {
161 	NUM_ELEMENTS = 32
162 };
163 
164 enum DataType
165 {
166 	DATA_TYPE_INT32 = 0,
167 	DATA_TYPE_UINT32,
168 	DATA_TYPE_INT64,
169 	DATA_TYPE_UINT64,
170 
171 	DATA_TYPE_LAST
172 };
173 
dataType2Str(DataType type)174 std::string dataType2Str(DataType type)
175 {
176 	static const char* const s_names[] =
177 	{
178 		"int",
179 		"uint",
180 		"int64_t",
181 		"uint64_t",
182 	};
183 	return de::getSizedArrayElement<DATA_TYPE_LAST>(s_names, type);
184 }
185 
186 class BufferInterface
187 {
188 public:
189 	virtual void setBuffer(void* ptr) = 0;
190 
191 	virtual size_t bufferSize() = 0;
192 
193 	virtual void fillWithTestData(de::Random &rnd) = 0;
194 
195 	virtual void checkResults(tcu::ResultCollector& resultCollector) = 0;
196 
~BufferInterface()197 	virtual ~BufferInterface() {};
198 };
199 
200 template<typename dataTypeT>
201 class TestBuffer : public BufferInterface
202 {
203 public:
204 
TestBuffer(AtomicOperation atomicOp)205 	TestBuffer(AtomicOperation	atomicOp)
206 		: m_atomicOp(atomicOp)
207 	{}
208 
209 	template<typename T>
210 	struct BufferData
211 	{
212 		// Use half the number of elements for inout to cause overlap between atomic operations.
213 		// Each inout element at index i will have two atomic operations using input from
214 		// indices i and i + NUM_ELEMENTS / 2.
215 		T			inout[NUM_ELEMENTS / 2];
216 		T			input[NUM_ELEMENTS];
217 		T			compare[NUM_ELEMENTS];
218 		T			output[NUM_ELEMENTS];
219 		deInt32		index;
220 	};
221 
setBuffer(void * ptr)222 	virtual void setBuffer(void* ptr)
223 	{
224 		m_ptr = static_cast<BufferData<dataTypeT>*>(ptr);
225 	}
226 
bufferSize()227 	virtual size_t bufferSize()
228 	{
229 		return sizeof(BufferData<dataTypeT>);
230 	}
231 
fillWithTestData(de::Random & rnd)232 	virtual void fillWithTestData(de::Random &rnd)
233 	{
234 		dataTypeT pattern;
235 		deMemset(&pattern, 0xcd, sizeof(dataTypeT));
236 
237 		for (int i = 0; i < NUM_ELEMENTS / 2; i++)
238 		{
239 			m_ptr->inout[i] = static_cast<dataTypeT>(rnd.getUint64());
240 			// The first half of compare elements match with every even index.
241 			// The second half matches with odd indices. This causes the
242 			// overlapping operations to only select one.
243 			m_ptr->compare[i] = m_ptr->inout[i] + (i % 2);
244 			m_ptr->compare[i + NUM_ELEMENTS / 2] = m_ptr->inout[i] + 1 - (i % 2);
245 		}
246 		for (int i = 0; i < NUM_ELEMENTS; i++)
247 		{
248 			m_ptr->input[i] = static_cast<dataTypeT>(rnd.getUint64());
249 			m_ptr->output[i] = pattern;
250 		}
251 		m_ptr->index = 0;
252 
253 		// Take a copy to be used when calculating expected values.
254 		m_original = *m_ptr;
255 	}
256 
checkResults(tcu::ResultCollector & resultCollector)257 	virtual void checkResults(tcu::ResultCollector&	resultCollector)
258 	{
259 		checkOperation(m_original, *m_ptr, resultCollector);
260 	}
261 
262 	template<typename T>
263 	struct Expected
264 	{
265 		T m_inout;
266 		T m_output[2];
267 
Expectedvkt::shaderexecutor::__anonffe0b4930111::TestBuffer::Expected268 		Expected (T inout, T output0, T output1)
269 		: m_inout(inout)
270 		{
271 			m_output[0] = output0;
272 			m_output[1] = output1;
273 		}
274 
comparevkt::shaderexecutor::__anonffe0b4930111::TestBuffer::Expected275 		bool compare (T inout, T output0, T output1)
276 		{
277 			return (deMemCmp((const void*)&m_inout, (const void*)&inout, sizeof(inout)) == 0
278 					&& deMemCmp((const void*)&m_output[0], (const void*)&output0, sizeof(output0)) == 0
279 					&& deMemCmp((const void*)&m_output[1], (const void*)&output1, sizeof(output1)) == 0);
280 		}
281 	};
282 
283 	void checkOperation	(const BufferData<dataTypeT>&	original,
284 						 const BufferData<dataTypeT>&	result,
285 						 tcu::ResultCollector&			resultCollector);
286 
287 	const AtomicOperation	m_atomicOp;
288 
289 	BufferData<dataTypeT>* m_ptr;
290 	BufferData<dataTypeT>  m_original;
291 
292 };
293 
createTestBuffer(DataType type,AtomicOperation atomicOp)294 static BufferInterface* createTestBuffer(DataType type, AtomicOperation atomicOp)
295 {
296 	switch (type)
297 	{
298 	case DATA_TYPE_INT32:
299 		return new TestBuffer<deInt32>(atomicOp);
300 	case DATA_TYPE_UINT32:
301 		return new TestBuffer<deUint32>(atomicOp);
302 	case DATA_TYPE_INT64:
303 		return new TestBuffer<deInt64>(atomicOp);
304 	case DATA_TYPE_UINT64:
305 		return new TestBuffer<deUint64>(atomicOp);
306 	default:
307 		DE_ASSERT(false);
308 		return DE_NULL;
309 	}
310 }
311 
312 // Use template to handle both signed and unsigned cases. SPIR-V should
313 // have separate operations for both.
314 template<typename T>
checkOperation(const BufferData<T> & original,const BufferData<T> & result,tcu::ResultCollector & resultCollector)315 void TestBuffer<T>::checkOperation (const BufferData<T>&	original,
316 									const BufferData<T>&	result,
317 									tcu::ResultCollector&	resultCollector)
318 {
319 	// originalInout = original inout
320 	// input0 = input at index i
321 	// iinput1 = input at index i + NUM_ELEMENTS / 2
322 	//
323 	// atomic operation will return the memory contents before
324 	// the operation and this is stored as output. Two operations
325 	// are executed for each InOut value (using input0 and input1).
326 	//
327 	// Since there is an overlap of two operations per each
328 	// InOut element, the outcome of the resulting InOut and
329 	// the outputs of the operations have two result candidates
330 	// depending on the execution order. Verification passes
331 	// if the results match one of these options.
332 
333 	for (int elementNdx = 0; elementNdx < NUM_ELEMENTS / 2; elementNdx++)
334 	{
335 		// Needed when reinterpeting the data as signed values.
336 		const T originalInout	= *reinterpret_cast<const T*>(&original.inout[elementNdx]);
337 		const T input0			= *reinterpret_cast<const T*>(&original.input[elementNdx]);
338 		const T input1			= *reinterpret_cast<const T*>(&original.input[elementNdx + NUM_ELEMENTS / 2]);
339 
340 		// Expected results are collected to this vector.
341 		vector<Expected<T> > exp;
342 
343 		switch (m_atomicOp)
344 		{
345 			case ATOMIC_OP_ADD:
346 			{
347 				exp.push_back(Expected<T>(originalInout + input0 + input1, originalInout, originalInout + input0));
348 				exp.push_back(Expected<T>(originalInout + input0 + input1, originalInout + input1, originalInout));
349 			}
350 			break;
351 
352 			case ATOMIC_OP_AND:
353 			{
354 				exp.push_back(Expected<T>(originalInout & input0 & input1, originalInout, originalInout & input0));
355 				exp.push_back(Expected<T>(originalInout & input0 & input1, originalInout & input1, originalInout));
356 			}
357 			break;
358 
359 			case ATOMIC_OP_OR:
360 			{
361 				exp.push_back(Expected<T>(originalInout | input0 | input1, originalInout, originalInout | input0));
362 				exp.push_back(Expected<T>(originalInout | input0 | input1, originalInout | input1, originalInout));
363 			}
364 			break;
365 
366 			case ATOMIC_OP_XOR:
367 			{
368 				exp.push_back(Expected<T>(originalInout ^ input0 ^ input1, originalInout, originalInout ^ input0));
369 				exp.push_back(Expected<T>(originalInout ^ input0 ^ input1, originalInout ^ input1, originalInout));
370 			}
371 			break;
372 
373 			case ATOMIC_OP_MIN:
374 			{
375 				exp.push_back(Expected<T>(de::min(de::min(originalInout, input0), input1), originalInout, de::min(originalInout, input0)));
376 				exp.push_back(Expected<T>(de::min(de::min(originalInout, input0), input1), de::min(originalInout, input1), originalInout));
377 			}
378 			break;
379 
380 			case ATOMIC_OP_MAX:
381 			{
382 				exp.push_back(Expected<T>(de::max(de::max(originalInout, input0), input1), originalInout, de::max(originalInout, input0)));
383 				exp.push_back(Expected<T>(de::max(de::max(originalInout, input0), input1), de::max(originalInout, input1), originalInout));
384 			}
385 			break;
386 
387 			case ATOMIC_OP_EXCHANGE:
388 			{
389 				exp.push_back(Expected<T>(input1, originalInout, input0));
390 				exp.push_back(Expected<T>(input0, input1, originalInout));
391 			}
392 			break;
393 
394 			case ATOMIC_OP_COMP_SWAP:
395 			{
396 				if (elementNdx % 2 == 0)
397 				{
398 					exp.push_back(Expected<T>(input0, originalInout, input0));
399 					exp.push_back(Expected<T>(input0, originalInout, originalInout));
400 				}
401 				else
402 				{
403 					exp.push_back(Expected<T>(input1, input1, originalInout));
404 					exp.push_back(Expected<T>(input1, originalInout, originalInout));
405 				}
406 			}
407 			break;
408 
409 
410 			default:
411 				DE_FATAL("Unexpected atomic operation.");
412 				break;
413 		};
414 
415 		const T resIo			= result.inout[elementNdx];
416 		const T resOutput0	= result.output[elementNdx];
417 		const T resOutput1	= result.output[elementNdx + NUM_ELEMENTS / 2];
418 
419 
420 		if (!exp[0].compare(resIo, resOutput0, resOutput1) && !exp[1].compare(resIo, resOutput0, resOutput1))
421 		{
422 			std::ostringstream errorMessage;
423 			errorMessage	<< "ERROR: Result value check failed at index " << elementNdx
424 							<< ". Expected one of the two outcomes: InOut = " << tcu::toHex(exp[0].m_inout)
425 							<< ", Output0 = " << tcu::toHex(exp[0].m_output[0]) << ", Output1 = "
426 							<< tcu::toHex(exp[0].m_output[1]) << ", or InOut = " << tcu::toHex(exp[1].m_inout)
427 							<< ", Output0 = " << tcu::toHex(exp[1].m_output[0]) << ", Output1 = "
428 							<< tcu::toHex(exp[1].m_output[1]) << ". Got: InOut = " << tcu::toHex(resIo)
429 							<< ", Output0 = " << tcu::toHex(resOutput0) << ", Output1 = "
430 							<< tcu::toHex(resOutput1) << ". Using Input0 = " << tcu::toHex(original.input[elementNdx])
431 							<< " and Input1 = " << tcu::toHex(original.input[elementNdx + NUM_ELEMENTS / 2]) << ".";
432 
433 			resultCollector.fail(errorMessage.str());
434 		}
435 	}
436 }
437 
438 
439 class AtomicOperationCaseInstance : public TestInstance
440 {
441 public:
442 									AtomicOperationCaseInstance		(Context&			context,
443 																	 const ShaderSpec&	shaderSpec,
444 																	 glu::ShaderType	shaderType,
445 																	 DataType			dataType,
446 																	 AtomicOperation	atomicOp);
447 
448 	virtual tcu::TestStatus			iterate							(void);
449 
450 private:
451 	const ShaderSpec&				m_shaderSpec;
452 	glu::ShaderType					m_shaderType;
453 	const DataType					m_dataType;
454 	AtomicOperation					m_atomicOp;
455 
456 };
457 
AtomicOperationCaseInstance(Context & context,const ShaderSpec & shaderSpec,glu::ShaderType shaderType,DataType dataType,AtomicOperation atomicOp)458 AtomicOperationCaseInstance::AtomicOperationCaseInstance (Context&				context,
459 														  const ShaderSpec&		shaderSpec,
460 														  glu::ShaderType		shaderType,
461 														  DataType				dataType,
462 														  AtomicOperation		atomicOp)
463 	: TestInstance	(context)
464 	, m_shaderSpec	(shaderSpec)
465 	, m_shaderType	(shaderType)
466 	, m_dataType	(dataType)
467 	, m_atomicOp	(atomicOp)
468 {
469 	if ((m_dataType == DATA_TYPE_INT64) || (m_dataType == DATA_TYPE_UINT64))
470 	{
471 		if (!isDeviceExtensionSupported(context.getUsedApiVersion(), context.getDeviceExtensions(), "VK_KHR_shader_atomic_int64"))
472 			TCU_THROW(NotSupportedError, "Missing extension: VK_KHR_shader_atomic_int64");
473 
474 		VkPhysicalDeviceShaderAtomicInt64FeaturesKHR shaderAtomicInt64Features;
475 		deMemset(&shaderAtomicInt64Features, 0x0, sizeof(VkPhysicalDeviceShaderAtomicInt64FeaturesKHR));
476 		shaderAtomicInt64Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR;
477 		shaderAtomicInt64Features.pNext = DE_NULL;
478 
479 		VkPhysicalDeviceFeatures2 features;
480 		deMemset(&features, 0x0, sizeof(VkPhysicalDeviceFeatures2));
481 		features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
482 		features.pNext = &shaderAtomicInt64Features;
483 
484 		context.getInstanceInterface().getPhysicalDeviceFeatures2(context.getPhysicalDevice(), &features);
485 
486 		if (shaderAtomicInt64Features.shaderBufferInt64Atomics == VK_FALSE)
487 		{
488 			TCU_THROW(NotSupportedError, "VkShaderAtomicInt64: 64-bit unsigned and signed integer atomic operations not supported");
489 		}
490 	}
491 }
492 
iterate(void)493 tcu::TestStatus AtomicOperationCaseInstance::iterate(void)
494 {
495 	//Check stores and atomic operation support.
496 	switch (m_shaderType)
497 	{
498 	case glu::SHADERTYPE_VERTEX:
499 	case glu::SHADERTYPE_TESSELLATION_CONTROL:
500 	case glu::SHADERTYPE_TESSELLATION_EVALUATION:
501 	case glu::SHADERTYPE_GEOMETRY:
502 		if (!m_context.getDeviceFeatures().vertexPipelineStoresAndAtomics)
503 			TCU_THROW(NotSupportedError, "Stores and atomic operations are not supported in Vertex, Tessellation, and Geometry shader.");
504 		break;
505 	case glu::SHADERTYPE_FRAGMENT:
506 		if (!m_context.getDeviceFeatures().fragmentStoresAndAtomics)
507 			TCU_THROW(NotSupportedError, "Stores and atomic operations are not supported in fragment shader.");
508 		break;
509 	case glu::SHADERTYPE_COMPUTE:
510 		break;
511 	default:
512 		DE_FATAL("Unsupported shader type");
513 	}
514 
515 	de::UniquePtr<BufferInterface>	testBuffer	(createTestBuffer(m_dataType, m_atomicOp));
516 	tcu::TestLog&					log			= m_context.getTestContext().getLog();
517 	const DeviceInterface&			vkd			= m_context.getDeviceInterface();
518 	const VkDevice					device		= m_context.getDevice();
519 	de::Random						rnd			(0x62a15e34);
520 	Buffer							buffer		(m_context, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, testBuffer->bufferSize());
521 
522 	testBuffer->setBuffer(buffer.getHostPtr());
523 	testBuffer->fillWithTestData(rnd);
524 
525 	buffer.flush();
526 
527 	Move<VkDescriptorSetLayout>	extraResourcesLayout;
528 	Move<VkDescriptorPool>		extraResourcesSetPool;
529 	Move<VkDescriptorSet>		extraResourcesSet;
530 
531 	const VkDescriptorSetLayoutBinding bindings[] =
532 	{
533 		{ 0u, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, DE_NULL }
534 	};
535 
536 	const VkDescriptorSetLayoutCreateInfo	layoutInfo	=
537 	{
538 		VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
539 		DE_NULL,
540 		(VkDescriptorSetLayoutCreateFlags)0u,
541 		DE_LENGTH_OF_ARRAY(bindings),
542 		bindings
543 	};
544 
545 	extraResourcesLayout = createDescriptorSetLayout(vkd, device, &layoutInfo);
546 
547 	const VkDescriptorPoolSize poolSizes[] =
548 	{
549 		{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u }
550 	};
551 	const VkDescriptorPoolCreateInfo poolInfo =
552 	{
553 		VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
554 		DE_NULL,
555 		(VkDescriptorPoolCreateFlags)VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
556 		1u,		// maxSets
557 		DE_LENGTH_OF_ARRAY(poolSizes),
558 		poolSizes
559 	};
560 
561 	extraResourcesSetPool = createDescriptorPool(vkd, device, &poolInfo);
562 
563 	const VkDescriptorSetAllocateInfo allocInfo =
564 	{
565 		VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
566 		DE_NULL,
567 		*extraResourcesSetPool,
568 		1u,
569 		&extraResourcesLayout.get()
570 	};
571 
572 	extraResourcesSet = allocateDescriptorSet(vkd, device, &allocInfo);
573 
574 	VkDescriptorBufferInfo bufferInfo;
575 	bufferInfo.buffer	= buffer.getBuffer();
576 	bufferInfo.offset	= 0u;
577 	bufferInfo.range	= VK_WHOLE_SIZE;
578 
579 	const VkWriteDescriptorSet descriptorWrite =
580 	{
581 		VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
582 		DE_NULL,
583 		*extraResourcesSet,
584 		0u,		// dstBinding
585 		0u,		// dstArrayElement
586 		1u,
587 		VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
588 		(const VkDescriptorImageInfo*)DE_NULL,
589 		&bufferInfo,
590 		(const VkBufferView*)DE_NULL
591 	};
592 
593 
594 	vkd.updateDescriptorSets(device, 1u, &descriptorWrite, 0u, DE_NULL);
595 
596 	// Storage for output varying data.
597 	std::vector<deUint32>	outputs		(NUM_ELEMENTS);
598 	std::vector<void*>		outputPtr	(NUM_ELEMENTS);
599 
600 	for (size_t i = 0; i < NUM_ELEMENTS; i++)
601 	{
602 		outputs[i] = 0xcdcdcdcd;
603 		outputPtr[i] = &outputs[i];
604 	}
605 
606 	UniquePtr<ShaderExecutor> executor(createExecutor(m_context, m_shaderType, m_shaderSpec, *extraResourcesLayout));
607 	executor->execute(NUM_ELEMENTS, DE_NULL, &outputPtr[0], *extraResourcesSet);
608 	buffer.invalidate();
609 
610 	tcu::ResultCollector resultCollector(log);
611 
612 	// Check the results of the atomic operation
613 	testBuffer->checkResults(resultCollector);
614 
615 	return tcu::TestStatus(resultCollector.getResult(), resultCollector.getMessage());
616 }
617 
618 class AtomicOperationCase : public TestCase
619 {
620 public:
621 							AtomicOperationCase		(tcu::TestContext&		testCtx,
622 													 const char*			name,
623 													 const char*			description,
624 													 glu::ShaderType		type,
625 													 DataType				dataType,
626 													 AtomicOperation		atomicOp);
627 	virtual					~AtomicOperationCase	(void);
628 
629 	virtual TestInstance*	createInstance			(Context& ctx) const;
initPrograms(vk::SourceCollections & programCollection) const630 	virtual void			initPrograms			(vk::SourceCollections& programCollection) const
631 	{
632 		generateSources(m_shaderType, m_shaderSpec, programCollection);
633 	}
634 
635 private:
636 
637 	void					createShaderSpec();
638 	ShaderSpec				m_shaderSpec;
639 	const glu::ShaderType	m_shaderType;
640 	const DataType			m_dataType;
641 	const AtomicOperation	m_atomicOp;
642 };
643 
AtomicOperationCase(tcu::TestContext & testCtx,const char * name,const char * description,glu::ShaderType shaderType,DataType dataType,AtomicOperation atomicOp)644 AtomicOperationCase::AtomicOperationCase (tcu::TestContext&	testCtx,
645 										  const char*		name,
646 										  const char*		description,
647 										  glu::ShaderType	shaderType,
648 										  DataType			dataType,
649 										  AtomicOperation	atomicOp)
650 	: TestCase			(testCtx, name, description)
651 	, m_shaderType		(shaderType)
652 	, m_dataType		(dataType)
653 	, m_atomicOp		(atomicOp)
654 {
655 	createShaderSpec();
656 	init();
657 }
658 
~AtomicOperationCase(void)659 AtomicOperationCase::~AtomicOperationCase (void)
660 {
661 }
662 
createInstance(Context & ctx) const663 TestInstance* AtomicOperationCase::createInstance (Context& ctx) const
664 {
665 	return new AtomicOperationCaseInstance(ctx, m_shaderSpec, m_shaderType, m_dataType, m_atomicOp);
666 }
667 
createShaderSpec(void)668 void AtomicOperationCase::createShaderSpec (void)
669 {
670 	const tcu::StringTemplate shaderTemplateGlobal(
671 		"${EXTENSIONS}\n"
672 		"layout (set = ${SETIDX}, binding = 0) buffer AtomicBuffer\n"
673 		"{\n"
674 		"    ${DATATYPE} inoutValues[${N}/2];\n"
675 		"    ${DATATYPE} inputValues[${N}];\n"
676 		"    ${DATATYPE} compareValues[${N}];\n"
677 		"    ${DATATYPE} outputValues[${N}];\n"
678 		"    int index;\n"
679 		"} buf;\n");
680 
681 	std::map<std::string, std::string> specializations;
682 	if ((m_dataType == DATA_TYPE_INT64) || (m_dataType == DATA_TYPE_UINT64))
683 	{
684 		specializations["EXTENSIONS"] = "#extension GL_ARB_gpu_shader_int64 : enable\n"
685 										"#extension GL_EXT_shader_atomic_int64 : enable\n";
686 	}
687 	else
688 	{
689 		specializations["EXTENSIONS"] = "";
690 	}
691 	specializations["DATATYPE"] = dataType2Str(m_dataType);
692 	specializations["ATOMICOP"] = atomicOp2Str(m_atomicOp);
693 	specializations["SETIDX"] = de::toString((int)EXTRA_RESOURCES_DESCRIPTOR_SET_INDEX);
694 	specializations["N"] = de::toString((int)NUM_ELEMENTS);
695 	specializations["COMPARE_ARG"] = m_atomicOp == ATOMIC_OP_COMP_SWAP ? "buf.compareValues[idx], " : "";
696 
697 	const tcu::StringTemplate shaderTemplateSrc(
698 		"int idx = atomicAdd(buf.index, 1);\n"
699 		"buf.outputValues[idx] = ${ATOMICOP}(buf.inoutValues[idx % (${N}/2)], ${COMPARE_ARG}buf.inputValues[idx]);\n");
700 
701 	m_shaderSpec.outputs.push_back(Symbol("outData", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
702 	m_shaderSpec.globalDeclarations = shaderTemplateGlobal.specialize(specializations);
703 	m_shaderSpec.source = shaderTemplateSrc.specialize(specializations);
704 	m_shaderSpec.glslVersion = glu::GLSL_VERSION_450;
705 }
706 
addAtomicOperationTests(tcu::TestCaseGroup * atomicOperationTestsGroup)707 void addAtomicOperationTests (tcu::TestCaseGroup* atomicOperationTestsGroup)
708 {
709 	tcu::TestContext& testCtx = atomicOperationTestsGroup->getTestContext();
710 
711 	static const struct
712 	{
713 		glu::ShaderType	type;
714 		const char*		name;
715 	} shaderTypes[] =
716 	{
717 		{ glu::SHADERTYPE_VERTEX,					"vertex"	},
718 		{ glu::SHADERTYPE_FRAGMENT,					"fragment"	},
719 		{ glu::SHADERTYPE_GEOMETRY,					"geometry"	},
720 		{ glu::SHADERTYPE_TESSELLATION_CONTROL,		"tess_ctrl"	},
721 		{ glu::SHADERTYPE_TESSELLATION_EVALUATION,	"tess_eval"	},
722 		{ glu::SHADERTYPE_COMPUTE,					"compute"	}
723 	};
724 
725 	static const struct
726 	{
727 		DataType		dataType;
728 		const char*		name;
729 		const char*		description;
730 	} dataSign[] =
731 	{
732 		{ DATA_TYPE_INT32,	"signed",			"Tests using signed data (int)"				},
733 		{ DATA_TYPE_UINT32,	"unsigned",			"Tests using unsigned data (uint)"			},
734 		{ DATA_TYPE_INT64,	"signed64bit",		"Tests using 64 bit signed data (int64)"	},
735 		{ DATA_TYPE_UINT64,	"unsigned64bit",	"Tests using 64 bit unsigned data (uint64)"	}
736 	};
737 
738 	static const struct
739 	{
740 		AtomicOperation		value;
741 		const char*			name;
742 	} atomicOp[] =
743 	{
744 		{ ATOMIC_OP_EXCHANGE,	"exchange"	},
745 		{ ATOMIC_OP_COMP_SWAP,	"comp_swap"	},
746 		{ ATOMIC_OP_ADD,		"add"		},
747 		{ ATOMIC_OP_MIN,		"min"		},
748 		{ ATOMIC_OP_MAX,		"max"		},
749 		{ ATOMIC_OP_AND,		"and"		},
750 		{ ATOMIC_OP_OR,			"or"		},
751 		{ ATOMIC_OP_XOR,		"xor"		}
752 	};
753 
754 	for (int opNdx = 0; opNdx < DE_LENGTH_OF_ARRAY(atomicOp); opNdx++)
755 	{
756 		for (int signNdx = 0; signNdx < DE_LENGTH_OF_ARRAY(dataSign); signNdx++)
757 		{
758 			for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(shaderTypes); shaderTypeNdx++)
759 			{
760 				const std::string description = std::string("Tests atomic operation ") + atomicOp2Str(atomicOp[opNdx].value) + std::string(".");
761 				std::string name = std::string(atomicOp[opNdx].name) + "_" + std::string(dataSign[signNdx].name) + "_" + std::string(shaderTypes[shaderTypeNdx].name);
762 				atomicOperationTestsGroup->addChild(new AtomicOperationCase(testCtx, name.c_str(), description.c_str(), shaderTypes[shaderTypeNdx].type, dataSign[signNdx].dataType, atomicOp[opNdx].value));
763 			}
764 		}
765 	}
766 }
767 
768 } // anonymous
769 
createAtomicOperationTests(tcu::TestContext & testCtx)770 tcu::TestCaseGroup* createAtomicOperationTests (tcu::TestContext& testCtx)
771 {
772 	return createTestGroup(testCtx, "atomic_operations", "Atomic Operation Tests", addAtomicOperationTests);
773 }
774 
775 } // shaderexecutor
776 } // vkt
777