From 037ee47f7e93cbe74e9e19065778ac5cd4fde3cd Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 7 Aug 2026 19:53:46 +1000 Subject: [PATCH] VulkanDevice: Validate imported host buffer memory Create the buffer before allocation, intersect both memory-type masks, honor size and dedicated requirements, and propagate bind failures. Also require that the memory is coherent, because otherwise we'd have to resort to architecture-specific cache flush methods. vkInvalidateMappedMemoryRanges() is only valid on memory mapped by vkMapMemory(). --- src/util/vulkan_device.cpp | 100 ++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 29 deletions(-) diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index c3180eaf6..0af0ed9a6 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -2651,32 +2651,89 @@ bool VulkanDevice::TryImportHostMemory(void* data, size_t data_size, VkBufferUsa // Full amount of data that must be imported, including the pages const size_t data_size_aligned = Common::AlignUpPow2(data_offset + data_size, HOST_PAGE_SIZE); + 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; + VkResult res = vkCreateBuffer(m_device, &buffer_info, nullptr, &imported_buffer); + if (res != VK_SUCCESS) + { + Vulkan::SetErrorObject(error, "vkCreateBuffer() failed: ", res); + return false; + } + + VkMemoryDedicatedRequirements dedicated_requirements = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, nullptr, + VK_FALSE, VK_FALSE}; + VkMemoryRequirements buffer_requirements; + if (vkGetBufferMemoryRequirements2) + { + const VkBufferMemoryRequirementsInfo2 requirements_info = {VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, + nullptr, imported_buffer}; + VkMemoryRequirements2 requirements = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicated_requirements, {}}; + vkGetBufferMemoryRequirements2(m_device, &requirements_info, &requirements); + buffer_requirements = requirements.memoryRequirements; + } + else + { + vkGetBufferMemoryRequirements(m_device, imported_buffer, &buffer_requirements); + } + + if (buffer_requirements.size > data_size_aligned) + { + Error::SetStringFmt(error, "Imported host allocation is too small for buffer requirements ({} < {}).", + data_size_aligned, buffer_requirements.size); + vkDestroyBuffer(m_device, imported_buffer, nullptr); + return false; + } + 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); + 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) { Vulkan::SetErrorObject(error, "vkGetMemoryHostPointerPropertiesEXT() failed: ", res); + vkDestroyBuffer(m_device, imported_buffer, nullptr); + return false; + } + + const u32 memory_type_bits = pointer_properties.memoryTypeBits & buffer_requirements.memoryTypeBits; + if (memory_type_bits == 0) + { + Error::SetStringView(error, "Imported host allocation has no buffer-compatible memory type."); + vkDestroyBuffer(m_device, imported_buffer, nullptr); 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; + vma_alloc_info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + vma_alloc_info.preferredFlags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + vma_alloc_info.memoryTypeBits = memory_type_bits; u32 memory_index = 0; - res = vmaFindMemoryTypeIndex(m_allocator, pointer_properties.memoryTypeBits, &vma_alloc_info, &memory_index); + res = vmaFindMemoryTypeIndex(m_allocator, memory_type_bits, &vma_alloc_info, &memory_index); if (res != VK_SUCCESS) { Vulkan::SetErrorObject(error, "vmaFindMemoryTypeIndex() failed: ", res); + vkDestroyBuffer(m_device, imported_buffer, nullptr); 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 VkMemoryDedicatedAllocateInfo dedicated_info = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, nullptr, + VK_NULL_HANDLE, imported_buffer}; + const VkImportMemoryHostPointerInfoEXT import_info = { + VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT, + dedicated_requirements.requiresDedicatedAllocation ? &dedicated_info : 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}; @@ -2687,34 +2744,19 @@ bool VulkanDevice::TryImportHostMemory(void* data, size_t data_size, VkBufferUsa if (res != VK_SUCCESS) { Vulkan::SetErrorObject(error, "vkAllocateMemory() failed: ", res); + vkDestroyBuffer(m_device, imported_buffer, nullptr); 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); + res = vkBindBufferMemory(m_device, imported_buffer, imported_memory, 0); if (res != VK_SUCCESS) { - Vulkan::SetErrorObject(error, "vkCreateBuffer() failed: ", res); - if (imported_memory != VK_NULL_HANDLE) - vkFreeMemory(m_device, imported_memory, nullptr); - + Vulkan::SetErrorObject(error, "vkBindBufferMemory() failed: ", res); + vkFreeMemory(m_device, imported_memory, nullptr); + vkDestroyBuffer(m_device, imported_buffer, nullptr); return false; } - vkBindBufferMemory(m_device, imported_buffer, imported_memory, 0); - *out_memory = imported_memory; *out_buffer = imported_buffer; *out_offset = data_offset;