From 4309d8ebf3ee6ae0374dac66e763ebb5717a0873 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Sat, 20 Jan 2024 15:57:22 -0800 Subject: [PATCH] VulkanDevice: Add `TryImportHostMemory` Utilizing `VK_EXT_external_memory_host`, attempt to import the host-pointer into a `VkBuffer`, `VkDeviceMemory`, and an offset to the start of the data. When the page size matches the import alignment, then it is possible to import an entire range of memory from the host directly into a vulkan-side buffer to read and write into. --- src/util/vulkan_device.cpp | 84 ++++++++++++++++++++++++++++++++++++++ src/util/vulkan_device.h | 3 ++ 2 files changed, 87 insertions(+) diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index b54239d4b..41c43d841 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -2981,6 +2981,90 @@ void VulkanDevice::RenderBlankFrame() InvalidateCachedState(); } +bool VulkanDevice::TryImportHostMemory(const void* data, u32 data_size, VkBufferUsageFlags buffer_usage, + VkDeviceMemory* out_memory, VkBuffer* out_buffer, u32* out_offset) +{ + if (!m_optional_extensions.vk_ext_external_memory_host) + return false; + + // Align to the nearest page + const void* data_aligned = + reinterpret_cast(Common::AlignDownPow2(reinterpret_cast(data), HOST_PAGE_SIZE)); + + // Offset to the start of the data within the page + const u32 data_offset = reinterpret_cast(data) & (HOST_PAGE_SIZE - 1); + + // Full amount of data that must be imported, including the pages + const u32 data_size_aligned = Common::AlignUpPow2(data_offset + data_size, HOST_PAGE_SIZE); + + VkMemoryHostPointerPropertiesEXT pointer_properties = {VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT, nullptr, + 0}; + VkResult res = vkGetMemoryHostPointerPropertiesEXT(m_device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, + data_aligned, &pointer_properties); + if (res != VK_SUCCESS || pointer_properties.memoryTypeBits == 0) + { + return false; + } + + VmaAllocationCreateInfo vma_alloc_info = {}; + vma_alloc_info.preferredFlags = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + vma_alloc_info.memoryTypeBits = pointer_properties.memoryTypeBits; + + u32 memory_index = 0; + res = vmaFindMemoryTypeIndex(m_allocator, pointer_properties.memoryTypeBits, &vma_alloc_info, &memory_index); + if (res != VK_SUCCESS) + { + return false; + } + + const VkImportMemoryHostPointerInfoEXT import_info = {VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT, nullptr, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, + const_cast(data_aligned)}; + + const VkMemoryAllocateInfo alloc_info = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, &import_info, data_size_aligned, + memory_index}; + + VkDeviceMemory imported_memory = VK_NULL_HANDLE; + + res = vkAllocateMemory(m_device, &alloc_info, nullptr, &imported_memory); + if (res != VK_SUCCESS) + { + return false; + } + + const VkExternalMemoryBufferCreateInfo external_info = {VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, nullptr, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT}; + + const VkBufferCreateInfo buffer_info = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + &external_info, + 0, + data_size_aligned, + buffer_usage, + VK_SHARING_MODE_EXCLUSIVE, + 0, + nullptr}; + + VkBuffer imported_buffer = VK_NULL_HANDLE; + res = vkCreateBuffer(m_device, &buffer_info, nullptr, &imported_buffer); + if (res != VK_SUCCESS) + { + if (imported_memory != VK_NULL_HANDLE) + { + vkFreeMemory(m_device, imported_memory, nullptr); + } + return false; + } + + vkBindBufferMemory(m_device, imported_buffer, imported_memory, 0); + + *out_memory = imported_memory; + *out_buffer = imported_buffer; + *out_offset = data_offset; + + return true; +} + void VulkanDevice::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds) { bool changed = (m_num_current_render_targets != num_rts || m_current_depth_target != ds); diff --git a/src/util/vulkan_device.h b/src/util/vulkan_device.h index d4f5fdc86..d06fb8179 100644 --- a/src/util/vulkan_device.h +++ b/src/util/vulkan_device.h @@ -341,6 +341,9 @@ private: void RenderBlankFrame(); + bool TryImportHostMemory(const void* data, u32 data_size, VkBufferUsageFlags buffer_usage, VkDeviceMemory* out_memory, + VkBuffer* out_buffer, u32* out_offset); + bool CheckDownloadBufferSize(u32 required_size); void DestroyDownloadBuffer();