diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp index 010cd11bbb..42d03935b2 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp @@ -568,6 +568,13 @@ void CommandBufferManager::DeferImageDestruction(VkImage object) [object]() { vkDestroyImage(g_vulkan_context->GetDevice(), object, nullptr); }); } +void CommandBufferManager::DeferImageDestruction(VkImage image, VmaAllocation alloc) +{ + CmdBufferResources& cmd_buffer_resources = GetCurrentCmdBufferResources(); + cmd_buffer_resources.cleanup_resources.push_back( + [image, alloc]() { vmaDestroyImage(g_vulkan_context->GetMemoryAllocator(), image, alloc); }); +} + void CommandBufferManager::DeferImageViewDestruction(VkImageView object) { CmdBufferResources& cmd_buffer_resources = GetCurrentCmdBufferResources(); diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h index 858eb9fab4..5a4e5ff1bc 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h @@ -92,6 +92,7 @@ public: void DeferBufferDestruction(VkBuffer buffer, VmaAllocation alloc); void DeferFramebufferDestruction(VkFramebuffer object); void DeferImageDestruction(VkImage object); + void DeferImageDestruction(VkImage object, VmaAllocation alloc); void DeferImageViewDestruction(VkImageView object); private: diff --git a/Source/Core/VideoBackends/Vulkan/VKStreamBuffer.cpp b/Source/Core/VideoBackends/Vulkan/VKStreamBuffer.cpp index f196730700..3efa00f6fa 100644 --- a/Source/Core/VideoBackends/Vulkan/VKStreamBuffer.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKStreamBuffer.cpp @@ -22,13 +22,9 @@ StreamBuffer::StreamBuffer(VkBufferUsageFlags usage, u32 size) : m_usage(usage), StreamBuffer::~StreamBuffer() { - if (m_host_pointer) - vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory); - + // VMA_ALLOCATION_CREATE_MAPPED_BIT automatically handles unmapping for us if (m_buffer != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferBufferDestruction(m_buffer); - if (m_memory != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); + g_command_buffer_mgr->DeferBufferDestruction(m_buffer, m_alloc); } std::unique_ptr StreamBuffer::Create(VkBufferUsageFlags usage, u32 size) @@ -54,74 +50,38 @@ bool StreamBuffer::AllocateBuffer() nullptr // const uint32_t* pQueueFamilyIndices }; + VmaAllocationCreateInfo alloc_create_info = {}; + alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + alloc_create_info.usage = + VMA_MEMORY_USAGE_AUTO_PREFER_HOST; // Host visible VRAM is slower in practice + alloc_create_info.pool = VK_NULL_HANDLE; + alloc_create_info.pUserData = nullptr; + alloc_create_info.priority = 0.0; + alloc_create_info.requiredFlags = 0; + alloc_create_info.preferredFlags = 0; + VkBuffer buffer = VK_NULL_HANDLE; - VkResult res = - vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, &buffer); + VmaAllocation alloc = VK_NULL_HANDLE; + VmaAllocationInfo alloc_info; + VkResult res = vmaCreateBuffer(g_vulkan_context->GetMemoryAllocator(), &buffer_create_info, + &alloc_create_info, &buffer, &alloc, &alloc_info); if (res != VK_SUCCESS) { - LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: "); + LOG_VULKAN_ERROR(res, "vmaCreateBuffer failed: "); return false; } - // Get memory requirements (types etc) for this buffer - VkMemoryRequirements memory_requirements; - vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), buffer, &memory_requirements); - - // Aim for a coherent mapping if possible. - u32 memory_type_index = g_vulkan_context->GetUploadMemoryType(memory_requirements.memoryTypeBits, - &m_coherent_mapping); - - // Allocate memory for backing this buffer - VkMemoryAllocateInfo memory_allocate_info = { - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - memory_requirements.size, // VkDeviceSize allocationSize - memory_type_index // uint32_t memoryTypeIndex - }; - VkDeviceMemory memory = VK_NULL_HANDLE; - res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); - return false; - } - - // Bind memory to buffer - res = vkBindBufferMemory(g_vulkan_context->GetDevice(), buffer, memory, 0); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr); - return false; - } - - // Map this buffer into user-space - void* mapped_ptr = nullptr; - res = vkMapMemory(g_vulkan_context->GetDevice(), memory, 0, m_size, 0, &mapped_ptr); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkMapMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr); - return false; - } - - // Unmap current host pointer (if there was a previous buffer) - if (m_host_pointer) - vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory); - // Destroy the backings for the buffer after the command buffer executes + // VMA_ALLOCATION_CREATE_MAPPED_BIT automatically handles unmapping for us if (m_buffer != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferBufferDestruction(m_buffer); - if (m_memory != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); + g_command_buffer_mgr->DeferBufferDestruction(m_buffer, m_alloc); // Replace with the new buffer m_buffer = buffer; - m_memory = memory; - m_host_pointer = reinterpret_cast(mapped_ptr); + m_alloc = alloc; + m_host_pointer = reinterpret_cast(alloc_info.pMappedData); m_current_offset = 0; m_current_gpu_position = 0; m_tracked_fences.clear(); @@ -201,12 +161,9 @@ void StreamBuffer::CommitMemory(u32 final_num_bytes) ASSERT(final_num_bytes <= m_last_allocation_size); // For non-coherent mappings, flush the memory range - if (!m_coherent_mapping) - { - VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, - m_current_offset, final_num_bytes}; - vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); - } + // vmaFlushAllocation checks whether the allocation uses a coherent memory type internally + vmaFlushAllocation(g_vulkan_context->GetMemoryAllocator(), m_alloc, m_current_offset, + final_num_bytes); m_current_offset += final_num_bytes; } diff --git a/Source/Core/VideoBackends/Vulkan/VKStreamBuffer.h b/Source/Core/VideoBackends/Vulkan/VKStreamBuffer.h index 1528d3289c..61979c6f3f 100644 --- a/Source/Core/VideoBackends/Vulkan/VKStreamBuffer.h +++ b/Source/Core/VideoBackends/Vulkan/VKStreamBuffer.h @@ -20,7 +20,6 @@ public: ~StreamBuffer(); VkBuffer GetBuffer() const { return m_buffer; } - VkDeviceMemory GetDeviceMemory() const { return m_memory; } u8* GetHostPointer() const { return m_host_pointer; } u8* GetCurrentHostPointer() const { return m_host_pointer + m_current_offset; } u32 GetCurrentSize() const { return m_size; } @@ -45,13 +44,11 @@ private: u32 m_last_allocation_size = 0; VkBuffer m_buffer = VK_NULL_HANDLE; - VkDeviceMemory m_memory = VK_NULL_HANDLE; + VmaAllocation m_alloc = VK_NULL_HANDLE; u8* m_host_pointer = nullptr; // List of fences and the corresponding positions in the buffer std::deque> m_tracked_fences; - - bool m_coherent_mapping = false; }; } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index 54360a6c79..412deaf5f9 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -26,10 +26,10 @@ namespace Vulkan { -VKTexture::VKTexture(const TextureConfig& tex_config, VkDeviceMemory device_memory, VkImage image, +VKTexture::VKTexture(const TextureConfig& tex_config, VmaAllocation alloc, VkImage image, std::string_view name, VkImageLayout layout /* = VK_IMAGE_LAYOUT_UNDEFINED */, ComputeImageLayout compute_layout /* = ComputeImageLayout::Undefined */) - : AbstractTexture(tex_config), m_device_memory(device_memory), m_image(image), m_layout(layout), + : AbstractTexture(tex_config), m_alloc(alloc), m_image(image), m_layout(layout), m_compute_layout(compute_layout), m_name(name) { if (!m_name.empty() && g_ActiveConfig.backend_info.bSupportsSettingObjectNames) @@ -49,10 +49,9 @@ VKTexture::~VKTexture() g_command_buffer_mgr->DeferImageViewDestruction(m_view); // If we don't have device memory allocated, the image is not owned by us (e.g. swapchain) - if (m_device_memory != VK_NULL_HANDLE) + if (m_alloc != VK_NULL_HANDLE) { - g_command_buffer_mgr->DeferImageDestruction(m_image); - g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_device_memory); + g_command_buffer_mgr->DeferImageDestruction(m_image, m_alloc); } } @@ -85,46 +84,28 @@ std::unique_ptr VKTexture::Create(const TextureConfig& tex_config, st nullptr, VK_IMAGE_LAYOUT_UNDEFINED}; + VmaAllocationCreateInfo alloc_create_info = {}; + alloc_create_info.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; + alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + alloc_create_info.pool = VK_NULL_HANDLE; + alloc_create_info.pUserData = nullptr; + alloc_create_info.priority = + tex_config.IsComputeImage() || tex_config.IsRenderTarget() ? 1.0 : 0.0; + alloc_create_info.requiredFlags = 0; + alloc_create_info.preferredFlags = 0; + VkImage image = VK_NULL_HANDLE; - VkResult res = vkCreateImage(g_vulkan_context->GetDevice(), &image_info, nullptr, &image); + VmaAllocation alloc = VK_NULL_HANDLE; + VkResult res = vmaCreateImage(g_vulkan_context->GetMemoryAllocator(), &image_info, + &alloc_create_info, &image, &alloc, nullptr); if (res != VK_SUCCESS) { - LOG_VULKAN_ERROR(res, "vkCreateImage failed: "); + LOG_VULKAN_ERROR(res, "vmaCreateImage failed: "); return nullptr; } - // Allocate memory to back this texture, we want device local memory in this case - VkMemoryRequirements memory_requirements; - vkGetImageMemoryRequirements(g_vulkan_context->GetDevice(), image, &memory_requirements); - - VkMemoryAllocateInfo memory_info = { - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, nullptr, memory_requirements.size, - g_vulkan_context - ->GetMemoryType(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - false) - .value_or(0)}; - - VkDeviceMemory device_memory; - res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_info, nullptr, &device_memory); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); - vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); - return nullptr; - } - - res = vkBindImageMemory(g_vulkan_context->GetDevice(), image, device_memory, 0); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkBindImageMemory failed: "); - vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr); - return nullptr; - } - - std::unique_ptr texture = - std::make_unique(tex_config, device_memory, image, name, VK_IMAGE_LAYOUT_UNDEFINED, - ComputeImageLayout::Undefined); + std::unique_ptr texture = std::make_unique( + tex_config, alloc, image, name, VK_IMAGE_LAYOUT_UNDEFINED, ComputeImageLayout::Undefined); if (!texture->CreateView(VK_IMAGE_VIEW_TYPE_2D_ARRAY)) return nullptr; @@ -135,7 +116,7 @@ std::unique_ptr VKTexture::CreateAdopted(const TextureConfig& tex_con VkImageViewType view_type, VkImageLayout layout) { std::unique_ptr texture = std::make_unique( - tex_config, VkDeviceMemory(VK_NULL_HANDLE), image, "", layout, ComputeImageLayout::Undefined); + tex_config, VmaAllocation(VK_NULL_HANDLE), image, "", layout, ComputeImageLayout::Undefined); if (!texture->CreateView(view_type)) return nullptr; @@ -701,9 +682,9 @@ void VKTexture::TransitionToLayout(VkCommandBuffer command_buffer, VKStagingTexture::VKStagingTexture(PrivateTag, StagingTextureType type, const TextureConfig& config, std::unique_ptr buffer, VkImage linear_image, - VkDeviceMemory linear_image_memory) + VmaAllocation linear_image_alloc) : AbstractStagingTexture(type, config), m_staging_buffer(std::move(buffer)), - m_linear_image(linear_image), m_linear_image_memory(linear_image_memory) + m_linear_image(linear_image), m_linear_image_alloc(linear_image_alloc) { } @@ -711,8 +692,7 @@ VKStagingTexture::~VKStagingTexture() { if (m_linear_image != VK_NULL_HANDLE) { - g_command_buffer_mgr->DeferImageDestruction(m_linear_image); - g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_linear_image_memory); + g_command_buffer_mgr->DeferImageDestruction(m_linear_image, m_linear_image_alloc); } } @@ -751,18 +731,17 @@ std::unique_ptr VKStagingTexture::Create(StagingTextureType ty // Linear image VkImage linear_image = VK_NULL_HANDLE; - VkDeviceMemory linear_image_device_memory = VK_NULL_HANDLE; + VmaAllocation linear_image_alloc = VK_NULL_HANDLE; if (DriverDetails::HasBug(DriverDetails::BUG_SLOW_OPTIMAL_IMAGE_TO_BUFFER_COPY) && type == StagingTextureType::Readback && config.samples == 1) { - std::tie(linear_image, linear_image_device_memory) = CreateLinearImage(type, config); + std::tie(linear_image, linear_image_alloc) = CreateLinearImage(type, config); } std::unique_ptr staging_buffer = std::make_unique(buffer_type, buffer, alloc, buffer_size, map_ptr); - std::unique_ptr staging_tex = - std::make_unique(PrivateTag{}, type, config, std::move(staging_buffer), - linear_image, linear_image_device_memory); + std::unique_ptr staging_tex = std::make_unique( + PrivateTag{}, type, config, std::move(staging_buffer), linear_image, linear_image_alloc); // Use persistent mapping. if (!staging_tex->m_staging_buffer->Map()) @@ -772,8 +751,8 @@ std::unique_ptr VKStagingTexture::Create(StagingTextureType ty return staging_tex; } -std::pair VKStagingTexture::CreateLinearImage(StagingTextureType type, - const TextureConfig& config) +std::pair VKStagingTexture::CreateLinearImage(StagingTextureType type, + const TextureConfig& config) { // Create a intermediate texture with linear tiling VkImageCreateInfo image_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, @@ -802,43 +781,25 @@ std::pair VKStagingTexture::CreateLinearImage(StagingTe return std::make_pair(VK_NULL_HANDLE, VK_NULL_HANDLE); } + VmaAllocationCreateInfo alloc_create_info = {}; + alloc_create_info.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; + alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + alloc_create_info.pool = VK_NULL_HANDLE; + alloc_create_info.pUserData = nullptr; + alloc_create_info.priority = 0.0; + alloc_create_info.requiredFlags = 0; + alloc_create_info.preferredFlags = 0; + VkImage image; - res = vkCreateImage(g_vulkan_context->GetDevice(), &image_info, nullptr, &image); + VmaAllocation alloc; + res = vmaCreateImage(g_vulkan_context->GetMemoryAllocator(), &image_info, &alloc_create_info, + &image, &alloc, nullptr); if (res != VK_SUCCESS) { - LOG_VULKAN_ERROR(res, "vkCreateImage failed: "); + LOG_VULKAN_ERROR(res, "vmaCreateImage failed: "); return std::make_pair(VK_NULL_HANDLE, VK_NULL_HANDLE); } - - // Allocate memory to back this texture, we want device local memory in this case - VkMemoryRequirements memory_requirements; - vkGetImageMemoryRequirements(g_vulkan_context->GetDevice(), image, &memory_requirements); - - VkMemoryAllocateInfo memory_info = { - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, nullptr, memory_requirements.size, - g_vulkan_context - ->GetMemoryType(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - false) - .value_or(0)}; - - VkDeviceMemory device_memory; - res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_info, nullptr, &device_memory); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); - vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); - return std::make_pair(VK_NULL_HANDLE, VK_NULL_HANDLE); - } - - res = vkBindImageMemory(g_vulkan_context->GetDevice(), image, device_memory, 0); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkBindImageMemory failed: "); - vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr); - return std::make_pair(VK_NULL_HANDLE, VK_NULL_HANDLE); - } - return std::make_pair(image, device_memory); + return std::make_pair(image, alloc); } void VKStagingTexture::CopyFromTexture(const AbstractTexture* src, diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.h b/Source/Core/VideoBackends/Vulkan/VKTexture.h index be74442ba2..f2eae6d80c 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.h +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.h @@ -30,7 +30,7 @@ public: }; VKTexture() = delete; - VKTexture(const TextureConfig& tex_config, VkDeviceMemory device_memory, VkImage image, + VKTexture(const TextureConfig& tex_config, VmaAllocation alloc, VkImage image, std::string_view name, VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED, ComputeImageLayout compute_layout = ComputeImageLayout::Undefined); ~VKTexture(); @@ -51,11 +51,10 @@ public: void FinishedRendering() override; VkImage GetImage() const { return m_image; } - VkDeviceMemory GetDeviceMemory() const { return m_device_memory; } VkImageView GetView() const { return m_view; } VkImageLayout GetLayout() const { return m_layout; } VkFormat GetVkFormat() const { return GetVkFormatForHostTextureFormat(m_config.format); } - bool IsAdopted() const { return m_device_memory != VkDeviceMemory(VK_NULL_HANDLE); } + bool IsAdopted() const { return m_alloc != VmaAllocation(VK_NULL_HANDLE); } static std::unique_ptr Create(const TextureConfig& tex_config, std::string_view name); static std::unique_ptr @@ -74,7 +73,7 @@ public: private: bool CreateView(VkImageViewType type); - VkDeviceMemory m_device_memory; + VmaAllocation m_alloc; VkImage m_image; VkImageView m_view = VK_NULL_HANDLE; mutable VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -92,7 +91,7 @@ public: VKStagingTexture() = delete; VKStagingTexture(PrivateTag, StagingTextureType type, const TextureConfig& config, std::unique_ptr buffer, VkImage linear_image, - VkDeviceMemory linear_image_memory); + VmaAllocation linear_image_alloc); ~VKStagingTexture(); @@ -110,8 +109,8 @@ public: static std::unique_ptr Create(StagingTextureType type, const TextureConfig& config); - static std::pair CreateLinearImage(StagingTextureType type, - const TextureConfig& config); + static std::pair CreateLinearImage(StagingTextureType type, + const TextureConfig& config); private: void CopyFromTextureToLinearImage(const VKTexture* src_tex, @@ -120,7 +119,7 @@ private: std::unique_ptr m_staging_buffer; VkImage m_linear_image = VK_NULL_HANDLE; - VkDeviceMemory m_linear_image_memory = VK_NULL_HANDLE; + VmaAllocation m_linear_image_alloc = VK_NULL_HANDLE; u64 m_flush_fence_counter = 0; };