Kaynağa Gözat

renderer_vulkan: Use VMA for buffers

GPUCode 3 yıl önce
ebeveyn
işleme
7b2f680468

+ 5 - 4
src/video_core/renderer_vulkan/renderer_vulkan.cpp

@@ -233,8 +233,8 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
         .queueFamilyIndexCount = 0,
         .queueFamilyIndexCount = 0,
         .pQueueFamilyIndices = nullptr,
         .pQueueFamilyIndices = nullptr,
     };
     };
-    const vk::Buffer dst_buffer = device.GetLogical().CreateBuffer(dst_buffer_info);
-    MemoryCommit dst_buffer_memory = memory_allocator.Commit(dst_buffer, MemoryUsage::Download);
+    const vk::Buffer dst_buffer =
+        memory_allocator.CreateBuffer(dst_buffer_info, MemoryUsage::Download);
 
 
     scheduler.RequestOutsideRenderPassOperationContext();
     scheduler.RequestOutsideRenderPassOperationContext();
     scheduler.Record([&](vk::CommandBuffer cmdbuf) {
     scheduler.Record([&](vk::CommandBuffer cmdbuf) {
@@ -308,8 +308,9 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
     scheduler.Finish();
     scheduler.Finish();
 
 
     // Copy backing image data to the QImage screenshot buffer
     // Copy backing image data to the QImage screenshot buffer
-    const auto dst_memory_map = dst_buffer_memory.Map();
-    std::memcpy(renderer_settings.screenshot_bits, dst_memory_map.data(), dst_memory_map.size());
+    dst_buffer.Invalidate();
+    std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(),
+                dst_buffer.Mapped().size());
     renderer_settings.screenshot_complete_callback(false);
     renderer_settings.screenshot_complete_callback(false);
     renderer_settings.screenshot_requested = false;
     renderer_settings.screenshot_requested = false;
 }
 }

+ 2 - 4
src/video_core/renderer_vulkan/vk_blit_screen.cpp

@@ -162,7 +162,7 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
     SetUniformData(data, layout);
     SetUniformData(data, layout);
     SetVertexData(data, framebuffer, layout);
     SetVertexData(data, framebuffer, layout);
 
 
-    const std::span<u8> mapped_span = buffer_commit.Map();
+    const std::span<u8> mapped_span = buffer.Mapped();
     std::memcpy(mapped_span.data(), &data, sizeof(data));
     std::memcpy(mapped_span.data(), &data, sizeof(data));
 
 
     if (!use_accelerated) {
     if (!use_accelerated) {
@@ -1074,7 +1074,6 @@ void BlitScreen::ReleaseRawImages() {
     aa_image_view.reset();
     aa_image_view.reset();
     aa_image.reset();
     aa_image.reset();
     buffer.reset();
     buffer.reset();
-    buffer_commit = MemoryCommit{};
 }
 }
 
 
 void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
 void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
@@ -1090,8 +1089,7 @@ void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer
         .pQueueFamilyIndices = nullptr,
         .pQueueFamilyIndices = nullptr,
     };
     };
 
 
-    buffer = device.GetLogical().CreateBuffer(ci);
-    buffer_commit = memory_allocator.Commit(buffer, MemoryUsage::Upload);
+    buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload);
 }
 }
 
 
 void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
 void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {

+ 0 - 1
src/video_core/renderer_vulkan/vk_blit_screen.h

@@ -142,7 +142,6 @@ private:
     vk::Sampler sampler;
     vk::Sampler sampler;
 
 
     vk::Buffer buffer;
     vk::Buffer buffer;
-    MemoryCommit buffer_commit;
 
 
     std::vector<u64> resource_ticks;
     std::vector<u64> resource_ticks;
 
 

+ 46 - 35
src/video_core/renderer_vulkan/vk_buffer_cache.cpp

@@ -50,7 +50,7 @@ size_t BytesPerIndex(VkIndexType index_type) {
     }
     }
 }
 }
 
 
-vk::Buffer CreateBuffer(const Device& device, u64 size) {
+vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allocator, u64 size) {
     VkBufferUsageFlags flags =
     VkBufferUsageFlags flags =
         VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
         VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
         VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
         VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
@@ -60,7 +60,7 @@ vk::Buffer CreateBuffer(const Device& device, u64 size) {
     if (device.IsExtTransformFeedbackSupported()) {
     if (device.IsExtTransformFeedbackSupported()) {
         flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
         flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
     }
     }
-    return device.GetLogical().CreateBuffer({
+    const VkBufferCreateInfo buffer_ci = {
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .pNext = nullptr,
         .pNext = nullptr,
         .flags = 0,
         .flags = 0,
@@ -69,7 +69,8 @@ vk::Buffer CreateBuffer(const Device& device, u64 size) {
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .queueFamilyIndexCount = 0,
         .queueFamilyIndexCount = 0,
         .pQueueFamilyIndices = nullptr,
         .pQueueFamilyIndices = nullptr,
-    });
+    };
+    return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
 }
 }
 } // Anonymous namespace
 } // Anonymous namespace
 
 
