1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2016 The Khronos Group Inc.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Synchronization primitive tests with multi queue
22 *//*--------------------------------------------------------------------*/
24 #include "vktSynchronizationOperationMultiQueueTests.hpp"
25 #include "vktCustomInstancesDevices.hpp"
27 #include "vktTestCase.hpp"
28 #include "vktTestCaseUtil.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"
55 namespace synchronization
74 QueuePair (const deUint32 familyWrite, const deUint32 familyRead, const VkQueue write, const VkQueue read)
75 : familyIndexWrite (familyWrite)
76 , familyIndexRead (familyRead)
81 deUint32 familyIndexWrite;
82 deUint32 familyIndexRead;
89 Queue (const deUint32 familyOp, const VkQueue queueOp)
98 bool checkQueueFlags (VkQueueFlags availableFlags, const VkQueueFlags neededFlags)
100 if ((availableFlags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) != 0)
101 availableFlags |= VK_QUEUE_TRANSFER_BIT;
103 return (availableFlags & neededFlags) != 0;
111 std::vector<VkQueue> queue;
114 MultiQueues (Context& context, SynchronizationType type, bool timelineSemaphore)
115 #ifdef CTS_USES_VULKANSC
116 : m_instance (createCustomInstanceFromContext(context)),
119 #endif // CTS_USES_VULKANSC
122 #ifdef CTS_USES_VULKANSC
123 const InstanceInterface& instanceDriver = m_instance.getDriver();
124 const VkPhysicalDevice physicalDevice = chooseDevice(instanceDriver, m_instance, context.getTestContext().getCommandLine());
125 const VkInstance instance = m_instance;
127 const InstanceInterface& instanceDriver = context.getInstanceInterface();
128 const VkPhysicalDevice physicalDevice = context.getPhysicalDevice();
129 const VkInstance instance = context.getInstance();
130 #endif // CTS_USES_VULKANSC
131 const std::vector<VkQueueFamilyProperties> queueFamilyProperties = getPhysicalDeviceQueueFamilyProperties(instanceDriver, physicalDevice);
133 for (deUint32 queuePropertiesNdx = 0; queuePropertiesNdx < queueFamilyProperties.size(); ++queuePropertiesNdx)
135 addQueueIndex(queuePropertiesNdx,
136 std::min(2u, queueFamilyProperties[queuePropertiesNdx].queueCount),
137 queueFamilyProperties[queuePropertiesNdx].queueFlags);
140 std::vector<VkDeviceQueueCreateInfo> queueInfos;
141 const float queuePriorities[2] = { 1.0f, 1.0f }; //get max 2 queues from one family
143 for (std::map<deUint32, QueueData>::iterator it = m_queues.begin(); it!= m_queues.end(); ++it)
145 const VkDeviceQueueCreateInfo queueInfo =
147 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, //VkStructureType sType;
148 DE_NULL, //const void* pNext;
149 (VkDeviceQueueCreateFlags)0u, //VkDeviceQueueCreateFlags flags;
150 it->first, //deUint32 queueFamilyIndex;
151 static_cast<deUint32>(it->second.queue.size()), //deUint32 queueCount;
152 &queuePriorities[0] //const float* pQueuePriorities;
154 queueInfos.push_back(queueInfo);
158 VkPhysicalDeviceFeatures2 createPhysicalFeature { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, DE_NULL, context.getDeviceFeatures() };
159 VkPhysicalDeviceTimelineSemaphoreFeatures timelineSemaphoreFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, DE_NULL, DE_TRUE };
160 VkPhysicalDeviceSynchronization2FeaturesKHR synchronization2Features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR, DE_NULL, DE_TRUE };
161 void** nextPtr = &createPhysicalFeature.pNext;
163 std::vector<const char*> deviceExtensions;
164 if (timelineSemaphore)
166 if (!isCoreDeviceExtension(context.getUsedApiVersion(), "VK_KHR_timeline_semaphore"))
167 deviceExtensions.push_back("VK_KHR_timeline_semaphore");
168 addToChainVulkanStructure(&nextPtr, timelineSemaphoreFeatures);
170 if (type == SynchronizationType::SYNCHRONIZATION2)
172 deviceExtensions.push_back("VK_KHR_synchronization2");
173 addToChainVulkanStructure(&nextPtr, synchronization2Features);
176 void* pNext = &createPhysicalFeature;
177 #ifdef CTS_USES_VULKANSC
178 VkDeviceObjectReservationCreateInfo memReservationInfo = context.getTestContext().getCommandLine().isSubProcess() ? context.getResourceInterface()->getStatMax() : resetDeviceObjectReservationCreateInfo();
179 memReservationInfo.pNext = pNext;
180 pNext = &memReservationInfo;
182 VkPhysicalDeviceVulkanSC10Features sc10Features = createDefaultSC10Features();
183 sc10Features.pNext = pNext;
184 pNext = &sc10Features;
186 VkPipelineCacheCreateInfo pcCI;
187 std::vector<VkPipelinePoolSize> poolSizes;
188 if (context.getTestContext().getCommandLine().isSubProcess())
190 if (context.getResourceInterface()->getCacheDataSize() > 0)
194 VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, // VkStructureType sType;
195 DE_NULL, // const void* pNext;
196 VK_PIPELINE_CACHE_CREATE_READ_ONLY_BIT |
197 VK_PIPELINE_CACHE_CREATE_USE_APPLICATION_STORAGE_BIT, // VkPipelineCacheCreateFlags flags;
198 context.getResourceInterface()->getCacheDataSize(), // deUintptr initialDataSize;
199 context.getResourceInterface()->getCacheData() // const void* pInitialData;
201 memReservationInfo.pipelineCacheCreateInfoCount = 1;
202 memReservationInfo.pPipelineCacheCreateInfos = &pcCI;
205 poolSizes = context.getResourceInterface()->getPipelinePoolSizes();
206 if (!poolSizes.empty())
208 memReservationInfo.pipelinePoolSizeCount = deUint32(poolSizes.size());
209 memReservationInfo.pPipelinePoolSizes = poolSizes.data();
212 #endif // CTS_USES_VULKANSC
214 const VkDeviceCreateInfo deviceInfo =
216 VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, //VkStructureType sType;
217 pNext, //const void* pNext;
218 0u, //VkDeviceCreateFlags flags;
219 static_cast<deUint32>(queueInfos.size()), //deUint32 queueCreateInfoCount;
220 &queueInfos[0], //const VkDeviceQueueCreateInfo* pQueueCreateInfos;
221 0u, //deUint32 enabledLayerCount;
222 DE_NULL, //const char* const* ppEnabledLayerNames;
223 static_cast<deUint32>(deviceExtensions.size()), //deUint32 enabledExtensionCount;
224 deviceExtensions.empty() ? DE_NULL : &deviceExtensions[0], //const char* const* ppEnabledExtensionNames;
225 DE_NULL //const VkPhysicalDeviceFeatures* pEnabledFeatures;
228 m_logicalDevice = createCustomDevice(context.getTestContext().getCommandLine().isValidationEnabled(), context.getPlatformInterface(), instance, instanceDriver, physicalDevice, &deviceInfo);
229 #ifndef CTS_USES_VULKANSC
230 m_deviceDriver = de::MovePtr<DeviceDriver>(new DeviceDriver(context.getPlatformInterface(), instance, *m_logicalDevice));
232 m_deviceDriver = de::MovePtr<DeviceDriverSC, DeinitDeviceDeleter>(new DeviceDriverSC(context.getPlatformInterface(), instance, *m_logicalDevice, context.getTestContext().getCommandLine(), context.getResourceInterface(), context.getDeviceVulkanSC10Properties(), context.getDeviceProperties()), vk::DeinitDeviceDeleter(context.getResourceInterface().get(), *m_logicalDevice));
233 #endif // CTS_USES_VULKANSC
234 m_allocator = MovePtr<Allocator>(new SimpleAllocator(*m_deviceDriver, *m_logicalDevice, getPhysicalDeviceMemoryProperties(instanceDriver, physicalDevice)));
236 for (std::map<deUint32, QueueData>::iterator it = m_queues.begin(); it != m_queues.end(); ++it)
237 for (int queueNdx = 0; queueNdx < static_cast<int>(it->second.queue.size()); ++queueNdx)
238 m_deviceDriver->getDeviceQueue(*m_logicalDevice, it->first, queueNdx, &it->second.queue[queueNdx]);
242 void addQueueIndex (const deUint32 queueFamilyIndex, const deUint32 count, const VkQueueFlags flags)
244 QueueData dataToPush;
245 dataToPush.flags = flags;
246 dataToPush.queue.resize(count);
247 m_queues[queueFamilyIndex] = dataToPush;
257 std::vector<QueuePair> getQueuesPairs (const VkQueueFlags flagsWrite, const VkQueueFlags flagsRead) const
259 std::map<deUint32, QueueData> queuesWrite;
260 std::map<deUint32, QueueData> queuesRead;
261 std::vector<QueuePair> queuesPairs;
263 for (std::map<deUint32, QueueData>::const_iterator it = m_queues.begin(); it != m_queues.end(); ++it)
265 const bool writeQueue = checkQueueFlags(it->second.flags, flagsWrite);
266 const bool readQueue = checkQueueFlags(it->second.flags, flagsRead);
268 if (!(writeQueue || readQueue))
271 if (writeQueue && readQueue)
273 queuesWrite[it->first] = it->second;
274 queuesRead[it->first] = it->second;
277 queuesWrite[it->first] = it->second;
279 queuesRead[it->first] = it->second;
282 for (std::map<deUint32, QueueData>::iterator write = queuesWrite.begin(); write != queuesWrite.end(); ++write)
283 for (std::map<deUint32, QueueData>::iterator read = queuesRead.begin(); read != queuesRead.end(); ++read)
285 const int writeSize = static_cast<int>(write->second.queue.size());
286 const int readSize = static_cast<int>(read->second.queue.size());
288 for (int writeNdx = 0; writeNdx < writeSize; ++writeNdx)
289 for (int readNdx = 0; readNdx < readSize; ++readNdx)
291 if (write->second.queue[writeNdx] != read->second.queue[readNdx])
293 queuesPairs.push_back(QueuePair(write->first, read->first, write->second.queue[writeNdx], read->second.queue[readNdx]));
294 writeNdx = readNdx = std::max(writeSize, readSize); //exit from the loops
299 if (queuesPairs.empty())
300 TCU_THROW(NotSupportedError, "Queue not found");
305 Queue getDefaultQueue(const VkQueueFlags flagsOp) const
307 for (std::map<deUint32, QueueData>::const_iterator it = m_queues.begin(); it!= m_queues.end(); ++it)
309 if (checkQueueFlags(it->second.flags, flagsOp))
310 return Queue(it->first, it->second.queue[0]);
313 TCU_THROW(NotSupportedError, "Queue not found");
316 Queue getQueue (const deUint32 familyIdx, const deUint32 queueIdx)
318 return Queue(familyIdx, m_queues[familyIdx].queue[queueIdx]);
321 VkQueueFlags getQueueFamilyFlags (const deUint32 familyIdx)
323 return m_queues[familyIdx].flags;
326 deUint32 queueFamilyCount (const deUint32 familyIdx)
328 return (deUint32) m_queues[familyIdx].queue.size();
331 deUint32 familyCount (void) const
333 return (deUint32) m_queues.size();
336 deUint32 totalQueueCount (void)
340 for (deUint32 familyIdx = 0; familyIdx < familyCount(); familyIdx++)
342 count += queueFamilyCount(familyIdx);
348 VkDevice getDevice (void) const
350 return *m_logicalDevice;
353 const DeviceInterface& getDeviceInterface (void) const
355 return *m_deviceDriver;
358 Allocator& getAllocator (void)
363 static SharedPtr<MultiQueues> getInstance(Context& context, SynchronizationType type, bool timelineSemaphore)
366 m_multiQueues = SharedPtr<MultiQueues>(new MultiQueues(context, type, timelineSemaphore));
368 return m_multiQueues;
370 static void destroy()
372 m_multiQueues.clear();
376 #ifdef CTS_USES_VULKANSC
377 CustomInstance m_instance;
378 #endif // CTS_USES_VULKANSC
379 Move<VkDevice> m_logicalDevice;
380 #ifndef CTS_USES_VULKANSC
381 de::MovePtr<vk::DeviceDriver> m_deviceDriver;
383 de::MovePtr<DeviceDriverSC, DeinitDeviceDeleter> m_deviceDriver;
384 #endif // CTS_USES_VULKANSC
385 MovePtr<Allocator> m_allocator;
386 std::map<deUint32, QueueData> m_queues;
387 deUint32 m_queueCount;
389 static SharedPtr<MultiQueues> m_multiQueues;
391 SharedPtr<MultiQueues> MultiQueues::m_multiQueues;
393 void createBarrierMultiQueue (SynchronizationWrapperPtr synchronizationWrapper,
394 const VkCommandBuffer& cmdBuffer,
395 const SyncInfo& writeSync,
396 const SyncInfo& readSync,
397 const Resource& resource,
398 const deUint32 writeFamily,
399 const deUint32 readFamily,
400 const VkSharingMode sharingMode,
401 const bool secondQueue = false)
403 if (resource.getType() == RESOURCE_TYPE_IMAGE)
405 VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2(
406 secondQueue ? VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) : writeSync.stageMask,
407 secondQueue ? 0u : writeSync.accessMask,
408 !secondQueue ? VkPipelineStageFlags(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT) : readSync.stageMask,
409 !secondQueue ? 0u : readSync.accessMask,
410 writeSync.imageLayout,
411 readSync.imageLayout,
412 resource.getImage().handle,
413 resource.getImage().subresourceRange
416 if (writeFamily != readFamily && VK_SHARING_MODE_EXCLUSIVE == sharingMode)
418 imageMemoryBarrier2.srcQueueFamilyIndex = writeFamily;
419 imageMemoryBarrier2.dstQueueFamilyIndex = readFamily;
421 VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2);
422 synchronizationWrapper->cmdPipelineBarrier(cmdBuffer, &dependencyInfo);
424 else if (!secondQueue)
426 VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2);
427 synchronizationWrapper->cmdPipelineBarrier(cmdBuffer, &dependencyInfo);
432 VkBufferMemoryBarrier2KHR bufferMemoryBarrier2 = makeBufferMemoryBarrier2(
433 secondQueue ? VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT) : writeSync.stageMask,
434 secondQueue ? 0u : writeSync.accessMask,
435 !secondQueue ? VkPipelineStageFlags(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT) : readSync.stageMask,
436 !secondQueue ? 0u : readSync.accessMask,
437 resource.getBuffer().handle,
438 resource.getBuffer().offset,
439 resource.getBuffer().size
442 if (writeFamily != readFamily && VK_SHARING_MODE_EXCLUSIVE == sharingMode)
444 bufferMemoryBarrier2.srcQueueFamilyIndex = writeFamily;
445 bufferMemoryBarrier2.dstQueueFamilyIndex = readFamily;
448 VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, &bufferMemoryBarrier2);
449 synchronizationWrapper->cmdPipelineBarrier(cmdBuffer, &dependencyInfo);
453 class BaseTestInstance : public TestInstance
456 BaseTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData, bool timelineSemaphore)
457 : TestInstance (context)
459 , m_queues (MultiQueues::getInstance(context, type, timelineSemaphore))
460 , m_opContext (new OperationContext(context, type, m_queues->getDeviceInterface(), m_queues->getDevice(), m_queues->getAllocator(), pipelineCacheData))
461 , m_resourceDesc (resourceDesc)
462 , m_writeOp (writeOp)
468 const SynchronizationType m_type;
469 const SharedPtr<MultiQueues> m_queues;
470 const UniquePtr<OperationContext> m_opContext;
471 const ResourceDescription m_resourceDesc;
472 const OperationSupport& m_writeOp;
473 const OperationSupport& m_readOp;
476 class BinarySemaphoreTestInstance : public BaseTestInstance
479 BinarySemaphoreTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData, const VkSharingMode sharingMode)
480 : BaseTestInstance (context, type, resourceDesc, writeOp, readOp, pipelineCacheData, false)
481 , m_sharingMode (sharingMode)
485 tcu::TestStatus iterate (void)
487 const DeviceInterface& vk = m_opContext->getDeviceInterface();
488 const VkDevice device = m_opContext->getDevice();
489 const std::vector<QueuePair> queuePairs = m_queues->getQueuesPairs(m_writeOp.getQueueFlags(*m_opContext), m_readOp.getQueueFlags(*m_opContext));
491 for (deUint32 pairNdx = 0; pairNdx < static_cast<deUint32>(queuePairs.size()); ++pairNdx)
493 const UniquePtr<Resource> resource (new Resource(*m_opContext, m_resourceDesc, m_writeOp.getOutResourceUsageFlags() | m_readOp.getInResourceUsageFlags()));
494 const UniquePtr<Operation> writeOp (m_writeOp.build(*m_opContext, *resource));
495 const UniquePtr<Operation> readOp (m_readOp.build (*m_opContext, *resource));
497 const Move<VkCommandPool> cmdPool[] =
499 createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queuePairs[pairNdx].familyIndexWrite),
500 createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queuePairs[pairNdx].familyIndexRead)
502 const Move<VkCommandBuffer> ptrCmdBuffer[] =
504 makeCommandBuffer(vk, device, *cmdPool[QUEUETYPE_WRITE]),
505 makeCommandBuffer(vk, device, *cmdPool[QUEUETYPE_READ])
507 const VkCommandBufferSubmitInfoKHR cmdBufferInfos[] =
509 makeCommonCommandBufferSubmitInfo(*ptrCmdBuffer[QUEUETYPE_WRITE]),
510 makeCommonCommandBufferSubmitInfo(*ptrCmdBuffer[QUEUETYPE_READ]),
512 const Unique<VkSemaphore> semaphore (createSemaphore(vk, device));
513 VkSemaphoreSubmitInfoKHR waitSemaphoreSubmitInfo =
514 makeCommonSemaphoreSubmitInfo(*semaphore, 0u, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR);
515 VkSemaphoreSubmitInfoKHR signalSemaphoreSubmitInfo =
516 makeCommonSemaphoreSubmitInfo(*semaphore, 0u, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR);
517 SynchronizationWrapperPtr synchronizationWrapper[]
519 getSynchronizationWrapper(m_type, vk, DE_FALSE),
520 getSynchronizationWrapper(m_type, vk, DE_FALSE),
523 synchronizationWrapper[QUEUETYPE_WRITE]->addSubmitInfo(
527 &cmdBufferInfos[QUEUETYPE_WRITE],
529 &signalSemaphoreSubmitInfo
531 synchronizationWrapper[QUEUETYPE_READ]->addSubmitInfo(
533 &waitSemaphoreSubmitInfo,
535 &cmdBufferInfos[QUEUETYPE_READ],
540 const SyncInfo writeSync = writeOp->getOutSyncInfo();
541 const SyncInfo readSync = readOp->getInSyncInfo();
542 VkCommandBuffer writeCmdBuffer = cmdBufferInfos[QUEUETYPE_WRITE].commandBuffer;
543 VkCommandBuffer readCmdBuffer = cmdBufferInfos[QUEUETYPE_READ].commandBuffer;
545 beginCommandBuffer (vk, writeCmdBuffer);
546 writeOp->recordCommands (writeCmdBuffer);
547 createBarrierMultiQueue (synchronizationWrapper[QUEUETYPE_WRITE], writeCmdBuffer, writeSync, readSync, *resource, queuePairs[pairNdx].familyIndexWrite, queuePairs[pairNdx].familyIndexRead, m_sharingMode);
548 endCommandBuffer (vk, writeCmdBuffer);
550 beginCommandBuffer (vk, readCmdBuffer);
551 createBarrierMultiQueue (synchronizationWrapper[QUEUETYPE_READ], readCmdBuffer, writeSync, readSync, *resource, queuePairs[pairNdx].familyIndexWrite, queuePairs[pairNdx].familyIndexRead, m_sharingMode, true);
552 readOp->recordCommands (readCmdBuffer);
553 endCommandBuffer (vk, readCmdBuffer);
555 VK_CHECK(synchronizationWrapper[QUEUETYPE_WRITE]->queueSubmit(queuePairs[pairNdx].queueWrite, DE_NULL));
556 VK_CHECK(synchronizationWrapper[QUEUETYPE_READ]->queueSubmit(queuePairs[pairNdx].queueRead, DE_NULL));
557 VK_CHECK(vk.queueWaitIdle(queuePairs[pairNdx].queueWrite));
558 VK_CHECK(vk.queueWaitIdle(queuePairs[pairNdx].queueRead));
561 const Data expected = writeOp->getData();
562 const Data actual = readOp->getData();
564 #ifdef CTS_USES_VULKANSC
565 if (m_context.getTestContext().getCommandLine().isSubProcess())
566 #endif // CTS_USES_VULKANSC
568 if (isIndirectBuffer(m_resourceDesc.type))
570 const deUint32 expectedValue = reinterpret_cast<const deUint32*>(expected.data)[0];
571 const deUint32 actualValue = reinterpret_cast<const deUint32*>(actual.data)[0];
573 if (actualValue < expectedValue)
574 return tcu::TestStatus::fail("Counter value is smaller than expected");
578 if (0 != deMemCmp(expected.data, actual.data, expected.size))
579 return tcu::TestStatus::fail("Memory contents don't match");
584 return tcu::TestStatus::pass("OK");
588 const VkSharingMode m_sharingMode;
592 inline SharedPtr<Move<T> > makeVkSharedPtr (Move<T> move)
594 return SharedPtr<Move<T> >(new Move<T>(move));
597 class TimelineSemaphoreTestInstance : public BaseTestInstance
600 TimelineSemaphoreTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const SharedPtr<OperationSupport>& writeOp, const SharedPtr<OperationSupport>& readOp, PipelineCacheData& pipelineCacheData, const VkSharingMode sharingMode)
601 : BaseTestInstance (context, type, resourceDesc, *writeOp, *readOp, pipelineCacheData, true)
602 , m_sharingMode (sharingMode)
604 deUint32 maxQueues = 0;
605 std::vector<deUint32> queueFamilies;
607 if (m_queues->totalQueueCount() < 2)
608 TCU_THROW(NotSupportedError, "Not enough queues");
610 for (deUint32 familyNdx = 0; familyNdx < m_queues->familyCount(); familyNdx++)
612 maxQueues = std::max(m_queues->queueFamilyCount(familyNdx), maxQueues);
613 queueFamilies.push_back(familyNdx);
616 // Create a chain of operations copying data from one resource
617 // to another across at least every single queue of the system
618 // at least once. Each of the operation will be executing with
619 // a dependency on the previous using timeline points.
620 m_opSupports.push_back(writeOp);
621 m_opQueues.push_back(m_queues->getDefaultQueue(writeOp->getQueueFlags(*m_opContext)));
623 for (deUint32 queueIdx = 0; queueIdx < maxQueues; queueIdx++)
625 for (deUint32 familyIdx = 0; familyIdx < m_queues->familyCount(); familyIdx++)
627 for (deUint32 copyOpIdx = 0; copyOpIdx < DE_LENGTH_OF_ARRAY(s_copyOps); copyOpIdx++)
629 if (isResourceSupported(s_copyOps[copyOpIdx], resourceDesc))
631 SharedPtr<OperationSupport> opSupport (makeOperationSupport(s_copyOps[copyOpIdx], m_resourceDesc).release());
633 if (!checkQueueFlags(opSupport->getQueueFlags(*m_opContext), m_queues->getQueueFamilyFlags(familyIdx)))
636 m_opSupports.push_back(opSupport);
637 m_opQueues.push_back(m_queues->getQueue(familyIdx, queueIdx % m_queues->queueFamilyCount(familyIdx)));
644 m_opSupports.push_back(readOp);
645 m_opQueues.push_back(m_queues->getDefaultQueue(readOp->getQueueFlags(*m_opContext)));
647 // Now create the resources with the usage associated to the
648 // operation performed on the resource.
649 for (deUint32 opIdx = 0; opIdx < (m_opSupports.size() - 1); opIdx++)
651 deUint32 usage = m_opSupports[opIdx]->getOutResourceUsageFlags() | m_opSupports[opIdx + 1]->getInResourceUsageFlags();
653 m_resources.push_back(SharedPtr<Resource>(new Resource(*m_opContext, m_resourceDesc, usage, m_sharingMode, queueFamilies)));
656 // Finally create the operations using the resources.
657 m_ops.push_back(SharedPtr<Operation>(m_opSupports[0]->build(*m_opContext, *m_resources[0]).release()));
658 for (deUint32 opIdx = 1; opIdx < (m_opSupports.size() - 1); opIdx++)
659 m_ops.push_back(SharedPtr<Operation>(m_opSupports[opIdx]->build(*m_opContext, *m_resources[opIdx - 1], *m_resources[opIdx]).release()));
660 m_ops.push_back(SharedPtr<Operation>(m_opSupports[m_opSupports.size() - 1]->build(*m_opContext, *m_resources.back()).release()));
663 tcu::TestStatus iterate (void)
665 const DeviceInterface& vk = m_opContext->getDeviceInterface();
666 const VkDevice device = m_opContext->getDevice();
667 de::Random rng (1234);
668 const Unique<VkSemaphore> semaphore (createSemaphoreType(vk, device, VK_SEMAPHORE_TYPE_TIMELINE));
669 std::vector<SharedPtr<Move<VkCommandPool> > > cmdPools;
670 std::vector<SharedPtr<Move<VkCommandBuffer> > > ptrCmdBuffers;
671 std::vector<VkCommandBufferSubmitInfoKHR> cmdBufferInfos;
672 std::vector<deUint64> timelineValues;
674 cmdPools.resize(m_queues->familyCount());
675 for (deUint32 familyIdx = 0; familyIdx < m_queues->familyCount(); familyIdx++)
676 cmdPools[familyIdx] = makeVkSharedPtr(createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, familyIdx));
678 ptrCmdBuffers.resize(m_ops.size());
679 cmdBufferInfos.resize(m_ops.size());
680 for (deUint32 opIdx = 0; opIdx < m_ops.size(); opIdx++)
682 deUint64 increment = 1 + rng.getUint8();
684 ptrCmdBuffers[opIdx] = makeVkSharedPtr(makeCommandBuffer(vk, device, **cmdPools[m_opQueues[opIdx].family]));
685 cmdBufferInfos[opIdx] = makeCommonCommandBufferSubmitInfo(**ptrCmdBuffers[opIdx]);
687 timelineValues.push_back(timelineValues.empty() ? increment : (timelineValues.back() + increment));
690 for (deUint32 opIdx = 0; opIdx < m_ops.size(); opIdx++)
692 VkCommandBuffer cmdBuffer = cmdBufferInfos[opIdx].commandBuffer;
693 VkSemaphoreSubmitInfoKHR waitSemaphoreSubmitInfo =
694 makeCommonSemaphoreSubmitInfo(*semaphore, (opIdx == 0 ? 0u : timelineValues[opIdx - 1]), VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR);
695 VkSemaphoreSubmitInfoKHR signalSemaphoreSubmitInfo =
696 makeCommonSemaphoreSubmitInfo(*semaphore, timelineValues[opIdx], VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR);
697 SynchronizationWrapperPtr synchronizationWrapper = getSynchronizationWrapper(m_type, vk, DE_TRUE);
699 synchronizationWrapper->addSubmitInfo(
700 opIdx == 0 ? 0u : 1u,
701 &waitSemaphoreSubmitInfo,
703 &cmdBufferInfos[opIdx],
705 &signalSemaphoreSubmitInfo,
706 opIdx == 0 ? DE_FALSE : DE_TRUE,
710 beginCommandBuffer(vk, cmdBuffer);
714 const SyncInfo writeSync = m_ops[opIdx - 1]->getOutSyncInfo();
715 const SyncInfo readSync = m_ops[opIdx]->getInSyncInfo();
716 const Resource& resource = *m_resources[opIdx - 1].get();
718 createBarrierMultiQueue(synchronizationWrapper, cmdBuffer, writeSync, readSync, resource, m_opQueues[opIdx - 1].family, m_opQueues[opIdx].family, m_sharingMode, true);
721 m_ops[opIdx]->recordCommands(cmdBuffer);
723 if (opIdx < (m_ops.size() - 1))
725 const SyncInfo writeSync = m_ops[opIdx]->getOutSyncInfo();
726 const SyncInfo readSync = m_ops[opIdx + 1]->getInSyncInfo();
727 const Resource& resource = *m_resources[opIdx].get();
729 createBarrierMultiQueue(synchronizationWrapper, cmdBuffer, writeSync, readSync, resource, m_opQueues[opIdx].family, m_opQueues[opIdx + 1].family, m_sharingMode);
732 endCommandBuffer(vk, cmdBuffer);
734 VK_CHECK(synchronizationWrapper->queueSubmit(m_opQueues[opIdx].queue, DE_NULL));
738 VK_CHECK(vk.queueWaitIdle(m_opQueues.back().queue));
741 const Data expected = m_ops.front()->getData();
742 const Data actual = m_ops.back()->getData();
744 if (isIndirectBuffer(m_resourceDesc.type))
746 const deUint32 expectedValue = reinterpret_cast<const deUint32*>(expected.data)[0];
747 const deUint32 actualValue = reinterpret_cast<const deUint32*>(actual.data)[0];
749 if (actualValue < expectedValue)
750 return tcu::TestStatus::fail("Counter value is smaller than expected");
754 if (0 != deMemCmp(expected.data, actual.data, expected.size))
755 return tcu::TestStatus::fail("Memory contents don't match");
759 // Make the validation layers happy.
760 for (deUint32 opIdx = 0; opIdx < m_opQueues.size(); opIdx++)
761 VK_CHECK(vk.queueWaitIdle(m_opQueues[opIdx].queue));
763 return tcu::TestStatus::pass("OK");
767 const VkSharingMode m_sharingMode;
768 std::vector<SharedPtr<OperationSupport> > m_opSupports;
769 std::vector<SharedPtr<Operation> > m_ops;
770 std::vector<SharedPtr<Resource> > m_resources;
771 std::vector<Queue> m_opQueues;
774 class FenceTestInstance : public BaseTestInstance
777 FenceTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData, const VkSharingMode sharingMode)
778 : BaseTestInstance (context, type, resourceDesc, writeOp, readOp, pipelineCacheData, false)
779 , m_sharingMode (sharingMode)
783 tcu::TestStatus iterate (void)
785 const DeviceInterface& vk = m_opContext->getDeviceInterface();
786 const VkDevice device = m_opContext->getDevice();
787 const std::vector<QueuePair> queuePairs = m_queues->getQueuesPairs(m_writeOp.getQueueFlags(*m_opContext), m_readOp.getQueueFlags(*m_opContext));
789 for (deUint32 pairNdx = 0; pairNdx < static_cast<deUint32>(queuePairs.size()); ++pairNdx)
791 const UniquePtr<Resource> resource (new Resource(*m_opContext, m_resourceDesc, m_writeOp.getOutResourceUsageFlags() | m_readOp.getInResourceUsageFlags()));
792 const UniquePtr<Operation> writeOp (m_writeOp.build(*m_opContext, *resource));
793 const UniquePtr<Operation> readOp (m_readOp.build(*m_opContext, *resource));
794 const Move<VkCommandPool> cmdPool[]
796 createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queuePairs[pairNdx].familyIndexWrite),
797 createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queuePairs[pairNdx].familyIndexRead)
799 const Move<VkCommandBuffer> ptrCmdBuffer[]
801 makeCommandBuffer(vk, device, *cmdPool[QUEUETYPE_WRITE]),
802 makeCommandBuffer(vk, device, *cmdPool[QUEUETYPE_READ])
804 const VkCommandBufferSubmitInfoKHR cmdBufferInfos[]
806 makeCommonCommandBufferSubmitInfo(*ptrCmdBuffer[QUEUETYPE_WRITE]),
807 makeCommonCommandBufferSubmitInfo(*ptrCmdBuffer[QUEUETYPE_READ])
809 SynchronizationWrapperPtr synchronizationWrapper[]
811 getSynchronizationWrapper(m_type, vk, DE_FALSE),
812 getSynchronizationWrapper(m_type, vk, DE_FALSE),
814 const SyncInfo writeSync = writeOp->getOutSyncInfo();
815 const SyncInfo readSync = readOp->getInSyncInfo();
816 VkCommandBuffer writeCmdBuffer = cmdBufferInfos[QUEUETYPE_WRITE].commandBuffer;
817 VkCommandBuffer readCmdBuffer = cmdBufferInfos[QUEUETYPE_READ].commandBuffer;
819 beginCommandBuffer (vk, writeCmdBuffer);
820 writeOp->recordCommands (writeCmdBuffer);
821 createBarrierMultiQueue (synchronizationWrapper[QUEUETYPE_WRITE], writeCmdBuffer, writeSync, readSync, *resource, queuePairs[pairNdx].familyIndexWrite, queuePairs[pairNdx].familyIndexRead, m_sharingMode);
822 endCommandBuffer (vk, writeCmdBuffer);
824 submitCommandsAndWait (synchronizationWrapper[QUEUETYPE_WRITE], vk, device, queuePairs[pairNdx].queueWrite, writeCmdBuffer);
826 beginCommandBuffer (vk, readCmdBuffer);
827 createBarrierMultiQueue (synchronizationWrapper[QUEUETYPE_READ], readCmdBuffer, writeSync, readSync, *resource, queuePairs[pairNdx].familyIndexWrite, queuePairs[pairNdx].familyIndexRead, m_sharingMode, true);
828 readOp->recordCommands (readCmdBuffer);
829 endCommandBuffer (vk, readCmdBuffer);
831 submitCommandsAndWait(synchronizationWrapper[QUEUETYPE_READ], vk, device, queuePairs[pairNdx].queueRead, readCmdBuffer);
834 const Data expected = writeOp->getData();
835 const Data actual = readOp->getData();
837 #ifdef CTS_USES_VULKANSC
838 if (m_context.getTestContext().getCommandLine().isSubProcess())
839 #endif // CTS_USES_VULKANSC
841 if (isIndirectBuffer(m_resourceDesc.type))
843 const deUint32 expectedValue = reinterpret_cast<const deUint32*>(expected.data)[0];
844 const deUint32 actualValue = reinterpret_cast<const deUint32*>(actual.data)[0];
846 if (actualValue < expectedValue)
847 return tcu::TestStatus::fail("Counter value is smaller than expected");
851 if (0 != deMemCmp(expected.data, actual.data, expected.size))
852 return tcu::TestStatus::fail("Memory contents don't match");
857 return tcu::TestStatus::pass("OK");
861 const VkSharingMode m_sharingMode;
864 class BaseTestCase : public TestCase
867 BaseTestCase (tcu::TestContext& testCtx,
868 const std::string& name,
869 const std::string& description,
870 SynchronizationType type,
871 const SyncPrimitive syncPrimitive,
872 const ResourceDescription resourceDesc,
873 const OperationName writeOp,
874 const OperationName readOp,
875 const VkSharingMode sharingMode,
876 PipelineCacheData& pipelineCacheData)
877 : TestCase (testCtx, name, description)
879 , m_resourceDesc (resourceDesc)
880 , m_writeOp (makeOperationSupport(writeOp, resourceDesc).release())
881 , m_readOp (makeOperationSupport(readOp, resourceDesc).release())
882 , m_syncPrimitive (syncPrimitive)
883 , m_sharingMode (sharingMode)
884 , m_pipelineCacheData (pipelineCacheData)
888 void initPrograms (SourceCollections& programCollection) const
890 m_writeOp->initPrograms(programCollection);
891 m_readOp->initPrograms(programCollection);
893 if (m_syncPrimitive == SYNC_PRIMITIVE_TIMELINE_SEMAPHORE)
895 for (deUint32 copyOpNdx = 0; copyOpNdx < DE_LENGTH_OF_ARRAY(s_copyOps); copyOpNdx++)
897 if (isResourceSupported(s_copyOps[copyOpNdx], m_resourceDesc))
898 makeOperationSupport(s_copyOps[copyOpNdx], m_resourceDesc)->initPrograms(programCollection);
903 void checkSupport(Context& context) const
905 if (m_type == SynchronizationType::SYNCHRONIZATION2)
906 context.requireDeviceFunctionality("VK_KHR_synchronization2");
907 if (m_syncPrimitive == SYNC_PRIMITIVE_TIMELINE_SEMAPHORE)
908 context.requireDeviceFunctionality("VK_KHR_timeline_semaphore");
910 const InstanceInterface& instance = context.getInstanceInterface();
911 const VkPhysicalDevice physicalDevice = context.getPhysicalDevice();
912 const std::vector<VkQueueFamilyProperties> queueFamilyProperties = getPhysicalDeviceQueueFamilyProperties(instance, physicalDevice);
913 if (m_sharingMode == VK_SHARING_MODE_CONCURRENT && queueFamilyProperties.size() < 2)
914 TCU_THROW(NotSupportedError, "Concurrent requires more than 1 queue family");
916 if (m_syncPrimitive == SYNC_PRIMITIVE_TIMELINE_SEMAPHORE &&
917 !context.getTimelineSemaphoreFeatures().timelineSemaphore)
918 TCU_THROW(NotSupportedError, "Timeline semaphore not supported");
920 if (m_resourceDesc.type == RESOURCE_TYPE_IMAGE)
922 VkImageFormatProperties imageFormatProperties;
923 const deUint32 usage = m_writeOp->getOutResourceUsageFlags() | m_readOp->getInResourceUsageFlags();
924 const VkResult formatResult = instance.getPhysicalDeviceImageFormatProperties(physicalDevice, m_resourceDesc.imageFormat, m_resourceDesc.imageType, VK_IMAGE_TILING_OPTIMAL, usage, (VkImageCreateFlags)0, &imageFormatProperties);
926 if (formatResult != VK_SUCCESS)
927 TCU_THROW(NotSupportedError, "Image format is not supported");
929 if ((imageFormatProperties.sampleCounts & m_resourceDesc.imageSamples) != m_resourceDesc.imageSamples)
930 TCU_THROW(NotSupportedError, "Requested sample count is not supported");
934 TestInstance* createInstance (Context& context) const
936 switch (m_syncPrimitive)
938 case SYNC_PRIMITIVE_FENCE:
939 return new FenceTestInstance(context, m_type, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData, m_sharingMode);
940 case SYNC_PRIMITIVE_BINARY_SEMAPHORE:
941 return new BinarySemaphoreTestInstance(context, m_type, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData, m_sharingMode);
942 case SYNC_PRIMITIVE_TIMELINE_SEMAPHORE:
943 return new TimelineSemaphoreTestInstance(context, m_type, m_resourceDesc, m_writeOp, m_readOp, m_pipelineCacheData, m_sharingMode);
951 const SynchronizationType m_type;
952 const ResourceDescription m_resourceDesc;
953 const SharedPtr<OperationSupport> m_writeOp;
954 const SharedPtr<OperationSupport> m_readOp;
955 const SyncPrimitive m_syncPrimitive;
956 const VkSharingMode m_sharingMode;
957 PipelineCacheData& m_pipelineCacheData;
962 SynchronizationType type;
963 PipelineCacheData* pipelineCacheData;
966 void createTests (tcu::TestCaseGroup* group, TestData data)
968 tcu::TestContext& testCtx = group->getTestContext();
973 SyncPrimitive syncPrimitive;
977 { "fence", SYNC_PRIMITIVE_FENCE, 1 },
978 { "binary_semaphore", SYNC_PRIMITIVE_BINARY_SEMAPHORE, 1 },
979 { "timeline_semaphore", SYNC_PRIMITIVE_TIMELINE_SEMAPHORE, 1 }
982 for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(groups); ++groupNdx)
984 MovePtr<tcu::TestCaseGroup> synchGroup (new tcu::TestCaseGroup(testCtx, groups[groupNdx].name, ""));
986 for (int writeOpNdx = 0; writeOpNdx < DE_LENGTH_OF_ARRAY(s_writeOps); ++writeOpNdx)
987 for (int readOpNdx = 0; readOpNdx < DE_LENGTH_OF_ARRAY(s_readOps); ++readOpNdx)
989 const OperationName writeOp = s_writeOps[writeOpNdx];
990 const OperationName readOp = s_readOps[readOpNdx];
991 const std::string opGroupName = getOperationName(writeOp) + "_" + getOperationName(readOp);
994 MovePtr<tcu::TestCaseGroup> opGroup (new tcu::TestCaseGroup(testCtx, opGroupName.c_str(), ""));
996 for (int optionNdx = 0; optionNdx <= groups[groupNdx].numOptions; ++optionNdx)
997 for (int resourceNdx = 0; resourceNdx < DE_LENGTH_OF_ARRAY(s_resources); ++resourceNdx)
999 const ResourceDescription& resource = s_resources[resourceNdx];
1000 if (isResourceSupported(writeOp, resource) && isResourceSupported(readOp, resource))
1002 std::string name = getResourceName(resource);
1003 VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1005 // queue family sharing mode used for resource
1008 name += "_concurrent";
1009 sharingMode = VK_SHARING_MODE_CONCURRENT;
1012 name += "_exclusive";
1014 opGroup->addChild(new BaseTestCase(testCtx, name, "", data.type, groups[groupNdx].syncPrimitive, resource, writeOp, readOp, sharingMode, *data.pipelineCacheData));
1019 synchGroup->addChild(opGroup.release());
1021 group->addChild(synchGroup.release());
1025 void cleanupGroup (tcu::TestCaseGroup* group, TestData data)
1028 DE_UNREF(data.pipelineCacheData);
1029 // Destroy singleton object
1030 MultiQueues::destroy();
1035 tcu::TestCaseGroup* createSynchronizedOperationMultiQueueTests (tcu::TestContext& testCtx, SynchronizationType type, PipelineCacheData& pipelineCacheData)
1043 return createTestGroup(testCtx, "multi_queue", "Synchronization of a memory-modifying operation", createTests, data, cleanupGroup);
1046 } // synchronization