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
21 * \brief Synchronization primitive tests with multi queue
22 *//*--------------------------------------------------------------------*/
23
24#include "vktSynchronizationOperationMultiQueueTests.hpp"
25#include "vktCustomInstancesDevices.hpp"
26#include "vkDefs.hpp"
27#include "vktTestCase.hpp"
28#include "vktTestCaseUtil.hpp"
29#include "vkRef.hpp"
30#include "vkRefUtil.hpp"
31#include "vkMemUtil.hpp"
32#include "vkBarrierUtil.hpp"
33#include "vkQueryUtil.hpp"
34#include "vkDeviceUtil.hpp"
35#include "vkTypeUtil.hpp"
36#include "vkPlatform.hpp"
37#include "vkCmdUtil.hpp"
38#include "vkSafetyCriticalUtil.hpp"
39#include "deRandom.hpp"
40#include "deUniquePtr.hpp"
41#include "deSharedPtr.hpp"
42#include "tcuTestLog.hpp"
43#include "vktSynchronizationUtil.hpp"
44#include "vktSynchronizationOperation.hpp"
45#include "vktSynchronizationOperationTestData.hpp"
46#include "vktSynchronizationOperationResources.hpp"
47#include "vktTestGroupUtil.hpp"
48#include "tcuCommandLine.hpp"
49
50#include <set>
51#include <unordered_map>
52
53namespace vkt
54{
55
56namespace synchronization
57{
58
59namespace
60{
61using namespace vk;
62using de::MovePtr;
63using de::SharedPtr;
64using de::UniquePtr;
65using de::SharedPtr;
66
67enum QueueType
68{
69	QUEUETYPE_WRITE,
70	QUEUETYPE_READ
71};
72
73struct QueuePair
74{
75	QueuePair	(const deUint32 familyWrite, const deUint32 familyRead, const VkQueue write, const VkQueue read)
76		: familyIndexWrite	(familyWrite)
77		, familyIndexRead	(familyRead)
78		, queueWrite		(write)
79		, queueRead			(read)
80	{}
81
82	deUint32	familyIndexWrite;
83	deUint32	familyIndexRead;
84	VkQueue		queueWrite;
85	VkQueue		queueRead;
86};
87
88struct Queue
89{
90	Queue	(const deUint32 familyOp, const VkQueue queueOp)
91		:	family	(familyOp)
92		,	queue	(queueOp)
93	{}
94
95	deUint32	family;
96	VkQueue		queue;
97};
98
99bool checkQueueFlags (VkQueueFlags availableFlags, const VkQueueFlags neededFlags)
100{
101	if ((availableFlags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) != 0)
102		availableFlags |= VK_QUEUE_TRANSFER_BIT;
103
104	return (availableFlags & neededFlags) != 0;
105}
106
107class MultiQueues
108{
109	struct QueueData
110	{
111		VkQueueFlags			flags;
112		std::vector<VkQueue>	queue;
113	};
114
115	MultiQueues	(Context& context, SynchronizationType type, bool timelineSemaphore)
116#ifdef CTS_USES_VULKANSC
117		: m_instance	(createCustomInstanceFromContext(context)),
118#else
119		:
120#endif // CTS_USES_VULKANSC
121		m_queueCount	(0)
122	{
123#ifdef CTS_USES_VULKANSC
124		const InstanceInterface&					instanceDriver			= m_instance.getDriver();
125		const VkPhysicalDevice						physicalDevice			= chooseDevice(instanceDriver, m_instance, context.getTestContext().getCommandLine());
126		const VkInstance							instance				= m_instance;
127#else
128		const InstanceInterface&					instanceDriver			= context.getInstanceInterface();
129		const VkPhysicalDevice						physicalDevice			= context.getPhysicalDevice();
130		const VkInstance							instance				= context.getInstance();
131#endif // CTS_USES_VULKANSC
132		const std::vector<VkQueueFamilyProperties>	queueFamilyProperties	= getPhysicalDeviceQueueFamilyProperties(instanceDriver, physicalDevice);
133
134		for (deUint32 queuePropertiesNdx = 0; queuePropertiesNdx < queueFamilyProperties.size(); ++queuePropertiesNdx)
135		{
136			addQueueIndex(queuePropertiesNdx,
137						  std::min(2u, queueFamilyProperties[queuePropertiesNdx].queueCount),
138						  queueFamilyProperties[queuePropertiesNdx].queueFlags);
139		}
140
141		std::vector<VkDeviceQueueCreateInfo>	queueInfos;
142		const float								queuePriorities[2] = { 1.0f, 1.0f };	//get max 2 queues from one family
143
144		for (std::map<deUint32, QueueData>::iterator it = m_queues.begin(); it!= m_queues.end(); ++it)
145		{
146			const VkDeviceQueueCreateInfo queueInfo	=
147			{
148				VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,		//VkStructureType			sType;
149				DE_NULL,										//const void*				pNext;
150				(VkDeviceQueueCreateFlags)0u,					//VkDeviceQueueCreateFlags	flags;
151				it->first,										//deUint32					queueFamilyIndex;
152				static_cast<deUint32>(it->second.queue.size()),	//deUint32					queueCount;
153				&queuePriorities[0]								//const float*				pQueuePriorities;
154			};
155			queueInfos.push_back(queueInfo);
156		}
157
158		{
159			VkPhysicalDeviceFeatures2					createPhysicalFeature		{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, DE_NULL, context.getDeviceFeatures() };
160			VkPhysicalDeviceTimelineSemaphoreFeatures	timelineSemaphoreFeatures	{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, DE_NULL, DE_TRUE };
161			VkPhysicalDeviceSynchronization2FeaturesKHR	synchronization2Features	{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR, DE_NULL, DE_TRUE };
162			void**										nextPtr						= &createPhysicalFeature.pNext;
163
164			std::vector<const char*> deviceExtensions;
165			if (timelineSemaphore)
166			{
167				if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_timeline_semaphore"))
168					deviceExtensions.push_back("VK_KHR_timeline_semaphore");
169				addToChainVulkanStructure(&nextPtr, timelineSemaphoreFeatures);
170			}
171			if (type == SynchronizationType::SYNCHRONIZATION2)
172			{
173				deviceExtensions.push_back("VK_KHR_synchronization2");
174				addToChainVulkanStructure(&nextPtr, synchronization2Features);
175			}
176
177			void* pNext												= &createPhysicalFeature;
178#ifdef CTS_USES_VULKANSC
179			VkDeviceObjectReservationCreateInfo memReservationInfo	= context.getTestContext().getCommandLine().isSubProcess() ? context.getResourceInterface()->getStatMax() : resetDeviceObjectReservationCreateInfo();
180			memReservationInfo.pNext								= pNext;
181			pNext													= &memReservationInfo;
182
183			VkPhysicalDeviceVulkanSC10Features sc10Features			= createDefaultSC10Features();
184			sc10Features.pNext										= pNext;
185			pNext													= &sc10Features;
186
187			VkPipelineCacheCreateInfo			pcCI;
188			std::vector<VkPipelinePoolSize>		poolSizes;
189			if (context.getTestContext().getCommandLine().isSubProcess())
190			{
191				if (context.getResourceInterface()->getCacheDataSize() > 0)
192				{
193					pcCI =
194					{
195						VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,		// VkStructureType				sType;
196						DE_NULL,											// const void*					pNext;
197						VK_PIPELINE_CACHE_CREATE_READ_ONLY_BIT |
198							VK_PIPELINE_CACHE_CREATE_USE_APPLICATION_STORAGE_BIT,	// VkPipelineCacheCreateFlags	flags;
199						context.getResourceInterface()->getCacheDataSize(),	// deUintptr					initialDataSize;
200						context.getResourceInterface()->getCacheData()		// const void*					pInitialData;
201					};
202					memReservationInfo.pipelineCacheCreateInfoCount		= 1;
203					memReservationInfo.pPipelineCacheCreateInfos		= &pcCI;
204				}
205
206				poolSizes							= context.getResourceInterface()->getPipelinePoolSizes();
207				if (!poolSizes.empty())
208				{
209					memReservationInfo.pipelinePoolSizeCount			= deUint32(poolSizes.size());
210					memReservationInfo.pPipelinePoolSizes				= poolSizes.data();
211				}
212			}
213#endif // CTS_USES_VULKANSC
214
215			const VkDeviceCreateInfo deviceInfo =
216			{
217				VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,							//VkStructureType					sType;
218				pNext,															//const void*						pNext;
219				0u,																//VkDeviceCreateFlags				flags;
220				static_cast<deUint32>(queueInfos.size()),						//deUint32							queueCreateInfoCount;
221				&queueInfos[0],													//const VkDeviceQueueCreateInfo*	pQueueCreateInfos;
222				0u,																//deUint32							enabledLayerCount;
223				DE_NULL,														//const char* const*				ppEnabledLayerNames;
224				static_cast<deUint32>(deviceExtensions.size()),					//deUint32							enabledExtensionCount;
225				deviceExtensions.empty() ? DE_NULL : &deviceExtensions[0],		//const char* const*				ppEnabledExtensionNames;
226				DE_NULL															//const VkPhysicalDeviceFeatures*	pEnabledFeatures;
227			};
228
229			m_logicalDevice	= createCustomDevice(context.getTestContext().getCommandLine().isValidationEnabled(), context.getPlatformInterface(), instance, instanceDriver, physicalDevice, &deviceInfo);
230#ifndef CTS_USES_VULKANSC
231			m_deviceDriver = de::MovePtr<DeviceDriver>(new DeviceDriver(context.getPlatformInterface(), instance, *m_logicalDevice, context.getUsedApiVersion()));
232#else
233			m_deviceDriver = de::MovePtr<DeviceDriverSC, DeinitDeviceDeleter>(new DeviceDriverSC(context.getPlatformInterface(), instance, *m_logicalDevice, context.getTestContext().getCommandLine(), context.getResourceInterface(), context.getDeviceVulkanSC10Properties(), context.getDeviceProperties(), context.getUsedApiVersion()), vk::DeinitDeviceDeleter(context.getResourceInterface().get(), *m_logicalDevice));
234#endif // CTS_USES_VULKANSC
235			m_allocator		= MovePtr<Allocator>(new SimpleAllocator(*m_deviceDriver, *m_logicalDevice, getPhysicalDeviceMemoryProperties(instanceDriver, physicalDevice)));
236
237			for (std::map<deUint32, QueueData>::iterator it = m_queues.begin(); it != m_queues.end(); ++it)
238			for (int queueNdx = 0; queueNdx < static_cast<int>(it->second.queue.size()); ++queueNdx)
239				m_deviceDriver->getDeviceQueue(*m_logicalDevice, it->first, queueNdx, &it->second.queue[queueNdx]);
240		}
241	}
242
243	void addQueueIndex (const deUint32 queueFamilyIndex, const deUint32 count, const VkQueueFlags flags)
244	{
245		QueueData dataToPush;
246		dataToPush.flags = flags;
247		dataToPush.queue.resize(count);
248		m_queues[queueFamilyIndex] = dataToPush;
249
250		m_queueCount++;
251	}
252
253public:
254	~MultiQueues()
255	{
256	}
257
258	std::vector<QueuePair> getQueuesPairs (const VkQueueFlags flagsWrite, const VkQueueFlags flagsRead) const
259	{
260		std::map<deUint32, QueueData>	queuesWrite;
261		std::map<deUint32, QueueData>	queuesRead;
262		std::vector<QueuePair>			queuesPairs;
263
264		for (std::map<deUint32, QueueData>::const_iterator it = m_queues.begin(); it != m_queues.end(); ++it)
265		{
266			const bool writeQueue	= checkQueueFlags(it->second.flags, flagsWrite);
267			const bool readQueue	= checkQueueFlags(it->second.flags, flagsRead);
268
269			if (!(writeQueue || readQueue))
270				continue;
271
272			if (writeQueue && readQueue)
273			{
274				queuesWrite[it->first]	= it->second;
275				queuesRead[it->first]	= it->second;
276			}
277			else if (writeQueue)
278				queuesWrite[it->first]	= it->second;
279			else if (readQueue)
280				queuesRead[it->first]	= it->second;
281		}
282
283		for (std::map<deUint32, QueueData>::iterator write = queuesWrite.begin(); write != queuesWrite.end(); ++write)
284		for (std::map<deUint32, QueueData>::iterator read  = queuesRead.begin();  read  != queuesRead.end();  ++read)
285		{
286			const int writeSize	= static_cast<int>(write->second.queue.size());
287			const int readSize	= static_cast<int>(read->second.queue.size());
288
289			for (int writeNdx = 0; writeNdx < writeSize; ++writeNdx)
290			for (int readNdx  = 0; readNdx  < readSize;  ++readNdx)
291			{
292				if (write->second.queue[writeNdx] != read->second.queue[readNdx])
293				{
294					queuesPairs.push_back(QueuePair(write->first, read->first, write->second.queue[writeNdx], read->second.queue[readNdx]));
295					writeNdx = readNdx = std::max(writeSize, readSize);	//exit from the loops
296				}
297			}
298		}
299
300		if (queuesPairs.empty())
301			TCU_THROW(NotSupportedError, "Queue not found");
302
303		return queuesPairs;
304	}
305
306	Queue getDefaultQueue(const VkQueueFlags flagsOp) const
307	{
308		for (std::map<deUint32, QueueData>::const_iterator it = m_queues.begin(); it!= m_queues.end(); ++it)
309		{
310			if (checkQueueFlags(it->second.flags, flagsOp))
311				return Queue(it->first, it->second.queue[0]);
312		}
313
314		TCU_THROW(NotSupportedError, "Queue not found");
315	}
316
317	Queue getQueue (const deUint32 familyIdx, const deUint32 queueIdx)
318	{
319		return Queue(familyIdx, m_queues[familyIdx].queue[queueIdx]);
320	}
321
322	VkQueueFlags getQueueFamilyFlags (const deUint32 familyIdx)
323	{
324		return m_queues[familyIdx].flags;
325	}
326
327	deUint32 queueFamilyCount (const deUint32 familyIdx)
328	{
329		return (deUint32) m_queues[familyIdx].queue.size();
330	}
331
332	deUint32 familyCount (void) const
333	{
334		return (deUint32) m_queues.size();
335	}
336
337	deUint32 totalQueueCount (void)
338	{
339		deUint32	count	= 0;
340
341		for (deUint32 familyIdx = 0; familyIdx < familyCount(); familyIdx++)
342		{
343			count	+= queueFamilyCount(familyIdx);
344		}
345
346		return count;
347	}
348
349	VkDevice getDevice (void) const
350	{
351		return *m_logicalDevice;
352	}
353
354	const DeviceInterface& getDeviceInterface (void) const
355	{
356		return *m_deviceDriver;
357	}
358
359	Allocator& getAllocator (void)
360	{
361		return *m_allocator;
362	}
363
364	static SharedPtr<MultiQueues> getInstance(Context& context, SynchronizationType type, bool timelineSemaphore)
365	{
366		deUint32 index = (deUint32)type << 1 | timelineSemaphore;
367		if (!m_multiQueues[index])
368			m_multiQueues[index] = SharedPtr<MultiQueues>(new MultiQueues(context, type, timelineSemaphore));
369
370		return m_multiQueues[index];
371	}
372	static void destroy()
373	{
374		m_multiQueues.clear();
375	}
376
377private:
378#ifdef CTS_USES_VULKANSC
379	CustomInstance					m_instance;
380#endif // CTS_USES_VULKANSC
381	Move<VkDevice>					m_logicalDevice;
382#ifndef CTS_USES_VULKANSC
383	de::MovePtr<vk::DeviceDriver>	m_deviceDriver;
384#else
385	de::MovePtr<DeviceDriverSC, DeinitDeviceDeleter>	m_deviceDriver;
386#endif // CTS_USES_VULKANSC
387	MovePtr<Allocator>				m_allocator;
388	std::map<deUint32, QueueData>	m_queues;
389	deUint32						m_queueCount;
390
391	static std::unordered_map<deUint32, SharedPtr<MultiQueues>>	m_multiQueues;
392};
393std::unordered_map<deUint32, SharedPtr<MultiQueues>>				MultiQueues::m_multiQueues;
394
395void createBarrierMultiQueue (SynchronizationWrapperPtr synchronizationWrapper,
396							  const VkCommandBuffer&	cmdBuffer,
397							  const SyncInfo&			writeSync,
398							  const SyncInfo&			readSync,
399							  const Resource&			resource,
400							  const deUint32			writeFamily,
401							  const deUint32			readFamily,
402							  const VkSharingMode		sharingMode,
403							  const bool				secondQueue = false)
404{
405	if (resource.getType() == RESOURCE_TYPE_IMAGE)
406	{
407		VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2(
408			secondQueue ? VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) : writeSync.stageMask,
409			secondQueue ? 0u : writeSync.accessMask,
410			!secondQueue ? VkPipelineStageFlags(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT) : readSync.stageMask,
411			!secondQueue ? 0u : readSync.accessMask,
412			writeSync.imageLayout,
413			readSync.imageLayout,
414			resource.getImage().handle,
415			resource.getImage().subresourceRange
416		);
417
418		if (writeFamily != readFamily && VK_SHARING_MODE_EXCLUSIVE == sharingMode)
419		{
420			imageMemoryBarrier2.srcQueueFamilyIndex = writeFamily;
421			imageMemoryBarrier2.dstQueueFamilyIndex = readFamily;
422
423			VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2);
424			synchronizationWrapper->cmdPipelineBarrier(cmdBuffer, &dependencyInfo);
425		}
426		else if (!secondQueue)
427		{
428			VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2);
429			synchronizationWrapper->cmdPipelineBarrier(cmdBuffer, &dependencyInfo);
430		}
431	}
432	else
433	{
434		VkBufferMemoryBarrier2KHR bufferMemoryBarrier2 = makeBufferMemoryBarrier2(
435			secondQueue ? VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) : writeSync.stageMask,
436			secondQueue ? 0u : writeSync.accessMask,
437			!secondQueue ? VkPipelineStageFlags(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT) : readSync.stageMask,
438			!secondQueue ? 0u : readSync.accessMask,
439			resource.getBuffer().handle,
440			resource.getBuffer().offset,
441			resource.getBuffer().size
442		);
443
444		if (writeFamily != readFamily && VK_SHARING_MODE_EXCLUSIVE == sharingMode)
445		{
446			bufferMemoryBarrier2.srcQueueFamilyIndex = writeFamily;
447			bufferMemoryBarrier2.dstQueueFamilyIndex = readFamily;
448		}
449
450		VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, &bufferMemoryBarrier2);
451		synchronizationWrapper->cmdPipelineBarrier(cmdBuffer, &dependencyInfo);
452	}
453}
454
455class BaseTestInstance : public TestInstance
456{
457public:
458	BaseTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData, bool timelineSemaphore)
459		: TestInstance		(context)
460		, m_type			(type)
461		, m_queues			(MultiQueues::getInstance(context, type, timelineSemaphore))
462		, m_opContext		(new OperationContext(context, type, m_queues->getDeviceInterface(), m_queues->getDevice(), m_queues->getAllocator(), pipelineCacheData))
463		, m_resourceDesc	(resourceDesc)
464		, m_writeOp			(writeOp)
465		, m_readOp			(readOp)
466	{
467	}
468
469protected:
470	const SynchronizationType			m_type;
471	const SharedPtr<MultiQueues>		m_queues;
472	const UniquePtr<OperationContext>	m_opContext;
473	const ResourceDescription			m_resourceDesc;
474	const OperationSupport&				m_writeOp;
475	const OperationSupport&				m_readOp;
476};
477
478class BinarySemaphoreTestInstance : public BaseTestInstance
479{
480public:
481	BinarySemaphoreTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData, const VkSharingMode sharingMode)
482		: BaseTestInstance	(context, type, resourceDesc, writeOp, readOp, pipelineCacheData, false)
483		, m_sharingMode		(sharingMode)
484	{
485	}
486
487	tcu::TestStatus	iterate (void)
488	{
489		const DeviceInterface&			vk			= m_opContext->getDeviceInterface();
490		const VkDevice					device		= m_opContext->getDevice();
491		const std::vector<QueuePair>	queuePairs	= m_queues->getQueuesPairs(m_writeOp.getQueueFlags(*m_opContext), m_readOp.getQueueFlags(*m_opContext));
492
493		for (deUint32 pairNdx = 0; pairNdx < static_cast<deUint32>(queuePairs.size()); ++pairNdx)
494		{
495			const UniquePtr<Resource>		resource		(new Resource(*m_opContext, m_resourceDesc, m_writeOp.getOutResourceUsageFlags() | m_readOp.getInResourceUsageFlags()));
496			const UniquePtr<Operation>		writeOp			(m_writeOp.build(*m_opContext, *resource));
497			const UniquePtr<Operation>		readOp			(m_readOp.build (*m_opContext, *resource));
498
499			const Move<VkCommandPool>			cmdPool[]		=
500			{
501				createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queuePairs[pairNdx].familyIndexWrite),
502				createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queuePairs[pairNdx].familyIndexRead)
503			};
504			const Move<VkCommandBuffer>			ptrCmdBuffer[]	=
505			{
506				makeCommandBuffer(vk, device, *cmdPool[QUEUETYPE_WRITE]),
507				makeCommandBuffer(vk, device, *cmdPool[QUEUETYPE_READ])
508			};
509			const VkCommandBufferSubmitInfoKHR	cmdBufferInfos[]	=
510			{
511				makeCommonCommandBufferSubmitInfo(*ptrCmdBuffer[QUEUETYPE_WRITE]),
512				makeCommonCommandBufferSubmitInfo(*ptrCmdBuffer[QUEUETYPE_READ]),
513			};
514			const Unique<VkSemaphore>			semaphore		(createSemaphore(vk, device));
515			VkSemaphoreSubmitInfoKHR			waitSemaphoreSubmitInfo =
516				makeCommonSemaphoreSubmitInfo(*semaphore, 0u, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR);
517			VkSemaphoreSubmitInfoKHR			signalSemaphoreSubmitInfo =
518				makeCommonSemaphoreSubmitInfo(*semaphore, 0u, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR);
519			SynchronizationWrapperPtr			synchronizationWrapper[]
520			{
521				getSynchronizationWrapper(m_type, vk, DE_FALSE),
522				getSynchronizationWrapper(m_type, vk, DE_FALSE),
523			};
524
525			synchronizationWrapper[QUEUETYPE_WRITE]->addSubmitInfo(
526				0u,
527				DE_NULL,
528				1u,
529				&cmdBufferInfos[QUEUETYPE_WRITE],
530				1u,
531				&signalSemaphoreSubmitInfo
532			);
533			synchronizationWrapper[QUEUETYPE_READ]->addSubmitInfo(
534				1u,
535				&waitSemaphoreSubmitInfo,
536				1u,
537				&cmdBufferInfos[QUEUETYPE_READ],
538				0u,
539				DE_NULL
540			);
541
542			const SyncInfo					writeSync		= writeOp->getOutSyncInfo();
543			const SyncInfo					readSync		= readOp->getInSyncInfo();
544			VkCommandBuffer					writeCmdBuffer	= cmdBufferInfos[QUEUETYPE_WRITE].commandBuffer;
545			VkCommandBuffer					readCmdBuffer	= cmdBufferInfos[QUEUETYPE_READ].commandBuffer;
546
547			beginCommandBuffer		(vk, writeCmdBuffer);
548			writeOp->recordCommands	(writeCmdBuffer);
549			createBarrierMultiQueue	(synchronizationWrapper[QUEUETYPE_WRITE], writeCmdBuffer, writeSync, readSync, *resource, queuePairs[pairNdx].familyIndexWrite, queuePairs[pairNdx].familyIndexRead, m_sharingMode);
550			endCommandBuffer		(vk, writeCmdBuffer);
551
552			beginCommandBuffer		(vk, readCmdBuffer);
553			createBarrierMultiQueue	(synchronizationWrapper[QUEUETYPE_READ], readCmdBuffer, writeSync, readSync, *resource, queuePairs[pairNdx].familyIndexWrite, queuePairs[pairNdx].familyIndexRead, m_sharingMode, true);
554			readOp->recordCommands	(readCmdBuffer);
555			endCommandBuffer		(vk, readCmdBuffer);
556
557			VK_CHECK(synchronizationWrapper[QUEUETYPE_WRITE]->queueSubmit(queuePairs[pairNdx].queueWrite, DE_NULL));
558			VK_CHECK(synchronizationWrapper[QUEUETYPE_READ]->queueSubmit(queuePairs[pairNdx].queueRead, DE_NULL));
559			VK_CHECK(vk.queueWaitIdle(queuePairs[pairNdx].queueWrite));
560			VK_CHECK(vk.queueWaitIdle(queuePairs[pairNdx].queueRead));
561
562			{
563				const Data	expected	= writeOp->getData();
564				const Data	actual		= readOp->getData();
565
566#ifdef CTS_USES_VULKANSC
567				if (m_context.getTestContext().getCommandLine().isSubProcess())
568#endif // CTS_USES_VULKANSC
569				{
570					if (isIndirectBuffer(m_resourceDesc.type))
571					{
572						const deUint32 expectedValue = reinterpret_cast<const deUint32*>(expected.data)[0];
573						const deUint32 actualValue   = reinterpret_cast<const deUint32*>(actual.data)[0];
574
575						if (actualValue < expectedValue)
576							return tcu::TestStatus::fail("Counter value is smaller than expected");
577					}
578					else
579					{
580						if (0 != deMemCmp(expected.data, actual.data, expected.size))
581							return tcu::TestStatus::fail("Memory contents don't match");
582					}
583				}
584			}
585		}
586		return tcu::TestStatus::pass("OK");
587	}
588
589private:
590	const VkSharingMode	m_sharingMode;
591};
592
593template<typename T>
594inline SharedPtr<Move<T> > makeVkSharedPtr (Move<T> move)
595{
596	return SharedPtr<Move<T> >(new Move<T>(move));
597}
598
599class TimelineSemaphoreTestInstance : public BaseTestInstance
600{
601public:
602	TimelineSemaphoreTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const SharedPtr<OperationSupport>& writeOp, const SharedPtr<OperationSupport>& readOp, PipelineCacheData& pipelineCacheData, const VkSharingMode sharingMode)
603		: BaseTestInstance	(context, type, resourceDesc, *writeOp, *readOp, pipelineCacheData, true)
604		, m_sharingMode		(sharingMode)
605	{
606		deUint32				maxQueues		= 0;
607		std::vector<deUint32>	queueFamilies;
608
609		if (m_queues->totalQueueCount() < 2)
610			TCU_THROW(NotSupportedError, "Not enough queues");
611
612		for (deUint32 familyNdx = 0; familyNdx < m_queues->familyCount(); familyNdx++)
613		{
614			maxQueues = std::max(m_queues->queueFamilyCount(familyNdx), maxQueues);
615			queueFamilies.push_back(familyNdx);
616		}
617
618		// Create a chain of operations copying data from one resource
619		// to another across at least every single queue of the system
620		// at least once. Each of the operation will be executing with
621		// a dependency on the previous using timeline points.
622		m_opSupports.push_back(writeOp);
623		m_opQueues.push_back(m_queues->getDefaultQueue(writeOp->getQueueFlags(*m_opContext)));
624
625		for (deUint32 queueIdx = 0; queueIdx < maxQueues; queueIdx++)
626		{
627			for (deUint32 familyIdx = 0; familyIdx < m_queues->familyCount(); familyIdx++)
628			{
629				for (deUint32 copyOpIdx = 0; copyOpIdx < DE_LENGTH_OF_ARRAY(s_copyOps); copyOpIdx++)
630				{
631					if (isResourceSupported(s_copyOps[copyOpIdx], resourceDesc))
632					{
633						SharedPtr<OperationSupport>	opSupport	(makeOperationSupport(s_copyOps[copyOpIdx], m_resourceDesc).release());
634
635						if (!checkQueueFlags(opSupport->getQueueFlags(*m_opContext), m_queues->getQueueFamilyFlags(familyIdx)))
636							continue;
637
638						m_opSupports.push_back(opSupport);
639						m_opQueues.push_back(m_queues->getQueue(familyIdx, queueIdx % m_queues->queueFamilyCount(familyIdx)));
640						break;
641					}
642				}
643			}
644		}
645
646		m_opSupports.push_back(readOp);
647		m_opQueues.push_back(m_queues->getDefaultQueue(readOp->getQueueFlags(*m_opContext)));
648
649		// Now create the resources with the usage associated to the
650		// operation performed on the resource.
651		for (deUint32 opIdx = 0; opIdx < (m_opSupports.size() - 1); opIdx++)
652		{
653			deUint32 usage = m_opSupports[opIdx]->getOutResourceUsageFlags() | m_opSupports[opIdx + 1]->getInResourceUsageFlags();
654
655			m_resources.push_back(SharedPtr<Resource>(new Resource(*m_opContext, m_resourceDesc, usage, m_sharingMode, queueFamilies)));
656		}
657
658		// Finally create the operations using the resources.
659		m_ops.push_back(SharedPtr<Operation>(m_opSupports[0]->build(*m_opContext, *m_resources[0]).release()));
660		for (deUint32 opIdx = 1; opIdx < (m_opSupports.size() - 1); opIdx++)
661			m_ops.push_back(SharedPtr<Operation>(m_opSupports[opIdx]->build(*m_opContext, *m_resources[opIdx - 1], *m_resources[opIdx]).release()));
662		m_ops.push_back(SharedPtr<Operation>(m_opSupports[m_opSupports.size() - 1]->build(*m_opContext, *m_resources.back()).release()));
663	}
664
665	tcu::TestStatus	iterate (void)
666	{
667		const DeviceInterface&							vk				= m_opContext->getDeviceInterface();
668		const VkDevice									device			= m_opContext->getDevice();
669		de::Random										rng				(1234);
670		const Unique<VkSemaphore>						semaphore		(createSemaphoreType(vk, device, VK_SEMAPHORE_TYPE_TIMELINE));
671		std::vector<SharedPtr<Move<VkCommandPool> > >	cmdPools;
672		std::vector<SharedPtr<Move<VkCommandBuffer> > >	ptrCmdBuffers;
673		std::vector<VkCommandBufferSubmitInfoKHR>		cmdBufferInfos;
674		std::vector<deUint64>							timelineValues;
675
676		cmdPools.resize(m_queues->familyCount());
677		for (deUint32 familyIdx = 0; familyIdx < m_queues->familyCount(); familyIdx++)
678			cmdPools[familyIdx] = makeVkSharedPtr(createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, familyIdx));
679
680		ptrCmdBuffers.resize(m_ops.size());
681		cmdBufferInfos.resize(m_ops.size());
682		for (deUint32 opIdx = 0; opIdx < m_ops.size(); opIdx++)
683		{
684			deUint64	increment	= 1 + rng.getUint8();
685
686			ptrCmdBuffers[opIdx] = makeVkSharedPtr(makeCommandBuffer(vk, device, **cmdPools[m_opQueues[opIdx].family]));
687			cmdBufferInfos[opIdx] = makeCommonCommandBufferSubmitInfo(**ptrCmdBuffers[opIdx]);
688
689			timelineValues.push_back(timelineValues.empty() ? increment : (timelineValues.back() + increment));
690		}
691
692		for (deUint32 opIdx = 0; opIdx < m_ops.size(); opIdx++)
693		{
694			VkCommandBuffer				cmdBuffer = cmdBufferInfos[opIdx].commandBuffer;
695			VkSemaphoreSubmitInfoKHR	waitSemaphoreSubmitInfo =
696				makeCommonSemaphoreSubmitInfo(*semaphore, (opIdx == 0 ? 0u : timelineValues[opIdx - 1]), VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR);
697			VkSemaphoreSubmitInfoKHR	signalSemaphoreSubmitInfo =
698				makeCommonSemaphoreSubmitInfo(*semaphore, timelineValues[opIdx], VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR);
699			SynchronizationWrapperPtr	synchronizationWrapper = getSynchronizationWrapper(m_type, vk, DE_TRUE);
700
701			synchronizationWrapper->addSubmitInfo(
702				opIdx == 0 ? 0u : 1u,
703				&waitSemaphoreSubmitInfo,
704				1u,
705				&cmdBufferInfos[opIdx],
706				1u,
707				&signalSemaphoreSubmitInfo,
708				opIdx == 0 ? DE_FALSE : DE_TRUE,
709				DE_TRUE
710			);
711
712			beginCommandBuffer(vk, cmdBuffer);
713
714			if (opIdx > 0)
715			{
716				const SyncInfo	writeSync	= m_ops[opIdx - 1]->getOutSyncInfo();
717				const SyncInfo	readSync	= m_ops[opIdx]->getInSyncInfo();
718				const Resource&	resource	= *m_resources[opIdx - 1].get();
719
720				createBarrierMultiQueue(synchronizationWrapper, cmdBuffer, writeSync, readSync, resource, m_opQueues[opIdx - 1].family, m_opQueues[opIdx].family, m_sharingMode, true);
721			}
722
723			m_ops[opIdx]->recordCommands(cmdBuffer);
724
725			if (opIdx < (m_ops.size() - 1))
726			{
727				const SyncInfo	writeSync	= m_ops[opIdx]->getOutSyncInfo();
728				const SyncInfo	readSync	= m_ops[opIdx + 1]->getInSyncInfo();
729				const Resource&	resource	= *m_resources[opIdx].get();
730
731				createBarrierMultiQueue(synchronizationWrapper, cmdBuffer, writeSync, readSync, resource, m_opQueues[opIdx].family, m_opQueues[opIdx + 1].family, m_sharingMode);
732			}
733
734			endCommandBuffer(vk, cmdBuffer);
735
736			VK_CHECK(synchronizationWrapper->queueSubmit(m_opQueues[opIdx].queue, DE_NULL));
737		}
738
739
740		VK_CHECK(vk.queueWaitIdle(m_opQueues.back().queue));
741
742		{
743			const Data	expected	= m_ops.front()->getData();
744			const Data	actual		= m_ops.back()->getData();
745
746			if (isIndirectBuffer(m_resourceDesc.type))
747			{
748				const deUint32 expectedValue = reinterpret_cast<const deUint32*>(expected.data)[0];
749				const deUint32 actualValue   = reinterpret_cast<const deUint32*>(actual.data)[0];
750
751				if (actualValue < expectedValue)
752					return tcu::TestStatus::fail("Counter value is smaller than expected");
753			}
754			else
755			{
756				if (0 != deMemCmp(expected.data, actual.data, expected.size))
757					return tcu::TestStatus::fail("Memory contents don't match");
758			}
759		}
760
761		// Make the validation layers happy.
762		for (deUint32 opIdx = 0; opIdx < m_opQueues.size(); opIdx++)
763			VK_CHECK(vk.queueWaitIdle(m_opQueues[opIdx].queue));
764
765		return tcu::TestStatus::pass("OK");
766	}
767
768private:
769	const VkSharingMode							m_sharingMode;
770	std::vector<SharedPtr<OperationSupport> >	m_opSupports;
771	std::vector<SharedPtr<Operation> >			m_ops;
772	std::vector<SharedPtr<Resource> >			m_resources;
773	std::vector<Queue>							m_opQueues;
774};
775
776class FenceTestInstance : public BaseTestInstance
777{
778public:
779	FenceTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData, const VkSharingMode sharingMode)
780		: BaseTestInstance	(context, type, resourceDesc, writeOp, readOp, pipelineCacheData, false)
781		, m_sharingMode		(sharingMode)
782	{
783	}
784
785	tcu::TestStatus	iterate (void)
786	{
787		const DeviceInterface&			vk			= m_opContext->getDeviceInterface();
788		const VkDevice					device		= m_opContext->getDevice();
789		const std::vector<QueuePair>	queuePairs	= m_queues->getQueuesPairs(m_writeOp.getQueueFlags(*m_opContext), m_readOp.getQueueFlags(*m_opContext));
790
791		for (deUint32 pairNdx = 0; pairNdx < static_cast<deUint32>(queuePairs.size()); ++pairNdx)
792		{
793			const UniquePtr<Resource>		resource		(new Resource(*m_opContext, m_resourceDesc, m_writeOp.getOutResourceUsageFlags() | m_readOp.getInResourceUsageFlags()));
794			const UniquePtr<Operation>		writeOp			(m_writeOp.build(*m_opContext, *resource));
795			const UniquePtr<Operation>		readOp			(m_readOp.build(*m_opContext, *resource));
796			const Move<VkCommandPool>		cmdPool[]
797			{
798				createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queuePairs[pairNdx].familyIndexWrite),
799				createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queuePairs[pairNdx].familyIndexRead)
800			};
801			const Move<VkCommandBuffer>		ptrCmdBuffer[]
802			{
803				makeCommandBuffer(vk, device, *cmdPool[QUEUETYPE_WRITE]),
804				makeCommandBuffer(vk, device, *cmdPool[QUEUETYPE_READ])
805			};
806			const VkCommandBufferSubmitInfoKHR	cmdBufferInfos[]
807			{
808				makeCommonCommandBufferSubmitInfo(*ptrCmdBuffer[QUEUETYPE_WRITE]),
809				makeCommonCommandBufferSubmitInfo(*ptrCmdBuffer[QUEUETYPE_READ])
810			};
811			SynchronizationWrapperPtr		synchronizationWrapper[]
812			{
813				getSynchronizationWrapper(m_type, vk, DE_FALSE),
814				getSynchronizationWrapper(m_type, vk, DE_FALSE),
815			};
816			const SyncInfo					writeSync		= writeOp->getOutSyncInfo();
817			const SyncInfo					readSync		= readOp->getInSyncInfo();
818			VkCommandBuffer					writeCmdBuffer	= cmdBufferInfos[QUEUETYPE_WRITE].commandBuffer;
819			VkCommandBuffer					readCmdBuffer	= cmdBufferInfos[QUEUETYPE_READ].commandBuffer;
820
821			beginCommandBuffer		(vk, writeCmdBuffer);
822			writeOp->recordCommands	(writeCmdBuffer);
823			createBarrierMultiQueue	(synchronizationWrapper[QUEUETYPE_WRITE], writeCmdBuffer, writeSync, readSync, *resource, queuePairs[pairNdx].familyIndexWrite, queuePairs[pairNdx].familyIndexRead, m_sharingMode);
824			endCommandBuffer		(vk, writeCmdBuffer);
825
826			submitCommandsAndWait	(synchronizationWrapper[QUEUETYPE_WRITE], vk, device, queuePairs[pairNdx].queueWrite, writeCmdBuffer);
827
828			beginCommandBuffer		(vk, readCmdBuffer);
829			createBarrierMultiQueue	(synchronizationWrapper[QUEUETYPE_READ], readCmdBuffer, writeSync, readSync, *resource, queuePairs[pairNdx].familyIndexWrite, queuePairs[pairNdx].familyIndexRead, m_sharingMode, true);
830			readOp->recordCommands	(readCmdBuffer);
831			endCommandBuffer		(vk, readCmdBuffer);
832
833			submitCommandsAndWait(synchronizationWrapper[QUEUETYPE_READ], vk, device, queuePairs[pairNdx].queueRead, readCmdBuffer);
834
835			{
836				const Data	expected = writeOp->getData();
837				const Data	actual	 = readOp->getData();
838
839#ifdef CTS_USES_VULKANSC
840				if (m_context.getTestContext().getCommandLine().isSubProcess())
841#endif // CTS_USES_VULKANSC
842				{
843					if (isIndirectBuffer(m_resourceDesc.type))
844					{
845						const deUint32 expectedValue = reinterpret_cast<const deUint32*>(expected.data)[0];
846						const deUint32 actualValue   = reinterpret_cast<const deUint32*>(actual.data)[0];
847
848						if (actualValue < expectedValue)
849							return tcu::TestStatus::fail("Counter value is smaller than expected");
850					}
851					else
852					{
853						if (0 != deMemCmp(expected.data, actual.data, expected.size))
854							return tcu::TestStatus::fail("Memory contents don't match");
855					}
856				}
857			}
858		}
859		return tcu::TestStatus::pass("OK");
860	}
861
862private:
863	const VkSharingMode	m_sharingMode;
864};
865
866class BaseTestCase : public TestCase
867{
868public:
869	BaseTestCase (tcu::TestContext&			testCtx,
870				  const std::string&		name,
871				  SynchronizationType		type,
872				  const SyncPrimitive		syncPrimitive,
873				  const ResourceDescription	resourceDesc,
874				  const OperationName		writeOp,
875				  const OperationName		readOp,
876				  const VkSharingMode		sharingMode,
877				  PipelineCacheData&		pipelineCacheData)
878		: TestCase				(testCtx, name)
879		, m_type				(type)
880		, m_resourceDesc		(resourceDesc)
881		, m_writeOp				(makeOperationSupport(writeOp, resourceDesc).release())
882		, m_readOp				(makeOperationSupport(readOp, resourceDesc).release())
883		, m_syncPrimitive		(syncPrimitive)
884		, m_sharingMode			(sharingMode)
885		, m_pipelineCacheData	(pipelineCacheData)
886	{
887	}
888
889	void initPrograms (SourceCollections& programCollection) const
890	{
891		m_writeOp->initPrograms(programCollection);
892		m_readOp->initPrograms(programCollection);
893
894		if (m_syncPrimitive == SYNC_PRIMITIVE_TIMELINE_SEMAPHORE)
895		{
896			for (deUint32 copyOpNdx = 0; copyOpNdx < DE_LENGTH_OF_ARRAY(s_copyOps); copyOpNdx++)
897			{
898				if (isResourceSupported(s_copyOps[copyOpNdx], m_resourceDesc))
899					makeOperationSupport(s_copyOps[copyOpNdx], m_resourceDesc)->initPrograms(programCollection);
900			}
901		}
902	}
903
904	void checkSupport(Context& context) const
905	{
906		if (m_type == SynchronizationType::SYNCHRONIZATION2)
907			context.requireDeviceFunctionality("VK_KHR_synchronization2");
908		if (m_syncPrimitive == SYNC_PRIMITIVE_TIMELINE_SEMAPHORE)
909			context.requireDeviceFunctionality("VK_KHR_timeline_semaphore");
910
911		const InstanceInterface&					instance				= context.getInstanceInterface();
912		const VkPhysicalDevice						physicalDevice			= context.getPhysicalDevice();
913		const std::vector<VkQueueFamilyProperties>	queueFamilyProperties	= getPhysicalDeviceQueueFamilyProperties(instance, physicalDevice);
914		if (m_sharingMode == VK_SHARING_MODE_CONCURRENT && queueFamilyProperties.size() < 2)
915			TCU_THROW(NotSupportedError, "Concurrent requires more than 1 queue family");
916
917		if (m_syncPrimitive == SYNC_PRIMITIVE_TIMELINE_SEMAPHORE &&
918			!context.getTimelineSemaphoreFeatures().timelineSemaphore)
919			TCU_THROW(NotSupportedError, "Timeline semaphore not supported");
920
921		if (m_resourceDesc.type == RESOURCE_TYPE_IMAGE)
922		{
923			VkImageFormatProperties	imageFormatProperties;
924			const deUint32			usage					= m_writeOp->getOutResourceUsageFlags() | m_readOp->getInResourceUsageFlags();
925			const VkResult			formatResult			= instance.getPhysicalDeviceImageFormatProperties(physicalDevice, m_resourceDesc.imageFormat, m_resourceDesc.imageType, VK_IMAGE_TILING_OPTIMAL, usage, (VkImageCreateFlags)0, &imageFormatProperties);
926
927			if (formatResult != VK_SUCCESS)
928				TCU_THROW(NotSupportedError, "Image format is not supported");
929
930			if ((imageFormatProperties.sampleCounts & m_resourceDesc.imageSamples) != m_resourceDesc.imageSamples)
931				TCU_THROW(NotSupportedError, "Requested sample count is not supported");
932		}
933	}
934
935	TestInstance* createInstance (Context& context) const
936	{
937		switch (m_syncPrimitive)
938		{
939			case SYNC_PRIMITIVE_FENCE:
940				return new FenceTestInstance(context, m_type, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData, m_sharingMode);
941			case SYNC_PRIMITIVE_BINARY_SEMAPHORE:
942				return new BinarySemaphoreTestInstance(context, m_type, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData, m_sharingMode);
943			case SYNC_PRIMITIVE_TIMELINE_SEMAPHORE:
944				return new TimelineSemaphoreTestInstance(context, m_type, m_resourceDesc, m_writeOp, m_readOp, m_pipelineCacheData, m_sharingMode);
945			default:
946				DE_ASSERT(0);
947				return DE_NULL;
948		}
949	}
950
951private:
952	const SynchronizationType				m_type;
953	const ResourceDescription				m_resourceDesc;
954	const SharedPtr<OperationSupport>		m_writeOp;
955	const SharedPtr<OperationSupport>		m_readOp;
956	const SyncPrimitive						m_syncPrimitive;
957	const VkSharingMode						m_sharingMode;
958	PipelineCacheData&						m_pipelineCacheData;
959};
960
961struct TestData
962{
963	SynchronizationType		type;
964	PipelineCacheData*		pipelineCacheData;
965};
966
967void createTests (tcu::TestCaseGroup* group, TestData data)
968{
969	tcu::TestContext& testCtx = group->getTestContext();
970
971	static const struct
972	{
973		const char*		name;
974		SyncPrimitive	syncPrimitive;
975		int				numOptions;
976	} groups[] =
977	{
978		{ "fence",				SYNC_PRIMITIVE_FENCE,				1 },
979		{ "binary_semaphore",	SYNC_PRIMITIVE_BINARY_SEMAPHORE,	1 },
980		{ "timeline_semaphore",	SYNC_PRIMITIVE_TIMELINE_SEMAPHORE,	1 }
981	};
982
983	for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(groups); ++groupNdx)
984	{
985		MovePtr<tcu::TestCaseGroup> synchGroup (new tcu::TestCaseGroup(testCtx, groups[groupNdx].name));
986
987		for (int writeOpNdx = 0; writeOpNdx < DE_LENGTH_OF_ARRAY(s_writeOps); ++writeOpNdx)
988		for (int readOpNdx  = 0; readOpNdx  < DE_LENGTH_OF_ARRAY(s_readOps);  ++readOpNdx)
989		{
990			const OperationName	writeOp		= s_writeOps[writeOpNdx];
991			const OperationName	readOp		= s_readOps[readOpNdx];
992			const std::string	opGroupName = getOperationName(writeOp) + "_" + getOperationName(readOp);
993			bool				empty		= true;
994
995			MovePtr<tcu::TestCaseGroup> opGroup		(new tcu::TestCaseGroup(testCtx, opGroupName.c_str()));
996
997			for (int optionNdx = 0; optionNdx <= groups[groupNdx].numOptions; ++optionNdx)
998			for (int resourceNdx = 0; resourceNdx < DE_LENGTH_OF_ARRAY(s_resources); ++resourceNdx)
999			{
1000				const ResourceDescription&	resource	= s_resources[resourceNdx];
1001				if (isResourceSupported(writeOp, resource) && isResourceSupported(readOp, resource))
1002				{
1003					std::string					name		= getResourceName(resource);
1004					VkSharingMode				sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1005
1006					// queue family sharing mode used for resource
1007					if (optionNdx)
1008					{
1009						name += "_concurrent";
1010						sharingMode = VK_SHARING_MODE_CONCURRENT;
1011					}
1012					else
1013						name += "_exclusive";
1014
1015					opGroup->addChild(new BaseTestCase(testCtx, name, data.type, groups[groupNdx].syncPrimitive, resource, writeOp, readOp, sharingMode, *data.pipelineCacheData));
1016					empty = false;
1017				}
1018			}
1019			if (!empty)
1020				synchGroup->addChild(opGroup.release());
1021		}
1022		group->addChild(synchGroup.release());
1023	}
1024}
1025
1026void cleanupGroup (tcu::TestCaseGroup* group, TestData data)
1027{
1028	DE_UNREF(group);
1029	DE_UNREF(data.pipelineCacheData);
1030	// Destroy singleton object
1031	MultiQueues::destroy();
1032}
1033
1034} // anonymous
1035
1036tcu::TestCaseGroup* createSynchronizedOperationMultiQueueTests (tcu::TestContext& testCtx, SynchronizationType type, PipelineCacheData& pipelineCacheData)
1037{
1038	TestData data
1039	{
1040		type,
1041		&pipelineCacheData
1042	};
1043
1044	// Synchronization of a memory-modifying operation
1045	return createTestGroup(testCtx, "multi_queue", createTests, data, cleanupGroup);
1046}
1047
1048} // synchronization
1049} // vkt
1050