@@ -79,8 +80,8 @@ Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
 Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
 Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
                VAddr cpu_addr_, u64 size_bytes_)
                VAddr cpu_addr_, u64 size_bytes_)
     : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_),
     : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_),
-      device{&runtime.device}, buffer{CreateBuffer(*device, SizeBytes())},
-      commit{runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal)} {
+      device{&runtime.device}, buffer{
+                                   CreateBuffer(*device, runtime.memory_allocator, SizeBytes())} {
     if (runtime.device.HasDebuggingToolAttached()) {
     if (runtime.device.HasDebuggingToolAttached()) {
         buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
         buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
     }
     }
@@ -138,7 +139,7 @@ public:
         const u32 num_first_offset_copies = 4;
         const u32 num_first_offset_copies = 4;
         const size_t bytes_per_index = BytesPerIndex(index_type);
         const size_t bytes_per_index = BytesPerIndex(index_type);
         const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies;
         const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies;
-        buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
+        const VkBufferCreateInfo buffer_ci = {
             .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
             .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
             .pNext = nullptr,
             .pNext = nullptr,
             .flags = 0,
             .flags = 0,
@@ -147,14 +148,21 @@ public:
             .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
             .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
             .queueFamilyIndexCount = 0,
             .queueFamilyIndexCount = 0,
             .pQueueFamilyIndices = nullptr,
             .pQueueFamilyIndices = nullptr,
-        });
+        };
+        buffer = memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
         if (device.HasDebuggingToolAttached()) {
         if (device.HasDebuggingToolAttached()) {
             buffer.SetObjectNameEXT("Quad LUT");
             buffer.SetObjectNameEXT("Quad LUT");
         }
         }
-        memory_commit = memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
 
 
-        const StagingBufferRef staging = staging_pool.Request(size_bytes, MemoryUsage::Upload);
-        u8* staging_data = staging.mapped_span.data();
+        const bool host_visible = buffer.IsHostVisible();
+        const StagingBufferRef staging = [&] {
+            if (host_visible) {
+                return StagingBufferRef{};
+            }
+            return staging_pool.Request(size_bytes, MemoryUsage::Upload);
+        }();
+
+        u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data();
         const size_t quad_size = bytes_per_index * 6;
         const size_t quad_size = bytes_per_index * 6;
 
 
         for (u32 first = 0; first < num_first_offset_copies; ++first) {
         for (u32 first = 0; first < num_first_offset_copies; ++first) {
@@ -164,29 +172,33 @@ public:
             }
             }
         }
         }
 
 
