Home | History | Annotate | Download | only in shaderexecutor
      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 
     64 	VkBuffer			getBuffer			(void) const { return *m_buffer;					}
     65 	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 
     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 
     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 
    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 
    117 void Buffer::flush (void)
    118 {
    119 	flushMappedMemoryRange(m_vkd, m_device, m_allocation->getMemory(), m_allocation->getOffset(), VK_WHOLE_SIZE);
    120 }
    121 
    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 
    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 
    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 
    197 	virtual ~BufferInterface() {};
    198 };
    199 
    200 template<typename dataTypeT>
    201 class TestBuffer : public BufferInterface
    202 {
    203 public:
    204 
    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 
    222 	virtual void setBuffer(void* ptr)
    223 	{
    224 		m_ptr = static_cast<BufferData<dataTypeT>*>(ptr);
    225 	}
    226 
    227 	virtual size_t bufferSize()
    228 	{
    229 		return sizeof(BufferData<dataTypeT>);
    230 	}
    231 
    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 
    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 
    268 		Expected (T inout, T output0, T output1)
    269 		: m_inout(inout)
    270 		{
    271 			m_output[0] = output0;
    272 			m_output[1] = output1;
    273 		}
    274 
    275 		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 
    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>
    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 
    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 
    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;
    630 	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 
    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 
    659 AtomicOperationCase::~AtomicOperationCase (void)
    660 {
    661 }
    662 
    663 TestInstance* AtomicOperationCase::createInstance (Context& ctx) const
    664 {
    665 	return new AtomicOperationCaseInstance(ctx, m_shaderSpec, m_shaderType, m_dataType, m_atomicOp);
    666 }
    667 
    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 
    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 
    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