| /*------------------------------------------------------------------------ |
| * 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 "vktTestCaseUtil.hpp" |
| |
| #include "vktSynchronizationUtil.hpp" |
| #include "vktSynchronizationOperation.hpp" |
| #include "vktSynchronizationOperationTestData.hpp" |
| #include "vktSynchronizationOperationResources.hpp" |
| #include "vktExternalMemoryUtil.hpp" |
| |
| #include "tcuResultCollector.hpp" |
| #include "tcuTestLog.hpp" |
| |
| using tcu::TestLog; |
| using namespace vkt::ExternalMemoryUtil; |
| |
| namespace vkt |
| { |
| namespace synchronization |
| { |
| namespace |
| { |
| |
| struct TestConfig |
| { |
| TestConfig (const ResourceDescription& resource_, |
| OperationName writeOp_, |
| OperationName readOp_, |
| vk::VkExternalMemoryHandleTypeFlagBitsKHR memoryHandleType_, |
| vk::VkExternalSemaphoreHandleTypeFlagBitsKHR semaphoreHandleType_, |
| bool dedicated_) |
| : resource (resource_) |
| , writeOp (writeOp_) |
| , readOp (readOp_) |
| , memoryHandleType (memoryHandleType_) |
| , semaphoreHandleType (semaphoreHandleType_) |
| , dedicated (dedicated_) |
| { |
| } |
| |
| const ResourceDescription resource; |
| const OperationName writeOp; |
| const OperationName readOp; |
| const vk::VkExternalMemoryHandleTypeFlagBitsKHR memoryHandleType; |
| const vk::VkExternalSemaphoreHandleTypeFlagBitsKHR semaphoreHandleType; |
| const bool dedicated; |
| }; |
| |
| 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); |
| } |
| |
| class DeviceId |
| { |
| public: |
| DeviceId (deUint32 vendorId, |
| deUint32 driverVersion, |
| const deUint8 driverUUID[VK_UUID_SIZE], |
| const deUint8 deviceUUID[VK_UUID_SIZE]); |
| |
| bool operator== (const DeviceId& other) const; |
| bool operator|= (const DeviceId& other) const; |
| |
| private: |
| const deUint32 m_vendorId; |
| const deUint32 m_driverVersion; |
| deUint8 m_driverUUID[VK_UUID_SIZE]; |
| deUint8 m_deviceUUID[VK_UUID_SIZE]; |
| }; |
| |
| DeviceId::DeviceId (deUint32 vendorId, |
| deUint32 driverVersion, |
| const deUint8 driverUUID[VK_UUID_SIZE], |
| const deUint8 deviceUUID[VK_UUID_SIZE]) |
| : m_vendorId (vendorId) |
| , m_driverVersion (driverVersion) |
| { |
| deMemcpy(m_driverUUID, driverUUID, sizeof(m_driverUUID)); |
| deMemcpy(m_deviceUUID, deviceUUID, sizeof(m_deviceUUID)); |
| } |
| |
| bool DeviceId::operator== (const DeviceId& other) const |
| { |
| if (this == &other) |
| return true; |
| |
| if (m_vendorId != other.m_vendorId) |
| return false; |
| |
| if (m_driverVersion != other.m_driverVersion) |
| return false; |
| |
| if (deMemCmp(m_driverUUID, other.m_driverUUID, sizeof(m_driverUUID)) != 0) |
| return false; |
| |
| return deMemCmp(m_deviceUUID, other.m_deviceUUID, sizeof(m_deviceUUID)) == 0; |
| } |
| |
| DeviceId getDeviceId (const vk::InstanceInterface& vki, |
| vk::VkPhysicalDevice physicalDevice) |
| { |
| vk::VkPhysicalDeviceIDPropertiesKHR propertiesId; |
| vk::VkPhysicalDeviceProperties2KHR properties; |
| |
| deMemset(&properties, 0, sizeof(properties)); |
| deMemset(&propertiesId, 0, sizeof(propertiesId)); |
| |
| propertiesId.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR; |
| |
| properties.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; |
| properties.pNext = &propertiesId; |
| |
| vki.getPhysicalDeviceProperties2KHR(physicalDevice, &properties); |
| |
| return DeviceId(properties.properties.vendorID, properties.properties.driverVersion, propertiesId.driverUUID, propertiesId.deviceUUID); |
| } |
| |
| vk::Move<vk::VkInstance> createInstance (const vk::PlatformInterface& vkp) |
| { |
| try |
| { |
| std::vector<std::string> 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 vk::createDefaultInstance(vkp, std::vector<std::string>(), extensions); |
| } |
| catch (const vk::Error& error) |
| { |
| if (error.getError() == vk::VK_ERROR_EXTENSION_NOT_PRESENT) |
| TCU_THROW(NotSupportedError, "Required external memory extensions not supported by the instance"); |
| else |
| throw; |
| } |
| } |
| |
| vk::VkPhysicalDevice getPhysicalDevice (const vk::InstanceInterface& vki, |
| vk::VkInstance instance, |
| const tcu::CommandLine& cmdLine) |
| { |
| return vk::chooseDevice(vki, instance, cmdLine); |
| } |
| |
| vk::VkPhysicalDevice getPhysicalDevice (const vk::InstanceInterface& vki, vk::VkInstance instance, const DeviceId& deviceId) |
| { |
| const std::vector<vk::VkPhysicalDevice> devices (vk::enumeratePhysicalDevices(vki, instance)); |
| |
| for (size_t deviceNdx = 0; deviceNdx < devices.size(); deviceNdx++) |
| { |
| if (deviceId == getDeviceId(vki, devices[deviceNdx])) |
| return devices[deviceNdx]; |
| } |
| |
| TCU_FAIL("No matching device found"); |
| |
| return (vk::VkPhysicalDevice)0; |
| } |
| |
| vk::Move<vk::VkDevice> createDevice (const vk::InstanceInterface& vki, |
| vk::VkPhysicalDevice physicalDevice, |
| vk::VkExternalMemoryHandleTypeFlagBitsKHR memoryHandleType, |
| vk::VkExternalSemaphoreHandleTypeFlagBitsKHR semaphoreHandleType, |
| bool dedicated, |
| bool khrMemReqSupported) |
| { |
| const float priority = 0.0f; |
| const std::vector<vk::VkQueueFamilyProperties> queueFamilyProperties = vk::getPhysicalDeviceQueueFamilyProperties(vki, physicalDevice); |
| std::vector<deUint32> queueFamilyIndices (queueFamilyProperties.size(), 0xFFFFFFFFu); |
| std::vector<const char*> extensions; |
| |
| if (dedicated) |
| extensions.push_back("VK_KHR_dedicated_allocation"); |
| |
| if (khrMemReqSupported) |
| extensions.push_back("VK_KHR_get_memory_requirements2"); |
| |
| extensions.push_back("VK_KHR_external_semaphore"); |
| extensions.push_back("VK_KHR_external_memory"); |
| |
| if (memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR |
| || semaphoreHandleType == vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR |
| || semaphoreHandleType == vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR) |
| { |
| extensions.push_back("VK_KHR_external_semaphore_fd"); |
| extensions.push_back("VK_KHR_external_memory_fd"); |
| } |
| |
| if (memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR |
| || memoryHandleType == vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR |
| || semaphoreHandleType == vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR |
| || semaphoreHandleType == vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR) |
| { |
| extensions.push_back("VK_KHR_external_semaphore_win32"); |
| extensions.push_back("VK_KHR_external_memory_win32"); |
| } |
| |
| try |
| { |
| std::vector<vk::VkDeviceQueueCreateInfo> 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, |
| DE_NULL, |
| 0u, |
| |
| (deUint32)queues.size(), |
| &queues[0], |
| |
| 0u, |
| DE_NULL, |
| |
| (deUint32)extensions.size(), |
| extensions.empty() ? DE_NULL : &extensions[0], |
| 0u |
| }; |
| |
| return vk::createDevice(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; |
| } |
| } |
| |
| 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<vk::VkCommandPool> 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<vk::VkCommandBuffer> 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); |
| } |
| |
| de::MovePtr<vk::Allocation> allocateAndBindMemory (const vk::DeviceInterface& vkd, |
| vk::VkDevice device, |
| vk::VkBuffer buffer, |
| vk::VkExternalMemoryHandleTypeFlagBitsKHR externalType, |
| deUint32& exportedMemoryTypeIndex, |
| bool dedicated, |
| bool getMemReq2Supported) |
| { |
| vk::VkMemoryRequirements memoryRequirements = { 0u, 0u, 0u, }; |
| |
| if (getMemReq2Supported) |
| { |
| const vk::VkBufferMemoryRequirementsInfo2KHR requirementInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR, |
| DE_NULL, |
| buffer |
| }; |
| vk::VkMemoryDedicatedRequirementsKHR dedicatedRequirements = |
| { |
| vk::VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR, |
| DE_NULL, |
| VK_FALSE, |
| VK_FALSE |
| }; |
| vk::VkMemoryRequirements2KHR requirements = |
| { |
| vk::VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR, |
| &dedicatedRequirements, |
| { 0u, 0u, 0u, } |
| }; |
| vkd.getBufferMemoryRequirements2KHR(device, &requirementInfo, &requirements); |
| |
| if (!dedicated && dedicatedRequirements.requiresDedicatedAllocation) |
| TCU_THROW(NotSupportedError, "Memory requires dedicated allocation"); |
| |
| memoryRequirements = requirements.memoryRequirements; |
| } |
| else |
| { |
| vkd.getBufferMemoryRequirements(device, buffer, &memoryRequirements); |
| } |
| |
| |
| vk::Move<vk::VkDeviceMemory> memory = allocateExportableMemory(vkd, device, memoryRequirements, externalType, dedicated ? buffer : (vk::VkBuffer)0, exportedMemoryTypeIndex); |
| VK_CHECK(vkd.bindBufferMemory(device, buffer, *memory, 0u)); |
| |
| return de::MovePtr<vk::Allocation>(new SimpleAllocation(vkd, device, memory.disown())); |
| } |
| |
| de::MovePtr<vk::Allocation> allocateAndBindMemory (const vk::DeviceInterface& vkd, |
| vk::VkDevice device, |
| vk::VkImage image, |
| vk::VkExternalMemoryHandleTypeFlagBitsKHR externalType, |
| deUint32& exportedMemoryTypeIndex, |
| bool dedicated, |
| bool getMemReq2Supported) |
| { |
| vk::VkMemoryRequirements memoryRequirements = { 0u, 0u, 0u, }; |
| |
| if (getMemReq2Supported) |
| { |
| const vk::VkImageMemoryRequirementsInfo2KHR requirementInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR, |
| DE_NULL, |
| image |
| }; |
| vk::VkMemoryDedicatedRequirementsKHR dedicatedRequirements = |
| { |
| vk::VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR, |
| DE_NULL, |
| VK_FALSE, |
| VK_FALSE |
| }; |
| vk::VkMemoryRequirements2KHR requirements = |
| { |
| vk::VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR, |
| &dedicatedRequirements, |
| { 0u, 0u, 0u, } |
| }; |
| vkd.getImageMemoryRequirements2KHR(device, &requirementInfo, &requirements); |
| |
| if (!dedicated && dedicatedRequirements.requiresDedicatedAllocation) |
| TCU_THROW(NotSupportedError, "Memory requires dedicated allocation"); |
| |
| memoryRequirements = requirements.memoryRequirements; |
| } |
| else |
| { |
| vkd.getImageMemoryRequirements(device, image, &memoryRequirements); |
| } |
| |
| vk::Move<vk::VkDeviceMemory> memory = allocateExportableMemory(vkd, device, memoryRequirements, externalType, dedicated ? image : (vk::VkImage)0, exportedMemoryTypeIndex); |
| VK_CHECK(vkd.bindImageMemory(device, image, *memory, 0u)); |
| |
| return de::MovePtr<vk::Allocation>(new SimpleAllocation(vkd, device, memory.disown())); |
| } |
| |
| de::MovePtr<Resource> createResource (const vk::DeviceInterface& vkd, |
| vk::VkDevice device, |
| const ResourceDescription& resourceDesc, |
| const std::vector<deUint32>& queueFamilyIndices, |
| const OperationSupport& readOp, |
| const OperationSupport& writeOp, |
| vk::VkExternalMemoryHandleTypeFlagBitsKHR externalType, |
| deUint32& exportedMemoryTypeIndex, |
| bool dedicated, |
| bool getMemReq2Supported) |
| { |
| 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::VkExternalMemoryImageCreateInfoKHR externalInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR, |
| DE_NULL, |
| (vk::VkExternalMemoryHandleTypeFlagsKHR)externalType |
| }; |
| const vk::VkImageCreateInfo createInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
| &externalInfo, |
| 0u, |
| |
| resourceDesc.imageType, |
| resourceDesc.imageFormat, |
| extent, |
| 1u, |
| 1u, |
| vk::VK_SAMPLE_COUNT_1_BIT, |
| vk::VK_IMAGE_TILING_OPTIMAL, |
| readOp.getResourceUsageFlags() | writeOp.getResourceUsageFlags(), |
| vk::VK_SHARING_MODE_EXCLUSIVE, |
| |
| (deUint32)queueFamilyIndices.size(), |
| &queueFamilyIndices[0], |
| vk::VK_IMAGE_LAYOUT_UNDEFINED |
| }; |
| |
| vk::Move<vk::VkImage> image = vk::createImage(vkd, device, &createInfo); |
| de::MovePtr<vk::Allocation> allocation = allocateAndBindMemory(vkd, device, *image, externalType, exportedMemoryTypeIndex, dedicated, getMemReq2Supported); |
| |
| return de::MovePtr<Resource>(new Resource(image, allocation, extent, resourceDesc.imageType, resourceDesc.imageFormat, subresourceRange, subresourceLayers)); |
| } |
| else |
| { |
| const vk::VkDeviceSize offset = 0u; |
| const vk::VkDeviceSize size = static_cast<vk::VkDeviceSize>(resourceDesc.size.x()); |
| const vk::VkBufferUsageFlags usage = readOp.getResourceUsageFlags() | writeOp.getResourceUsageFlags(); |
| const vk:: VkExternalMemoryBufferCreateInfoKHR externalInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR, |
| DE_NULL, |
| (vk::VkExternalMemoryHandleTypeFlagsKHR)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<vk::VkBuffer> buffer = vk::createBuffer(vkd, device, &createInfo); |
| de::MovePtr<vk::Allocation> allocation = allocateAndBindMemory(vkd, device, *buffer, externalType, exportedMemoryTypeIndex, dedicated, getMemReq2Supported); |
| |
| return de::MovePtr<Resource>(new Resource(resourceDesc.type, buffer, allocation, offset, size)); |
| } |
| } |
| |
| de::MovePtr<vk::Allocation> importAndBindMemory (const vk::DeviceInterface& vkd, |
| vk::VkDevice device, |
| vk::VkBuffer buffer, |
| NativeHandle& nativeHandle, |
| vk::VkExternalMemoryHandleTypeFlagBitsKHR externalType, |
| deUint32 exportedMemoryTypeIndex, |
| bool dedicated) |
| { |
| const vk::VkMemoryRequirements requirements = vk::getBufferMemoryRequirements(vkd, device, buffer); |
| vk::Move<vk::VkDeviceMemory> 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<vk::Allocation>(new SimpleAllocation(vkd, device, memory.disown())); |
| } |
| |
| de::MovePtr<vk::Allocation> importAndBindMemory (const vk::DeviceInterface& vkd, |
| vk::VkDevice device, |
| vk::VkImage image, |
| NativeHandle& nativeHandle, |
| vk::VkExternalMemoryHandleTypeFlagBitsKHR externalType, |
| deUint32 exportedMemoryTypeIndex, |
| bool dedicated) |
| { |
| const vk::VkMemoryRequirements requirements = vk::getImageMemoryRequirements(vkd, device, image); |
| vk::Move<vk::VkDeviceMemory> 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<vk::Allocation>(new SimpleAllocation(vkd, device, memory.disown())); |
| } |
| |
| de::MovePtr<Resource> importResource (const vk::DeviceInterface& vkd, |
| vk::VkDevice device, |
| const ResourceDescription& resourceDesc, |
| const std::vector<deUint32>& queueFamilyIndices, |
| const OperationSupport& readOp, |
| const OperationSupport& writeOp, |
| NativeHandle& nativeHandle, |
| vk::VkExternalMemoryHandleTypeFlagBitsKHR 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:: VkExternalMemoryImageCreateInfoKHR externalInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR, |
| DE_NULL, |
| (vk::VkExternalMemoryHandleTypeFlagsKHR)externalType |
| }; |
| const vk::VkImageCreateInfo createInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
| &externalInfo, |
| 0u, |
| |
| resourceDesc.imageType, |
| resourceDesc.imageFormat, |
| extent, |
| 1u, |
| 1u, |
| vk::VK_SAMPLE_COUNT_1_BIT, |
| vk::VK_IMAGE_TILING_OPTIMAL, |
| readOp.getResourceUsageFlags() | writeOp.getResourceUsageFlags(), |
| vk::VK_SHARING_MODE_EXCLUSIVE, |
| |
| (deUint32)queueFamilyIndices.size(), |
| &queueFamilyIndices[0], |
| vk::VK_IMAGE_LAYOUT_UNDEFINED |
| }; |
| |
| vk::Move<vk::VkImage> image = vk::createImage(vkd, device, &createInfo); |
| de::MovePtr<vk::Allocation> allocation = importAndBindMemory(vkd, device, *image, nativeHandle, externalType, exportedMemoryTypeIndex, dedicated); |
| |
| return de::MovePtr<Resource>(new Resource(image, allocation, extent, resourceDesc.imageType, resourceDesc.imageFormat, subresourceRange, subresourceLayers)); |
| } |
| else |
| { |
| const vk::VkDeviceSize offset = 0u; |
| const vk::VkDeviceSize size = static_cast<vk::VkDeviceSize>(resourceDesc.size.x()); |
| const vk::VkBufferUsageFlags usage = readOp.getResourceUsageFlags() | writeOp.getResourceUsageFlags(); |
| const vk:: VkExternalMemoryBufferCreateInfoKHR externalInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR, |
| DE_NULL, |
| (vk::VkExternalMemoryHandleTypeFlagsKHR)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<vk::VkBuffer> buffer = vk::createBuffer(vkd, device, &createInfo); |
| de::MovePtr<vk::Allocation> allocation = importAndBindMemory(vkd, device, *buffer, nativeHandle, externalType, exportedMemoryTypeIndex, dedicated); |
| |
| return de::MovePtr<Resource>(new Resource(resourceDesc.type, buffer, allocation, offset, size)); |
| } |
| } |
| |
| void recordWriteBarrier (const vk::DeviceInterface& vkd, |
| vk::VkCommandBuffer commandBuffer, |
| const Resource& resource, |
| const SyncInfo& writeSync, |
| deUint32 writeQueueFamilyIndex, |
| const SyncInfo& readSync) |
| { |
| const vk::VkPipelineStageFlags srcStageMask = writeSync.stageMask; |
| const vk::VkAccessFlags srcAccessMask = writeSync.accessMask; |
| |
| const vk::VkPipelineStageFlags dstStageMask = readSync.stageMask; |
| const vk::VkAccessFlags dstAccessMask = readSync.accessMask; |
| |
| const vk::VkDependencyFlags dependencyFlags = 0; |
| |
| if (resource.getType() == RESOURCE_TYPE_IMAGE) |
| { |
| const vk::VkImageMemoryBarrier barrier = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |
| DE_NULL, |
| |
| srcAccessMask, |
| dstAccessMask, |
| |
| writeSync.imageLayout, |
| readSync.imageLayout, |
| |
| writeQueueFamilyIndex, |
| VK_QUEUE_FAMILY_EXTERNAL_KHR, |
| |
| resource.getImage().handle, |
| resource.getImage().subresourceRange |
| }; |
| |
| vkd.cmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0u, (const vk::VkMemoryBarrier*)DE_NULL, 0u, (const vk::VkBufferMemoryBarrier*)DE_NULL, 1u, (const vk::VkImageMemoryBarrier*)&barrier); |
| } |
| else |
| { |
| const vk::VkBufferMemoryBarrier barrier = |
| { |
| vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, |
| DE_NULL, |
| |
| srcAccessMask, |
| dstAccessMask, |
| |
| writeQueueFamilyIndex, |
| VK_QUEUE_FAMILY_EXTERNAL_KHR, |
| |
| resource.getBuffer().handle, |
| 0u, |
| VK_WHOLE_SIZE |
| }; |
| |
| vkd.cmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0u, (const vk::VkMemoryBarrier*)DE_NULL, 1u, (const vk::VkBufferMemoryBarrier*)&barrier, 0u, (const vk::VkImageMemoryBarrier*)DE_NULL); |
| } |
| } |
| |
| void recordReadBarrier (const vk::DeviceInterface& vkd, |
| vk::VkCommandBuffer commandBuffer, |
| const Resource& resource, |
| const SyncInfo& writeSync, |
| const SyncInfo& readSync, |
| deUint32 readQueueFamilyIndex) |
| { |
| const vk::VkPipelineStageFlags srcStageMask = readSync.stageMask; |
| const vk::VkAccessFlags srcAccessMask = readSync.accessMask; |
| |
| const vk::VkPipelineStageFlags dstStageMask = readSync.stageMask; |
| const vk::VkAccessFlags dstAccessMask = readSync.accessMask; |
| |
| const vk::VkDependencyFlags dependencyFlags = 0; |
| |
| if (resource.getType() == RESOURCE_TYPE_IMAGE) |
| { |
| const vk::VkImageMemoryBarrier barrier = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |
| DE_NULL, |
| |
| srcAccessMask, |
| dstAccessMask, |
| |
| writeSync.imageLayout, |
| readSync.imageLayout, |
| |
| VK_QUEUE_FAMILY_EXTERNAL_KHR, |
| readQueueFamilyIndex, |
| |
| resource.getImage().handle, |
| resource.getImage().subresourceRange |
| }; |
| |
| vkd.cmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0u, (const vk::VkMemoryBarrier*)DE_NULL, 0u, (const vk::VkBufferMemoryBarrier*)DE_NULL, 1u, (const vk::VkImageMemoryBarrier*)&barrier); |
| } |
| else |
| { |
| const vk::VkBufferMemoryBarrier barrier = |
| { |
| vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, |
| DE_NULL, |
| |
| srcAccessMask, |
| dstAccessMask, |
| |
| VK_QUEUE_FAMILY_EXTERNAL_KHR, |
| readQueueFamilyIndex, |
| |
| resource.getBuffer().handle, |
| 0u, |
| VK_WHOLE_SIZE |
| }; |
| |
| vkd.cmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0u, (const vk::VkMemoryBarrier*)DE_NULL, 1u, (const vk::VkBufferMemoryBarrier*)&barrier, 0u, (const vk::VkImageMemoryBarrier*)DE_NULL); |
| } |
| } |
| |
| std::vector<deUint32> getFamilyIndices (const std::vector<vk::VkQueueFamilyProperties>& properties) |
| { |
| std::vector<deUint32> 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<OperationSupport> m_supportWriteOp; |
| const de::UniquePtr<OperationSupport> m_supportReadOp; |
| |
| const vk::Unique<vk::VkInstance> m_instanceA; |
| |
| const vk::InstanceDriver m_vkiA; |
| const vk::VkPhysicalDevice m_physicalDeviceA; |
| const std::vector<vk::VkQueueFamilyProperties> m_queueFamiliesA; |
| const std::vector<deUint32> m_queueFamilyIndicesA; |
| |
| const bool m_getMemReq2Supported; |
| |
| const vk::Unique<vk::VkDevice> m_deviceA; |
| const vk::DeviceDriver m_vkdA; |
| |
| const vk::Unique<vk::VkInstance> m_instanceB; |
| const vk::InstanceDriver m_vkiB; |
| const vk::VkPhysicalDevice m_physicalDeviceB; |
| const std::vector<vk::VkQueueFamilyProperties> m_queueFamiliesB; |
| const std::vector<deUint32> m_queueFamilyIndicesB; |
| const vk::Unique<vk::VkDevice> m_deviceB; |
| const vk::DeviceDriver m_vkdB; |
| |
| const vk::VkExternalSemaphoreHandleTypeFlagBitsKHR m_semaphoreHandleType; |
| const vk::VkExternalMemoryHandleTypeFlagBitsKHR 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_instanceA (createInstance(context.getPlatformInterface())) |
| |
| , m_vkiA (context.getPlatformInterface(), *m_instanceA) |
| , m_physicalDeviceA (getPhysicalDevice(m_vkiA, *m_instanceA, context.getTestContext().getCommandLine())) |
| , m_queueFamiliesA (vk::getPhysicalDeviceQueueFamilyProperties(m_vkiA, m_physicalDeviceA)) |
| , m_queueFamilyIndicesA (getFamilyIndices(m_queueFamiliesA)) |
| , m_getMemReq2Supported (de::contains(context.getDeviceExtensions().begin(), context.getDeviceExtensions().end(), "VK_KHR_get_memory_requirements2")) |
| , m_deviceA (createDevice(m_vkiA, m_physicalDeviceA, m_config.memoryHandleType, m_config.semaphoreHandleType, m_config.dedicated, m_getMemReq2Supported)) |
| , m_vkdA (m_vkiA, *m_deviceA) |
| |
| , m_instanceB (createInstance(context.getPlatformInterface())) |
| |
| , m_vkiB (context.getPlatformInterface(), *m_instanceB) |
| , m_physicalDeviceB (getPhysicalDevice(m_vkiB, *m_instanceB, getDeviceId(m_vkiA, m_physicalDeviceA))) |
| , m_queueFamiliesB (vk::getPhysicalDeviceQueueFamilyProperties(m_vkiB, m_physicalDeviceB)) |
| , m_queueFamilyIndicesB (getFamilyIndices(m_queueFamiliesB)) |
| , m_deviceB (createDevice(m_vkiB, m_physicalDeviceB, m_config.memoryHandleType, m_config.semaphoreHandleType, m_config.dedicated, m_getMemReq2Supported)) |
| , m_vkdB (m_vkiB, *m_deviceB) |
| |
| , m_semaphoreHandleType (m_config.semaphoreHandleType) |
| , m_memoryHandleType (m_config.memoryHandleType) |
| |
| , m_resultCollector (context.getTestContext().getLog()) |
| , m_queueANdx (0) |
| , m_queueBNdx (0) |
| { |
| TestLog& log = m_context.getTestContext().getLog(); |
| |
| // Check resource support |
| if (m_config.resource.type == RESOURCE_TYPE_IMAGE) |
| { |
| const vk::VkPhysicalDeviceExternalImageFormatInfoKHR externalInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR, |
| DE_NULL, |
| m_memoryHandleType |
| }; |
| const vk::VkPhysicalDeviceImageFormatInfo2KHR imageFormatInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR, |
| &externalInfo, |
| m_config.resource.imageFormat, |
| m_config.resource.imageType, |
| vk::VK_IMAGE_TILING_OPTIMAL, |
| m_supportReadOp->getResourceUsageFlags() | m_supportWriteOp->getResourceUsageFlags(), |
| 0u |
| }; |
| vk::VkExternalImageFormatPropertiesKHR externalProperties = |
| { |
| vk::VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR, |
| DE_NULL, |
| { 0u, 0u, 0u } |
| }; |
| vk::VkImageFormatProperties2KHR formatProperties = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR, |
| &externalProperties, |
| { |
| { 0u, 0u, 0u }, |
| 0u, |
| 0u, |
| 0u, |
| 0u, |
| } |
| }; |
| |
| { |
| const vk::VkResult res = m_vkiA.getPhysicalDeviceImageFormatProperties2KHR(m_physicalDeviceA, &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 (!m_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"); |
| } |
| } |
| else |
| { |
| const vk::VkPhysicalDeviceExternalBufferInfoKHR info = |
| { |
| vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR, |
| DE_NULL, |
| |
| 0u, |
| m_supportReadOp->getResourceUsageFlags() | m_supportWriteOp->getResourceUsageFlags(), |
| m_memoryHandleType |
| }; |
| vk::VkExternalBufferPropertiesKHR properties = |
| { |
| vk::VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR, |
| DE_NULL, |
| { 0u, 0u, 0u} |
| }; |
| m_vkiA.getPhysicalDeviceExternalBufferPropertiesKHR(m_physicalDeviceA, &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 (!m_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::VkPhysicalDeviceExternalSemaphoreInfoKHR info = |
| { |
| vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR, |
| DE_NULL, |
| m_semaphoreHandleType |
| }; |
| vk::VkExternalSemaphorePropertiesKHR properties; |
| |
| m_vkiA.getPhysicalDeviceExternalSemaphorePropertiesKHR(m_physicalDeviceA, &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"); |
| } |
| } |
| |
| tcu::TestStatus SharingTestInstance::iterate (void) |
| { |
| TestLog& log (m_context.getTestContext().getLog()); |
| |
| 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<vk::VkSemaphore> semaphoreA (createExportableSemaphore(m_vkdA, *m_deviceA, m_semaphoreHandleType)); |
| const vk::Unique<vk::VkSemaphore> semaphoreB (createSemaphore(m_vkdB, *m_deviceB)); |
| |
| deUint32 exportedMemoryTypeIndex = ~0U; |
| const de::UniquePtr<Resource> resourceA (createResource(m_vkdA, *m_deviceA, m_config.resource, m_queueFamilyIndicesA, *m_supportReadOp, *m_supportWriteOp, m_memoryHandleType, exportedMemoryTypeIndex, m_config.dedicated, m_getMemReq2Supported)); |
| |
| NativeHandle nativeMemoryHandle; |
| getMemoryNative(m_vkdA, *m_deviceA, resourceA->getMemory(), m_memoryHandleType, nativeMemoryHandle); |
| |
| const de::UniquePtr<Resource> resourceB (importResource(m_vkdB, *m_deviceB, m_config.resource, m_queueFamilyIndicesB, *m_supportReadOp, *m_supportWriteOp, nativeMemoryHandle, m_memoryHandleType, exportedMemoryTypeIndex, m_config.dedicated)); |
| |
| try |
| { |
| const vk::VkQueue queueA (getQueue(m_vkdA, *m_deviceA, queueFamilyA)); |
| const vk::Unique<vk::VkCommandPool> commandPoolA (createCommandPool(m_vkdA, *m_deviceA, queueFamilyA)); |
| const vk::Unique<vk::VkCommandBuffer> commandBufferA (createCommandBuffer(m_vkdA, *m_deviceA, *commandPoolA)); |
| vk::SimpleAllocator allocatorA (m_vkdA, *m_deviceA, vk::getPhysicalDeviceMemoryProperties(m_vkiA, m_physicalDeviceA)); |
| const std::vector<std::string> deviceExtensionsA; |
| OperationContext operationContextA (m_vkiA, m_vkdA, m_physicalDeviceA, *m_deviceA, allocatorA, deviceExtensionsA, 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<vk::VkCommandPool> commandPoolB (createCommandPool(m_vkdB, *m_deviceB, queueFamilyB)); |
| const vk::Unique<vk::VkCommandBuffer> commandBufferB (createCommandBuffer(m_vkdB, *m_deviceB, *commandPoolB)); |
| vk::SimpleAllocator allocatorB (m_vkdB, *m_deviceB, vk::getPhysicalDeviceMemoryProperties(m_vkiB, m_physicalDeviceB)); |
| const std::vector<std::string> deviceExtensionsB; |
| OperationContext operationContextB (m_vkiB, m_vkdB, m_physicalDeviceB, *m_deviceB, allocatorB, deviceExtensionsB, 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<Operation> writeOp (m_supportWriteOp->build(operationContextA, *resourceA)); |
| const de::UniquePtr<Operation> readOp (m_supportReadOp->build(operationContextB, *resourceB)); |
| |
| const SyncInfo writeSync = writeOp->getSyncInfo(); |
| const SyncInfo readSync = readOp->getSyncInfo(); |
| |
| beginCommandBuffer(m_vkdA, *commandBufferA); |
| writeOp->recordCommands(*commandBufferA); |
| recordWriteBarrier(m_vkdA, *commandBufferA, *resourceA, writeSync, queueFamilyA, readSync); |
| endCommandBuffer(m_vkdA, *commandBufferA); |
| |
| beginCommandBuffer(m_vkdB, *commandBufferB); |
| recordReadBarrier(m_vkdB, *commandBufferB, *resourceB, writeSync, readSync, queueFamilyB); |
| readOp->recordCommands(*commandBufferB); |
| endCommandBuffer(m_vkdB, *commandBufferB); |
| |
| { |
| const vk::VkCommandBuffer commandBuffer = *commandBufferA; |
| const vk::VkSemaphore semaphore = *semaphoreA; |
| const vk::VkSubmitInfo submitInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_SUBMIT_INFO, |
| DE_NULL, |
| |
| 0u, |
| DE_NULL, |
| DE_NULL, |
| |
| 1u, |
| &commandBuffer, |
| 1u, |
| &semaphore |
| }; |
| |
| VK_CHECK(m_vkdA.queueSubmit(queueA, 1u, &submitInfo, DE_NULL)); |
| |
| { |
| NativeHandle nativeSemaphoreHandle; |
| |
| getSemaphoreNative(m_vkdA, *m_deviceA, *semaphoreA, m_semaphoreHandleType, nativeSemaphoreHandle); |
| importSemaphore(m_vkdB, *m_deviceB, *semaphoreB, m_semaphoreHandleType, nativeSemaphoreHandle, 0u); |
| } |
| } |
| { |
| const vk::VkCommandBuffer commandBuffer = *commandBufferB; |
| const vk::VkSemaphore semaphore = *semaphoreB; |
| const vk::VkPipelineStageFlags dstStage = readSync.stageMask; |
| const vk::VkSubmitInfo submitInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_SUBMIT_INFO, |
| DE_NULL, |
| |
| 1u, |
| &semaphore, |
| &dstStage, |
| |
| 1u, |
| &commandBuffer, |
| 0u, |
| DE_NULL, |
| }; |
| |
| VK_CHECK(m_vkdB.queueSubmit(queueB, 1u, &submitInfo, DE_NULL)); |
| } |
| |
| VK_CHECK(m_vkdA.queueWaitIdle(queueA)); |
| VK_CHECK(m_vkdB.queueWaitIdle(queueB)); |
| |
| { |
| const Data expected = writeOp->getData(); |
| const Data actual = readOp->getData(); |
| |
| DE_ASSERT(expected.size == actual.size); |
| |
| 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"); |
| } |
| } |
| } |
| 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()); |
| } |
| |
| // 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<OperationSupport> readOp (makeOperationSupport(config.readOp, config.resource)); |
| const de::UniquePtr<OperationSupport> writeOp (makeOperationSupport(config.writeOp, config.resource)); |
| |
| readOp->initPrograms(dst); |
| writeOp->initPrograms(dst); |
| } |
| }; |
| |
| } // anonymous |
| |
| tcu::TestCaseGroup* createCrossInstanceSharingTest (tcu::TestContext& testCtx) |
| { |
| const struct |
| { |
| vk::VkExternalMemoryHandleTypeFlagBitsKHR memoryType; |
| vk::VkExternalSemaphoreHandleTypeFlagBitsKHR semaphoreType; |
| const char* nameSuffix; |
| } cases[] = |
| { |
| { |
| vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR, |
| vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR, |
| "_fd" |
| }, |
| { |
| vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR, |
| vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR, |
| "_fence_fd" |
| }, |
| { |
| vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR, |
| vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR, |
| "_win32_kmt" |
| }, |
| { |
| vk::VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR, |
| vk::VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR, |
| "_win32" |
| }, |
| }; |
| de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "cross_instance", "")); |
| |
| for (size_t dedicatedNdx = 0; dedicatedNdx < 2; dedicatedNdx++) |
| { |
| const bool dedicated (dedicatedNdx == 1); |
| de::MovePtr<tcu::TestCaseGroup> 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<tcu::TestCaseGroup> 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++) |
| { |
| std::string name= getResourceName(resource) + cases[caseNdx].nameSuffix; |
| |
| if (isResourceSupported(writeOp, resource) && isResourceSupported(readOp, resource)) |
| { |
| const TestConfig config (resource, writeOp, readOp, cases[caseNdx].memoryType, cases[caseNdx].semaphoreType, dedicated); |
| |
| opGroup->addChild(new InstanceFactory1<SharingTestInstance, TestConfig, Progs>(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, "", Progs(), config)); |
| empty = false; |
| } |
| } |
| } |
| |
| if (!empty) |
| dedicatedGroup->addChild(opGroup.release()); |
| } |
| |
| group->addChild(dedicatedGroup.release()); |
| } |
| |
| return group.release(); |
| } |
| |
| } // synchronization |
| } // vkt |