-        scheduler.RequestOutsideRenderPassOperationContext();
-        scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset,
-                          dst_buffer = *buffer, size_bytes](vk::CommandBuffer cmdbuf) {
-            const VkBufferCopy copy{
-                .srcOffset = src_offset,
-                .dstOffset = 0,
-                .size = size_bytes,
-            };
-            const VkBufferMemoryBarrier write_barrier{
-                .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
-                .pNext = nullptr,
-                .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
-                .dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
-                .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
-                .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
-                .buffer = dst_buffer,
-                .offset = 0,
-                .size = size_bytes,
-            };
-            cmdbuf.CopyBuffer(src_buffer, dst_buffer, copy);
-            cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
-                                   VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, write_barrier);
-        });
+        if (!host_visible) {
+            scheduler.RequestOutsideRenderPassOperationContext();
+            scheduler.Record([src_buffer = staging.buffer, src_offset = staging.offset,
+                              dst_buffer = *buffer, size_bytes](vk::CommandBuffer cmdbuf) {
+                const VkBufferCopy copy{
+                    .srcOffset = src_offset,
+                    .dstOffset = 0,
+                    .size = size_bytes,
+                };
+                const VkBufferMemoryBarrier write_barrier{
+                    .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+                    .pNext = nullptr,
+                    .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+                    .dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
+                    .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                    .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                    .buffer = dst_buffer,
+                    .offset = 0,
+                    .size = size_bytes,
+                };
+                cmdbuf.CopyBuffer(src_buffer, dst_buffer, copy);
+                cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
+                                       VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, write_barrier);
+            });
+        } else {
+            buffer.Flush();
+        }
     }
     }
 
 
     void BindBuffer(u32 first) {
     void BindBuffer(u32 first) {
@@ -587,11 +599,10 @@ void BufferCacheRuntime::ReserveNullBuffer() {
         create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
         create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
     }
     }
     create_info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
     create_info.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
-    null_buffer = device.GetLogical().CreateBuffer(create_info);
+    null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
     if (device.HasDebuggingToolAttached()) {
     if (device.HasDebuggingToolAttached()) {
         null_buffer.SetObjectNameEXT("Null buffer");
         null_buffer.SetObjectNameEXT("Null buffer");
     }
     }
-    null_buffer_commit = memory_allocator.Commit(null_buffer, MemoryUsage::DeviceLocal);
 
 
     scheduler.RequestOutsideRenderPassOperationContext();
     scheduler.RequestOutsideRenderPassOperationContext();
     scheduler.Record([buffer = *null_buffer](vk::CommandBuffer cmdbuf) {
     scheduler.Record([buffer = *null_buffer](vk::CommandBuffer cmdbuf) {

+ 0 - 2
src/video_core/renderer_vulkan/vk_buffer_cache.h

@@ -48,7 +48,6 @@ private:
 
 
     const Device* device{};
     const Device* device{};
     vk::Buffer buffer;
     vk::Buffer buffer;
-    MemoryCommit commit;
     std::vector<BufferView> views;
     std::vector<BufferView> views;
 };
 };
 
 
@@ -142,7 +141,6 @@ private:
     std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
     std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
 
 
     vk::Buffer null_buffer;
     vk::Buffer null_buffer;
-    MemoryCommit null_buffer_commit;
 
 
     std::unique_ptr<Uint8Pass> uint8_pass;
     std::unique_ptr<Uint8Pass> uint8_pass;
     QuadIndexedPass quad_index_pass;
     QuadIndexedPass quad_index_pass;

+ 5 - 7
src/video_core/renderer_vulkan/vk_smaa.cpp

@@ -76,7 +76,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo
 void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler,
 void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler,
                  vk::Image& image, VkExtent2D dimensions, VkFormat format,
                  vk::Image& image, VkExtent2D dimensions, VkFormat format,
                  std::span<const u8> initial_contents = {}) {
                  std::span<const u8> initial_contents = {}) {
-    auto upload_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
+    const VkBufferCreateInfo upload_ci = {
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .pNext = nullptr,
         .pNext = nullptr,
         .flags = 0,
         .flags = 0,
@@ -85,9 +85,10 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .queueFamilyIndexCount = 0,
         .queueFamilyIndexCount = 0,
         .pQueueFamilyIndices = nullptr,
         .pQueueFamilyIndices = nullptr,
-    });
-    auto upload_commit = allocator.Commit(upload_buffer, MemoryUsage::Upload);
-    std::ranges::copy(initial_contents, upload_commit.Map().begin());
+    };
+    auto upload_buffer = allocator.CreateBuffer(upload_ci, MemoryUsage::Upload);
+    std::ranges::copy(initial_contents, upload_buffer.Mapped().begin());
+    upload_buffer.Flush();
 
 
     const std::array<VkBufferImageCopy, 1> regions{{{
     const std::array<VkBufferImageCopy, 1> regions{{{
         .bufferOffset = 0,
         .bufferOffset = 0,
@@ -111,9 +112,6 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc
                               VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
                               VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
     });
     });
     scheduler.Finish();
     scheduler.Finish();
-
-    // This should go out of scope before the commit
-    auto upload_buffer2 = std::move(upload_buffer);
 }
 }
 
 
 vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) {
 vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) {

+ 10 - 95
src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp

@@ -30,55 +30,6 @@ constexpr VkDeviceSize MAX_STREAM_BUFFER_REQUEST_SIZE = 8_MiB;
 constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB;
 constexpr VkDeviceSize STREAM_BUFFER_SIZE = 128_MiB;
 constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS;
 constexpr VkDeviceSize REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS;
 
 
-constexpr VkMemoryPropertyFlags HOST_FLAGS =
-    VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
-constexpr VkMemoryPropertyFlags STREAM_FLAGS = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | HOST_FLAGS;
-
-bool IsStreamHeap(VkMemoryHeap heap) noexcept {
-    return STREAM_BUFFER_SIZE < (heap.size * 2) / 3;
-}
-
-std::optional<u32> FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask,
-                                       VkMemoryPropertyFlags flags) noexcept {
-    for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) {
-        if (((type_mask >> type_index) & 1) == 0) {
-            // Memory type is incompatible
-            continue;
-        }
-        const VkMemoryType& memory_type = props.memoryTypes[type_index];
-        if ((memory_type.propertyFlags & flags) != flags) {
-            // Memory type doesn't have the flags we want
-            continue;
-        }
-        if (!IsStreamHeap(props.memoryHeaps[memory_type.heapIndex])) {
-            // Memory heap is not suitable for streaming
-            continue;
-        }
-        // Success!
-        return type_index;
-    }
-    return std::nullopt;
-}
-
-u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask,
-                        bool try_device_local) {
-    std::optional<u32> type;
-    if (try_device_local) {
-        // Try to find a DEVICE_LOCAL_BIT type, Nvidia and AMD have a dedicated heap for this
-        type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS);
-        if (type) {
-            return *type;
-        }
-    }
-    // Otherwise try without the DEVICE_LOCAL_BIT
-    type = FindMemoryTypeIndex(props, type_mask, HOST_FLAGS);
-    if (type) {
-        return *type;
-    }
-    // This should never happen, and in case it does, signal it as an out of memory situation
-    throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY);
-}
-
 size_t Region(size_t iterator) noexcept {
 size_t Region(size_t iterator) noexcept {
     return iterator / REGION_SIZE;
     return iterator / REGION_SIZE;
 }
 }
