/*------------------------------------------------------------------------ * 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 tests for resources shared between instances. *//*--------------------------------------------------------------------*/ #include "vktSynchronizationCrossInstanceSharingTests.hpp" #include "vkDeviceUtil.hpp" #include "vkPlatform.hpp" #include "vkBarrierUtil.hpp" #include "vkCmdUtil.hpp" #include "vktTestCaseUtil.hpp" #include "deSharedPtr.hpp" #include "vktSynchronizationUtil.hpp" #include "vktSynchronizationOperation.hpp" #include "vktSynchronizationOperationTestData.hpp" #include "vktSynchronizationOperationResources.hpp" #include "vktExternalMemoryUtil.hpp" #include "vktTestGroupUtil.hpp" #include "vktCustomInstancesDevices.hpp" #include "deRandom.hpp" #include "tcuResultCollector.hpp" #include "tcuTestLog.hpp" #include "tcuCommandLine.hpp" using tcu::TestLog; using namespace vkt::ExternalMemoryUtil; namespace vkt { namespace synchronization { namespace { using namespace vk; using de::SharedPtr; struct TestConfig { TestConfig (SynchronizationType type_, const ResourceDescription& resource_, vk::VkSemaphoreType semaphoreType_, OperationName writeOp_, OperationName readOp_, vk::VkExternalMemoryHandleTypeFlagBits memoryHandleType_, vk::VkExternalSemaphoreHandleTypeFlagBits semaphoreHandleType_, bool dedicated_) : type (type_) , resource (resource_) , semaphoreType (semaphoreType_) , writeOp (writeOp_) , readOp (readOp_) , memoryHandleType (memoryHandleType_) , semaphoreHandleType (semaphoreHandleType_) , dedicated (dedicated_) { } const SynchronizationType type; const ResourceDescription resource; const vk::VkSemaphoreType semaphoreType; const OperationName writeOp; const OperationName readOp; const vk::VkExternalMemoryHandleTypeFlagBits memoryHandleType; const vk::VkExternalSemaphoreHandleTypeFlagBits semaphoreHandleType; const bool dedicated; }; // A helper class to test for extensions upfront and throw not supported to speed up test runtimes compared to failing only // after creating unnecessary vkInstances. A common example of this is win32 platforms taking a long time to run _fd tests. class NotSupportedChecker { public: NotSupportedChecker (const Context& context, TestConfig config, const OperationSupport& writeOp, const OperationSupport& readOp) : m_context (context) { // Check instance support m_context.requireInstanceFunctionality("VK_KHR_get_physical_device_properties2"); m_context.requireInstanceFunctionality("VK_KHR_external_semaphore_capabilities"); m_context.requireInstanceFunctionality("VK_KHR_external_memory_capabilities"); // Check device support if (config.dedicated) m_context.requireDeviceFunctionality("VK_KHR_dedicated_allocation"); m_context.requireDeviceFunctionality("VK_KHR_external_semaphore"); m_context.requireDeviceFunctionality("VK_KHR_external_memory"); if (config.semaphoreType == vk::VK_SEMAPHORE_TYPE_TIMELINE_KHR) m_context.requireDeviceFunctionality("VK_KHR_timeline_semaphore"); if (config.type == SynchronizationType::SYNCHRONIZATION2) m_context.requireDeviceFunctionality("VK_KHR_synchronization2"); if (config.memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR || config.semaphoreHandleType == vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR || config.semaphoreHandleType == vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR) { m_context.requireDeviceFunctionality("VK_KHR_external_semaphore_fd"); m_context.requireDeviceFunctionality("VK_KHR_external_memory_fd"); } if (config.memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT) { m_context.requireDeviceFunctionality("VK_EXT_external_memory_dma_buf"); } if (config.memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR || config.memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR || config.semaphoreHandleType == vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR || config.semaphoreHandleType == vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR) { m_context.requireDeviceFunctionality("VK_KHR_external_semaphore_win32"); m_context.requireDeviceFunctionality("VK_KHR_external_memory_win32"); } TestLog& log = context.getTestContext().getLog(); const vk::InstanceInterface& vki = context.getInstanceInterface(); const vk::VkPhysicalDevice physicalDevice = context.getPhysicalDevice(); // Check resource support if (config.resource.type == RESOURCE_TYPE_IMAGE) { const vk::VkPhysicalDeviceExternalImageFormatInfo externalInfo = { vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, DE_NULL, config.memoryHandleType }; const vk::VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = { vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, &externalInfo, config.resource.imageFormat, config.resource.imageType, vk::VK_IMAGE_TILING_OPTIMAL, readOp.getInResourceUsageFlags() | writeOp.getOutResourceUsageFlags(), 0u }; vk::VkExternalImageFormatProperties externalProperties = { vk::VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES, DE_NULL, { 0u, 0u, 0u } }; vk::VkImageFormatProperties2 formatProperties = { vk::VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, &externalProperties, { { 0u, 0u, 0u }, 0u, 0u, 0u, 0u, } }; { const vk::VkResult res = vki.getPhysicalDeviceImageFormatProperties2(physicalDevice, &imageFormatInfo, &formatProperties); if (res == vk::VK_ERROR_FORMAT_NOT_SUPPORTED) TCU_THROW(NotSupportedError, "Image format not supported"); VK_CHECK(res); // Check other errors } log << TestLog::Message << "External image format properties: " << imageFormatInfo << "\n"<< externalProperties << TestLog::EndMessage; if ((externalProperties.externalMemoryProperties.externalMemoryFeatures & vk::VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR) == 0) TCU_THROW(NotSupportedError, "Exporting image resource not supported"); if ((externalProperties.externalMemoryProperties.externalMemoryFeatures & vk::VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) == 0) TCU_THROW(NotSupportedError, "Importing image resource not supported"); if (!config.dedicated && (externalProperties.externalMemoryProperties.externalMemoryFeatures & vk::VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR) != 0) { TCU_THROW(NotSupportedError, "Handle requires dedicated allocation, but test uses suballocated memory"); } if (!(formatProperties.imageFormatProperties.sampleCounts & config.resource.imageSamples)) { TCU_THROW(NotSupportedError, "Specified sample count for format not supported"); } } else { const vk::VkPhysicalDeviceExternalBufferInfo info = { vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO, DE_NULL, 0u, readOp.getInResourceUsageFlags() | writeOp.getOutResourceUsageFlags(), config.memoryHandleType }; vk::VkExternalBufferProperties properties = { vk::VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES, DE_NULL, { 0u, 0u, 0u} }; vki.getPhysicalDeviceExternalBufferProperties(physicalDevice, &info, &properties); log << TestLog::Message << "External buffer properties: " << info << "\n" << properties << TestLog::EndMessage; if ((properties.externalMemoryProperties.externalMemoryFeatures & vk::VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR) == 0 || (properties.externalMemoryProperties.externalMemoryFeatures & vk::VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) == 0) TCU_THROW(NotSupportedError, "Exporting and importing memory type not supported"); if (!config.dedicated && (properties.externalMemoryProperties.externalMemoryFeatures & vk::VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR) != 0) { TCU_THROW(NotSupportedError, "Handle requires dedicated allocation, but test uses suballocated memory"); } } // Check semaphore support { const vk::VkSemaphoreTypeCreateInfoKHR semaphoreTypeInfo = { vk::VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR, DE_NULL, config.semaphoreType, 0, }; const vk::VkPhysicalDeviceExternalSemaphoreInfo info = { vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, &semaphoreTypeInfo, config.semaphoreHandleType }; vk::VkExternalSemaphoreProperties properties = { vk::VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, DE_NULL, 0u, 0u, 0u }; vki.getPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &info, &properties); log << TestLog::Message << info << "\n" << properties << TestLog::EndMessage; if ((properties.externalSemaphoreFeatures & vk::VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR) == 0 || (properties.externalSemaphoreFeatures & vk::VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR) == 0) TCU_THROW(NotSupportedError, "Exporting and importing semaphore type not supported"); } } private: const Context& m_context; }; bool checkQueueFlags (vk::VkQueueFlags availableFlags, const vk::VkQueueFlags neededFlags) { if ((availableFlags & (vk::VK_QUEUE_GRAPHICS_BIT | vk::VK_QUEUE_COMPUTE_BIT)) != 0) availableFlags |= vk::VK_QUEUE_TRANSFER_BIT; return (availableFlags & neededFlags) != 0; } class SimpleAllocation : public vk::Allocation { public: SimpleAllocation (const vk::DeviceInterface& vkd, vk::VkDevice device, const vk::VkDeviceMemory memory); ~SimpleAllocation (void); private: const vk::DeviceInterface& m_vkd; const vk::VkDevice m_device; }; SimpleAllocation::SimpleAllocation (const vk::DeviceInterface& vkd, vk::VkDevice device, const vk::VkDeviceMemory memory) : Allocation (memory, 0, DE_NULL) , m_vkd (vkd) , m_device (device) { } SimpleAllocation::~SimpleAllocation (void) { m_vkd.freeMemory(m_device, getMemory(), DE_NULL); } CustomInstance createTestInstance (Context& context) { std::vector extensions; extensions.push_back("VK_KHR_get_physical_device_properties2"); extensions.push_back("VK_KHR_external_semaphore_capabilities"); extensions.push_back("VK_KHR_external_memory_capabilities"); return createCustomInstanceWithExtensions(context, extensions); } vk::Move createTestDevice (const Context& context, const vk::PlatformInterface& vkp, vk::VkInstance instance, const vk::InstanceInterface& vki, const vk::VkPhysicalDevice physicalDevice) { const bool validationEnabled = context.getTestContext().getCommandLine().isValidationEnabled(); const float priority = 0.0f; const std::vector queueFamilyProperties = vk::getPhysicalDeviceQueueFamilyProperties(vki, physicalDevice); std::vector queueFamilyIndices (queueFamilyProperties.size(), 0xFFFFFFFFu); VkPhysicalDeviceFeatures2 createPhysicalFeature { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, DE_NULL, context.getDeviceFeatures() }; VkPhysicalDeviceTimelineSemaphoreFeatures timelineSemaphoreFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, DE_NULL, DE_TRUE }; VkPhysicalDeviceSynchronization2FeaturesKHR synchronization2Features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR, DE_NULL, DE_TRUE }; void** nextPtr = &createPhysicalFeature.pNext; std::vector extensions; if (context.isDeviceFunctionalitySupported("VK_KHR_dedicated_allocation")) extensions.push_back("VK_KHR_dedicated_allocation"); if (context.isDeviceFunctionalitySupported("VK_KHR_get_memory_requirements2")) extensions.push_back("VK_KHR_get_memory_requirements2"); if (context.isDeviceFunctionalitySupported("VK_KHR_external_semaphore")) extensions.push_back("VK_KHR_external_semaphore"); if (context.isDeviceFunctionalitySupported("VK_KHR_external_memory")) extensions.push_back("VK_KHR_external_memory"); if (context.isDeviceFunctionalitySupported("VK_KHR_external_semaphore_fd")) extensions.push_back("VK_KHR_external_semaphore_fd"); if (context.isDeviceFunctionalitySupported("VK_KHR_external_memory_fd")) extensions.push_back("VK_KHR_external_memory_fd"); if (context.isDeviceFunctionalitySupported("VK_EXT_external_memory_dma_buf")) extensions.push_back("VK_EXT_external_memory_dma_buf"); if (context.isDeviceFunctionalitySupported("VK_KHR_external_semaphore_win32")) extensions.push_back("VK_KHR_external_semaphore_win32"); if (context.isDeviceFunctionalitySupported("VK_KHR_external_memory_win32")) extensions.push_back("VK_KHR_external_memory_win32"); if (context.isDeviceFunctionalitySupported("VK_KHR_timeline_semaphore")) { extensions.push_back("VK_KHR_timeline_semaphore"); addToChainVulkanStructure(&nextPtr, timelineSemaphoreFeatures); } if (context.isDeviceFunctionalitySupported("VK_KHR_synchronization2")) { extensions.push_back("VK_KHR_synchronization2"); addToChainVulkanStructure(&nextPtr, synchronization2Features); } try { std::vector queues; for (size_t ndx = 0; ndx < queueFamilyProperties.size(); ndx++) { const vk::VkDeviceQueueCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, DE_NULL, 0u, (deUint32)ndx, 1u, &priority }; queues.push_back(createInfo); } const vk::VkDeviceCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, &createPhysicalFeature, 0u, (deUint32)queues.size(), &queues[0], 0u, DE_NULL, (deUint32)extensions.size(), extensions.empty() ? DE_NULL : &extensions[0], 0u }; return vkt::createCustomDevice(validationEnabled, vkp, instance, vki, physicalDevice, &createInfo); } catch (const vk::Error& error) { if (error.getError() == vk::VK_ERROR_EXTENSION_NOT_PRESENT) TCU_THROW(NotSupportedError, "Required extensions not supported"); else throw; } } // Class to wrap a singleton instance and device class InstanceAndDevice { InstanceAndDevice (Context& context) : m_instance (createTestInstance(context)) , m_vki (m_instance.getDriver()) , m_physicalDevice (vk::chooseDevice(m_vki, m_instance, context.getTestContext().getCommandLine())) , m_logicalDevice (createTestDevice(context, context.getPlatformInterface(), m_instance, m_vki, m_physicalDevice)) { } public: static vk::VkInstance getInstanceA(Context& context) { if (!m_instanceA) m_instanceA = SharedPtr(new InstanceAndDevice(context)); return m_instanceA->m_instance; } static vk::VkInstance getInstanceB(Context& context) { if (!m_instanceB) m_instanceB = SharedPtr(new InstanceAndDevice(context)); return m_instanceB->m_instance; } static const vk::InstanceDriver& getDriverA() { DE_ASSERT(m_instanceA); return m_instanceA->m_instance.getDriver(); } static const vk::InstanceDriver& getDriverB() { DE_ASSERT(m_instanceB); return m_instanceB->m_instance.getDriver(); } static vk::VkPhysicalDevice getPhysicalDeviceA() { DE_ASSERT(m_instanceA); return m_instanceA->m_physicalDevice; } static vk::VkPhysicalDevice getPhysicalDeviceB() { DE_ASSERT(m_instanceB); return m_instanceB->m_physicalDevice; } static const Unique& getDeviceA() { DE_ASSERT(m_instanceA); return m_instanceA->m_logicalDevice; } static const Unique& getDeviceB() { DE_ASSERT(m_instanceB); return m_instanceB->m_logicalDevice; } static void collectMessagesA() { DE_ASSERT(m_instanceA); m_instanceA->m_instance.collectMessages(); } static void collectMessagesB() { DE_ASSERT(m_instanceB); m_instanceB->m_instance.collectMessages(); } static void destroy() { m_instanceA.clear(); m_instanceB.clear(); } private: CustomInstance m_instance; const vk::InstanceDriver& m_vki; const vk::VkPhysicalDevice m_physicalDevice; const Unique m_logicalDevice; static SharedPtr m_instanceA; static SharedPtr m_instanceB; }; SharedPtr InstanceAndDevice::m_instanceA; SharedPtr InstanceAndDevice::m_instanceB; vk::VkQueue getQueue (const vk::DeviceInterface& vkd, const vk::VkDevice device, deUint32 familyIndex) { vk::VkQueue queue; vkd.getDeviceQueue(device, familyIndex, 0u, &queue); return queue; } vk::Move createCommandPool (const vk::DeviceInterface& vkd, vk::VkDevice device, deUint32 queueFamilyIndex) { const vk::VkCommandPoolCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, DE_NULL, 0u, queueFamilyIndex }; return vk::createCommandPool(vkd, device, &createInfo); } vk::Move createCommandBuffer (const vk::DeviceInterface& vkd, vk::VkDevice device, vk::VkCommandPool commandPool) { const vk::VkCommandBufferLevel level = vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY; const vk::VkCommandBufferAllocateInfo allocateInfo = { vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, DE_NULL, commandPool, level, 1u }; return vk::allocateCommandBuffer(vkd, device, &allocateInfo); } vk::VkMemoryRequirements getMemoryRequirements(const vk::DeviceInterface& vkd, vk::VkDevice device, vk::VkImage image, bool dedicated, bool getMemReq2Supported) { vk::VkMemoryRequirements memoryRequirements = { 0u, 0u, 0u, }; if (getMemReq2Supported) { const vk::VkImageMemoryRequirementsInfo2 requirementInfo = { vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, DE_NULL, image }; vk::VkMemoryDedicatedRequirements dedicatedRequirements = { vk::VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, DE_NULL, VK_FALSE, VK_FALSE }; vk::VkMemoryRequirements2 requirements = { vk::VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicatedRequirements, { 0u, 0u, 0u, } }; vkd.getImageMemoryRequirements2(device, &requirementInfo, &requirements); if (!dedicated && dedicatedRequirements.requiresDedicatedAllocation) TCU_THROW(NotSupportedError, "Memory requires dedicated allocation"); memoryRequirements = requirements.memoryRequirements; } else { vkd.getImageMemoryRequirements(device, image, &memoryRequirements); } return memoryRequirements; } vk::VkMemoryRequirements getMemoryRequirements(const vk::DeviceInterface& vkd, vk::VkDevice device, vk::VkBuffer buffer, bool dedicated, bool getMemReq2Supported) { vk::VkMemoryRequirements memoryRequirements = { 0u, 0u, 0u, }; if (getMemReq2Supported) { const vk::VkBufferMemoryRequirementsInfo2 requirementInfo = { vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, DE_NULL, buffer }; vk::VkMemoryDedicatedRequirements dedicatedRequirements = { vk::VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, DE_NULL, VK_FALSE, VK_FALSE }; vk::VkMemoryRequirements2 requirements = { vk::VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicatedRequirements, { 0u, 0u, 0u, } }; vkd.getBufferMemoryRequirements2(device, &requirementInfo, &requirements); if (!dedicated && dedicatedRequirements.requiresDedicatedAllocation) TCU_THROW(NotSupportedError, "Memory requires dedicated allocation"); memoryRequirements = requirements.memoryRequirements; } else { vkd.getBufferMemoryRequirements(device, buffer, &memoryRequirements); } return memoryRequirements; } Move createImage(const vk::DeviceInterface& vkd, vk::VkDevice device, const ResourceDescription& resourceDesc, const vk::VkExtent3D extent, const std::vector& queueFamilyIndices, const OperationSupport& readOp, const OperationSupport& writeOp, vk::VkExternalMemoryHandleTypeFlagBits externalType) { const vk::VkExternalMemoryImageCreateInfo externalInfo = { vk::VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, DE_NULL, (vk::VkExternalMemoryHandleTypeFlags)externalType }; const vk::VkImageCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, &externalInfo, 0u, resourceDesc.imageType, resourceDesc.imageFormat, extent, 1u, 1u, resourceDesc.imageSamples, vk::VK_IMAGE_TILING_OPTIMAL, readOp.getInResourceUsageFlags() | writeOp.getOutResourceUsageFlags(), vk::VK_SHARING_MODE_EXCLUSIVE, (deUint32)queueFamilyIndices.size(), &queueFamilyIndices[0], vk::VK_IMAGE_LAYOUT_UNDEFINED }; return vk::createImage(vkd, device, &createInfo); } Move createBuffer(const vk::DeviceInterface& vkd, vk::VkDevice device, const vk::VkDeviceSize size, const vk::VkBufferUsageFlags usage, const vk::VkExternalMemoryHandleTypeFlagBits memoryHandleType, const std::vector& queueFamilyIndices) { const vk::VkExternalMemoryBufferCreateInfo externalInfo = { vk::VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, DE_NULL, (vk::VkExternalMemoryHandleTypeFlags)memoryHandleType }; const vk::VkBufferCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, &externalInfo, 0u, size, usage, vk::VK_SHARING_MODE_EXCLUSIVE, (deUint32)queueFamilyIndices.size(), &queueFamilyIndices[0] }; return vk::createBuffer(vkd, device, &createInfo); } de::MovePtr importAndBindMemory (const vk::DeviceInterface& vkd, vk::VkDevice device, vk::VkBuffer buffer, NativeHandle& nativeHandle, vk::VkExternalMemoryHandleTypeFlagBits externalType, deUint32 exportedMemoryTypeIndex, bool dedicated) { const vk::VkMemoryRequirements requirements = vk::getBufferMemoryRequirements(vkd, device, buffer); vk::Move memory = dedicated ? importDedicatedMemory(vkd, device, buffer, requirements, externalType, exportedMemoryTypeIndex, nativeHandle) : importMemory(vkd, device, requirements, externalType, exportedMemoryTypeIndex, nativeHandle); VK_CHECK(vkd.bindBufferMemory(device, buffer, *memory, 0u)); return de::MovePtr(new SimpleAllocation(vkd, device, memory.disown())); } de::MovePtr importAndBindMemory (const vk::DeviceInterface& vkd, vk::VkDevice device, vk::VkImage image, NativeHandle& nativeHandle, vk::VkExternalMemoryHandleTypeFlagBits externalType, deUint32 exportedMemoryTypeIndex, bool dedicated) { const vk::VkMemoryRequirements requirements = vk::getImageMemoryRequirements(vkd, device, image); vk::Move memory = dedicated ? importDedicatedMemory(vkd, device, image, requirements, externalType, exportedMemoryTypeIndex, nativeHandle) : importMemory(vkd, device, requirements, externalType, exportedMemoryTypeIndex, nativeHandle); VK_CHECK(vkd.bindImageMemory(device, image, *memory, 0u)); return de::MovePtr(new SimpleAllocation(vkd, device, memory.disown())); } de::MovePtr importResource (const vk::DeviceInterface& vkd, vk::VkDevice device, const ResourceDescription& resourceDesc, const std::vector& queueFamilyIndices, const OperationSupport& readOp, const OperationSupport& writeOp, NativeHandle& nativeHandle, vk::VkExternalMemoryHandleTypeFlagBits externalType, deUint32 exportedMemoryTypeIndex, bool dedicated) { if (resourceDesc.type == RESOURCE_TYPE_IMAGE) { const vk::VkExtent3D extent = { (deUint32)resourceDesc.size.x(), de::max(1u, (deUint32)resourceDesc.size.y()), de::max(1u, (deUint32)resourceDesc.size.z()) }; const vk::VkImageSubresourceRange subresourceRange = { resourceDesc.imageAspect, 0u, 1u, 0u, 1u }; const vk::VkImageSubresourceLayers subresourceLayers = { resourceDesc.imageAspect, 0u, 0u, 1u }; const vk:: VkExternalMemoryImageCreateInfo externalInfo = { vk::VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, DE_NULL, (vk::VkExternalMemoryHandleTypeFlags)externalType }; const vk::VkImageCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, &externalInfo, 0u, resourceDesc.imageType, resourceDesc.imageFormat, extent, 1u, 1u, resourceDesc.imageSamples, vk::VK_IMAGE_TILING_OPTIMAL, readOp.getInResourceUsageFlags() | writeOp.getOutResourceUsageFlags(), vk::VK_SHARING_MODE_EXCLUSIVE, (deUint32)queueFamilyIndices.size(), &queueFamilyIndices[0], vk::VK_IMAGE_LAYOUT_UNDEFINED }; vk::Move image = vk::createImage(vkd, device, &createInfo); de::MovePtr allocation = importAndBindMemory(vkd, device, *image, nativeHandle, externalType, exportedMemoryTypeIndex, dedicated); return de::MovePtr(new Resource(image, allocation, extent, resourceDesc.imageType, resourceDesc.imageFormat, subresourceRange, subresourceLayers)); } else { const vk::VkDeviceSize offset = 0u; const vk::VkDeviceSize size = static_cast(resourceDesc.size.x()); const vk::VkBufferUsageFlags usage = readOp.getInResourceUsageFlags() | writeOp.getOutResourceUsageFlags(); const vk:: VkExternalMemoryBufferCreateInfo externalInfo = { vk::VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, DE_NULL, (vk::VkExternalMemoryHandleTypeFlags)externalType }; const vk::VkBufferCreateInfo createInfo = { vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, &externalInfo, 0u, size, usage, vk::VK_SHARING_MODE_EXCLUSIVE, (deUint32)queueFamilyIndices.size(), &queueFamilyIndices[0] }; vk::Move buffer = vk::createBuffer(vkd, device, &createInfo); de::MovePtr allocation = importAndBindMemory(vkd, device, *buffer, nativeHandle, externalType, exportedMemoryTypeIndex, dedicated); return de::MovePtr(new Resource(resourceDesc.type, buffer, allocation, offset, size)); } } void recordWriteBarrier (SynchronizationWrapperPtr synchronizationWrapper, vk::VkCommandBuffer commandBuffer, const Resource& resource, const SyncInfo& writeSync, deUint32 writeQueueFamilyIndex, const SyncInfo& readSync) { const vk::VkPipelineStageFlags2KHR srcStageMask = writeSync.stageMask; const vk::VkAccessFlags2KHR srcAccessMask = writeSync.accessMask; const vk::VkPipelineStageFlags2KHR dstStageMask = readSync.stageMask; const vk::VkAccessFlags2KHR dstAccessMask = readSync.accessMask; if (resource.getType() == RESOURCE_TYPE_IMAGE) { const VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2( srcStageMask, // VkPipelineStageFlags2KHR srcStageMask srcAccessMask, // VkAccessFlags2KHR srcAccessMask dstStageMask, // VkPipelineStageFlags2KHR dstStageMask dstAccessMask, // VkAccessFlags2KHR dstAccessMask writeSync.imageLayout, // VkImageLayout oldLayout readSync.imageLayout, // VkImageLayout newLayout resource.getImage().handle, // VkImage image resource.getImage().subresourceRange, // VkImageSubresourceRange subresourceRange writeQueueFamilyIndex, // deUint32 srcQueueFamilyIndex VK_QUEUE_FAMILY_EXTERNAL // deUint32 dstQueueFamilyIndex ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2); synchronizationWrapper->cmdPipelineBarrier(commandBuffer, &dependencyInfo); } else { const VkBufferMemoryBarrier2KHR bufferMemoryBarrier2 = makeBufferMemoryBarrier2( srcStageMask, // VkPipelineStageFlags2KHR srcStageMask srcAccessMask, // VkAccessFlags2KHR srcAccessMask dstStageMask, // VkPipelineStageFlags2KHR dstStageMask dstAccessMask, // VkAccessFlags2KHR dstAccessMask resource.getBuffer().handle, // VkBuffer buffer 0, // VkDeviceSize offset VK_WHOLE_SIZE, // VkDeviceSize size writeQueueFamilyIndex, // deUint32 srcQueueFamilyIndex VK_QUEUE_FAMILY_EXTERNAL // deUint32 dstQueueFamilyIndex ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, &bufferMemoryBarrier2); synchronizationWrapper->cmdPipelineBarrier(commandBuffer, &dependencyInfo); } } void recordReadBarrier (SynchronizationWrapperPtr synchronizationWrapper, vk::VkCommandBuffer commandBuffer, const Resource& resource, const SyncInfo& writeSync, const SyncInfo& readSync, deUint32 readQueueFamilyIndex) { const vk::VkPipelineStageFlags2KHR srcStageMask = readSync.stageMask; const vk::VkAccessFlags2KHR srcAccessMask = readSync.accessMask; const vk::VkPipelineStageFlags2KHR dstStageMask = readSync.stageMask; const vk::VkAccessFlags2KHR dstAccessMask = readSync.accessMask; if (resource.getType() == RESOURCE_TYPE_IMAGE) { const VkImageMemoryBarrier2KHR imageMemoryBarrier2 = makeImageMemoryBarrier2( srcStageMask, // VkPipelineStageFlags2KHR srcStageMask srcAccessMask, // VkAccessFlags2KHR srcAccessMask dstStageMask, // VkPipelineStageFlags2KHR dstStageMask dstAccessMask, // VkAccessFlags2KHR dstAccessMask writeSync.imageLayout, // VkImageLayout oldLayout readSync.imageLayout, // VkImageLayout newLayout resource.getImage().handle, // VkImage image resource.getImage().subresourceRange, // VkImageSubresourceRange subresourceRange VK_QUEUE_FAMILY_EXTERNAL, // deUint32 srcQueueFamilyIndex readQueueFamilyIndex // deUint32 dstQueueFamilyIndex ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, DE_NULL, &imageMemoryBarrier2); synchronizationWrapper->cmdPipelineBarrier(commandBuffer, &dependencyInfo); } else { const VkBufferMemoryBarrier2KHR bufferMemoryBarrier2 = makeBufferMemoryBarrier2( srcStageMask, // VkPipelineStageFlags2KHR srcStageMask srcAccessMask, // VkAccessFlags2KHR srcAccessMask dstStageMask, // VkPipelineStageFlags2KHR dstStageMask dstAccessMask, // VkAccessFlags2KHR dstAccessMask resource.getBuffer().handle, // VkBuffer buffer 0, // VkDeviceSize offset VK_WHOLE_SIZE, // VkDeviceSize size VK_QUEUE_FAMILY_EXTERNAL, // deUint32 srcQueueFamilyIndex readQueueFamilyIndex // deUint32 dstQueueFamilyIndex ); VkDependencyInfoKHR dependencyInfo = makeCommonDependencyInfo(DE_NULL, &bufferMemoryBarrier2); synchronizationWrapper->cmdPipelineBarrier(commandBuffer, &dependencyInfo); } } std::vector getFamilyIndices (const std::vector& properties) { std::vector indices (properties.size(), 0); for (deUint32 ndx = 0; ndx < properties.size(); ndx++) indices[ndx] = ndx; return indices; } class SharingTestInstance : public TestInstance { public: SharingTestInstance (Context& context, TestConfig config); virtual tcu::TestStatus iterate (void); private: const TestConfig m_config; const de::UniquePtr m_supportWriteOp; const de::UniquePtr m_supportReadOp; const NotSupportedChecker m_notSupportedChecker; // Must declare before VkInstance to effectively reduce runtimes! const bool m_getMemReq2Supported; const vk::VkInstance m_instanceA; const vk::InstanceDriver& m_vkiA; const vk::VkPhysicalDevice m_physicalDeviceA; const std::vector m_queueFamiliesA; const std::vector m_queueFamilyIndicesA; const vk::Unique& m_deviceA; const vk::DeviceDriver m_vkdA; const vk::VkInstance m_instanceB; const vk::InstanceDriver& m_vkiB; const vk::VkPhysicalDevice m_physicalDeviceB; const std::vector m_queueFamiliesB; const std::vector m_queueFamilyIndicesB; const vk::Unique& m_deviceB; const vk::DeviceDriver m_vkdB; const vk::VkExternalSemaphoreHandleTypeFlagBits m_semaphoreHandleType; const vk::VkExternalMemoryHandleTypeFlagBits m_memoryHandleType; // \todo Should this be moved to the group same way as in the other tests? PipelineCacheData m_pipelineCacheData; tcu::ResultCollector m_resultCollector; size_t m_queueANdx; size_t m_queueBNdx; }; SharingTestInstance::SharingTestInstance (Context& context, TestConfig config) : TestInstance (context) , m_config (config) , m_supportWriteOp (makeOperationSupport(config.writeOp, config.resource)) , m_supportReadOp (makeOperationSupport(config.readOp, config.resource)) , m_notSupportedChecker (context, m_config, *m_supportWriteOp, *m_supportReadOp) , m_getMemReq2Supported (context.isDeviceFunctionalitySupported("VK_KHR_get_memory_requirements2")) , m_instanceA (InstanceAndDevice::getInstanceA(context)) , m_vkiA (InstanceAndDevice::getDriverA()) , m_physicalDeviceA (InstanceAndDevice::getPhysicalDeviceA()) , m_queueFamiliesA (vk::getPhysicalDeviceQueueFamilyProperties(m_vkiA, m_physicalDeviceA)) , m_queueFamilyIndicesA (getFamilyIndices(m_queueFamiliesA)) , m_deviceA (InstanceAndDevice::getDeviceA()) , m_vkdA (context.getPlatformInterface(), m_instanceA, *m_deviceA) , m_instanceB (InstanceAndDevice::getInstanceB(context)) , m_vkiB (InstanceAndDevice::getDriverB()) , m_physicalDeviceB (InstanceAndDevice::getPhysicalDeviceB()) , m_queueFamiliesB (vk::getPhysicalDeviceQueueFamilyProperties(m_vkiB, m_physicalDeviceB)) , m_queueFamilyIndicesB (getFamilyIndices(m_queueFamiliesB)) , m_deviceB (InstanceAndDevice::getDeviceB()) , m_vkdB (context.getPlatformInterface(), m_instanceB, *m_deviceB) , m_semaphoreHandleType (m_config.semaphoreHandleType) , m_memoryHandleType (m_config.memoryHandleType) , m_resultCollector (context.getTestContext().getLog()) , m_queueANdx (0) , m_queueBNdx (0) { } tcu::TestStatus SharingTestInstance::iterate (void) { TestLog& log (m_context.getTestContext().getLog()); bool isTimelineSemaphore (m_config.semaphoreType == vk::VK_SEMAPHORE_TYPE_TIMELINE_KHR); try { const deUint32 queueFamilyA = (deUint32)m_queueANdx; const deUint32 queueFamilyB = (deUint32)m_queueBNdx; const tcu::ScopedLogSection queuePairSection (log, "WriteQueue-" + de::toString(queueFamilyA) + "-ReadQueue-" + de::toString(queueFamilyB), "WriteQueue-" + de::toString(queueFamilyA) + "-ReadQueue-" + de::toString(queueFamilyB)); const vk::Unique semaphoreA (createExportableSemaphoreType(m_vkdA, *m_deviceA, m_config.semaphoreType, m_semaphoreHandleType)); const vk::Unique semaphoreB (createSemaphoreType(m_vkdB, *m_deviceB, m_config.semaphoreType)); const ResourceDescription& resourceDesc = m_config.resource; de::MovePtr resourceA; deUint32 exportedMemoryTypeIndex = ~0U; if (resourceDesc.type == RESOURCE_TYPE_IMAGE) { const vk::VkExtent3D extent = { (deUint32)resourceDesc.size.x(), de::max(1u, (deUint32)resourceDesc.size.y()), de::max(1u, (deUint32)resourceDesc.size.z()) }; const vk::VkImageSubresourceRange subresourceRange = { resourceDesc.imageAspect, 0u, 1u, 0u, 1u }; const vk::VkImageSubresourceLayers subresourceLayers = { resourceDesc.imageAspect, 0u, 0u, 1u }; vk::Move image = createImage(m_vkdA, *m_deviceA, resourceDesc, extent, m_queueFamilyIndicesA, *m_supportReadOp, *m_supportWriteOp, m_memoryHandleType); const vk::VkMemoryRequirements requirements = getMemoryRequirements(m_vkdA, *m_deviceA, *image, m_config.dedicated, m_getMemReq2Supported); exportedMemoryTypeIndex = chooseMemoryType(requirements.memoryTypeBits); vk::Move memory = allocateExportableMemory(m_vkdA, *m_deviceA, requirements.size, exportedMemoryTypeIndex, m_memoryHandleType, m_config.dedicated ? *image : (vk::VkImage)0); VK_CHECK(m_vkdA.bindImageMemory(*m_deviceA, *image, *memory, 0u)); de::MovePtr allocation = de::MovePtr(new SimpleAllocation(m_vkdA, *m_deviceA, memory.disown())); resourceA = de::MovePtr(new Resource(image, allocation, extent, resourceDesc.imageType, resourceDesc.imageFormat, subresourceRange, subresourceLayers)); } else { const vk::VkDeviceSize offset = 0u; const vk::VkDeviceSize size = static_cast(resourceDesc.size.x()); const vk::VkBufferUsageFlags usage = m_supportReadOp->getInResourceUsageFlags() | m_supportWriteOp->getOutResourceUsageFlags(); vk::Move buffer = createBuffer(m_vkdA, *m_deviceA, size, usage, m_memoryHandleType, m_queueFamilyIndicesA); const vk::VkMemoryRequirements requirements = getMemoryRequirements(m_vkdA, *m_deviceA, *buffer, m_config.dedicated, m_getMemReq2Supported); exportedMemoryTypeIndex = chooseMemoryType(requirements.memoryTypeBits); vk::Move memory = allocateExportableMemory(m_vkdA, *m_deviceA, requirements.size, exportedMemoryTypeIndex, m_memoryHandleType, m_config.dedicated ? *buffer : (vk::VkBuffer)0); VK_CHECK(m_vkdA.bindBufferMemory(*m_deviceA, *buffer, *memory, 0u)); de::MovePtr allocation = de::MovePtr(new SimpleAllocation(m_vkdA, *m_deviceA, memory.disown())); resourceA = de::MovePtr(new Resource(resourceDesc.type, buffer, allocation, offset, size)); } NativeHandle nativeMemoryHandle; getMemoryNative(m_vkdA, *m_deviceA, resourceA->getMemory(), m_memoryHandleType, nativeMemoryHandle); const de::UniquePtr resourceB (importResource(m_vkdB, *m_deviceB, resourceDesc, m_queueFamilyIndicesB, *m_supportReadOp, *m_supportWriteOp, nativeMemoryHandle, m_memoryHandleType, exportedMemoryTypeIndex, m_config.dedicated)); const vk::VkQueue queueA (getQueue(m_vkdA, *m_deviceA, queueFamilyA)); const vk::Unique commandPoolA (createCommandPool(m_vkdA, *m_deviceA, queueFamilyA)); const vk::Unique commandBufferA (createCommandBuffer(m_vkdA, *m_deviceA, *commandPoolA)); vk::SimpleAllocator allocatorA (m_vkdA, *m_deviceA, vk::getPhysicalDeviceMemoryProperties(m_vkiA, m_physicalDeviceA)); OperationContext operationContextA (m_context, m_config.type, m_vkiA, m_vkdA, m_physicalDeviceA, *m_deviceA, allocatorA, m_context.getBinaryCollection(), m_pipelineCacheData); if (!checkQueueFlags(m_queueFamiliesA[m_queueANdx].queueFlags , m_supportWriteOp->getQueueFlags(operationContextA))) TCU_THROW(NotSupportedError, "Operation not supported by the source queue"); const vk::VkQueue queueB (getQueue(m_vkdB, *m_deviceB, queueFamilyB)); const vk::Unique commandPoolB (createCommandPool(m_vkdB, *m_deviceB, queueFamilyB)); const vk::Unique commandBufferB (createCommandBuffer(m_vkdB, *m_deviceB, *commandPoolB)); vk::SimpleAllocator allocatorB (m_vkdB, *m_deviceB, vk::getPhysicalDeviceMemoryProperties(m_vkiB, m_physicalDeviceB)); OperationContext operationContextB (m_context, m_config.type, m_vkiB, m_vkdB, m_physicalDeviceB, *m_deviceB, allocatorB, m_context.getBinaryCollection(), m_pipelineCacheData); if (!checkQueueFlags(m_queueFamiliesB[m_queueBNdx].queueFlags , m_supportReadOp->getQueueFlags(operationContextB))) TCU_THROW(NotSupportedError, "Operation not supported by the destination queue"); const de::UniquePtr writeOp (m_supportWriteOp->build(operationContextA, *resourceA)); const de::UniquePtr readOp (m_supportReadOp->build(operationContextB, *resourceB)); const SyncInfo writeSync = writeOp->getOutSyncInfo(); const SyncInfo readSync = readOp->getInSyncInfo(); SynchronizationWrapperPtr synchronizationWrapperA = getSynchronizationWrapper(m_config.type, m_vkdA, isTimelineSemaphore); SynchronizationWrapperPtr synchronizationWrapperB = getSynchronizationWrapper(m_config.type, m_vkdB, isTimelineSemaphore); beginCommandBuffer(m_vkdA, *commandBufferA); writeOp->recordCommands(*commandBufferA); recordWriteBarrier(synchronizationWrapperA, *commandBufferA, *resourceA, writeSync, queueFamilyA, readSync); endCommandBuffer(m_vkdA, *commandBufferA); beginCommandBuffer(m_vkdB, *commandBufferB); recordReadBarrier(synchronizationWrapperB, *commandBufferB, *resourceB, writeSync, readSync, queueFamilyB); readOp->recordCommands(*commandBufferB); endCommandBuffer(m_vkdB, *commandBufferB); { de::Random rng (1234); vk::VkCommandBufferSubmitInfoKHR cmdBufferInfos = makeCommonCommandBufferSubmitInfo(*commandBufferA); VkSemaphoreSubmitInfoKHR signalSemaphoreSubmitInfo = makeCommonSemaphoreSubmitInfo(*semaphoreA, rng.getInt(1, deIntMaxValue32(32)), VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR); synchronizationWrapperA->addSubmitInfo( 0u, DE_NULL, 1u, &cmdBufferInfos, 1u, &signalSemaphoreSubmitInfo, DE_FALSE, isTimelineSemaphore ); VK_CHECK(synchronizationWrapperA->queueSubmit(queueA, DE_NULL)); { NativeHandle nativeSemaphoreHandle; getSemaphoreNative(m_vkdA, *m_deviceA, *semaphoreA, m_semaphoreHandleType, nativeSemaphoreHandle); importSemaphore(m_vkdB, *m_deviceB, *semaphoreB, m_semaphoreHandleType, nativeSemaphoreHandle, 0u); } } { vk::VkCommandBufferSubmitInfoKHR cmdBufferInfos = makeCommonCommandBufferSubmitInfo(*commandBufferB); VkSemaphoreSubmitInfoKHR waitSemaphoreSubmitInfo = makeCommonSemaphoreSubmitInfo(*semaphoreB, 1u, readSync.stageMask); synchronizationWrapperB->addSubmitInfo( 1u, &waitSemaphoreSubmitInfo, 1u, &cmdBufferInfos, 0u, DE_NULL, isTimelineSemaphore ); VK_CHECK(synchronizationWrapperB->queueSubmit(queueB, DE_NULL)); } VK_CHECK(m_vkdA.queueWaitIdle(queueA)); VK_CHECK(m_vkdB.queueWaitIdle(queueB)); if (m_config.semaphoreType == vk::VK_SEMAPHORE_TYPE_TIMELINE_KHR) { deUint64 valueA; deUint64 valueB; VK_CHECK(m_vkdA.getSemaphoreCounterValue(*m_deviceA, *semaphoreA, &valueA)); VK_CHECK(m_vkdB.getSemaphoreCounterValue(*m_deviceB, *semaphoreB, &valueB)); if (valueA != valueB) return tcu::TestStatus::fail("Inconsistent values between shared semaphores"); } { const Data expected = writeOp->getData(); const Data actual = readOp->getData(); DE_ASSERT(expected.size == actual.size); if (!isIndirectBuffer(m_config.resource.type)) { if (0 != deMemCmp(expected.data, actual.data, expected.size)) { const size_t maxBytesLogged = 256; std::ostringstream expectedData; std::ostringstream actualData; size_t byteNdx = 0; // Find first byte difference for (; actual.data[byteNdx] == expected.data[byteNdx]; byteNdx++) { // Nothing } log << TestLog::Message << "First different byte at offset: " << byteNdx << TestLog::EndMessage; // Log 8 previous bytes before the first incorrect byte if (byteNdx > 8) { expectedData << "... "; actualData << "... "; byteNdx -= 8; } else byteNdx = 0; for (size_t i = 0; i < maxBytesLogged && byteNdx < expected.size; i++, byteNdx++) { expectedData << (i > 0 ? ", " : "") << (deUint32)expected.data[byteNdx]; actualData << (i > 0 ? ", " : "") << (deUint32)actual.data[byteNdx]; } if (expected.size > byteNdx) { expectedData << "..."; actualData << "..."; } log << TestLog::Message << "Expected data: (" << expectedData.str() << ")" << TestLog::EndMessage; log << TestLog::Message << "Actual data: (" << actualData.str() << ")" << TestLog::EndMessage; m_resultCollector.fail("Memory contents don't match"); } } else { const deUint32 expectedValue = reinterpret_cast(expected.data)[0]; const deUint32 actualValue = reinterpret_cast(actual.data)[0]; if (actualValue < expectedValue) { log << TestLog::Message << "Expected counter value: (" << expectedValue << ")" << TestLog::EndMessage; log << TestLog::Message << "Actual counter value: (" << actualValue << ")" << TestLog::EndMessage; m_resultCollector.fail("Counter value is smaller than expected"); } } } } catch (const tcu::NotSupportedError& error) { log << TestLog::Message << "Not supported: " << error.getMessage() << TestLog::EndMessage; } catch (const tcu::TestError& error) { m_resultCollector.fail(std::string("Exception: ") + error.getMessage()); } // Collect possible validation errors. InstanceAndDevice::collectMessagesA(); InstanceAndDevice::collectMessagesB(); // Move to next queue { m_queueBNdx++; if (m_queueBNdx >= m_queueFamiliesB.size()) { m_queueANdx++; if (m_queueANdx >= m_queueFamiliesA.size()) { return tcu::TestStatus(m_resultCollector.getResult(), m_resultCollector.getMessage()); } else { m_queueBNdx = 0; return tcu::TestStatus::incomplete(); } } else return tcu::TestStatus::incomplete(); } } struct Progs { void init (vk::SourceCollections& dst, TestConfig config) const { const de::UniquePtr readOp (makeOperationSupport(config.readOp, config.resource)); const de::UniquePtr writeOp (makeOperationSupport(config.writeOp, config.resource)); readOp->initPrograms(dst); writeOp->initPrograms(dst); } }; } // anonymous static void createTests (tcu::TestCaseGroup* group, SynchronizationType type) { tcu::TestContext& testCtx = group->getTestContext(); const struct { vk::VkExternalMemoryHandleTypeFlagBits memoryType; vk::VkExternalSemaphoreHandleTypeFlagBits semaphoreType; const char* nameSuffix; } cases[] = { { vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, "_fd" }, { vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, "_fence_fd" }, { vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT, vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT, "_win32_kmt" }, { vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT, "_win32" }, { vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, "_dma_buf" }, }; const std::string semaphoreNames[vk::VK_SEMAPHORE_TYPE_LAST] = { "_binary_semaphore", "_timeline_semaphore", }; for (size_t dedicatedNdx = 0; dedicatedNdx < 2; dedicatedNdx++) { const bool dedicated (dedicatedNdx == 1); de::MovePtr dedicatedGroup (new tcu::TestCaseGroup(testCtx, dedicated ? "dedicated" : "suballocated", "")); for (size_t writeOpNdx = 0; writeOpNdx < DE_LENGTH_OF_ARRAY(s_writeOps); ++writeOpNdx) for (size_t 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 (size_t resourceNdx = 0; resourceNdx < DE_LENGTH_OF_ARRAY(s_resources); ++resourceNdx) { const ResourceDescription& resource = s_resources[resourceNdx]; for (size_t caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++) { for (int semaphoreType = 0; semaphoreType < vk::VK_SEMAPHORE_TYPE_LAST; semaphoreType++) { if (isResourceSupported(writeOp, resource) && isResourceSupported(readOp, resource)) { const TestConfig config (type, resource, (vk::VkSemaphoreType)semaphoreType, writeOp, readOp, cases[caseNdx].memoryType, cases[caseNdx].semaphoreType, dedicated); std::string name = getResourceName(resource) + semaphoreNames[semaphoreType] + cases[caseNdx].nameSuffix; opGroup->addChild(new InstanceFactory1(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, "", Progs(), config)); empty = false; } } } } if (!empty) dedicatedGroup->addChild(opGroup.release()); } group->addChild(dedicatedGroup.release()); } } static void cleanupGroup (tcu::TestCaseGroup* group, SynchronizationType type) { DE_UNREF(group); DE_UNREF(type); // Destroy singleton object InstanceAndDevice::destroy(); } tcu::TestCaseGroup* createCrossInstanceSharingTest (tcu::TestContext& testCtx, SynchronizationType type) { return createTestGroup(testCtx, "cross_instance", "", createTests, type, cleanupGroup); } } // synchronization } // vkt