/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2016 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Synchronization primitive tests with single queue *//*--------------------------------------------------------------------*/ #include "vktSynchronizationOperationSingleQueueTests.hpp" #include "vkDefs.hpp" #include "vktTestCase.hpp" #include "vktTestCaseUtil.hpp" #include "vktTestGroupUtil.hpp" #include "vkRef.hpp" #include "vkRefUtil.hpp" #include "vkMemUtil.hpp" #include "vkBarrierUtil.hpp" #include "vkQueryUtil.hpp" #include "vkCmdUtil.hpp" #include "vkTypeUtil.hpp" #include "vkCmdUtil.hpp" #include "deRandom.hpp" #include "deUniquePtr.hpp" #include "tcuTestLog.hpp" #include "vktSynchronizationUtil.hpp" #include "vktSynchronizationOperation.hpp" #include "vktSynchronizationOperationTestData.hpp" #include "vktSynchronizationOperationResources.hpp" namespace vkt { namespace synchronization { namespace { using namespace vk; using tcu::TestLog; class BaseTestInstance : public TestInstance { public: BaseTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData) : TestInstance (context) , m_type (type) , m_opContext (context, type, pipelineCacheData) , m_resource (new Resource(m_opContext, resourceDesc, writeOp.getOutResourceUsageFlags() | readOp.getInResourceUsageFlags())) , m_writeOp (writeOp.build(m_opContext, *m_resource)) , m_readOp (readOp.build(m_opContext, *m_resource)) { } protected: SynchronizationType m_type; OperationContext m_opContext; const de::UniquePtr m_resource; const de::UniquePtr m_writeOp; const de::UniquePtr m_readOp; }; class EventTestInstance : public BaseTestInstance { public: EventTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData) : BaseTestInstance (context, type, resourceDesc, writeOp, readOp, pipelineCacheData) { } tcu::TestStatus iterate (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); const VkQueue queue = m_context.getUniversalQueue(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); const Unique cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex)); const Unique cmdBuffer (makeCommandBuffer(vk, device, *cmdPool)); const Unique event (createEvent(vk, device)); const SyncInfo writeSync = m_writeOp->getOutSyncInfo(); const SyncInfo readSync = m_readOp->getInSyncInfo(); SynchronizationWrapperPtr synchronizationWrapper = getSynchronizationWrapper(m_type, vk, DE_FALSE); beginCommandBuffer(vk, *cmdBuffer); m_writeOp->recordCommands(*cmdBuffer); if (m_resource->getType() == RESOURCE_TYPE_IMAGE) { const VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2( writeSync.stageMask, // VkPipelineStageFlags2KHR srcStageMask writeSync.accessMask, // VkAccessFlags2KHR srcAccessMask readSync.stageMask, // VkPipelineStageFlags2KHR dstStageMask readSync.accessMask, // VkAccessFlags2KHR dstAccessMask writeSync.imageLayout, // VkImageLayout oldLayout readSync.imageLayout, // VkImageLayout newLayout m_resource->getImage().handle, // VkImage image m_resource->getImage().subresourceRange // VkImageSubresourceRange subresourceRange ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2, DE_TRUE); synchronizationWrapper->cmdSetEvent(*cmdBuffer, *event, &dependencyInfo); synchronizationWrapper->cmdWaitEvents(*cmdBuffer, 1u, &event.get(), &dependencyInfo); } else { const VkBufferMemoryBarrier2KHR bufferMemoryBarrier2 = makeBufferMemoryBarrier2( writeSync.stageMask, // VkPipelineStageFlags2KHR srcStageMask writeSync.accessMask, // VkAccessFlags2KHR srcAccessMask readSync.stageMask, // VkPipelineStageFlags2KHR dstStageMask readSync.accessMask, // VkAccessFlags2KHR dstAccessMask m_resource->getBuffer().handle, // VkBuffer buffer m_resource->getBuffer().offset, // VkDeviceSize offset m_resource->getBuffer().size // VkDeviceSize size ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, &bufferMemoryBarrier2, DE_NULL, DE_TRUE); synchronizationWrapper->cmdSetEvent(*cmdBuffer, *event, &dependencyInfo); synchronizationWrapper->cmdWaitEvents(*cmdBuffer, 1u, &event.get(), &dependencyInfo); } m_readOp->recordCommands(*cmdBuffer); endCommandBuffer(vk, *cmdBuffer); submitCommandsAndWait(synchronizationWrapper, vk, device, queue, *cmdBuffer); { const Data expected = m_writeOp->getData(); const Data actual = m_readOp->getData(); if (isIndirectBuffer(m_resource->getType())) { const deUint32 expectedValue = reinterpret_cast(expected.data)[0]; const deUint32 actualValue = reinterpret_cast(actual.data)[0]; if (actualValue < expectedValue) return tcu::TestStatus::fail("Counter value is smaller than expected"); } else { if (0 != deMemCmp(expected.data, actual.data, expected.size)) return tcu::TestStatus::fail("Memory contents don't match"); } } return tcu::TestStatus::pass("OK"); } }; class BarrierTestInstance : public BaseTestInstance { public: BarrierTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData) : BaseTestInstance (context, type, resourceDesc, writeOp, readOp, pipelineCacheData) { } tcu::TestStatus iterate (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); const VkQueue queue = m_context.getUniversalQueue(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); const Unique cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex)); const Move cmdBuffer (makeCommandBuffer(vk, device, *cmdPool)); const SyncInfo writeSync = m_writeOp->getOutSyncInfo(); const SyncInfo readSync = m_readOp->getInSyncInfo(); SynchronizationWrapperPtr synchronizationWrapper = getSynchronizationWrapper(m_type, vk, DE_FALSE); beginCommandBuffer(vk, *cmdBuffer); m_writeOp->recordCommands(*cmdBuffer); if (m_resource->getType() == RESOURCE_TYPE_IMAGE) { const VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2( writeSync.stageMask, // VkPipelineStageFlags2KHR srcStageMask writeSync.accessMask, // VkAccessFlags2KHR srcAccessMask readSync.stageMask, // VkPipelineStageFlags2KHR dstStageMask readSync.accessMask, // VkAccessFlags2KHR dstAccessMask writeSync.imageLayout, // VkImageLayout oldLayout readSync.imageLayout, // VkImageLayout newLayout m_resource->getImage().handle, // VkImage image m_resource->getImage().subresourceRange // VkImageSubresourceRange subresourceRange ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2); synchronizationWrapper->cmdPipelineBarrier(*cmdBuffer, &dependencyInfo); } else { const VkBufferMemoryBarrier2KHR bufferMemoryBarrier2 = makeBufferMemoryBarrier2( writeSync.stageMask, // VkPipelineStageFlags2KHR srcStageMask writeSync.accessMask, // VkAccessFlags2KHR srcAccessMask readSync.stageMask, // VkPipelineStageFlags2KHR dstStageMask readSync.accessMask, // VkAccessFlags2KHR dstAccessMask m_resource->getBuffer().handle, // VkBuffer buffer m_resource->getBuffer().offset, // VkDeviceSize offset m_resource->getBuffer().size // VkDeviceSize size ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, &bufferMemoryBarrier2); synchronizationWrapper->cmdPipelineBarrier(*cmdBuffer, &dependencyInfo); } m_readOp->recordCommands(*cmdBuffer); endCommandBuffer(vk, *cmdBuffer); submitCommandsAndWait(synchronizationWrapper, vk, device, queue, *cmdBuffer); { const Data expected = m_writeOp->getData(); const Data actual = m_readOp->getData(); if (isIndirectBuffer(m_resource->getType())) { const deUint32 expectedValue = reinterpret_cast(expected.data)[0]; const deUint32 actualValue = reinterpret_cast(actual.data)[0]; if (actualValue < expectedValue) return tcu::TestStatus::fail("Counter value is smaller than expected"); } else { if (0 != deMemCmp(expected.data, actual.data, expected.size)) return tcu::TestStatus::fail("Memory contents don't match"); } } return tcu::TestStatus::pass("OK"); } }; class BinarySemaphoreTestInstance : public BaseTestInstance { public: BinarySemaphoreTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData) : BaseTestInstance (context, type, resourceDesc, writeOp, readOp, pipelineCacheData) { } tcu::TestStatus iterate (void) { enum {WRITE=0, READ, COUNT}; const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); const VkQueue queue = m_context.getUniversalQueue(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); const Unique semaphore (createSemaphore (vk, device)); const Unique cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex)); const Move ptrCmdBuffer[COUNT] = {makeCommandBuffer(vk, device, *cmdPool), makeCommandBuffer(vk, device, *cmdPool)}; VkCommandBuffer cmdBuffers[COUNT] = {*ptrCmdBuffer[WRITE], *ptrCmdBuffer[READ]}; SynchronizationWrapperPtr synchronizationWrapper = getSynchronizationWrapper(m_type, vk, DE_FALSE, 2u); const SyncInfo writeSync = m_writeOp->getOutSyncInfo(); const SyncInfo readSync = m_readOp->getInSyncInfo(); VkSemaphoreSubmitInfoKHR signalSemaphoreSubmitInfo = makeCommonSemaphoreSubmitInfo(*semaphore, 0u, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR); VkSemaphoreSubmitInfoKHR waitSemaphoreSubmitInfo = makeCommonSemaphoreSubmitInfo(*semaphore, 0u, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR); VkCommandBufferSubmitInfoKHR commandBufferSubmitInfo[] { makeCommonCommandBufferSubmitInfo(cmdBuffers[WRITE]), makeCommonCommandBufferSubmitInfo(cmdBuffers[READ]) }; synchronizationWrapper->addSubmitInfo( 0u, DE_NULL, 1u, &commandBufferSubmitInfo[WRITE], 1u, &signalSemaphoreSubmitInfo ); synchronizationWrapper->addSubmitInfo( 1u, &waitSemaphoreSubmitInfo, 1u, &commandBufferSubmitInfo[READ], 0u, DE_NULL ); beginCommandBuffer(vk, cmdBuffers[WRITE]); m_writeOp->recordCommands(cmdBuffers[WRITE]); if (m_resource->getType() == RESOURCE_TYPE_IMAGE) { const VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2( writeSync.stageMask, // VkPipelineStageFlags2KHR srcStageMask writeSync.accessMask, // VkAccessFlags2KHR srcAccessMask readSync.stageMask, // VkPipelineStageFlags2KHR dstStageMask readSync.accessMask, // VkAccessFlags2KHR dstAccessMask writeSync.imageLayout, // VkImageLayout oldLayout readSync.imageLayout, // VkImageLayout newLayout m_resource->getImage().handle, // VkImage image m_resource->getImage().subresourceRange // VkImageSubresourceRange subresourceRange ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2); synchronizationWrapper->cmdPipelineBarrier(cmdBuffers[WRITE], &dependencyInfo); } else { const VkBufferMemoryBarrier2KHR bufferMemoryBarrier2 = makeBufferMemoryBarrier2( writeSync.stageMask, // VkPipelineStageFlags2KHR srcStageMask writeSync.accessMask, // VkAccessFlags2KHR srcAccessMask readSync.stageMask, // VkPipelineStageFlags2KHR dstStageMask readSync.accessMask, // VkAccessFlags2KHR dstAccessMask m_resource->getBuffer().handle, // VkBuffer buffer 0, // VkDeviceSize offset VK_WHOLE_SIZE // VkDeviceSize size ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, &bufferMemoryBarrier2); synchronizationWrapper->cmdPipelineBarrier(cmdBuffers[WRITE], &dependencyInfo); } endCommandBuffer(vk, cmdBuffers[WRITE]); beginCommandBuffer(vk, cmdBuffers[READ]); m_readOp->recordCommands(cmdBuffers[READ]); endCommandBuffer(vk, cmdBuffers[READ]); VK_CHECK(synchronizationWrapper->queueSubmit(queue, DE_NULL)); VK_CHECK(vk.queueWaitIdle(queue)); { const Data expected = m_writeOp->getData(); const Data actual = m_readOp->getData(); if (isIndirectBuffer(m_resource->getType())) { const deUint32 expectedValue = reinterpret_cast(expected.data)[0]; const deUint32 actualValue = reinterpret_cast(actual.data)[0]; if (actualValue < expectedValue) return tcu::TestStatus::fail("Counter value is smaller than expected"); } else { if (0 != deMemCmp(expected.data, actual.data, expected.size)) return tcu::TestStatus::fail("Memory contents don't match"); } } return tcu::TestStatus::pass("OK"); } }; template inline de::SharedPtr > makeVkSharedPtr (Move move) { return de::SharedPtr >(new Move(move)); } class TimelineSemaphoreTestInstance : public TestInstance { public: TimelineSemaphoreTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const de::SharedPtr& writeOp, const de::SharedPtr& readOp, PipelineCacheData& pipelineCacheData) : TestInstance (context) , m_type (type) , m_opContext (context, type, pipelineCacheData) { // Create a chain operation copying data from one resource to // another, each of the operation will be executing with a // dependency on the previous using timeline points. m_opSupports.push_back(writeOp); for (deUint32 copyOpNdx = 0; copyOpNdx < DE_LENGTH_OF_ARRAY(s_copyOps); copyOpNdx++) { if (isResourceSupported(s_copyOps[copyOpNdx], resourceDesc)) m_opSupports.push_back(de::SharedPtr(makeOperationSupport(s_copyOps[copyOpNdx], resourceDesc, false).release())); } m_opSupports.push_back(readOp); for (deUint32 opNdx = 0; opNdx < (m_opSupports.size() - 1); opNdx++) { deUint32 usage = m_opSupports[opNdx]->getOutResourceUsageFlags() | m_opSupports[opNdx + 1]->getInResourceUsageFlags(); m_resources.push_back(de::SharedPtr(new Resource(m_opContext, resourceDesc, usage))); } m_ops.push_back(de::SharedPtr(m_opSupports[0]->build(m_opContext, *m_resources[0]).release())); for (deUint32 opNdx = 1; opNdx < (m_opSupports.size() - 1); opNdx++) m_ops.push_back(de::SharedPtr(m_opSupports[opNdx]->build(m_opContext, *m_resources[opNdx - 1], *m_resources[opNdx]).release())); m_ops.push_back(de::SharedPtr(m_opSupports[m_opSupports.size() - 1]->build(m_opContext, *m_resources.back()).release())); } tcu::TestStatus iterate (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); const VkQueue queue = m_context.getUniversalQueue(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); de::Random rng (1234); const Unique semaphore (createSemaphoreType(vk, device, VK_SEMAPHORE_TYPE_TIMELINE)); const Unique cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex)); std::vector > > ptrCmdBuffers; std::vector cmdBuffersInfo (m_ops.size(), makeCommonCommandBufferSubmitInfo(0u)); std::vector waitSemaphoreSubmitInfos (m_ops.size(), makeCommonSemaphoreSubmitInfo(*semaphore, 0u, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR)); std::vector signalSemaphoreSubmitInfos (m_ops.size(), makeCommonSemaphoreSubmitInfo(*semaphore, 0u, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR)); SynchronizationWrapperPtr synchronizationWrapper = getSynchronizationWrapper(m_type, vk, DE_TRUE, static_cast(m_ops.size())); deUint64 increment = 0u; for (deUint32 opNdx = 0; opNdx < m_ops.size(); opNdx++) { ptrCmdBuffers.push_back(makeVkSharedPtr(makeCommandBuffer(vk, device, *cmdPool))); cmdBuffersInfo[opNdx].commandBuffer = **(ptrCmdBuffers.back()); } for (deUint32 opNdx = 0; opNdx < m_ops.size(); opNdx++) { increment += (1 + rng.getUint8()); signalSemaphoreSubmitInfos[opNdx].value = increment; waitSemaphoreSubmitInfos[opNdx].value = increment; synchronizationWrapper->addSubmitInfo( opNdx == 0 ? 0u : 1u, opNdx == 0 ? DE_NULL : &waitSemaphoreSubmitInfos[opNdx-1], 1u, &cmdBuffersInfo[opNdx], 1u, &signalSemaphoreSubmitInfos[opNdx], opNdx == 0 ? DE_FALSE : DE_TRUE, DE_TRUE ); VkCommandBuffer cmdBuffer = cmdBuffersInfo[opNdx].commandBuffer; beginCommandBuffer(vk, cmdBuffer); if (opNdx > 0) { const SyncInfo lastSync = m_ops[opNdx - 1]->getOutSyncInfo(); const SyncInfo currentSync = m_ops[opNdx]->getInSyncInfo(); const Resource& resource = *m_resources[opNdx - 1].get(); if (resource.getType() == RESOURCE_TYPE_IMAGE) { DE_ASSERT(lastSync.imageLayout != VK_IMAGE_LAYOUT_UNDEFINED); DE_ASSERT(currentSync.imageLayout != VK_IMAGE_LAYOUT_UNDEFINED); const VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2( lastSync.stageMask, // VkPipelineStageFlags2KHR srcStageMask lastSync.accessMask, // VkAccessFlags2KHR srcAccessMask currentSync.stageMask, // VkPipelineStageFlags2KHR dstStageMask currentSync.accessMask, // VkAccessFlags2KHR dstAccessMask lastSync.imageLayout, // VkImageLayout oldLayout currentSync.imageLayout, // VkImageLayout newLayout resource.getImage().handle, // VkImage image resource.getImage().subresourceRange // VkImageSubresourceRange subresourceRange ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2); synchronizationWrapper->cmdPipelineBarrier(cmdBuffer, &dependencyInfo); } else { const VkBufferMemoryBarrier2KHR bufferMemoryBarrier2 = makeBufferMemoryBarrier2( lastSync.stageMask, // VkPipelineStageFlags2KHR srcStageMask lastSync.accessMask, // VkAccessFlags2KHR srcAccessMask currentSync.stageMask, // VkPipelineStageFlags2KHR dstStageMask currentSync.accessMask, // VkAccessFlags2KHR dstAccessMask resource.getBuffer().handle, // VkBuffer buffer 0, // VkDeviceSize offset VK_WHOLE_SIZE // VkDeviceSize size ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, &bufferMemoryBarrier2); synchronizationWrapper->cmdPipelineBarrier(cmdBuffer, &dependencyInfo); } } m_ops[opNdx]->recordCommands(cmdBuffer); endCommandBuffer(vk, cmdBuffer); } VK_CHECK(synchronizationWrapper->queueSubmit(queue, DE_NULL)); VK_CHECK(vk.queueWaitIdle(queue)); { const Data expected = m_ops.front()->getData(); const Data actual = m_ops.back()->getData(); if (isIndirectBuffer(m_resources[0]->getType())) { const deUint32 expectedValue = reinterpret_cast(expected.data)[0]; const deUint32 actualValue = reinterpret_cast(actual.data)[0]; if (actualValue < expectedValue) return tcu::TestStatus::fail("Counter value is smaller than expected"); } else { if (0 != deMemCmp(expected.data, actual.data, expected.size)) return tcu::TestStatus::fail("Memory contents don't match"); } } return tcu::TestStatus::pass("OK"); } protected: SynchronizationType m_type; OperationContext m_opContext; std::vector > m_opSupports; std::vector > m_ops; std::vector > m_resources; }; class FenceTestInstance : public BaseTestInstance { public: FenceTestInstance (Context& context, SynchronizationType type, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData) : BaseTestInstance (context, type, resourceDesc, writeOp, readOp, pipelineCacheData) { } tcu::TestStatus iterate (void) { enum {WRITE=0, READ, COUNT}; const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); const VkQueue queue = m_context.getUniversalQueue(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); const Unique cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex)); const Move ptrCmdBuffer[COUNT] = {makeCommandBuffer(vk, device, *cmdPool), makeCommandBuffer(vk, device, *cmdPool)}; VkCommandBuffer cmdBuffers[COUNT] = {*ptrCmdBuffer[WRITE], *ptrCmdBuffer[READ]}; const SyncInfo writeSync = m_writeOp->getOutSyncInfo(); const SyncInfo readSync = m_readOp->getInSyncInfo(); SynchronizationWrapperPtr synchronizationWrapper[COUNT] { getSynchronizationWrapper(m_type, vk, DE_FALSE), getSynchronizationWrapper(m_type, vk, DE_FALSE) }; beginCommandBuffer(vk, cmdBuffers[WRITE]); m_writeOp->recordCommands(cmdBuffers[WRITE]); if (m_resource->getType() == RESOURCE_TYPE_IMAGE) { const VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2( writeSync.stageMask, // VkPipelineStageFlags2KHR srcStageMask writeSync.accessMask, // VkAccessFlags2KHR srcAccessMask readSync.stageMask, // VkPipelineStageFlags2KHR dstStageMask readSync.accessMask, // VkAccessFlags2KHR dstAccessMask writeSync.imageLayout, // VkImageLayout oldLayout readSync.imageLayout, // VkImageLayout newLayout m_resource->getImage().handle, // VkImage image m_resource->getImage().subresourceRange // VkImageSubresourceRange subresourceRange ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2); synchronizationWrapper[WRITE]->cmdPipelineBarrier(cmdBuffers[WRITE], &dependencyInfo); } endCommandBuffer(vk, cmdBuffers[WRITE]); submitCommandsAndWait(synchronizationWrapper[WRITE], vk, device, queue, cmdBuffers[WRITE]); beginCommandBuffer(vk, cmdBuffers[READ]); m_readOp->recordCommands(cmdBuffers[READ]); endCommandBuffer(vk, cmdBuffers[READ]); submitCommandsAndWait(synchronizationWrapper[READ], vk, device, queue, cmdBuffers[READ]); { const Data expected = m_writeOp->getData(); const Data actual = m_readOp->getData(); if (isIndirectBuffer(m_resource->getType())) { const deUint32 expectedValue = reinterpret_cast(expected.data)[0]; const deUint32 actualValue = reinterpret_cast(actual.data)[0]; if (actualValue < expectedValue) return tcu::TestStatus::fail("Counter value is smaller than expected"); } else { if (0 != deMemCmp(expected.data, actual.data, expected.size)) return tcu::TestStatus::fail("Memory contents don't match"); } } return tcu::TestStatus::pass("OK"); } }; class SyncTestCase : public TestCase { public: SyncTestCase (tcu::TestContext& testCtx, const std::string& name, SynchronizationType type, const SyncPrimitive syncPrimitive, const ResourceDescription resourceDesc, const OperationName writeOp, const OperationName readOp, const bool specializedAccess, PipelineCacheData& pipelineCacheData) : TestCase (testCtx, name) , m_type (type) , m_resourceDesc (resourceDesc) , m_writeOp (makeOperationSupport(writeOp, resourceDesc, specializedAccess).release()) , m_readOp (makeOperationSupport(readOp, resourceDesc, specializedAccess).release()) , m_syncPrimitive (syncPrimitive) , m_pipelineCacheData (pipelineCacheData) { } void initPrograms (SourceCollections& programCollection) const { m_writeOp->initPrograms(programCollection); m_readOp->initPrograms(programCollection); if (m_syncPrimitive == SYNC_PRIMITIVE_TIMELINE_SEMAPHORE) { for (deUint32 copyOpNdx = 0; copyOpNdx < DE_LENGTH_OF_ARRAY(s_copyOps); copyOpNdx++) { if (isResourceSupported(s_copyOps[copyOpNdx], m_resourceDesc)) makeOperationSupport(s_copyOps[copyOpNdx], m_resourceDesc, false)->initPrograms(programCollection); } } } void checkSupport(Context& context) const { if (m_type == SynchronizationType::SYNCHRONIZATION2) context.requireDeviceFunctionality("VK_KHR_synchronization2"); #ifndef CTS_USES_VULKANSC if (SYNC_PRIMITIVE_EVENT == m_syncPrimitive && context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") && !context.getPortabilitySubsetFeatures().events) { TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: Events are not supported by this implementation"); } #endif // CTS_USES_VULKANSC if (m_syncPrimitive == SYNC_PRIMITIVE_TIMELINE_SEMAPHORE && !context.getTimelineSemaphoreFeatures().timelineSemaphore) TCU_THROW(NotSupportedError, "Timeline semaphore not supported"); if (m_resourceDesc.type == RESOURCE_TYPE_IMAGE) { VkImageFormatProperties imageFormatProperties; const deUint32 usage = m_writeOp->getOutResourceUsageFlags() | m_readOp->getInResourceUsageFlags(); const InstanceInterface& instance = context.getInstanceInterface(); const VkPhysicalDevice physicalDevice = context.getPhysicalDevice(); const VkResult formatResult = instance.getPhysicalDeviceImageFormatProperties(physicalDevice, m_resourceDesc.imageFormat, m_resourceDesc.imageType, VK_IMAGE_TILING_OPTIMAL, usage, (VkImageCreateFlags)0, &imageFormatProperties); if (formatResult != VK_SUCCESS) TCU_THROW(NotSupportedError, "Image format is not supported"); if ((imageFormatProperties.sampleCounts & m_resourceDesc.imageSamples) != m_resourceDesc.imageSamples) TCU_THROW(NotSupportedError, "Requested sample count is not supported"); } } TestInstance* createInstance (Context& context) const { switch (m_syncPrimitive) { case SYNC_PRIMITIVE_FENCE: return new FenceTestInstance(context, m_type, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData); case SYNC_PRIMITIVE_BINARY_SEMAPHORE: return new BinarySemaphoreTestInstance(context, m_type, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData); case SYNC_PRIMITIVE_TIMELINE_SEMAPHORE: return new TimelineSemaphoreTestInstance(context, m_type, m_resourceDesc, m_writeOp, m_readOp, m_pipelineCacheData); case SYNC_PRIMITIVE_BARRIER: return new BarrierTestInstance(context, m_type, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData); case SYNC_PRIMITIVE_EVENT: return new EventTestInstance(context, m_type, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData); } DE_ASSERT(0); return DE_NULL; } private: SynchronizationType m_type; const ResourceDescription m_resourceDesc; const de::SharedPtr m_writeOp; const de::SharedPtr m_readOp; const SyncPrimitive m_syncPrimitive; PipelineCacheData& m_pipelineCacheData; }; struct TestData { SynchronizationType type; PipelineCacheData* pipelineCacheData; }; void createTests (tcu::TestCaseGroup* group, TestData data) { tcu::TestContext& testCtx = group->getTestContext(); static const struct { const char* name; SyncPrimitive syncPrimitive; int numOptions; } groups[] = { { "fence", SYNC_PRIMITIVE_FENCE, 0, }, { "binary_semaphore", SYNC_PRIMITIVE_BINARY_SEMAPHORE, 0, }, { "timeline_semaphore", SYNC_PRIMITIVE_TIMELINE_SEMAPHORE, 0, }, { "barrier", SYNC_PRIMITIVE_BARRIER, 1, }, { "event", SYNC_PRIMITIVE_EVENT, 1, }, }; for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(groups); ++groupNdx) { de::MovePtr synchGroup (new tcu::TestCaseGroup(testCtx, groups[groupNdx].name)); for (int writeOpNdx = 0; writeOpNdx < DE_LENGTH_OF_ARRAY(s_writeOps); ++writeOpNdx) for (int readOpNdx = 0; readOpNdx < DE_LENGTH_OF_ARRAY(s_readOps); ++readOpNdx) { const OperationName writeOp = s_writeOps[writeOpNdx]; const OperationName readOp = s_readOps[readOpNdx]; const std::string opGroupName = getOperationName(writeOp) + "_" + getOperationName(readOp); bool empty = true; de::MovePtr opGroup (new tcu::TestCaseGroup(testCtx, opGroupName.c_str())); for (int resourceNdx = 0; resourceNdx < DE_LENGTH_OF_ARRAY(s_resources); ++resourceNdx) { const ResourceDescription& resource = s_resources[resourceNdx]; std::string name = getResourceName(resource); if (isResourceSupported(writeOp, resource) && isResourceSupported(readOp, resource)) { if (data.type == SynchronizationType::SYNCHRONIZATION2) { if ((isSpecializedAccessFlagSupported(writeOp) || isSpecializedAccessFlagSupported(readOp))) { const std::string nameSp = name + "_specialized_access_flag"; opGroup->addChild(new SyncTestCase(testCtx, nameSp, data.type, groups[groupNdx].syncPrimitive, resource, writeOp, readOp, true, *data.pipelineCacheData)); } } opGroup->addChild(new SyncTestCase(testCtx, name, data.type, groups[groupNdx].syncPrimitive, resource, writeOp, readOp, false, *data.pipelineCacheData)); empty = false; } } if (!empty) synchGroup->addChild(opGroup.release()); } group->addChild(synchGroup.release()); } } } // anonymous tcu::TestCaseGroup* createSynchronizedOperationSingleQueueTests (tcu::TestContext& testCtx, SynchronizationType type, PipelineCacheData& pipelineCacheData) { TestData data { type, &pipelineCacheData }; // Synchronization of a memory-modifying operation return createTestGroup(testCtx, "single_queue", createTests, data); } } // synchronization } // vkt