@@ -87,8 +38,7 @@ size_t Region(size_t iterator) noexcept {
 StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
 StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
                                      Scheduler& scheduler_)
                                      Scheduler& scheduler_)
     : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} {
     : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} {
-    const vk::Device& dev = device.GetLogical();
-    stream_buffer = dev.CreateBuffer(VkBufferCreateInfo{
+    const VkBufferCreateInfo stream_ci = {
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .pNext = nullptr,
         .pNext = nullptr,
         .flags = 0,
         .flags = 0,
@@ -99,46 +49,13 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .queueFamilyIndexCount = 0,
         .queueFamilyIndexCount = 0,
         .pQueueFamilyIndices = nullptr,
         .pQueueFamilyIndices = nullptr,
-    });
-    if (device.HasDebuggingToolAttached()) {
-        stream_buffer.SetObjectNameEXT("Stream Buffer");
-    }
-    VkMemoryDedicatedRequirements dedicated_reqs{
-        .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS,
-        .pNext = nullptr,
-        .prefersDedicatedAllocation = VK_FALSE,
-        .requiresDedicatedAllocation = VK_FALSE,
-    };
-    const auto requirements = dev.GetBufferMemoryRequirements(*stream_buffer, &dedicated_reqs);
-    const bool make_dedicated = dedicated_reqs.prefersDedicatedAllocation == VK_TRUE ||
-                                dedicated_reqs.requiresDedicatedAllocation == VK_TRUE;
-    const VkMemoryDedicatedAllocateInfo dedicated_info{
-        .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
-        .pNext = nullptr,
-        .image = nullptr,
-        .buffer = *stream_buffer,
     };
     };
-    const auto memory_properties = device.GetPhysical().GetMemoryProperties().memoryProperties;
-    VkMemoryAllocateInfo stream_memory_info{
-        .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
-        .pNext = make_dedicated ? &dedicated_info : nullptr,
-        .allocationSize = requirements.size,
-        .memoryTypeIndex =
-            FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, true),
-    };
-    stream_memory = dev.TryAllocateMemory(stream_memory_info);
-    if (!stream_memory) {
-        LOG_INFO(Render_Vulkan, "Dynamic memory allocation failed, trying with system memory");
-        stream_memory_info.memoryTypeIndex =
-            FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, false);
-        stream_memory = dev.AllocateMemory(stream_memory_info);
-    }
-
+    stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
     if (device.HasDebuggingToolAttached()) {
     if (device.HasDebuggingToolAttached()) {
-        stream_memory.SetObjectNameEXT("Stream Buffer Memory");
+        stream_buffer.SetObjectNameEXT("Stream Buffer");
     }
     }
-    stream_buffer.BindMemory(*stream_memory, 0);
-    stream_pointer = stream_memory.Map(0, STREAM_BUFFER_SIZE);
+    stream_pointer = stream_buffer.Mapped();
+    ASSERT_MSG(!stream_pointer.empty(), "Stream buffer must be host visible!");
 }
 }
 
 
 StagingBufferPool::~StagingBufferPool() = default;
 StagingBufferPool::~StagingBufferPool() = default;
