| /* |
| * Copyright (c) 2015-2025 The Khronos Group Inc. |
| * Copyright (c) 2015-2025 Valve Corporation |
| * Copyright (c) 2015-2025 LunarG, Inc. |
| * Copyright (c) 2015-2024 Google, 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 |
| */ |
| |
| #include "../framework/layer_validation_tests.h" |
| #include "../framework/external_memory_sync.h" |
| #include "utils/math_utils.h" |
| |
| class PositiveExternalMemorySync : public ExternalMemorySyncTest {}; |
| |
| TEST_F(PositiveExternalMemorySync, GetMemoryFdHandle) { |
| TEST_DESCRIPTION("Get POXIS handle for memory allocation"); |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); |
| RETURN_IF_SKIP(Init()); |
| VkExportMemoryAllocateInfo export_info = vku::InitStructHelper(); |
| export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| |
| VkMemoryAllocateInfo alloc_info = vku::InitStructHelper(&export_info); |
| alloc_info.allocationSize = 1024; |
| alloc_info.memoryTypeIndex = 0; |
| |
| vkt::DeviceMemory memory(*m_device, alloc_info); |
| VkMemoryGetFdInfoKHR get_handle_info = vku::InitStructHelper(); |
| get_handle_info.memory = memory; |
| get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| |
| int fd = -1; |
| vk::GetMemoryFdKHR(*m_device, &get_handle_info, &fd); |
| } |
| |
| TEST_F(PositiveExternalMemorySync, ImportMemoryFd) { |
| TEST_DESCRIPTION("Basic importing of POXIS handle for memory allocation"); |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); |
| // Required to pass in various memory flags without querying for corresponding extensions. |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); |
| RETURN_IF_SKIP(Init()); |
| IgnoreHandleTypeError(m_errorMonitor); |
| |
| VkExternalMemoryBufferCreateInfo external_buffer_info = vku::InitStructHelper(); |
| external_buffer_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| |
| auto buffer_info = vkt::Buffer::CreateInfo(1024, VK_BUFFER_USAGE_TRANSFER_DST_BIT, {}, &external_buffer_info); |
| if (!FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT)) { |
| GTEST_SKIP() << "Unable to find exportable handle type"; |
| } |
| if (!FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) { |
| GTEST_SKIP() << "Unable to find importable handle type"; |
| } |
| const auto compatible_types = GetCompatibleHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT); |
| if ((VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT & compatible_types) == 0) { |
| GTEST_SKIP() << "Cannot find handle types that are supported but not compatible with each other"; |
| } |
| |
| vkt::Buffer buffer(*m_device, buffer_info, vkt::no_mem); |
| |
| VkMemoryDedicatedAllocateInfo dedicated_info = vku::InitStructHelper(); |
| dedicated_info.image = VK_NULL_HANDLE; |
| dedicated_info.buffer = buffer; |
| |
| VkExportMemoryAllocateInfo export_info = vku::InitStructHelper(&dedicated_info); |
| export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| auto alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer.MemoryRequirements(), 0, &export_info); |
| |
| vkt::DeviceMemory memory_export(*m_device, alloc_info); |
| |
| VkMemoryGetFdInfoKHR mgfi = vku::InitStructHelper(); |
| mgfi.memory = memory_export; |
| mgfi.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| |
| int fd; |
| vk::GetMemoryFdKHR(device(), &mgfi, &fd); |
| |
| VkImportMemoryFdInfoKHR import_info = vku::InitStructHelper(&dedicated_info); |
| import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| import_info.fd = fd; |
| |
| alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer.MemoryRequirements(), 0, &import_info); |
| vkt::DeviceMemory memory_import(*m_device, alloc_info); |
| } |
| |
| TEST_F(PositiveExternalMemorySync, ImportMemoryHost) { |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| AddRequiredExtensions(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME); |
| RETURN_IF_SKIP(Init()); |
| |
| VkPhysicalDeviceExternalMemoryHostPropertiesEXT memory_host_props = vku::InitStructHelper(); |
| GetPhysicalDeviceProperties2(memory_host_props); |
| |
| VkDeviceSize alloc_size = memory_host_props.minImportedHostPointerAlignment; |
| void *host_memory = ::operator new((size_t)alloc_size, std::align_val_t(alloc_size)); |
| if (!host_memory) { |
| GTEST_SKIP() << "Can't allocate host memory"; |
| } |
| |
| VkMemoryHostPointerPropertiesEXT host_pointer_props = vku::InitStructHelper(); |
| vk::GetMemoryHostPointerPropertiesEXT(*m_device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, host_memory, |
| &host_pointer_props); |
| |
| // test it is ignored when using null handle |
| VkMemoryDedicatedAllocateInfo dedicated_info = vku::InitStructHelper(); |
| dedicated_info.buffer = VK_NULL_HANDLE; |
| dedicated_info.image = VK_NULL_HANDLE; |
| |
| VkImportMemoryHostPointerInfoEXT import_info = vku::InitStructHelper(&dedicated_info); |
| import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT; |
| import_info.pHostPointer = host_memory; |
| |
| VkMemoryAllocateInfo alloc_info = vku::InitStructHelper(&import_info); |
| alloc_info.allocationSize = alloc_size; |
| if (!m_device->Physical().SetMemoryType(host_pointer_props.memoryTypeBits, &alloc_info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { |
| ::operator delete(host_memory, std::align_val_t(alloc_size)); |
| GTEST_SKIP() << "Failed to set memory type."; |
| } |
| vkt::DeviceMemory memory_import(*m_device, alloc_info); |
| |
| ::operator delete(host_memory, std::align_val_t(alloc_size)); |
| } |
| |
| TEST_F(PositiveExternalMemorySync, ExternalMemory) { |
| TEST_DESCRIPTION("Perform a copy through a pair of buffers linked by external memory"); |
| |
| #ifdef _WIN32 |
| const auto ext_mem_extension_name = VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME; |
| const auto handle_type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; |
| #else |
| const auto ext_mem_extension_name = VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME; |
| const auto handle_type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| #endif |
| |
| AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME); |
| AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME); |
| AddRequiredExtensions(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); |
| AddRequiredExtensions(ext_mem_extension_name); |
| RETURN_IF_SKIP(InitFramework()); |
| // Check for import/export capability |
| VkPhysicalDeviceExternalBufferInfoKHR ebi = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR, nullptr, 0, |
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, handle_type}; |
| VkExternalBufferPropertiesKHR ebp = vku::InitStructHelper(); |
| vk::GetPhysicalDeviceExternalBufferPropertiesKHR(Gpu(), &ebi, &ebp); |
| if (!(ebp.externalMemoryProperties.compatibleHandleTypes & handle_type) || |
| !(ebp.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT) || |
| !(ebp.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) { |
| GTEST_SKIP() << "External buffer does not support importing and exporting"; |
| } |
| |
| // Check if dedicated allocation is required |
| bool dedicated_allocation = ebp.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT; |
| if (dedicated_allocation && !IsExtensionsEnabled(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME)) { |
| GTEST_SKIP() << "Dedicated allocation extension not supported"; |
| } |
| |
| RETURN_IF_SKIP(InitState()); |
| |
| VkMemoryPropertyFlags mem_flags = 0; |
| const VkDeviceSize buffer_size = 1024; |
| |
| // Create export and import buffers |
| const VkExternalMemoryBufferCreateInfo external_buffer_info = {VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR, |
| nullptr, handle_type}; |
| auto buffer_info = vkt::Buffer::CreateInfo(buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| buffer_info.pNext = &external_buffer_info; |
| vkt::Buffer buffer_export(*m_device, buffer_info, vkt::no_mem); |
| vkt::Buffer buffer_import(*m_device, buffer_info, vkt::no_mem); |
| |
| // Allocation info |
| auto alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer_export.MemoryRequirements(), mem_flags); |
| |
| // Add export allocation info to pNext chain |
| VkExportMemoryAllocateInfo export_info = {VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR, nullptr, handle_type}; |
| alloc_info.pNext = &export_info; |
| |
| // Add dedicated allocation info to pNext chain if required |
| VkMemoryDedicatedAllocateInfo dedicated_info = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, nullptr, VK_NULL_HANDLE, |
| buffer_export.handle()}; |
| if (dedicated_allocation) { |
| export_info.pNext = &dedicated_info; |
| } |
| |
| // Allocate memory to be exported |
| vkt::DeviceMemory memory_export(*m_device, alloc_info); |
| |
| // Bind exported memory |
| buffer_export.BindMemory(memory_export, 0); |
| |
| #ifdef _WIN32 |
| // Export memory to handle |
| VkMemoryGetWin32HandleInfoKHR mghi = {VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR, nullptr, memory_export.handle(), |
| handle_type}; |
| HANDLE handle; |
| ASSERT_EQ(VK_SUCCESS, vk::GetMemoryWin32HandleKHR(device(), &mghi, &handle)); |
| |
| VkImportMemoryWin32HandleInfoKHR import_info = {VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR, nullptr, handle_type, |
| handle}; |
| #else |
| // Export memory to fd |
| VkMemoryGetFdInfoKHR mgfi = {VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, nullptr, memory_export.handle(), handle_type}; |
| int fd; |
| ASSERT_EQ(VK_SUCCESS, vk::GetMemoryFdKHR(device(), &mgfi, &fd)); |
| |
| VkImportMemoryFdInfoKHR import_info = {VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, nullptr, handle_type, fd}; |
| #endif |
| |
| // Import |
| if (dedicated_allocation) { |
| dedicated_info.buffer = buffer_import; |
| import_info.pNext = &dedicated_info; |
| } |
| alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer_import.MemoryRequirements(), mem_flags); |
| alloc_info.pNext = &import_info; |
| vkt::DeviceMemory memory_import(*m_device, alloc_info); |
| |
| // Bind imported memory |
| buffer_import.BindMemory(memory_import, 0); |
| |
| // Create test buffers and fill input buffer |
| vkt::Buffer buffer_input(*m_device, buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, |
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); |
| auto input_mem = (uint8_t*)buffer_input.Memory().Map(); |
| for (uint32_t i = 0; i < buffer_size; i++) { |
| input_mem[i] = (i & 0xFF); |
| } |
| vkt::Buffer buffer_output(*m_device, buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, |
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); |
| |
| // Copy from input buffer to output buffer through the exported/imported memory |
| m_command_buffer.Begin(); |
| VkBufferCopy copy_info = {0, 0, buffer_size}; |
| vk::CmdCopyBuffer(m_command_buffer, buffer_input, buffer_export, 1, ©_info); |
| // Insert memory barrier to guarantee copy order |
| VkMemoryBarrier mem_barrier = {VK_STRUCTURE_TYPE_MEMORY_BARRIER, nullptr, VK_ACCESS_TRANSFER_WRITE_BIT, |
| VK_ACCESS_TRANSFER_READ_BIT}; |
| vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, &mem_barrier, 0, |
| nullptr, 0, nullptr); |
| vk::CmdCopyBuffer(m_command_buffer, buffer_import, buffer_output, 1, ©_info); |
| m_command_buffer.End(); |
| m_default_queue->SubmitAndWait(m_command_buffer); |
| } |
| |
| TEST_F(PositiveExternalMemorySync, BufferDedicatedAllocation) { |
| TEST_DESCRIPTION("Create external buffer that requires dedicated allocation."); |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| // Required to pass in various memory flags without querying for corresponding extensions. |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); |
| RETURN_IF_SKIP(Init()); |
| IgnoreHandleTypeError(m_errorMonitor); |
| |
| VkExternalMemoryBufferCreateInfo external_buffer_info = vku::InitStructHelper(); |
| const auto buffer_info = vkt::Buffer::CreateInfo(4096, VK_BUFFER_USAGE_TRANSFER_DST_BIT, {}, &external_buffer_info); |
| const auto exportable_types = |
| FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT); |
| if (!exportable_types) { |
| GTEST_SKIP() << "Unable to find exportable handle type"; |
| } |
| |
| auto exportable_dedicated_types = FindSupportedExternalMemoryHandleTypes( |
| Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT); |
| if (!exportable_dedicated_types) { |
| GTEST_SKIP() << "Unable to find exportable handle type that requires dedicated allocation"; |
| } |
| const auto handle_type = LeastSignificantFlag<VkExternalMemoryHandleTypeFlagBits>(exportable_dedicated_types); |
| |
| external_buffer_info.handleTypes = handle_type; |
| vkt::Buffer buffer(*m_device, buffer_info, vkt::no_mem); |
| |
| VkMemoryDedicatedAllocateInfo dedicated_info = vku::InitStructHelper(); |
| dedicated_info.buffer = buffer; |
| |
| VkExportMemoryAllocateInfo export_memory_info = vku::InitStructHelper(&dedicated_info); |
| export_memory_info.handleTypes = handle_type; |
| |
| buffer.AllocateAndBindMemory(*m_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &export_memory_info); |
| } |
| |
| TEST_F(PositiveExternalMemorySync, SyncFdSemaphore) { |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| AddRequiredExtensions(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME); |
| AddRequiredExtensions(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME); |
| RETURN_IF_SKIP(Init()); |
| |
| const auto handle_type = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; |
| |
| VkPhysicalDeviceExternalSemaphoreInfo external_semahpore_info = vku::InitStructHelper(); |
| external_semahpore_info.handleType = handle_type; |
| |
| VkExternalSemaphoreProperties external_semahpore_props = vku::InitStructHelper(); |
| vk::GetPhysicalDeviceExternalSemaphoreProperties(Gpu(), &external_semahpore_info, &external_semahpore_props); |
| if (!(external_semahpore_props.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) || |
| !(external_semahpore_props.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT)) { |
| GTEST_SKIP() << "External semaphore does not support importing and exporting"; |
| } |
| if (!(external_semahpore_props.compatibleHandleTypes & handle_type)) { |
| GTEST_SKIP() << "External semaphore does not support VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT"; |
| } |
| |
| VkExportSemaphoreCreateInfo esci = vku::InitStructHelper(); |
| esci.handleTypes = handle_type; |
| VkSemaphoreTypeCreateInfo stci = vku::InitStructHelper(&esci); |
| stci.semaphoreType = VK_SEMAPHORE_TYPE_BINARY; |
| VkSemaphoreCreateInfo sci = vku::InitStructHelper(&stci); |
| vkt::Semaphore binary_sem(*m_device, sci); |
| |
| VkSubmitInfo si = vku::InitStructHelper(); |
| si.signalSemaphoreCount = 1; |
| si.pSignalSemaphores = &binary_sem.handle(); |
| |
| vk::QueueSubmit(m_default_queue->handle(), 1, &si, VK_NULL_HANDLE); |
| |
| int fd_handle = -1; |
| binary_sem.ExportHandle(fd_handle, handle_type); |
| |
| vkt::Semaphore import_semaphore(*m_device); |
| import_semaphore.ImportHandle(fd_handle, handle_type, VK_SEMAPHORE_IMPORT_TEMPORARY_BIT); |
| |
| m_default_queue->Wait(); |
| } |
| |
| #ifdef VK_USE_PLATFORM_METAL_EXT |
| TEST_F(PositiveExternalMemorySync, ExportMetalObjects) { |
| TEST_DESCRIPTION("Test vkExportMetalObjectsEXT"); |
| |
| AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| AddRequiredExtensions(VK_EXT_METAL_OBJECTS_EXTENSION_NAME); |
| |
| // Initialize framework |
| { |
| VkExportMetalObjectCreateInfoEXT queue_info = vku::InitStructHelper(); |
| queue_info.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_COMMAND_QUEUE_BIT_EXT; |
| |
| VkExportMetalObjectCreateInfoEXT metal_info = vku::InitStructHelper(&queue_info); |
| metal_info.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_DEVICE_BIT_EXT; |
| |
| RETURN_IF_SKIP(InitFramework(&metal_info)); |
| |
| VkPhysicalDevicePortabilitySubsetFeaturesKHR portability_features = vku::InitStructHelper(); |
| auto features2 = GetPhysicalDeviceFeatures2(portability_features); |
| |
| RETURN_IF_SKIP(InitState(nullptr, &features2)); |
| } |
| |
| const VkDevice device = this->device(); |
| |
| // Get Metal Device and Metal Command Queue in 1 call |
| { |
| const VkQueue queue = m_default_queue->handle(); |
| |
| VkExportMetalCommandQueueInfoEXT queueInfo = vku::InitStructHelper(); |
| queueInfo.queue = queue; |
| VkExportMetalDeviceInfoEXT deviceInfo = vku::InitStructHelper(&queueInfo); |
| VkExportMetalObjectsInfoEXT objectsInfo = vku::InitStructHelper(&deviceInfo); |
| |
| // This tests both device, queue, and pNext chaining |
| vk::ExportMetalObjectsEXT(device, &objectsInfo); |
| |
| ASSERT_TRUE(deviceInfo.mtlDevice != nullptr); |
| ASSERT_TRUE(queueInfo.mtlCommandQueue != nullptr); |
| } |
| |
| // Get Metal Buffer |
| { |
| VkExportMetalObjectCreateInfoEXT metalBufferCreateInfo = vku::InitStructHelper(); |
| metalBufferCreateInfo.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_BUFFER_BIT_EXT; |
| |
| VkMemoryAllocateInfo mem_info = vku::InitStructHelper(&metalBufferCreateInfo); |
| mem_info.allocationSize = 1024; |
| |
| VkDeviceMemory memory; |
| const VkResult err = vk::AllocateMemory(device, &mem_info, NULL, &memory); |
| ASSERT_EQ(VK_SUCCESS, err); |
| |
| VkExportMetalBufferInfoEXT bufferInfo = vku::InitStructHelper(); |
| bufferInfo.memory = memory; |
| VkExportMetalObjectsInfoEXT objectsInfo = vku::InitStructHelper(&bufferInfo); |
| |
| vk::ExportMetalObjectsEXT(device, &objectsInfo); |
| |
| ASSERT_TRUE(bufferInfo.mtlBuffer != nullptr); |
| |
| vk::FreeMemory(device, memory, nullptr); |
| } |
| |
| // Get Metal Texture and Metal IOSurfaceRef |
| { |
| VkExportMetalObjectCreateInfoEXT metalSurfaceInfo = vku::InitStructHelper(); |
| metalSurfaceInfo.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_IOSURFACE_BIT_EXT; |
| VkExportMetalObjectCreateInfoEXT metalTextureCreateInfo = vku::InitStructHelper(&metalSurfaceInfo); |
| metalTextureCreateInfo.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_TEXTURE_BIT_EXT; |
| |
| // Image contents don't matter |
| VkImageCreateInfo ici = vku::InitStructHelper(&metalTextureCreateInfo); |
| ici.imageType = VK_IMAGE_TYPE_2D; |
| ici.format = VK_FORMAT_B8G8R8A8_UNORM; |
| ici.extent = {32, 32, 1}; |
| ici.mipLevels = 1; |
| ici.arrayLayers = 1; |
| ici.samples = VK_SAMPLE_COUNT_1_BIT; |
| ici.tiling = VK_IMAGE_TILING_LINEAR; |
| ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; |
| vkt::Image image(*m_device, ici, vkt::set_layout); |
| |
| VkExportMetalIOSurfaceInfoEXT surfaceInfo = vku::InitStructHelper(); |
| surfaceInfo.image = image; |
| VkExportMetalTextureInfoEXT textureInfo = vku::InitStructHelper(&surfaceInfo); |
| textureInfo.image = image; |
| textureInfo.plane = VK_IMAGE_ASPECT_PLANE_0_BIT; // Image is not multi-planar |
| VkExportMetalObjectsInfoEXT objectsInfo = vku::InitStructHelper(&textureInfo); |
| |
| // This tests both texture, surface, and pNext chaining |
| vk::ExportMetalObjectsEXT(device, &objectsInfo); |
| |
| ASSERT_TRUE(textureInfo.mtlTexture != nullptr); |
| ASSERT_TRUE(surfaceInfo.ioSurface != nullptr); |
| } |
| |
| // Get Metal Shared Event |
| { |
| VkExportMetalObjectCreateInfoEXT metalEventCreateInfo = vku::InitStructHelper(); |
| metalEventCreateInfo.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_SHARED_EVENT_BIT_EXT; |
| |
| VkEventCreateInfo eventCreateInfo = vku::InitStructHelper(&metalEventCreateInfo); |
| vkt::Event event(*m_device, eventCreateInfo); |
| ASSERT_TRUE(event.initialized()); |
| |
| VkExportMetalSharedEventInfoEXT eventInfo = vku::InitStructHelper(); |
| eventInfo.event = event; |
| VkExportMetalObjectsInfoEXT objectsInfo = vku::InitStructHelper(&eventInfo); |
| |
| vk::ExportMetalObjectsEXT(device, &objectsInfo); |
| |
| ASSERT_TRUE(eventInfo.mtlSharedEvent != nullptr); |
| } |
| } |
| #endif // VK_USE_PLATFORM_METAL_EXT |
| |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| TEST_F(PositiveExternalMemorySync, ExportFromImportedFence) { |
| TEST_DESCRIPTION("Export from fence with imported payload"); |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| AddRequiredExtensions(VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME); |
| // Required to pass in various memory flags without querying for corresponding extensions. |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); |
| RETURN_IF_SKIP(Init()); |
| IgnoreHandleTypeError(m_errorMonitor); |
| |
| const auto handle_type = VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT; |
| { |
| const auto handle_types = FindSupportedExternalFenceHandleTypes( |
| Gpu(), VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT); |
| if ((handle_types & VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT) == 0) { |
| GTEST_SKIP() << "VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT is not both exportable and importable"; |
| } |
| } |
| VkPhysicalDeviceExternalFenceInfo fence_info = vku::InitStructHelper(); |
| fence_info.handleType = handle_type; |
| VkExternalFenceProperties fence_properties = vku::InitStructHelper(); |
| vk::GetPhysicalDeviceExternalFenceProperties(Gpu(), &fence_info, &fence_properties); |
| if ((handle_type & fence_properties.exportFromImportedHandleTypes) == 0) { |
| GTEST_SKIP() << "can't find handle type that can be exported from imported fence"; |
| } |
| |
| // create fence and export payload |
| VkExportFenceCreateInfo export_info = vku::InitStructHelper(); |
| export_info.handleTypes = handle_type; // at first export handle type, then import it |
| const VkFenceCreateInfo create_info = vku::InitStructHelper(&export_info); |
| vkt::Fence fence(*m_device, create_info); |
| HANDLE handle = NULL; |
| fence.ExportHandle(handle, handle_type); |
| |
| // create fence and import payload |
| VkExportFenceCreateInfo export_info2 = vku::InitStructHelper(); // prepare to export from imported fence |
| export_info2.handleTypes = handle_type; |
| const VkFenceCreateInfo create_info2 = vku::InitStructHelper(&export_info2); |
| vkt::Fence import_fence(*m_device, create_info2); |
| import_fence.ImportHandle(handle, handle_type); |
| |
| // export from imported fence |
| HANDLE handle2 = NULL; |
| import_fence.ExportHandle(handle2, handle_type); |
| |
| ::CloseHandle(handle); |
| if (handle2 != handle) { |
| ::CloseHandle(handle2); |
| } |
| } |
| |
| TEST_F(PositiveExternalMemorySync, ImportMemoryWin32BufferDifferentDedicated) { |
| TEST_DESCRIPTION("https://gitlab.khronos.org/vulkan/Vulkan-ValidationLayers/-/issues/35"); |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME); |
| // Required to pass in various memory flags without querying for corresponding extensions. |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); |
| RETURN_IF_SKIP(Init()); |
| IgnoreHandleTypeError(m_errorMonitor); |
| |
| auto buffer_info = vkt::Buffer::CreateInfo(1024, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| |
| const auto handle_types = FindSupportedExternalMemoryHandleTypes( |
| Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT); |
| if ((handle_types & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) == 0) { |
| GTEST_SKIP() << "VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT is not both exportable and importable"; |
| } |
| |
| VkExternalMemoryBufferCreateInfo external_buffer_info = vku::InitStructHelper(); |
| external_buffer_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; |
| buffer_info.pNext = &external_buffer_info; |
| |
| vkt::Buffer buffer(*m_device, buffer_info, vkt::no_mem); |
| |
| VkMemoryDedicatedAllocateInfo dedicated_info = vku::InitStructHelper(); |
| dedicated_info.buffer = buffer; |
| |
| VkExportMemoryAllocateInfo export_info = vku::InitStructHelper(&dedicated_info); |
| export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; |
| auto alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer.MemoryRequirements(), 0, &export_info); |
| |
| vkt::DeviceMemory memory_export(*m_device, alloc_info); |
| |
| VkMemoryGetWin32HandleInfoKHR get_handle_info = vku::InitStructHelper(); |
| get_handle_info.memory = memory_export; |
| get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; |
| |
| HANDLE handle = NULL; |
| vk::GetMemoryWin32HandleKHR(device(), &get_handle_info, &handle); |
| |
| vkt::Buffer buffer2(*m_device, buffer_info, vkt::no_mem); |
| dedicated_info.buffer = buffer2; |
| |
| VkImportMemoryWin32HandleInfoKHR import_info = vku::InitStructHelper(&dedicated_info); |
| import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; |
| import_info.handle = handle; |
| |
| alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer2.MemoryRequirements(), 0, &import_info); |
| vkt::DeviceMemory memory_import(*m_device, alloc_info); |
| |
| // "For handle types defined as NT handles, the handles returned by vkGetFenceWin32HandleKHR are owned by the application. To |
| // avoid leaking resources, the application must release ownership of them using the CloseHandle system call when they are no |
| // longer needed." |
| ::CloseHandle(handle); |
| } |
| |
| #endif // VK_USE_PLATFORM_WIN32_KHR |
| |
| TEST_F(PositiveExternalMemorySync, MultipleExportOpaqueFd) { |
| TEST_DESCRIPTION("regression from dEQP-VK.api.external.semaphore.opaque_fd.export_multiple_times_temporary"); |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| AddRequiredExtensions(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME); |
| // Required to pass in various memory flags without querying for corresponding extensions. |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); |
| RETURN_IF_SKIP(Init()); |
| IgnoreHandleTypeError(m_errorMonitor); |
| |
| const auto handle_types = FindSupportedExternalSemaphoreHandleTypes(Gpu(), VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT); |
| if ((handle_types & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) == 0) { |
| GTEST_SKIP() << "VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT is not exportable"; |
| } |
| |
| VkExportSemaphoreCreateInfo export_info = vku::InitStructHelper(); |
| export_info.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; |
| const VkSemaphoreCreateInfo create_info = vku::InitStructHelper(&export_info); |
| vkt::Semaphore semaphore(*m_device, create_info); |
| |
| int handle = 0; |
| semaphore.ExportHandle(handle, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT); |
| semaphore.ExportHandle(handle, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT); |
| } |
| |
| TEST_F(PositiveExternalMemorySync, ImportMemoryFdBufferDifferentDedicated) { |
| TEST_DESCRIPTION("https://gitlab.khronos.org/vulkan/Vulkan-ValidationLayers/-/issues/35"); |
| SetTargetApiVersion(VK_API_VERSION_1_1); |
| AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); |
| // Required to pass in various memory flags without querying for corresponding extensions. |
| AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); |
| RETURN_IF_SKIP(Init()); |
| IgnoreHandleTypeError(m_errorMonitor); |
| |
| VkExternalMemoryBufferCreateInfo external_buffer_info = vku::InitStructHelper(); |
| external_buffer_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| |
| auto buffer_info = vkt::Buffer::CreateInfo(1024, VK_BUFFER_USAGE_TRANSFER_DST_BIT, {}, &external_buffer_info); |
| if (!FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT)) { |
| GTEST_SKIP() << "Unable to find exportable handle type"; |
| } |
| if (!FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) { |
| GTEST_SKIP() << "Unable to find importable handle type"; |
| } |
| const auto compatible_types = GetCompatibleHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT); |
| if ((VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT & compatible_types) == 0) { |
| GTEST_SKIP() << "Cannot find handle types that are supported but not compatible with each other"; |
| } |
| |
| vkt::Buffer buffer(*m_device, buffer_info, vkt::no_mem); |
| |
| VkMemoryDedicatedAllocateInfo dedicated_info = vku::InitStructHelper(); |
| dedicated_info.image = VK_NULL_HANDLE; |
| dedicated_info.buffer = buffer; |
| |
| VkExportMemoryAllocateInfo export_info = vku::InitStructHelper(&dedicated_info); |
| export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| auto alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer.MemoryRequirements(), 0, &export_info); |
| |
| vkt::DeviceMemory memory_export(*m_device, alloc_info); |
| |
| VkMemoryGetFdInfoKHR mgfi = vku::InitStructHelper(); |
| mgfi.memory = memory_export; |
| mgfi.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| |
| int fd; |
| vk::GetMemoryFdKHR(device(), &mgfi, &fd); |
| |
| vkt::Buffer buffer2(*m_device, buffer_info, vkt::no_mem); |
| |
| dedicated_info.buffer = buffer2; |
| |
| VkImportMemoryFdInfoKHR import_info = vku::InitStructHelper(&dedicated_info); |
| import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; |
| import_info.fd = fd; |
| |
| alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer2.MemoryRequirements(), 0, &import_info); |
| vkt::DeviceMemory memory_import(*m_device, alloc_info); |
| } |
| |
| TEST_F(PositiveExternalMemorySync, BinarySyncDependsOnExternalTimelineSignal) { |
| // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/10211 |
| TEST_DESCRIPTION("Binary semaphore waits for binary signal that depends on timeline signal from imported semaphore"); |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| const auto extension_name = VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME; |
| const auto handle_type = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT; |
| #else |
| const auto extension_name = VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME; |
| const auto handle_type = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; |
| #endif |
| SetTargetApiVersion(VK_API_VERSION_1_3); |
| AddRequiredExtensions(extension_name); |
| AddRequiredFeature(vkt::Feature::synchronization2); |
| AddRequiredFeature(vkt::Feature::timelineSemaphore); |
| RETURN_IF_SKIP(Init()); |
| |
| if (!SemaphoreExportImportSupported(Gpu(), VK_SEMAPHORE_TYPE_TIMELINE, handle_type)) { |
| GTEST_SKIP() << "Semaphore does not support export and import through opaque handle"; |
| } |
| |
| VkSemaphoreTypeCreateInfo semaphore_type_ci = vku::InitStructHelper(); |
| semaphore_type_ci.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; |
| |
| VkExportSemaphoreCreateInfo export_info = vku::InitStructHelper(&semaphore_type_ci); |
| export_info.handleTypes = handle_type; |
| VkSemaphoreCreateInfo semaphore_ci = vku::InitStructHelper(&export_info); |
| |
| vkt::Semaphore semaphore(*m_device, semaphore_ci); |
| vkt::Semaphore imported_semaphore(*m_device, VK_SEMAPHORE_TYPE_TIMELINE); |
| vkt::Semaphore binary_semaphore(*m_device); |
| |
| ExternalHandle handle{}; |
| semaphore.ExportHandle(handle, handle_type); |
| imported_semaphore.ImportHandle(handle, handle_type); |
| |
| // Check that this scenario does not generate VUID-vkQueueSubmit2-semaphore-03873. |
| // In case of regression when the exported signal is not handled correctly, the |
| // validation may assume that timeline wait does not have resolving signal submitted yet. |
| m_default_queue->Submit2(vkt::no_cmd, vkt::TimelineSignal(semaphore, 1)); |
| m_default_queue->Submit2(vkt::no_cmd, vkt::TimelineWait(imported_semaphore, 1), vkt::TimelineSignal(imported_semaphore, 2)); |
| m_default_queue->Submit2(vkt::no_cmd, vkt::TimelineWait(semaphore, 2), vkt::Signal(binary_semaphore)); |
| m_default_queue->Submit2(vkt::no_cmd, vkt::Wait(binary_semaphore)); |
| m_device->Wait(); |
| } |