@@ -199,7 +116,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
     return StagingBufferRef{
     return StagingBufferRef{
         .buffer = *stream_buffer,
         .buffer = *stream_buffer,
         .offset = static_cast<VkDeviceSize>(offset),
         .offset = static_cast<VkDeviceSize>(offset),
-        .mapped_span = std::span<u8>(stream_pointer + offset, size),
+        .mapped_span = stream_pointer.subspan(offset, size),
         .usage{},
         .usage{},
         .log2_level{},
         .log2_level{},
         .index{},
         .index{},
@@ -247,7 +164,7 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s
 StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage,
 StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage,
                                                         bool deferred) {
                                                         bool deferred) {
     const u32 log2 = Common::Log2Ceil64(size);
     const u32 log2 = Common::Log2Ceil64(size);
-    vk::Buffer buffer = device.GetLogical().CreateBuffer({
+    const VkBufferCreateInfo buffer_ci = {
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .pNext = nullptr,
         .pNext = nullptr,
         .flags = 0,
         .flags = 0,
@@ -259,17 +176,15 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .queueFamilyIndexCount = 0,
         .queueFamilyIndexCount = 0,
         .pQueueFamilyIndices = nullptr,
         .pQueueFamilyIndices = nullptr,
-    });
+    };
+    vk::Buffer buffer = memory_allocator.CreateBuffer(buffer_ci, usage);
     if (device.HasDebuggingToolAttached()) {
     if (device.HasDebuggingToolAttached()) {
         ++buffer_index;
         ++buffer_index;
         buffer.SetObjectNameEXT(fmt::format("Staging Buffer {}", buffer_index).c_str());
         buffer.SetObjectNameEXT(fmt::format("Staging Buffer {}", buffer_index).c_str());
     }
     }
-    MemoryCommit commit = memory_allocator.Commit(buffer, usage);
-    const std::span<u8> mapped_span = IsHostVisible(usage) ? commit.Map() : std::span<u8>{};
-
+    const std::span<u8> mapped_span = buffer.Mapped();
     StagingBuffer& entry = GetCache(usage)[log2].entries.emplace_back(StagingBuffer{
     StagingBuffer& entry = GetCache(usage)[log2].entries.emplace_back(StagingBuffer{
         .buffer = std::move(buffer),
         .buffer = std::move(buffer),
-        .commit = std::move(commit),
         .mapped_span = mapped_span,
         .mapped_span = mapped_span,
         .usage = usage,
         .usage = usage,
         .log2_level = log2,
         .log2_level = log2,

+ 1 - 3
src/video_core/renderer_vulkan/vk_staging_buffer_pool.h

@@ -46,7 +46,6 @@ private:
 
 
     struct StagingBuffer {
     struct StagingBuffer {
         vk::Buffer buffer;
         vk::Buffer buffer;
-        MemoryCommit commit;
         std::span<u8> mapped_span;
         std::span<u8> mapped_span;
         MemoryUsage usage;
         MemoryUsage usage;
         u32 log2_level;
         u32 log2_level;
@@ -97,8 +96,7 @@ private:
     Scheduler& scheduler;
     Scheduler& scheduler;
 
 
     vk::Buffer stream_buffer;
     vk::Buffer stream_buffer;
-    vk::DeviceMemory stream_memory;
-    u8* stream_pointer = nullptr;
+    std::span<u8> stream_pointer;
 
 
     size_t iterator = 0;
     size_t iterator = 0;
     size_t used_iterator = 0;
     size_t used_iterator = 0;

+ 4 - 5
src/video_core/renderer_vulkan/vk_texture_cache.cpp

@@ -839,14 +839,14 @@ bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) {
 
 
 VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
 VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
     const auto level = (8 * sizeof(size_t)) - std::countl_zero(needed_size - 1ULL);
     const auto level = (8 * sizeof(size_t)) - std::countl_zero(needed_size - 1ULL);
-    if (buffer_commits[level]) {
+    if (buffers[level]) {
         return *buffers[level];
         return *buffers[level];
     }
     }
     const auto new_size = Common::NextPow2(needed_size);
     const auto new_size = Common::NextPow2(needed_size);
     static constexpr VkBufferUsageFlags flags =
     static constexpr VkBufferUsageFlags flags =
         VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
         VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
         VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
         VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
-    buffers[level] = device.GetLogical().CreateBuffer({
+    const VkBufferCreateInfo temp_ci = {
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .pNext = nullptr,
         .pNext = nullptr,
         .flags = 0,
         .flags = 0,
@@ -855,9 +855,8 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .queueFamilyIndexCount = 0,
         .queueFamilyIndexCount = 0,
         .pQueueFamilyIndices = nullptr,
         .pQueueFamilyIndices = nullptr,
-    });
-    buffer_commits[level] = std::make_unique<MemoryCommit>(
-        memory_allocator.Commit(buffers[level], MemoryUsage::DeviceLocal));
+    };
+    buffers[level] = memory_allocator.CreateBuffer(temp_ci, MemoryUsage::DeviceLocal);
     return *buffers[level];
     return *buffers[level];
 }
 }
 
 

+ 0 - 1
src/video_core/renderer_vulkan/vk_texture_cache.h

@@ -116,7 +116,6 @@ public:
 
 
     static constexpr size_t indexing_slots = 8 * sizeof(size_t);
     static constexpr size_t indexing_slots = 8 * sizeof(size_t);
     std::array<vk::Buffer, indexing_slots> buffers{};
     std::array<vk::Buffer, indexing_slots> buffers{};
-    std::array<std::unique_ptr<MemoryCommit>, indexing_slots> buffer_commits{};
 };
 };
 
 
 class Image : public VideoCommon::ImageBase {
 class Image : public VideoCommon::ImageBase {

+ 3 - 5
src/video_core/renderer_vulkan/vk_turbo_mode.cpp

@@ -41,7 +41,7 @@ void TurboMode::Run(std::stop_token stop_token) {
     auto& dld = m_device.GetLogical();
     auto& dld = m_device.GetLogical();
 
 
     // Allocate buffer. 2MiB should be sufficient.
     // Allocate buffer. 2MiB should be sufficient.
-    auto buffer = dld.CreateBuffer(VkBufferCreateInfo{
+    const VkBufferCreateInfo buffer_ci = {
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .pNext = nullptr,
         .pNext = nullptr,
         .flags = 0,
         .flags = 0,
@@ -50,10 +50,8 @@ void TurboMode::Run(std::stop_token stop_token) {
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .queueFamilyIndexCount = 0,
         .queueFamilyIndexCount = 0,
         .pQueueFamilyIndices = nullptr,
         .pQueueFamilyIndices = nullptr,
-    });
-
-    // Commit some device local memory for the buffer.
-    auto commit = m_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
+    };
+    vk::Buffer buffer = m_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
 
 
     // Create the descriptor pool to contain our descriptor.
     // Create the descriptor pool to contain our descriptor.
     static constexpr VkDescriptorPoolSize pool_size{
     static constexpr VkDescriptorPoolSize pool_size{

+ 1 - 0
src/video_core/vulkan_common/vulkan_device.cpp

@@ -603,6 +603,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
     };
     };
 
 
     const VmaAllocatorCreateInfo allocator_info = {
     const VmaAllocatorCreateInfo allocator_info = {
+        .flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT,
         .physicalDevice = physical,
         .physicalDevice = physical,
         .device = *logical,
         .device = *logical,
         .pVulkanFunctions = &functions,
         .pVulkanFunctions = &functions,

+ 83 - 24
src/video_core/vulkan_common/vulkan_memory_allocator.cpp

@@ -51,11 +51,59 @@ struct Range {
     case MemoryUsage::Download:
     case MemoryUsage::Download:
         return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
         return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
                VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
                VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+    case MemoryUsage::Stream:
+        return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+               VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
     }
     }
     ASSERT_MSG(false, "Invalid memory usage={}", usage);
     ASSERT_MSG(false, "Invalid memory usage={}", usage);
     return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
     return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
 }
 }
 
 
+[[nodiscard]] VkMemoryPropertyFlags MemoryUsageRequiredVmaFlags(MemoryUsage usage) {
+    switch (usage) {
+    case MemoryUsage::DeviceLocal:
+        return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+    case MemoryUsage::Upload:
+    case MemoryUsage::Stream:
+        return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+    case MemoryUsage::Download:
+        return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+    }
+    ASSERT_MSG(false, "Invalid memory usage={}", usage);
+    return {};
+}
+
+[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferedVmaFlags(MemoryUsage usage) {
+    return usage != MemoryUsage::DeviceLocal ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
+                                             : VkMemoryPropertyFlags{};
+}
+
+[[nodiscard]] VmaAllocationCreateFlags MemoryUsageVmaFlags(MemoryUsage usage) {
+    switch (usage) {
+    case MemoryUsage::Upload:
+    case MemoryUsage::Stream:
+        return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
+    case MemoryUsage::Download:
+        return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
+    case MemoryUsage::DeviceLocal:
+        return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
+               VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT;
+    }
+    return {};
+}
+
+[[nodiscard]] VmaMemoryUsage MemoryUsageVma(MemoryUsage usage) {
+    switch (usage) {
+    case MemoryUsage::DeviceLocal:
+    case MemoryUsage::Stream:
+        return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
+    case MemoryUsage::Upload:
+    case MemoryUsage::Download:
+        return VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
+    }
+    return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
+}
+
 } // Anonymous namespace
 } // Anonymous namespace
 
 
 class MemoryAllocation {
 class MemoryAllocation {
@@ -178,17 +226,18 @@ void MemoryCommit::Release() {
 }
 }
 
 
 MemoryAllocator::MemoryAllocator(const Device& device_)
 MemoryAllocator::MemoryAllocator(const Device& device_)
-    : device{device_}, properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},
+    : device{device_}, allocator{device.GetAllocator()},
+      properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},
       buffer_image_granularity{
       buffer_image_granularity{
           device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {}
           device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {}
 
 
 MemoryAllocator::~MemoryAllocator() = default;
 MemoryAllocator::~MemoryAllocator() = default;
 
 
 vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const {
 vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const {
-    const VmaAllocationCreateInfo alloc_info = {
+    const VmaAllocationCreateInfo alloc_ci = {
         .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT,
         .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT,
         .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
         .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
-        .requiredFlags = 0,
+        .requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
         .preferredFlags = 0,
         .preferredFlags = 0,
         .pool = VK_NULL_HANDLE,
         .pool = VK_NULL_HANDLE,
         .pUserData = nullptr,
         .pUserData = nullptr,
@@ -196,12 +245,40 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const {
 
 
     VkImage handle{};
     VkImage handle{};
     VmaAllocation allocation{};
     VmaAllocation allocation{};
-    vk::Check(
-        vmaCreateImage(device.GetAllocator(), &ci, &alloc_info, &handle, &allocation, nullptr));
-    return vk::Image(handle, *device.GetLogical(), device.GetAllocator(), allocation,
+
+    vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, nullptr));
+
+    return vk::Image(handle, *device.GetLogical(), allocator, allocation,
                      device.GetDispatchLoader());
                      device.GetDispatchLoader());
 }
 }
 
 
+vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const {
+    const VmaAllocationCreateInfo alloc_ci = {
+        .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT |
+                 MemoryUsageVmaFlags(usage),
+        .usage = MemoryUsageVma(usage),
+        .requiredFlags = MemoryUsageRequiredVmaFlags(usage),
+        .preferredFlags = MemoryUsagePreferedVmaFlags(usage),
+        .pool = VK_NULL_HANDLE,
+        .pUserData = nullptr,
+    };
+
+    VkBuffer handle{};
+    VmaAllocationInfo alloc_info{};
+    VmaAllocation allocation{};
+    VkMemoryPropertyFlags property_flags{};
+
+    vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info));
+    vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags);
+
+    u8* data = reinterpret_cast<u8*>(alloc_info.pMappedData);
+    const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{};
+    const bool is_coherent = property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+
+    return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, is_coherent,
+                      device.GetDispatchLoader());
+}
+
 MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) {
 MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) {
     // Find the fastest memory flags we can afford with the current requirements
     // Find the fastest memory flags we can afford with the current requirements
     const u32 type_mask = requirements.memoryTypeBits;
     const u32 type_mask = requirements.memoryTypeBits;
@@ -221,12 +298,6 @@ MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, M
     return TryCommit(requirements, flags).value();
     return TryCommit(requirements, flags).value();
 }
 }
 
 
-MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage) {
-    auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), usage);
-    buffer.BindMemory(commit.Memory(), commit.Offset());
-    return commit;
-}
-
 bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) {
 bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) {
     const u32 type = FindType(flags, type_mask).value();
     const u32 type = FindType(flags, type_mask).value();
     vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({
     vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({
@@ -302,16 +373,4 @@ std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 ty
     return std::nullopt;
     return std::nullopt;
 }
 }
 
 
-bool IsHostVisible(MemoryUsage usage) noexcept {
-    switch (usage) {
-    case MemoryUsage::DeviceLocal:
-        return false;
-    case MemoryUsage::Upload:
-    case MemoryUsage::Download:
-        return true;
-    }
-    ASSERT_MSG(false, "Invalid memory usage={}", usage);
-    return false;
-}
-
 } // namespace Vulkan
 } // namespace Vulkan

+ 8 - 4
src/video_core/vulkan_common/vulkan_memory_allocator.h

@@ -9,6 +9,8 @@
 #include "common/common_types.h"
 #include "common/common_types.h"
 #include "video_core/vulkan_common/vulkan_wrapper.h"
 #include "video_core/vulkan_common/vulkan_wrapper.h"
 
 
+VK_DEFINE_HANDLE(VmaAllocator)
+
 namespace Vulkan {
 namespace Vulkan {
 
 
 class Device;
 class Device;
@@ -17,9 +19,11 @@ class MemoryAllocation;
 
 
 /// Hints and requirements for the backing memory type of a commit
 /// Hints and requirements for the backing memory type of a commit
 enum class MemoryUsage {
 enum class MemoryUsage {
-    DeviceLocal, ///< Hints device local usages, fastest memory type to read and write from the GPU
+    DeviceLocal, ///< Requests device local host visible buffer, falling back to device local
+                 ///< memory.
     Upload,      ///< Requires a host visible memory type optimized for CPU to GPU uploads
     Upload,      ///< Requires a host visible memory type optimized for CPU to GPU uploads
     Download,    ///< Requires a host visible memory type optimized for GPU to CPU readbacks
     Download,    ///< Requires a host visible memory type optimized for GPU to CPU readbacks
+    Stream,      ///< Requests device local host visible buffer, falling back host memory.
 };
 };
 
 
 /// Ownership handle of a memory commitment.
 /// Ownership handle of a memory commitment.
@@ -82,6 +86,8 @@ public:
 
 
     vk::Image CreateImage(const VkImageCreateInfo& ci) const;
     vk::Image CreateImage(const VkImageCreateInfo& ci) const;
 
 
+    vk::Buffer CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const;
+
     /**
     /**
      * Commits a memory with the specified requirements.
      * Commits a memory with the specified requirements.
      *
      *
@@ -113,13 +119,11 @@ private:
     std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const;
     std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const;
 
 
     const Device& device;                                       ///< Device handle.
     const Device& device;                                       ///< Device handle.
+    VmaAllocator allocator;                                     ///< Vma allocator.
     const VkPhysicalDeviceMemoryProperties properties;          ///< Physical device properties.
     const VkPhysicalDeviceMemoryProperties properties;          ///< Physical device properties.
     std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations.
     std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations.
     VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers
     VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers
                                            // and optimal images
                                            // and optimal images
 };
 };
 
 
-/// Returns true when a memory usage is guaranteed to be host visible.
-bool IsHostVisible(MemoryUsage usage) noexcept;
-
 } // namespace Vulkan
 } // namespace Vulkan

+ 16 - 8
src/video_core/vulkan_common/vulkan_wrapper.cpp

@@ -561,14 +561,28 @@ void Image::Release() const noexcept {
     }
     }
 }
 }
 
 
-void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const {
-    Check(dld->vkBindBufferMemory(owner, handle, memory, offset));
+void Buffer::Flush() const {
+    if (!is_coherent) {
+        vmaFlushAllocation(allocator, allocation, 0, VK_WHOLE_SIZE);
+    }
+}
+
+void Buffer::Invalidate() const {
+    if (!is_coherent) {
+        vmaInvalidateAllocation(allocator, allocation, 0, VK_WHOLE_SIZE);
+    }
 }
 }
 
 
 void Buffer::SetObjectNameEXT(const char* name) const {
 void Buffer::SetObjectNameEXT(const char* name) const {
     SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER, name);
     SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER, name);
 }
 }
 
 
+void Buffer::Release() const noexcept {
+    if (handle) {
+        vmaDestroyBuffer(allocator, handle, allocation);
+    }
+}
+
 void BufferView::SetObjectNameEXT(const char* name) const {
 void BufferView::SetObjectNameEXT(const char* name) const {
     SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER_VIEW, name);
     SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER_VIEW, name);
 }
 }
@@ -707,12 +721,6 @@ Queue Device::GetQueue(u32 family_index) const noexcept {
     return Queue(queue, *dld);
     return Queue(queue, *dld);
 }
 }
 
 
-Buffer Device::CreateBuffer(const VkBufferCreateInfo& ci) const {
-    VkBuffer object;
-    Check(dld->vkCreateBuffer(handle, &ci, nullptr, &object));
-    return Buffer(object, handle, *dld);
-}
-
 BufferView Device::CreateBufferView(const VkBufferViewCreateInfo& ci) const {
 BufferView Device::CreateBufferView(const VkBufferViewCreateInfo& ci) const {
     VkBufferView object;
     VkBufferView object;
     Check(dld->vkCreateBufferView(handle, &ci, nullptr, &object));
     Check(dld->vkCreateBufferView(handle, &ci, nullptr, &object));

+ 78 - 13
src/video_core/vulkan_common/vulkan_wrapper.h

@@ -673,6 +673,84 @@ private:
     const DeviceDispatch* dld = nullptr;
     const DeviceDispatch* dld = nullptr;
 };
 };
 
 
+class Buffer {
+public:
+    explicit Buffer(VkBuffer handle_, VkDevice owner_, VmaAllocator allocator_,
+                    VmaAllocation allocation_, std::span<u8> mapped_, bool is_coherent_,
+                    const DeviceDispatch& dld_) noexcept
+        : handle{handle_}, owner{owner_}, allocator{allocator_},
+          allocation{allocation_}, mapped{mapped_}, is_coherent{is_coherent_}, dld{&dld_} {}
+    Buffer() = default;
+
+    Buffer(const Buffer&) = delete;
+    Buffer& operator=(const Buffer&) = delete;
+
+    Buffer(Buffer&& rhs) noexcept
+        : handle{std::exchange(rhs.handle, nullptr)}, owner{rhs.owner}, allocator{rhs.allocator},
+          allocation{rhs.allocation}, mapped{rhs.mapped},
+          is_coherent{rhs.is_coherent}, dld{rhs.dld} {}
+
+    Buffer& operator=(Buffer&& rhs) noexcept {
+        Release();
+        handle = std::exchange(rhs.handle, nullptr);
+        owner = rhs.owner;
+        allocator = rhs.allocator;
+        allocation = rhs.allocation;
+        mapped = rhs.mapped;
+        is_coherent = rhs.is_coherent;
+        dld = rhs.dld;
+        return *this;
+    }
+
+    ~Buffer() noexcept {
+        Release();
+    }
+
+    VkBuffer operator*() const noexcept {
+        return handle;
+    }
+
+    void reset() noexcept {
+        Release();
+        handle = nullptr;
+    }
+
+    explicit operator bool() const noexcept {
+        return handle != nullptr;
+    }
+
+    /// Returns the host mapped memory, an empty span otherwise.
+    std::span<u8> Mapped() noexcept {
+        return mapped;
+    }
+
+    std::span<const u8> Mapped() const noexcept {
+        return mapped;
+    }
+
+    /// Returns true if the buffer is mapped to the host.
+    bool IsHostVisible() const noexcept {
+        return !mapped.empty();
+    }
+
+    void Flush() const;
+
+    void Invalidate() const;
+
+    void SetObjectNameEXT(const char* name) const;
+
+private:
+    void Release() const noexcept;
+
+    VkBuffer handle = nullptr;
+    VkDevice owner = nullptr;
+    VmaAllocator allocator = nullptr;
+    VmaAllocation allocation = nullptr;
+    std::span<u8> mapped = {};
+    bool is_coherent = false;
+    const DeviceDispatch* dld = nullptr;
+};
+
 class Queue {
 class Queue {
 public:
 public:
     /// Construct an empty queue handle.
     /// Construct an empty queue handle.
@@ -696,17 +774,6 @@ private:
     const DeviceDispatch* dld = nullptr;
     const DeviceDispatch* dld = nullptr;
 };
 };
 
 
-class Buffer : public Handle<VkBuffer, VkDevice, DeviceDispatch> {
-    using Handle<VkBuffer, VkDevice, DeviceDispatch>::Handle;
-
-public:
-    /// Attaches a memory allocation.
-    void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const;
-
-    /// Set object name.
-    void SetObjectNameEXT(const char* name) const;
-};
-
 class BufferView : public Handle<VkBufferView, VkDevice, DeviceDispatch> {
 class BufferView : public Handle<VkBufferView, VkDevice, DeviceDispatch> {
     using Handle<VkBufferView, VkDevice, DeviceDispatch>::Handle;
     using Handle<VkBufferView, VkDevice, DeviceDispatch>::Handle;
 
 
@@ -886,8 +953,6 @@ public:
 
 
     Queue GetQueue(u32 family_index) const noexcept;
     Queue GetQueue(u32 family_index) const noexcept;
 
 
-    Buffer CreateBuffer(const VkBufferCreateInfo& ci) const;
-
     BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const;
     BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const;
 
 
     ImageView CreateImageView(const VkImageViewCreateInfo& ci) const;
     ImageView CreateImageView(const VkImageViewCreateInfo& ci) const;