summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp10
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp22
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp40
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h22
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp32
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h15
-rw-r--r--src/video_core/renderer_vulkan/vk_memory_manager.cpp230
-rw-r--r--src/video_core/renderer_vulkan/vk_memory_manager.h132
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp126
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h62
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp22
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h22
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp268
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h118
19 files changed, 609 insertions, 554 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 25a4b1c5b..7a20d3a79 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -135,8 +135,6 @@ add_library(video_core STATIC
135 renderer_vulkan/vk_graphics_pipeline.h 135 renderer_vulkan/vk_graphics_pipeline.h
136 renderer_vulkan/vk_master_semaphore.cpp 136 renderer_vulkan/vk_master_semaphore.cpp
137 renderer_vulkan/vk_master_semaphore.h 137 renderer_vulkan/vk_master_semaphore.h
138 renderer_vulkan/vk_memory_manager.cpp
139 renderer_vulkan/vk_memory_manager.h
140 renderer_vulkan/vk_pipeline_cache.cpp 138 renderer_vulkan/vk_pipeline_cache.cpp
141 renderer_vulkan/vk_pipeline_cache.h 139 renderer_vulkan/vk_pipeline_cache.h
142 renderer_vulkan/vk_query_cache.cpp 140 renderer_vulkan/vk_query_cache.cpp
@@ -259,6 +257,8 @@ add_library(video_core STATIC
259 vulkan_common/vulkan_instance.h 257 vulkan_common/vulkan_instance.h
260 vulkan_common/vulkan_library.cpp 258 vulkan_common/vulkan_library.cpp
261 vulkan_common/vulkan_library.h 259 vulkan_common/vulkan_library.h
260 vulkan_common/vulkan_memory_allocator.cpp
261 vulkan_common/vulkan_memory_allocator.h
262 vulkan_common/vulkan_surface.cpp 262 vulkan_common/vulkan_surface.cpp
263 vulkan_common/vulkan_surface.h 263 vulkan_common/vulkan_surface.h
264 vulkan_common/vulkan_wrapper.cpp 264 vulkan_common/vulkan_wrapper.cpp
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index d7437e185..61796e33a 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -23,7 +23,6 @@
23#include "video_core/renderer_vulkan/renderer_vulkan.h" 23#include "video_core/renderer_vulkan/renderer_vulkan.h"
24#include "video_core/renderer_vulkan/vk_blit_screen.h" 24#include "video_core/renderer_vulkan/vk_blit_screen.h"
25#include "video_core/renderer_vulkan/vk_master_semaphore.h" 25#include "video_core/renderer_vulkan/vk_master_semaphore.h"
26#include "video_core/renderer_vulkan/vk_memory_manager.h"
27#include "video_core/renderer_vulkan/vk_rasterizer.h" 26#include "video_core/renderer_vulkan/vk_rasterizer.h"
28#include "video_core/renderer_vulkan/vk_scheduler.h" 27#include "video_core/renderer_vulkan/vk_scheduler.h"
29#include "video_core/renderer_vulkan/vk_state_tracker.h" 28#include "video_core/renderer_vulkan/vk_state_tracker.h"
@@ -32,6 +31,7 @@
32#include "video_core/vulkan_common/vulkan_device.h" 31#include "video_core/vulkan_common/vulkan_device.h"
33#include "video_core/vulkan_common/vulkan_instance.h" 32#include "video_core/vulkan_common/vulkan_instance.h"
34#include "video_core/vulkan_common/vulkan_library.h" 33#include "video_core/vulkan_common/vulkan_library.h"
34#include "video_core/vulkan_common/vulkan_memory_allocator.h"
35#include "video_core/vulkan_common/vulkan_surface.h" 35#include "video_core/vulkan_common/vulkan_surface.h"
36#include "video_core/vulkan_common/vulkan_wrapper.h" 36#include "video_core/vulkan_common/vulkan_wrapper.h"
37 37
@@ -137,7 +137,7 @@ bool RendererVulkan::Init() try {
137 InitializeDevice(); 137 InitializeDevice();
138 Report(); 138 Report();
139 139
140 memory_manager = std::make_unique<VKMemoryManager>(*device); 140 memory_allocator = std::make_unique<MemoryAllocator>(*device);
141 141
142 state_tracker = std::make_unique<StateTracker>(gpu); 142 state_tracker = std::make_unique<StateTracker>(gpu);
143 143
@@ -149,11 +149,11 @@ bool RendererVulkan::Init() try {
149 149
150 rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(), 150 rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(),
151 cpu_memory, screen_info, *device, 151 cpu_memory, screen_info, *device,
152 *memory_manager, *state_tracker, *scheduler); 152 *memory_allocator, *state_tracker, *scheduler);
153 153
154 blit_screen = 154 blit_screen =
155 std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device, 155 std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device,
156 *memory_manager, *swapchain, *scheduler, screen_info); 156 *memory_allocator, *swapchain, *scheduler, screen_info);
157 return true; 157 return true;
158 158
159} catch (const vk::Exception& exception) { 159} catch (const vk::Exception& exception) {
@@ -172,7 +172,7 @@ void RendererVulkan::ShutDown() {
172 blit_screen.reset(); 172 blit_screen.reset();
173 scheduler.reset(); 173 scheduler.reset();
174 swapchain.reset(); 174 swapchain.reset();
175 memory_manager.reset(); 175 memory_allocator.reset();
176 device.reset(); 176 device.reset();
177} 177}
178 178
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 5575ffc54..daf55b9b4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -29,8 +29,8 @@ namespace Vulkan {
29 29
30class Device; 30class Device;
31class StateTracker; 31class StateTracker;
32class MemoryAllocator;
32class VKBlitScreen; 33class VKBlitScreen;
33class VKMemoryManager;
34class VKSwapchain; 34class VKSwapchain;
35class VKScheduler; 35class VKScheduler;
36 36
@@ -75,7 +75,7 @@ private:
75 75
76 vk::DebugUtilsMessenger debug_callback; 76 vk::DebugUtilsMessenger debug_callback;
77 std::unique_ptr<Device> device; 77 std::unique_ptr<Device> device;
78 std::unique_ptr<VKMemoryManager> memory_manager; 78 std::unique_ptr<MemoryAllocator> memory_allocator;
79 std::unique_ptr<StateTracker> state_tracker; 79 std::unique_ptr<StateTracker> state_tracker;
80 std::unique_ptr<VKScheduler> scheduler; 80 std::unique_ptr<VKScheduler> scheduler;
81 std::unique_ptr<VKSwapchain> swapchain; 81 std::unique_ptr<VKSwapchain> swapchain;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 5e184eb42..3e3b895e0 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -22,13 +22,13 @@
22#include "video_core/renderer_vulkan/renderer_vulkan.h" 22#include "video_core/renderer_vulkan/renderer_vulkan.h"
23#include "video_core/renderer_vulkan/vk_blit_screen.h" 23#include "video_core/renderer_vulkan/vk_blit_screen.h"
24#include "video_core/renderer_vulkan/vk_master_semaphore.h" 24#include "video_core/renderer_vulkan/vk_master_semaphore.h"
25#include "video_core/renderer_vulkan/vk_memory_manager.h"
26#include "video_core/renderer_vulkan/vk_scheduler.h" 25#include "video_core/renderer_vulkan/vk_scheduler.h"
27#include "video_core/renderer_vulkan/vk_shader_util.h" 26#include "video_core/renderer_vulkan/vk_shader_util.h"
28#include "video_core/renderer_vulkan/vk_swapchain.h" 27#include "video_core/renderer_vulkan/vk_swapchain.h"
29#include "video_core/surface.h" 28#include "video_core/surface.h"
30#include "video_core/textures/decoders.h" 29#include "video_core/textures/decoders.h"
31#include "video_core/vulkan_common/vulkan_device.h" 30#include "video_core/vulkan_common/vulkan_device.h"
31#include "video_core/vulkan_common/vulkan_memory_allocator.h"
32#include "video_core/vulkan_common/vulkan_wrapper.h" 32#include "video_core/vulkan_common/vulkan_wrapper.h"
33 33
34namespace Vulkan { 34namespace Vulkan {
@@ -115,10 +115,10 @@ struct VKBlitScreen::BufferData {
115VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_, 115VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_,
116 Core::Frontend::EmuWindow& render_window_, 116 Core::Frontend::EmuWindow& render_window_,
117 VideoCore::RasterizerInterface& rasterizer_, const Device& device_, 117 VideoCore::RasterizerInterface& rasterizer_, const Device& device_,
118 VKMemoryManager& memory_manager_, VKSwapchain& swapchain_, 118 MemoryAllocator& memory_allocator_, VKSwapchain& swapchain_,
119 VKScheduler& scheduler_, const VKScreenInfo& screen_info_) 119 VKScheduler& scheduler_, const VKScreenInfo& screen_info_)
120 : cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_}, 120 : cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_},
121 device{device_}, memory_manager{memory_manager_}, swapchain{swapchain_}, 121 device{device_}, memory_allocator{memory_allocator_}, swapchain{swapchain_},
122 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { 122 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
123 resource_ticks.resize(image_count); 123 resource_ticks.resize(image_count);
124 124
@@ -150,8 +150,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
150 SetUniformData(data, framebuffer); 150 SetUniformData(data, framebuffer);
151 SetVertexData(data, framebuffer); 151 SetVertexData(data, framebuffer);
152 152
153 auto map = buffer_commit->Map(); 153 const std::span<u8> map = buffer_commit.Map();
154 std::memcpy(map.Address(), &data, sizeof(data)); 154 std::memcpy(map.data(), &data, sizeof(data));
155 155
156 if (!use_accelerated) { 156 if (!use_accelerated) {
157 const u64 image_offset = GetRawImageOffset(framebuffer, image_index); 157 const u64 image_offset = GetRawImageOffset(framebuffer, image_index);
@@ -165,8 +165,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
165 constexpr u32 block_height_log2 = 4; 165 constexpr u32 block_height_log2 = 4;
166 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); 166 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
167 Tegra::Texture::UnswizzleTexture( 167 Tegra::Texture::UnswizzleTexture(
168 std::span(map.Address() + image_offset, size_bytes), std::span(host_ptr, size_bytes), 168 map.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes), bytes_per_pixel,
169 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); 169 framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
170 170
171 const VkBufferImageCopy copy{ 171 const VkBufferImageCopy copy{
172 .bufferOffset = image_offset, 172 .bufferOffset = image_offset,
@@ -224,8 +224,6 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
224 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier); 224 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier);
225 }); 225 });
226 } 226 }
227 map.Release();
228
229 scheduler.Record([renderpass = *renderpass, framebuffer = *framebuffers[image_index], 227 scheduler.Record([renderpass = *renderpass, framebuffer = *framebuffers[image_index],
230 descriptor_set = descriptor_sets[image_index], buffer = *buffer, 228 descriptor_set = descriptor_sets[image_index], buffer = *buffer,
231 size = swapchain.GetSize(), pipeline = *pipeline, 229 size = swapchain.GetSize(), pipeline = *pipeline,
@@ -642,7 +640,7 @@ void VKBlitScreen::ReleaseRawImages() {
642 raw_images.clear(); 640 raw_images.clear();
643 raw_buffer_commits.clear(); 641 raw_buffer_commits.clear();
644 buffer.reset(); 642 buffer.reset();
645 buffer_commit.reset(); 643 buffer_commit = MemoryCommit{};
646} 644}
647 645
648void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { 646void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
@@ -659,7 +657,7 @@ void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuff
659 }; 657 };
660 658
661 buffer = device.GetLogical().CreateBuffer(ci); 659 buffer = device.GetLogical().CreateBuffer(ci);
662 buffer_commit = memory_manager.Commit(buffer, true); 660 buffer_commit = memory_allocator.Commit(buffer, MemoryUsage::Upload);
663} 661}
664 662
665void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { 663void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
@@ -690,7 +688,7 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer)
690 .pQueueFamilyIndices = nullptr, 688 .pQueueFamilyIndices = nullptr,
691 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 689 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
692 }); 690 });
693 raw_buffer_commits[i] = memory_manager.Commit(raw_images[i], false); 691 raw_buffer_commits[i] = memory_allocator.Commit(raw_images[i], MemoryUsage::DeviceLocal);
694 raw_image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ 692 raw_image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
695 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 693 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
696 .pNext = nullptr, 694 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 69ed61770..b52576957 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -6,7 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8 8
9#include "video_core/renderer_vulkan/vk_memory_manager.h" 9#include "video_core/vulkan_common/vulkan_memory_allocator.h"
10#include "video_core/vulkan_common/vulkan_wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
11 11
12namespace Core { 12namespace Core {
@@ -43,7 +43,7 @@ public:
43 explicit VKBlitScreen(Core::Memory::Memory& cpu_memory, 43 explicit VKBlitScreen(Core::Memory::Memory& cpu_memory,
44 Core::Frontend::EmuWindow& render_window, 44 Core::Frontend::EmuWindow& render_window,
45 VideoCore::RasterizerInterface& rasterizer, const Device& device, 45 VideoCore::RasterizerInterface& rasterizer, const Device& device,
46 VKMemoryManager& memory_manager, VKSwapchain& swapchain, 46 MemoryAllocator& memory_allocator, VKSwapchain& swapchain,
47 VKScheduler& scheduler, const VKScreenInfo& screen_info); 47 VKScheduler& scheduler, const VKScreenInfo& screen_info);
48 ~VKBlitScreen(); 48 ~VKBlitScreen();
49 49
@@ -86,7 +86,7 @@ private:
86 Core::Frontend::EmuWindow& render_window; 86 Core::Frontend::EmuWindow& render_window;
87 VideoCore::RasterizerInterface& rasterizer; 87 VideoCore::RasterizerInterface& rasterizer;
88 const Device& device; 88 const Device& device;
89 VKMemoryManager& memory_manager; 89 MemoryAllocator& memory_allocator;
90 VKSwapchain& swapchain; 90 VKSwapchain& swapchain;
91 VKScheduler& scheduler; 91 VKScheduler& scheduler;
92 const std::size_t image_count; 92 const std::size_t image_count;
@@ -104,14 +104,14 @@ private:
104 vk::Sampler sampler; 104 vk::Sampler sampler;
105 105
106 vk::Buffer buffer; 106 vk::Buffer buffer;
107 VKMemoryCommit buffer_commit; 107 MemoryCommit buffer_commit;
108 108
109 std::vector<u64> resource_ticks; 109 std::vector<u64> resource_ticks;
110 110
111 std::vector<vk::Semaphore> semaphores; 111 std::vector<vk::Semaphore> semaphores;
112 std::vector<vk::Image> raw_images; 112 std::vector<vk::Image> raw_images;
113 std::vector<vk::ImageView> raw_image_views; 113 std::vector<vk::ImageView> raw_image_views;
114 std::vector<VKMemoryCommit> raw_buffer_commits; 114 std::vector<MemoryCommit> raw_buffer_commits;
115 u32 raw_width = 0; 115 u32 raw_width = 0;
116 u32 raw_height = 0; 116 u32 raw_height = 0;
117}; 117};
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 58c710344..d8ad40a0f 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -36,11 +36,11 @@ constexpr VkAccessFlags TRANSFORM_FEEDBACK_WRITE_ACCESS =
36 36
37} // Anonymous namespace 37} // Anonymous namespace
38 38
39Buffer::Buffer(const Device& device_, VKMemoryManager& memory_manager, VKScheduler& scheduler_, 39Buffer::Buffer(const Device& device_, MemoryAllocator& memory_allocator, VKScheduler& scheduler_,
40 VKStagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_) 40 StagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_)
41 : BufferBlock{cpu_addr_, size_}, device{device_}, scheduler{scheduler_}, staging_pool{ 41 : BufferBlock{cpu_addr_, size_}, device{device_}, scheduler{scheduler_}, staging_pool{
42 staging_pool_} { 42 staging_pool_} {
43 const VkBufferCreateInfo ci{ 43 buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
44 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 44 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
45 .pNext = nullptr, 45 .pNext = nullptr,
46 .flags = 0, 46 .flags = 0,
@@ -49,22 +49,20 @@ Buffer::Buffer(const Device& device_, VKMemoryManager& memory_manager, VKSchedul
49 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 49 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
50 .queueFamilyIndexCount = 0, 50 .queueFamilyIndexCount = 0,
51 .pQueueFamilyIndices = nullptr, 51 .pQueueFamilyIndices = nullptr,
52 }; 52 });
53 53 commit = memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
54 buffer.handle = device.GetLogical().CreateBuffer(ci);
55 buffer.commit = memory_manager.Commit(buffer.handle, false);
56} 54}
57 55
58Buffer::~Buffer() = default; 56Buffer::~Buffer() = default;
59 57
60void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) { 58void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) {
61 const auto& staging = staging_pool.GetUnusedBuffer(data_size, true); 59 const auto& staging = staging_pool.Request(data_size, MemoryUsage::Upload);
62 std::memcpy(staging.commit->Map(data_size), data, data_size); 60 std::memcpy(staging.mapped_span.data(), data, data_size);
63 61
64 scheduler.RequestOutsideRenderPassOperationContext(); 62 scheduler.RequestOutsideRenderPassOperationContext();
65 63
66 const VkBuffer handle = Handle(); 64 const VkBuffer handle = Handle();
67 scheduler.Record([staging = *staging.handle, handle, offset, data_size, 65 scheduler.Record([staging = staging.buffer, handle, offset, data_size,
68 &device = device](vk::CommandBuffer cmdbuf) { 66 &device = device](vk::CommandBuffer cmdbuf) {
69 const VkBufferMemoryBarrier read_barrier{ 67 const VkBufferMemoryBarrier read_barrier{
70 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 68 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
@@ -100,12 +98,12 @@ void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) {
100} 98}
101 99
102void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) { 100void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) {
103 const auto& staging = staging_pool.GetUnusedBuffer(data_size, true); 101 auto staging = staging_pool.Request(data_size, MemoryUsage::Download);
104 scheduler.RequestOutsideRenderPassOperationContext(); 102 scheduler.RequestOutsideRenderPassOperationContext();
105 103
106 const VkBuffer handle = Handle(); 104 const VkBuffer handle = Handle();
107 scheduler.Record( 105 scheduler.Record(
108 [staging = *staging.handle, handle, offset, data_size](vk::CommandBuffer cmdbuf) { 106 [staging = staging.buffer, handle, offset, data_size](vk::CommandBuffer cmdbuf) {
109 const VkBufferMemoryBarrier barrier{ 107 const VkBufferMemoryBarrier barrier{
110 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 108 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
111 .pNext = nullptr, 109 .pNext = nullptr,
@@ -126,7 +124,7 @@ void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) {
126 }); 124 });
127 scheduler.Finish(); 125 scheduler.Finish();
128 126
129 std::memcpy(data, staging.commit->Map(data_size), data_size); 127 std::memcpy(data, staging.mapped_span.data(), data_size);
130} 128}
131 129
132void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, 130void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
@@ -164,29 +162,29 @@ void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst
164 162
165VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer_, 163VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer_,
166 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, 164 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
167 const Device& device_, VKMemoryManager& memory_manager_, 165 const Device& device_, MemoryAllocator& memory_allocator_,
168 VKScheduler& scheduler_, VKStreamBuffer& stream_buffer_, 166 VKScheduler& scheduler_, VKStreamBuffer& stream_buffer_,
169 VKStagingBufferPool& staging_pool_) 167 StagingBufferPool& staging_pool_)
170 : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer_, gpu_memory_, 168 : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer_, gpu_memory_,
171 cpu_memory_, stream_buffer_}, 169 cpu_memory_, stream_buffer_},
172 device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{ 170 device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
173 staging_pool_} {} 171 staging_pool{staging_pool_} {}
174 172
175VKBufferCache::~VKBufferCache() = default; 173VKBufferCache::~VKBufferCache() = default;
176 174
177std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { 175std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) {
178 return std::make_shared<Buffer>(device, memory_manager, scheduler, staging_pool, cpu_addr, 176 return std::make_shared<Buffer>(device, memory_allocator, scheduler, staging_pool, cpu_addr,
179 size); 177 size);
180} 178}
181 179
182VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { 180VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) {
183 size = std::max(size, std::size_t(4)); 181 size = std::max(size, std::size_t(4));
184 const auto& empty = staging_pool.GetUnusedBuffer(size, false); 182 const auto& empty = staging_pool.Request(size, MemoryUsage::DeviceLocal);
185 scheduler.RequestOutsideRenderPassOperationContext(); 183 scheduler.RequestOutsideRenderPassOperationContext();
186 scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { 184 scheduler.Record([size, buffer = empty.buffer](vk::CommandBuffer cmdbuf) {
187 cmdbuf.FillBuffer(buffer, 0, size, 0); 185 cmdbuf.FillBuffer(buffer, 0, size, 0);
188 }); 186 });
189 return {*empty.handle, 0, 0}; 187 return {empty.buffer, 0, 0};
190} 188}
191 189
192} // namespace Vulkan 190} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 1c39aed34..41d577510 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -8,21 +8,20 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/buffer_cache/buffer_cache.h" 10#include "video_core/buffer_cache/buffer_cache.h"
11#include "video_core/renderer_vulkan/vk_memory_manager.h"
12#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 11#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
13#include "video_core/renderer_vulkan/vk_stream_buffer.h" 12#include "video_core/renderer_vulkan/vk_stream_buffer.h"
13#include "video_core/vulkan_common/vulkan_memory_allocator.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h" 14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
16namespace Vulkan { 16namespace Vulkan {
17 17
18class Device; 18class Device;
19class VKMemoryManager;
20class VKScheduler; 19class VKScheduler;
21 20
22class Buffer final : public VideoCommon::BufferBlock { 21class Buffer final : public VideoCommon::BufferBlock {
23public: 22public:
24 explicit Buffer(const Device& device, VKMemoryManager& memory_manager, VKScheduler& scheduler, 23 explicit Buffer(const Device& device, MemoryAllocator& memory_allocator, VKScheduler& scheduler,
25 VKStagingBufferPool& staging_pool, VAddr cpu_addr_, std::size_t size_); 24 StagingBufferPool& staging_pool, VAddr cpu_addr_, std::size_t size_);
26 ~Buffer(); 25 ~Buffer();
27 26
28 void Upload(std::size_t offset, std::size_t data_size, const u8* data); 27 void Upload(std::size_t offset, std::size_t data_size, const u8* data);
@@ -33,7 +32,7 @@ public:
33 std::size_t copy_size); 32 std::size_t copy_size);
34 33
35 VkBuffer Handle() const { 34 VkBuffer Handle() const {
36 return *buffer.handle; 35 return *buffer;
37 } 36 }
38 37
39 u64 Address() const { 38 u64 Address() const {
@@ -43,18 +42,19 @@ public:
43private: 42private:
44 const Device& device; 43 const Device& device;
45 VKScheduler& scheduler; 44 VKScheduler& scheduler;
46 VKStagingBufferPool& staging_pool; 45 StagingBufferPool& staging_pool;
47 46
48 VKBuffer buffer; 47 vk::Buffer buffer;
48 MemoryCommit commit;
49}; 49};
50 50
51class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> { 51class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> {
52public: 52public:
53 explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, 53 explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer,
54 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory, 54 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
55 const Device& device, VKMemoryManager& memory_manager, 55 const Device& device, MemoryAllocator& memory_allocator,
56 VKScheduler& scheduler, VKStreamBuffer& stream_buffer, 56 VKScheduler& scheduler, VKStreamBuffer& stream_buffer,
57 VKStagingBufferPool& staging_pool); 57 StagingBufferPool& staging_pool);
58 ~VKBufferCache(); 58 ~VKBufferCache();
59 59
60 BufferInfo GetEmptyBuffer(std::size_t size) override; 60 BufferInfo GetEmptyBuffer(std::size_t size) override;
@@ -64,9 +64,9 @@ protected:
64 64
65private: 65private:
66 const Device& device; 66 const Device& device;
67 VKMemoryManager& memory_manager; 67 MemoryAllocator& memory_allocator;
68 VKScheduler& scheduler; 68 VKScheduler& scheduler;
69 VKStagingBufferPool& staging_pool; 69 StagingBufferPool& staging_pool;
70}; 70};
71 71
72} // namespace Vulkan 72} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 02a6d54b7..5eb6a54be 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -164,7 +164,7 @@ VkDescriptorSet VKComputePass::CommitDescriptorSet(
164 164
165QuadArrayPass::QuadArrayPass(const Device& device_, VKScheduler& scheduler_, 165QuadArrayPass::QuadArrayPass(const Device& device_, VKScheduler& scheduler_,
166 VKDescriptorPool& descriptor_pool_, 166 VKDescriptorPool& descriptor_pool_,
167 VKStagingBufferPool& staging_buffer_pool_, 167 StagingBufferPool& staging_buffer_pool_,
168 VKUpdateDescriptorQueue& update_descriptor_queue_) 168 VKUpdateDescriptorQueue& update_descriptor_queue_)
169 : VKComputePass(device_, descriptor_pool_, BuildQuadArrayPassDescriptorSetLayoutBinding(), 169 : VKComputePass(device_, descriptor_pool_, BuildQuadArrayPassDescriptorSetLayoutBinding(),
170 BuildQuadArrayPassDescriptorUpdateTemplateEntry(), 170 BuildQuadArrayPassDescriptorUpdateTemplateEntry(),
@@ -177,18 +177,18 @@ QuadArrayPass::~QuadArrayPass() = default;
177std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) { 177std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) {
178 const u32 num_triangle_vertices = (num_vertices / 4) * 6; 178 const u32 num_triangle_vertices = (num_vertices / 4) * 6;
179 const std::size_t staging_size = num_triangle_vertices * sizeof(u32); 179 const std::size_t staging_size = num_triangle_vertices * sizeof(u32);
180 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); 180 const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
181 181
182 update_descriptor_queue.Acquire(); 182 update_descriptor_queue.Acquire();
183 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); 183 update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size);
184 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); 184 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
185 185
186 scheduler.RequestOutsideRenderPassOperationContext(); 186 scheduler.RequestOutsideRenderPassOperationContext();
187 187
188 ASSERT(num_vertices % 4 == 0); 188 ASSERT(num_vertices % 4 == 0);
189 const u32 num_quads = num_vertices / 4; 189 const u32 num_quads = num_vertices / 4;
190 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, num_quads, 190 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer,
191 first, set](vk::CommandBuffer cmdbuf) { 191 num_quads, first, set](vk::CommandBuffer cmdbuf) {
192 constexpr u32 dispatch_size = 1024; 192 constexpr u32 dispatch_size = 1024;
193 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); 193 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
194 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, set, {}); 194 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, set, {});
@@ -208,11 +208,11 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32
208 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 208 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
209 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, {barrier}, {}); 209 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, {barrier}, {});
210 }); 210 });
211 return {*buffer.handle, 0}; 211 return {staging_ref.buffer, 0};
212} 212}
213 213
214Uint8Pass::Uint8Pass(const Device& device, VKScheduler& scheduler_, 214Uint8Pass::Uint8Pass(const Device& device, VKScheduler& scheduler_,
215 VKDescriptorPool& descriptor_pool, VKStagingBufferPool& staging_buffer_pool_, 215 VKDescriptorPool& descriptor_pool, StagingBufferPool& staging_buffer_pool_,
216 VKUpdateDescriptorQueue& update_descriptor_queue_) 216 VKUpdateDescriptorQueue& update_descriptor_queue_)
217 : VKComputePass(device, descriptor_pool, BuildInputOutputDescriptorSetBindings(), 217 : VKComputePass(device, descriptor_pool, BuildInputOutputDescriptorSetBindings(),
218 BuildInputOutputDescriptorUpdateTemplate(), {}, VULKAN_UINT8_COMP_SPV), 218 BuildInputOutputDescriptorUpdateTemplate(), {}, VULKAN_UINT8_COMP_SPV),
@@ -224,15 +224,15 @@ Uint8Pass::~Uint8Pass() = default;
224std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer, 224std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
225 u64 src_offset) { 225 u64 src_offset) {
226 const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16)); 226 const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
227 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); 227 const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
228 228
229 update_descriptor_queue.Acquire(); 229 update_descriptor_queue.Acquire();
230 update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices); 230 update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
231 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); 231 update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size);
232 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); 232 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
233 233
234 scheduler.RequestOutsideRenderPassOperationContext(); 234 scheduler.RequestOutsideRenderPassOperationContext();
235 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, 235 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, set,
236 num_vertices](vk::CommandBuffer cmdbuf) { 236 num_vertices](vk::CommandBuffer cmdbuf) {
237 constexpr u32 dispatch_size = 1024; 237 constexpr u32 dispatch_size = 1024;
238 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); 238 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
@@ -252,12 +252,12 @@ std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buff
252 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 252 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
253 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {}); 253 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {});
254 }); 254 });
255 return {*buffer.handle, 0}; 255 return {staging_ref.buffer, 0};
256} 256}
257 257
258QuadIndexedPass::QuadIndexedPass(const Device& device_, VKScheduler& scheduler_, 258QuadIndexedPass::QuadIndexedPass(const Device& device_, VKScheduler& scheduler_,
259 VKDescriptorPool& descriptor_pool_, 259 VKDescriptorPool& descriptor_pool_,
260 VKStagingBufferPool& staging_buffer_pool_, 260 StagingBufferPool& staging_buffer_pool_,
261 VKUpdateDescriptorQueue& update_descriptor_queue_) 261 VKUpdateDescriptorQueue& update_descriptor_queue_)
262 : VKComputePass(device_, descriptor_pool_, BuildInputOutputDescriptorSetBindings(), 262 : VKComputePass(device_, descriptor_pool_, BuildInputOutputDescriptorSetBindings(),
263 BuildInputOutputDescriptorUpdateTemplate(), 263 BuildInputOutputDescriptorUpdateTemplate(),
@@ -286,15 +286,15 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble(
286 const u32 num_tri_vertices = (num_vertices / 4) * 6; 286 const u32 num_tri_vertices = (num_vertices / 4) * 6;
287 287
288 const std::size_t staging_size = num_tri_vertices * sizeof(u32); 288 const std::size_t staging_size = num_tri_vertices * sizeof(u32);
289 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); 289 const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
290 290
291 update_descriptor_queue.Acquire(); 291 update_descriptor_queue.Acquire();
292 update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size); 292 update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
293 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); 293 update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size);
294 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); 294 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
295 295
296 scheduler.RequestOutsideRenderPassOperationContext(); 296 scheduler.RequestOutsideRenderPassOperationContext();
297 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, 297 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, set,
298 num_tri_vertices, base_vertex, index_shift](vk::CommandBuffer cmdbuf) { 298 num_tri_vertices, base_vertex, index_shift](vk::CommandBuffer cmdbuf) {
299 static constexpr u32 dispatch_size = 1024; 299 static constexpr u32 dispatch_size = 1024;
300 const std::array push_constants = {base_vertex, index_shift}; 300 const std::array push_constants = {base_vertex, index_shift};
@@ -317,7 +317,7 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble(
317 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 317 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
318 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {}); 318 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {});
319 }); 319 });
320 return {*buffer.handle, 0}; 320 return {staging_ref.buffer, 0};
321} 321}
322 322
323} // namespace Vulkan 323} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index 7ddb09afb..f5c6f5f17 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -16,8 +16,8 @@
16namespace Vulkan { 16namespace Vulkan {
17 17
18class Device; 18class Device;
19class StagingBufferPool;
19class VKScheduler; 20class VKScheduler;
20class VKStagingBufferPool;
21class VKUpdateDescriptorQueue; 21class VKUpdateDescriptorQueue;
22 22
23class VKComputePass { 23class VKComputePass {
@@ -45,7 +45,7 @@ class QuadArrayPass final : public VKComputePass {
45public: 45public:
46 explicit QuadArrayPass(const Device& device_, VKScheduler& scheduler_, 46 explicit QuadArrayPass(const Device& device_, VKScheduler& scheduler_,
47 VKDescriptorPool& descriptor_pool_, 47 VKDescriptorPool& descriptor_pool_,
48 VKStagingBufferPool& staging_buffer_pool_, 48 StagingBufferPool& staging_buffer_pool_,
49 VKUpdateDescriptorQueue& update_descriptor_queue_); 49 VKUpdateDescriptorQueue& update_descriptor_queue_);
50 ~QuadArrayPass(); 50 ~QuadArrayPass();
51 51
@@ -53,15 +53,14 @@ public:
53 53
54private: 54private:
55 VKScheduler& scheduler; 55 VKScheduler& scheduler;
56 VKStagingBufferPool& staging_buffer_pool; 56 StagingBufferPool& staging_buffer_pool;
57 VKUpdateDescriptorQueue& update_descriptor_queue; 57 VKUpdateDescriptorQueue& update_descriptor_queue;
58}; 58};
59 59
60class Uint8Pass final : public VKComputePass { 60class Uint8Pass final : public VKComputePass {
61public: 61public:
62 explicit Uint8Pass(const Device& device_, VKScheduler& scheduler_, 62 explicit Uint8Pass(const Device& device_, VKScheduler& scheduler_,
63 VKDescriptorPool& descriptor_pool_, 63 VKDescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_,
64 VKStagingBufferPool& staging_buffer_pool_,
65 VKUpdateDescriptorQueue& update_descriptor_queue_); 64 VKUpdateDescriptorQueue& update_descriptor_queue_);
66 ~Uint8Pass(); 65 ~Uint8Pass();
67 66
@@ -69,7 +68,7 @@ public:
69 68
70private: 69private:
71 VKScheduler& scheduler; 70 VKScheduler& scheduler;
72 VKStagingBufferPool& staging_buffer_pool; 71 StagingBufferPool& staging_buffer_pool;
73 VKUpdateDescriptorQueue& update_descriptor_queue; 72 VKUpdateDescriptorQueue& update_descriptor_queue;
74}; 73};
75 74
@@ -77,7 +76,7 @@ class QuadIndexedPass final : public VKComputePass {
77public: 76public:
78 explicit QuadIndexedPass(const Device& device_, VKScheduler& scheduler_, 77 explicit QuadIndexedPass(const Device& device_, VKScheduler& scheduler_,
79 VKDescriptorPool& descriptor_pool_, 78 VKDescriptorPool& descriptor_pool_,
80 VKStagingBufferPool& staging_buffer_pool_, 79 StagingBufferPool& staging_buffer_pool_,
81 VKUpdateDescriptorQueue& update_descriptor_queue_); 80 VKUpdateDescriptorQueue& update_descriptor_queue_);
82 ~QuadIndexedPass(); 81 ~QuadIndexedPass();
83 82
@@ -87,7 +86,7 @@ public:
87 86
88private: 87private:
89 VKScheduler& scheduler; 88 VKScheduler& scheduler;
90 VKStagingBufferPool& staging_buffer_pool; 89 StagingBufferPool& staging_buffer_pool;
91 VKUpdateDescriptorQueue& update_descriptor_queue; 90 VKUpdateDescriptorQueue& update_descriptor_queue;
92}; 91};
93 92
diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.cpp b/src/video_core/renderer_vulkan/vk_memory_manager.cpp
deleted file mode 100644
index a6abd0eee..000000000
--- a/src/video_core/renderer_vulkan/vk_memory_manager.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <optional>
7#include <tuple>
8#include <vector>
9
10#include "common/alignment.h"
11#include "common/assert.h"
12#include "common/common_types.h"
13#include "common/logging/log.h"
14#include "video_core/renderer_vulkan/vk_memory_manager.h"
15#include "video_core/vulkan_common/vulkan_device.h"
16#include "video_core/vulkan_common/vulkan_wrapper.h"
17
18namespace Vulkan {
19
20namespace {
21
22u64 GetAllocationChunkSize(u64 required_size) {
23 static constexpr u64 sizes[] = {16ULL << 20, 32ULL << 20, 64ULL << 20, 128ULL << 20};
24 auto it = std::lower_bound(std::begin(sizes), std::end(sizes), required_size);
25 return it != std::end(sizes) ? *it : Common::AlignUp(required_size, 256ULL << 20);
26}
27
28} // Anonymous namespace
29
30class VKMemoryAllocation final {
31public:
32 explicit VKMemoryAllocation(const Device& device_, vk::DeviceMemory memory_,
33 VkMemoryPropertyFlags properties_, u64 allocation_size_, u32 type_)
34 : device{device_}, memory{std::move(memory_)}, properties{properties_},
35 allocation_size{allocation_size_}, shifted_type{ShiftType(type_)} {}
36
37 VKMemoryCommit Commit(VkDeviceSize commit_size, VkDeviceSize alignment) {
38 auto found = TryFindFreeSection(free_iterator, allocation_size,
39 static_cast<u64>(commit_size), static_cast<u64>(alignment));
40 if (!found) {
41 found = TryFindFreeSection(0, free_iterator, static_cast<u64>(commit_size),
42 static_cast<u64>(alignment));
43 if (!found) {
44 // Signal out of memory, it'll try to do more allocations.
45 return nullptr;
46 }
47 }
48 auto commit = std::make_unique<VKMemoryCommitImpl>(device, this, memory, *found,
49 *found + commit_size);
50 commits.push_back(commit.get());
51
52 // Last commit's address is highly probable to be free.
53 free_iterator = *found + commit_size;
54
55 return commit;
56 }
57
58 void Free(const VKMemoryCommitImpl* commit) {
59 ASSERT(commit);
60
61 const auto it = std::find(std::begin(commits), std::end(commits), commit);
62 if (it == commits.end()) {
63 UNREACHABLE_MSG("Freeing unallocated commit!");
64 return;
65 }
66 commits.erase(it);
67 }
68
69 /// Returns whether this allocation is compatible with the arguments.
70 bool IsCompatible(VkMemoryPropertyFlags wanted_properties, u32 type_mask) const {
71 return (wanted_properties & properties) && (type_mask & shifted_type) != 0;
72 }
73
74private:
75 static constexpr u32 ShiftType(u32 type) {
76 return 1U << type;
77 }
78
79 /// A memory allocator, it may return a free region between "start" and "end" with the solicited
80 /// requirements.
81 std::optional<u64> TryFindFreeSection(u64 start, u64 end, u64 size, u64 alignment) const {
82 u64 iterator = Common::AlignUp(start, alignment);
83 while (iterator + size <= end) {
84 const u64 try_left = iterator;
85 const u64 try_right = try_left + size;
86
87 bool overlap = false;
88 for (const auto& commit : commits) {
89 const auto [commit_left, commit_right] = commit->interval;
90 if (try_left < commit_right && commit_left < try_right) {
91 // There's an overlap, continue the search where the overlapping commit ends.
92 iterator = Common::AlignUp(commit_right, alignment);
93 overlap = true;
94 break;
95 }
96 }
97 if (!overlap) {
98 // A free address has been found.
99 return try_left;
100 }
101 }
102
103 // No free regions where found, return an empty optional.
104 return std::nullopt;
105 }
106
107 const Device& device; ///< Vulkan device.
108 const vk::DeviceMemory memory; ///< Vulkan memory allocation handler.
109 const VkMemoryPropertyFlags properties; ///< Vulkan properties.
110 const u64 allocation_size; ///< Size of this allocation.
111 const u32 shifted_type; ///< Stored Vulkan type of this allocation, shifted.
112
113 /// Hints where the next free region is likely going to be.
114 u64 free_iterator{};
115
116 /// Stores all commits done from this allocation.
117 std::vector<const VKMemoryCommitImpl*> commits;
118};
119
120VKMemoryManager::VKMemoryManager(const Device& device_)
121 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {}
122
123VKMemoryManager::~VKMemoryManager() = default;
124
125VKMemoryCommit VKMemoryManager::Commit(const VkMemoryRequirements& requirements,
126 bool host_visible) {
127 const u64 chunk_size = GetAllocationChunkSize(requirements.size);
128
129 // When a host visible commit is asked, search for host visible and coherent, otherwise search
130 // for a fast device local type.
131 const VkMemoryPropertyFlags wanted_properties =
132 host_visible ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
133 : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
134
135 if (auto commit = TryAllocCommit(requirements, wanted_properties)) {
136 return commit;
137 }
138
139 // Commit has failed, allocate more memory.
140 if (!AllocMemory(wanted_properties, requirements.memoryTypeBits, chunk_size)) {
141 // TODO(Rodrigo): Handle these situations in some way like flushing to guest memory.
142 // Allocation has failed, panic.
143 UNREACHABLE_MSG("Ran out of VRAM!");
144 return {};
145 }
146
147 // Commit again, this time it won't fail since there's a fresh allocation above. If it does,
148 // there's a bug.
149 auto commit = TryAllocCommit(requirements, wanted_properties);
150 ASSERT(commit);
151 return commit;
152}
153
154VKMemoryCommit VKMemoryManager::Commit(const vk::Buffer& buffer, bool host_visible) {
155 auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), host_visible);
156 buffer.BindMemory(commit->GetMemory(), commit->GetOffset());
157 return commit;
158}
159
160VKMemoryCommit VKMemoryManager::Commit(const vk::Image& image, bool host_visible) {
161 auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), host_visible);
162 image.BindMemory(commit->GetMemory(), commit->GetOffset());
163 return commit;
164}
165
166bool VKMemoryManager::AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 type_mask,
167 u64 size) {
168 const u32 type = [&] {
169 for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) {
170 const auto flags = properties.memoryTypes[type_index].propertyFlags;
171 if ((type_mask & (1U << type_index)) && (flags & wanted_properties)) {
172 // The type matches in type and in the wanted properties.
173 return type_index;
174 }
175 }
176 UNREACHABLE_MSG("Couldn't find a compatible memory type!");
177 return 0U;
178 }();
179
180 // Try to allocate found type.
181 vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({
182 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
183 .pNext = nullptr,
184 .allocationSize = size,
185 .memoryTypeIndex = type,
186 });
187 if (!memory) {
188 LOG_CRITICAL(Render_Vulkan, "Device allocation failed!");
189 return false;
190 }
191
192 allocations.push_back(std::make_unique<VKMemoryAllocation>(device, std::move(memory),
193 wanted_properties, size, type));
194 return true;
195}
196
197VKMemoryCommit VKMemoryManager::TryAllocCommit(const VkMemoryRequirements& requirements,
198 VkMemoryPropertyFlags wanted_properties) {
199 for (auto& allocation : allocations) {
200 if (!allocation->IsCompatible(wanted_properties, requirements.memoryTypeBits)) {
201 continue;
202 }
203 if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) {
204 return commit;
205 }
206 }
207 return {};
208}
209
210VKMemoryCommitImpl::VKMemoryCommitImpl(const Device& device_, VKMemoryAllocation* allocation_,
211 const vk::DeviceMemory& memory_, u64 begin_, u64 end_)
212 : device{device_}, memory{memory_}, interval{begin_, end_}, allocation{allocation_} {}
213
214VKMemoryCommitImpl::~VKMemoryCommitImpl() {
215 allocation->Free(this);
216}
217
218MemoryMap VKMemoryCommitImpl::Map(u64 size, u64 offset_) const {
219 return MemoryMap(this, std::span<u8>(memory.Map(interval.first + offset_, size), size));
220}
221
222void VKMemoryCommitImpl::Unmap() const {
223 memory.Unmap();
224}
225
226MemoryMap VKMemoryCommitImpl::Map() const {
227 return Map(interval.second - interval.first);
228}
229
230} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.h b/src/video_core/renderer_vulkan/vk_memory_manager.h
deleted file mode 100644
index 2452bca4e..000000000
--- a/src/video_core/renderer_vulkan/vk_memory_manager.h
+++ /dev/null
@@ -1,132 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <span>
9#include <utility>
10#include <vector>
11#include "common/common_types.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13
14namespace Vulkan {
15
16class Device;
17class MemoryMap;
18class VKMemoryAllocation;
19class VKMemoryCommitImpl;
20
21using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>;
22
23class VKMemoryManager final {
24public:
25 explicit VKMemoryManager(const Device& device_);
26 VKMemoryManager(const VKMemoryManager&) = delete;
27 ~VKMemoryManager();
28
29 /**
30 * Commits a memory with the specified requeriments.
31 * @param requirements Requirements returned from a Vulkan call.
32 * @param host_visible Signals the allocator that it *must* use host visible and coherent
33 * memory. When passing false, it will try to allocate device local memory.
34 * @returns A memory commit.
35 */
36 VKMemoryCommit Commit(const VkMemoryRequirements& requirements, bool host_visible);
37
38 /// Commits memory required by the buffer and binds it.
39 VKMemoryCommit Commit(const vk::Buffer& buffer, bool host_visible);
40
41 /// Commits memory required by the image and binds it.
42 VKMemoryCommit Commit(const vk::Image& image, bool host_visible);
43
44private:
45 /// Allocates a chunk of memory.
46 bool AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 type_mask, u64 size);
47
48 /// Tries to allocate a memory commit.
49 VKMemoryCommit TryAllocCommit(const VkMemoryRequirements& requirements,
50 VkMemoryPropertyFlags wanted_properties);
51
52 const Device& device; ///< Device handler.
53 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
54 std::vector<std::unique_ptr<VKMemoryAllocation>> allocations; ///< Current allocations.
55};
56
57class VKMemoryCommitImpl final {
58 friend VKMemoryAllocation;
59 friend MemoryMap;
60
61public:
62 explicit VKMemoryCommitImpl(const Device& device_, VKMemoryAllocation* allocation_,
63 const vk::DeviceMemory& memory_, u64 begin_, u64 end_);
64 ~VKMemoryCommitImpl();
65
66 /// Maps a memory region and returns a pointer to it.
67 /// It's illegal to have more than one memory map at the same time.
68 MemoryMap Map(u64 size, u64 offset = 0) const;
69
70 /// Maps the whole commit and returns a pointer to it.
71 /// It's illegal to have more than one memory map at the same time.
72 MemoryMap Map() const;
73
74 /// Returns the Vulkan memory handler.
75 VkDeviceMemory GetMemory() const {
76 return *memory;
77 }
78
79 /// Returns the start position of the commit relative to the allocation.
80 VkDeviceSize GetOffset() const {
81 return static_cast<VkDeviceSize>(interval.first);
82 }
83
84private:
85 /// Unmaps memory.
86 void Unmap() const;
87
88 const Device& device; ///< Vulkan device.
89 const vk::DeviceMemory& memory; ///< Vulkan device memory handler.
90 std::pair<u64, u64> interval{}; ///< Interval where the commit exists.
91 VKMemoryAllocation* allocation{}; ///< Pointer to the large memory allocation.
92};
93
94/// Holds ownership of a memory map.
95class MemoryMap final {
96public:
97 explicit MemoryMap(const VKMemoryCommitImpl* commit_, std::span<u8> span_)
98 : commit{commit_}, span{span_} {}
99
100 ~MemoryMap() {
101 if (commit) {
102 commit->Unmap();
103 }
104 }
105
106 /// Prematurely releases the memory map.
107 void Release() {
108 commit->Unmap();
109 commit = nullptr;
110 }
111
112 /// Returns a span to the memory map.
113 [[nodiscard]] std::span<u8> Span() const noexcept {
114 return span;
115 }
116
117 /// Returns the address of the memory map.
118 [[nodiscard]] u8* Address() const noexcept {
119 return span.data();
120 }
121
122 /// Returns the address of the memory map;
123 [[nodiscard]] operator u8*() const noexcept {
124 return span.data();
125 }
126
127private:
128 const VKMemoryCommitImpl* commit{}; ///< Mapped memory commit.
129 std::span<u8> span; ///< Address to the mapped memory.
130};
131
132} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index ce3db49bd..f0a111829 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -409,24 +409,24 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const {
409RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 409RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
410 Tegra::MemoryManager& gpu_memory_, 410 Tegra::MemoryManager& gpu_memory_,
411 Core::Memory::Memory& cpu_memory_, VKScreenInfo& screen_info_, 411 Core::Memory::Memory& cpu_memory_, VKScreenInfo& screen_info_,
412 const Device& device_, VKMemoryManager& memory_manager_, 412 const Device& device_, MemoryAllocator& memory_allocator_,
413 StateTracker& state_tracker_, VKScheduler& scheduler_) 413 StateTracker& state_tracker_, VKScheduler& scheduler_)
414 : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, 414 : RasterizerAccelerated{cpu_memory_}, gpu{gpu_},
415 gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()}, 415 gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()},
416 screen_info{screen_info_}, device{device_}, memory_manager{memory_manager_}, 416 screen_info{screen_info_}, device{device_}, memory_allocator{memory_allocator_},
417 state_tracker{state_tracker_}, scheduler{scheduler_}, stream_buffer(device, scheduler), 417 state_tracker{state_tracker_}, scheduler{scheduler_}, stream_buffer(device, scheduler),
418 staging_pool(device, memory_manager, scheduler), descriptor_pool(device, scheduler), 418 staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler),
419 update_descriptor_queue(device, scheduler), 419 update_descriptor_queue(device, scheduler),
420 blit_image(device, scheduler, state_tracker, descriptor_pool), 420 blit_image(device, scheduler, state_tracker, descriptor_pool),
421 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 421 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
422 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 422 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
423 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 423 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
424 texture_cache_runtime{device, scheduler, memory_manager, staging_pool, blit_image}, 424 texture_cache_runtime{device, scheduler, memory_allocator, staging_pool, blit_image},
425 texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), 425 texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory),
426 pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, 426 pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler,
427 descriptor_pool, update_descriptor_queue), 427 descriptor_pool, update_descriptor_queue),
428 buffer_cache(*this, gpu_memory, cpu_memory_, device, memory_manager, scheduler, stream_buffer, 428 buffer_cache(*this, gpu_memory, cpu_memory_, device, memory_allocator, scheduler,
429 staging_pool), 429 stream_buffer, staging_pool),
430 query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, 430 query_cache{*this, maxwell3d, gpu_memory, device, scheduler},
431 fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, scheduler), 431 fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, scheduler),
432 wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) { 432 wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) {
@@ -1445,7 +1445,7 @@ VkBuffer RasterizerVulkan::DefaultBuffer() {
1445 .queueFamilyIndexCount = 0, 1445 .queueFamilyIndexCount = 0,
1446 .pQueueFamilyIndices = nullptr, 1446 .pQueueFamilyIndices = nullptr,
1447 }); 1447 });
1448 default_buffer_commit = memory_manager.Commit(default_buffer, false); 1448 default_buffer_commit = memory_allocator.Commit(default_buffer, MemoryUsage::DeviceLocal);
1449 1449
1450 scheduler.RequestOutsideRenderPassOperationContext(); 1450 scheduler.RequestOutsideRenderPassOperationContext();
1451 scheduler.Record([buffer = *default_buffer](vk::CommandBuffer cmdbuf) { 1451 scheduler.Record([buffer = *default_buffer](vk::CommandBuffer cmdbuf) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 4695718e9..8e261b9bd 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -21,7 +21,6 @@
21#include "video_core/renderer_vulkan/vk_compute_pass.h" 21#include "video_core/renderer_vulkan/vk_compute_pass.h"
22#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 22#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
23#include "video_core/renderer_vulkan/vk_fence_manager.h" 23#include "video_core/renderer_vulkan/vk_fence_manager.h"
24#include "video_core/renderer_vulkan/vk_memory_manager.h"
25#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 24#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
26#include "video_core/renderer_vulkan/vk_query_cache.h" 25#include "video_core/renderer_vulkan/vk_query_cache.h"
27#include "video_core/renderer_vulkan/vk_scheduler.h" 26#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -30,6 +29,7 @@
30#include "video_core/renderer_vulkan/vk_texture_cache.h" 29#include "video_core/renderer_vulkan/vk_texture_cache.h"
31#include "video_core/renderer_vulkan/vk_update_descriptor.h" 30#include "video_core/renderer_vulkan/vk_update_descriptor.h"
32#include "video_core/shader/async_shaders.h" 31#include "video_core/shader/async_shaders.h"
32#include "video_core/vulkan_common/vulkan_memory_allocator.h"
33#include "video_core/vulkan_common/vulkan_wrapper.h" 33#include "video_core/vulkan_common/vulkan_wrapper.h"
34 34
35namespace Core { 35namespace Core {
@@ -56,7 +56,7 @@ public:
56 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 56 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
57 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, 57 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
58 VKScreenInfo& screen_info_, const Device& device_, 58 VKScreenInfo& screen_info_, const Device& device_,
59 VKMemoryManager& memory_manager_, StateTracker& state_tracker_, 59 MemoryAllocator& memory_allocator_, StateTracker& state_tracker_,
60 VKScheduler& scheduler_); 60 VKScheduler& scheduler_);
61 ~RasterizerVulkan() override; 61 ~RasterizerVulkan() override;
62 62
@@ -213,12 +213,12 @@ private:
213 213
214 VKScreenInfo& screen_info; 214 VKScreenInfo& screen_info;
215 const Device& device; 215 const Device& device;
216 VKMemoryManager& memory_manager; 216 MemoryAllocator& memory_allocator;
217 StateTracker& state_tracker; 217 StateTracker& state_tracker;
218 VKScheduler& scheduler; 218 VKScheduler& scheduler;
219 219
220 VKStreamBuffer stream_buffer; 220 VKStreamBuffer stream_buffer;
221 VKStagingBufferPool staging_pool; 221 StagingBufferPool staging_pool;
222 VKDescriptorPool descriptor_pool; 222 VKDescriptorPool descriptor_pool;
223 VKUpdateDescriptorQueue update_descriptor_queue; 223 VKUpdateDescriptorQueue update_descriptor_queue;
224 BlitImageHelper blit_image; 224 BlitImageHelper blit_image;
@@ -234,7 +234,7 @@ private:
234 VKFenceManager fence_manager; 234 VKFenceManager fence_manager;
235 235
236 vk::Buffer default_buffer; 236 vk::Buffer default_buffer;
237 VKMemoryCommit default_buffer_commit; 237 MemoryCommit default_buffer_commit;
238 vk::Event wfi_event; 238 vk::Event wfi_event;
239 VideoCommon::Shader::AsyncShaders async_shaders; 239 VideoCommon::Shader::AsyncShaders async_shaders;
240 240
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 1e0b8b922..97fd41cc1 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -3,10 +3,12 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <unordered_map>
7#include <utility> 6#include <utility>
8#include <vector> 7#include <vector>
9 8
9#include <fmt/format.h>
10
11#include "common/assert.h"
10#include "common/bit_util.h" 12#include "common/bit_util.h"
11#include "common/common_types.h" 13#include "common/common_types.h"
12#include "video_core/renderer_vulkan/vk_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -16,45 +18,51 @@
16 18
17namespace Vulkan { 19namespace Vulkan {
18 20
19VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_) 21StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
20 : buffer{std::move(buffer_)} {} 22 VKScheduler& scheduler_)
21 23 : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} {}
22VKStagingBufferPool::VKStagingBufferPool(const Device& device_, VKMemoryManager& memory_manager_,
23 VKScheduler& scheduler_)
24 : device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {}
25 24
26VKStagingBufferPool::~VKStagingBufferPool() = default; 25StagingBufferPool::~StagingBufferPool() = default;
27 26
28VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visible) { 27StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage) {
29 if (const auto buffer = TryGetReservedBuffer(size, host_visible)) { 28 if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage)) {
30 return *buffer; 29 return *ref;
31 } 30 }
32 return CreateStagingBuffer(size, host_visible); 31 return CreateStagingBuffer(size, usage);
33} 32}
34 33
35void VKStagingBufferPool::TickFrame() { 34void StagingBufferPool::TickFrame() {
36 current_delete_level = (current_delete_level + 1) % NumLevels; 35 current_delete_level = (current_delete_level + 1) % NUM_LEVELS;
37 36
38 ReleaseCache(true); 37 ReleaseCache(MemoryUsage::DeviceLocal);
39 ReleaseCache(false); 38 ReleaseCache(MemoryUsage::Upload);
39 ReleaseCache(MemoryUsage::Download);
40} 40}
41 41
42VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) { 42std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size,
43 for (StagingBuffer& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) { 43 MemoryUsage usage) {
44 if (!scheduler.IsFree(entry.tick)) { 44 StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)];
45 continue; 45
46 const auto is_free = [this](const StagingBuffer& entry) {
47 return scheduler.IsFree(entry.tick);
48 };
49 auto& entries = cache_level.entries;
50 const auto hint_it = entries.begin() + cache_level.iterate_index;
51 auto it = std::find_if(entries.begin() + cache_level.iterate_index, entries.end(), is_free);
52 if (it == entries.end()) {
53 it = std::find_if(entries.begin(), hint_it, is_free);
54 if (it == hint_it) {
55 return std::nullopt;
46 } 56 }
47 entry.tick = scheduler.CurrentTick();
48 return &*entry.buffer;
49 } 57 }
50 return nullptr; 58 cache_level.iterate_index = std::distance(entries.begin(), it) + 1;
59 it->tick = scheduler.CurrentTick();
60 return it->Ref();
51} 61}
52 62
53VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) { 63StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage) {
54 const u32 log2 = Common::Log2Ceil64(size); 64 const u32 log2 = Common::Log2Ceil64(size);
55 65 vk::Buffer buffer = device.GetLogical().CreateBuffer({
56 auto buffer = std::make_unique<VKBuffer>();
57 buffer->handle = device.GetLogical().CreateBuffer({
58 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 66 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
59 .pNext = nullptr, 67 .pNext = nullptr,
60 .flags = 0, 68 .flags = 0,
@@ -66,49 +74,63 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v
66 .queueFamilyIndexCount = 0, 74 .queueFamilyIndexCount = 0,
67 .pQueueFamilyIndices = nullptr, 75 .pQueueFamilyIndices = nullptr,
68 }); 76 });
69 buffer->commit = memory_manager.Commit(buffer->handle, host_visible); 77 if (device.HasDebuggingToolAttached()) {
70 78 ++buffer_index;
71 std::vector<StagingBuffer>& entries = GetCache(host_visible)[log2].entries; 79 buffer.SetObjectNameEXT(fmt::format("Staging Buffer {}", buffer_index).c_str());
72 StagingBuffer& entry = entries.emplace_back(std::move(buffer)); 80 }
73 entry.tick = scheduler.CurrentTick(); 81 MemoryCommit commit = memory_allocator.Commit(buffer, usage);
74 return *entry.buffer; 82 const std::span<u8> mapped_span = IsHostVisible(usage) ? commit.Map() : std::span<u8>{};
75} 83
76 84 StagingBuffer& entry = GetCache(usage)[log2].entries.emplace_back(StagingBuffer{
77VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) { 85 .buffer = std::move(buffer),
78 return host_visible ? host_staging_buffers : device_staging_buffers; 86 .commit = std::move(commit),
87 .mapped_span = mapped_span,
88 .tick = scheduler.CurrentTick(),
89 });
90 return entry.Ref();
79} 91}
80 92
81void VKStagingBufferPool::ReleaseCache(bool host_visible) { 93StagingBufferPool::StagingBuffersCache& StagingBufferPool::GetCache(MemoryUsage usage) {
82 auto& cache = GetCache(host_visible); 94 switch (usage) {
83 const u64 size = ReleaseLevel(cache, current_delete_level); 95 case MemoryUsage::DeviceLocal:
84 if (size == 0) { 96 return device_local_cache;
85 return; 97 case MemoryUsage::Upload:
98 return upload_cache;
99 case MemoryUsage::Download:
100 return download_cache;
101 default:
102 UNREACHABLE_MSG("Invalid memory usage={}", usage);
103 return upload_cache;
86 } 104 }
87} 105}
88 106
89u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) { 107void StagingBufferPool::ReleaseCache(MemoryUsage usage) {
90 static constexpr std::size_t deletions_per_tick = 16; 108 ReleaseLevel(GetCache(usage), current_delete_level);
109}
91 110
111void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) {
112 constexpr size_t deletions_per_tick = 16;
92 auto& staging = cache[log2]; 113 auto& staging = cache[log2];
93 auto& entries = staging.entries; 114 auto& entries = staging.entries;
94 const std::size_t old_size = entries.size(); 115 const size_t old_size = entries.size();
95 116
96 const auto is_deleteable = [this](const StagingBuffer& entry) { 117 const auto is_deleteable = [this](const StagingBuffer& entry) {
97 return scheduler.IsFree(entry.tick); 118 return scheduler.IsFree(entry.tick);
98 }; 119 };
99 const std::size_t begin_offset = staging.delete_index; 120 const size_t begin_offset = staging.delete_index;
100 const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size); 121 const size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
101 const auto begin = std::begin(entries) + begin_offset; 122 const auto begin = entries.begin() + begin_offset;
102 const auto end = std::begin(entries) + end_offset; 123 const auto end = entries.begin() + end_offset;
103 entries.erase(std::remove_if(begin, end, is_deleteable), end); 124 entries.erase(std::remove_if(begin, end, is_deleteable), end);
104 125
105 const std::size_t new_size = entries.size(); 126 const size_t new_size = entries.size();
106 staging.delete_index += deletions_per_tick; 127 staging.delete_index += deletions_per_tick;
107 if (staging.delete_index >= new_size) { 128 if (staging.delete_index >= new_size) {
108 staging.delete_index = 0; 129 staging.delete_index = 0;
109 } 130 }
110 131 if (staging.iterate_index > new_size) {
111 return (1ULL << log2) * (old_size - new_size); 132 staging.iterate_index = 0;
133 }
112} 134}
113 135
114} // namespace Vulkan 136} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
index 90dadcbbe..d42918a47 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -9,7 +9,7 @@
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12#include "video_core/renderer_vulkan/vk_memory_manager.h" 12#include "video_core/vulkan_common/vulkan_memory_allocator.h"
13#include "video_core/vulkan_common/vulkan_wrapper.h" 13#include "video_core/vulkan_common/vulkan_wrapper.h"
14 14
15namespace Vulkan { 15namespace Vulkan {
@@ -17,55 +17,65 @@ namespace Vulkan {
17class Device; 17class Device;
18class VKScheduler; 18class VKScheduler;
19 19
20struct VKBuffer final { 20struct StagingBufferRef {
21 vk::Buffer handle; 21 VkBuffer buffer;
22 VKMemoryCommit commit; 22 std::span<u8> mapped_span;
23}; 23};
24 24
25class VKStagingBufferPool final { 25class StagingBufferPool {
26public: 26public:
27 explicit VKStagingBufferPool(const Device& device, VKMemoryManager& memory_manager, 27 explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator,
28 VKScheduler& scheduler); 28 VKScheduler& scheduler);
29 ~VKStagingBufferPool(); 29 ~StagingBufferPool();
30 30
31 VKBuffer& GetUnusedBuffer(std::size_t size, bool host_visible); 31 StagingBufferRef Request(size_t size, MemoryUsage usage);
32 32
33 void TickFrame(); 33 void TickFrame();
34 34
35private: 35private:
36 struct StagingBuffer final { 36 struct StagingBuffer {
37 explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer); 37 vk::Buffer buffer;
38 38 MemoryCommit commit;
39 std::unique_ptr<VKBuffer> buffer; 39 std::span<u8> mapped_span;
40 u64 tick = 0; 40 u64 tick = 0;
41
42 StagingBufferRef Ref() const noexcept {
43 return {
44 .buffer = *buffer,
45 .mapped_span = mapped_span,
46 };
47 }
41 }; 48 };
42 49
43 struct StagingBuffers final { 50 struct StagingBuffers {
44 std::vector<StagingBuffer> entries; 51 std::vector<StagingBuffer> entries;
45 std::size_t delete_index = 0; 52 size_t delete_index = 0;
53 size_t iterate_index = 0;
46 }; 54 };
47 55
48 static constexpr std::size_t NumLevels = sizeof(std::size_t) * CHAR_BIT; 56 static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT;
49 using StagingBuffersCache = std::array<StagingBuffers, NumLevels>; 57 using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>;
50 58
51 VKBuffer* TryGetReservedBuffer(std::size_t size, bool host_visible); 59 std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage);
52 60
53 VKBuffer& CreateStagingBuffer(std::size_t size, bool host_visible); 61 StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage);
54 62
55 StagingBuffersCache& GetCache(bool host_visible); 63 StagingBuffersCache& GetCache(MemoryUsage usage);
56 64
57 void ReleaseCache(bool host_visible); 65 void ReleaseCache(MemoryUsage usage);
58 66
59 u64 ReleaseLevel(StagingBuffersCache& cache, std::size_t log2); 67 void ReleaseLevel(StagingBuffersCache& cache, size_t log2);
60 68
61 const Device& device; 69 const Device& device;
62 VKMemoryManager& memory_manager; 70 MemoryAllocator& memory_allocator;
63 VKScheduler& scheduler; 71 VKScheduler& scheduler;
64 72
65 StagingBuffersCache host_staging_buffers; 73 StagingBuffersCache device_local_cache;
66 StagingBuffersCache device_staging_buffers; 74 StagingBuffersCache upload_cache;
75 StagingBuffersCache download_cache;
67 76
68 std::size_t current_delete_level = 0; 77 size_t current_delete_level = 0;
78 u64 buffer_index = 0;
69}; 79};
70 80
71} // namespace Vulkan 81} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index bd11de012..ab14922d7 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -10,12 +10,12 @@
10#include "video_core/engines/fermi_2d.h" 10#include "video_core/engines/fermi_2d.h"
11#include "video_core/renderer_vulkan/blit_image.h" 11#include "video_core/renderer_vulkan/blit_image.h"
12#include "video_core/renderer_vulkan/maxwell_to_vk.h" 12#include "video_core/renderer_vulkan/maxwell_to_vk.h"
13#include "video_core/renderer_vulkan/vk_memory_manager.h"
14#include "video_core/renderer_vulkan/vk_rasterizer.h" 13#include "video_core/renderer_vulkan/vk_rasterizer.h"
15#include "video_core/renderer_vulkan/vk_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
16#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 15#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
17#include "video_core/renderer_vulkan/vk_texture_cache.h" 16#include "video_core/renderer_vulkan/vk_texture_cache.h"
18#include "video_core/vulkan_common/vulkan_device.h" 17#include "video_core/vulkan_common/vulkan_device.h"
18#include "video_core/vulkan_common/vulkan_memory_allocator.h"
19#include "video_core/vulkan_common/vulkan_wrapper.h" 19#include "video_core/vulkan_common/vulkan_wrapper.h"
20 20
21namespace Vulkan { 21namespace Vulkan {
@@ -554,10 +554,18 @@ void TextureCacheRuntime::Finish() {
554} 554}
555 555
556ImageBufferMap TextureCacheRuntime::MapUploadBuffer(size_t size) { 556ImageBufferMap TextureCacheRuntime::MapUploadBuffer(size_t size) {
557 const auto& buffer = staging_buffer_pool.GetUnusedBuffer(size, true); 557 const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Upload);
558 return ImageBufferMap{ 558 return {
559 .handle = *buffer.handle, 559 .handle = staging_ref.buffer,
560 .map = buffer.commit->Map(size), 560 .span = staging_ref.mapped_span,
561 };
562}
563
564ImageBufferMap TextureCacheRuntime::MapDownloadBuffer(size_t size) {
565 const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Download);
566 return {
567 .handle = staging_ref.buffer,
568 .span = staging_ref.mapped_span,
561 }; 569 };
562} 570}
563 571
@@ -788,9 +796,9 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_
788 image(MakeImage(runtime.device, info)), buffer(MakeBuffer(runtime.device, info)), 796 image(MakeImage(runtime.device, info)), buffer(MakeBuffer(runtime.device, info)),
789 aspect_mask(ImageAspectMask(info.format)) { 797 aspect_mask(ImageAspectMask(info.format)) {
790 if (image) { 798 if (image) {
791 commit = runtime.memory_manager.Commit(image, false); 799 commit = runtime.memory_allocator.Commit(image, MemoryUsage::DeviceLocal);
792 } else { 800 } else {
793 commit = runtime.memory_manager.Commit(buffer, false); 801 commit = runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
794 } 802 }
795 if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) { 803 if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) {
796 flags |= VideoCommon::ImageFlagBits::Converted; 804 flags |= VideoCommon::ImageFlagBits::Converted;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 92a7aad8b..a55d405d1 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -7,8 +7,8 @@
7#include <compare> 7#include <compare>
8#include <span> 8#include <span>
9 9
10#include "video_core/renderer_vulkan/vk_memory_manager.h"
11#include "video_core/texture_cache/texture_cache.h" 10#include "video_core/texture_cache/texture_cache.h"
11#include "video_core/vulkan_common/vulkan_memory_allocator.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h" 12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 13
14namespace Vulkan { 14namespace Vulkan {
@@ -19,14 +19,13 @@ using VideoCommon::Offset2D;
19using VideoCommon::RenderTargets; 19using VideoCommon::RenderTargets;
20using VideoCore::Surface::PixelFormat; 20using VideoCore::Surface::PixelFormat;
21 21
22class VKScheduler;
23class VKStagingBufferPool;
24
25class BlitImageHelper; 22class BlitImageHelper;
26class Device; 23class Device;
27class Image; 24class Image;
28class ImageView; 25class ImageView;
29class Framebuffer; 26class Framebuffer;
27class StagingBufferPool;
28class VKScheduler;
30 29
31struct RenderPassKey { 30struct RenderPassKey {
32 constexpr auto operator<=>(const RenderPassKey&) const noexcept = default; 31 constexpr auto operator<=>(const RenderPassKey&) const noexcept = default;
@@ -60,18 +59,18 @@ struct ImageBufferMap {
60 } 59 }
61 60
62 [[nodiscard]] std::span<u8> Span() const noexcept { 61 [[nodiscard]] std::span<u8> Span() const noexcept {
63 return map.Span(); 62 return span;
64 } 63 }
65 64
66 VkBuffer handle; 65 VkBuffer handle;
67 MemoryMap map; 66 std::span<u8> span;
68}; 67};
69 68
70struct TextureCacheRuntime { 69struct TextureCacheRuntime {
71 const Device& device; 70 const Device& device;
72 VKScheduler& scheduler; 71 VKScheduler& scheduler;
73 VKMemoryManager& memory_manager; 72 MemoryAllocator& memory_allocator;
74 VKStagingBufferPool& staging_buffer_pool; 73 StagingBufferPool& staging_buffer_pool;
75 BlitImageHelper& blit_image_helper; 74 BlitImageHelper& blit_image_helper;
76 std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache; 75 std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache;
77 76
@@ -79,10 +78,7 @@ struct TextureCacheRuntime {
79 78
80 [[nodiscard]] ImageBufferMap MapUploadBuffer(size_t size); 79 [[nodiscard]] ImageBufferMap MapUploadBuffer(size_t size);
81 80
82 [[nodiscard]] ImageBufferMap MapDownloadBuffer(size_t size) { 81 [[nodiscard]] ImageBufferMap MapDownloadBuffer(size_t size);
83 // TODO: Have a special function for this
84 return MapUploadBuffer(size);
85 }
86 82
87 void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, 83 void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
88 const std::array<Offset2D, 2>& dst_region, 84 const std::array<Offset2D, 2>& dst_region,
@@ -141,7 +137,7 @@ private:
141 VKScheduler* scheduler; 137 VKScheduler* scheduler;
142 vk::Image image; 138 vk::Image image;
143 vk::Buffer buffer; 139 vk::Buffer buffer;
144 VKMemoryCommit commit; 140 MemoryCommit commit;
145 VkImageAspectFlags aspect_mask = 0; 141 VkImageAspectFlags aspect_mask = 0;
146 bool initialized = false; 142 bool initialized = false;
147}; 143};
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
new file mode 100644
index 000000000..d6eb3af31
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -0,0 +1,268 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <bit>
7#include <optional>
8#include <vector>
9
10#include "common/alignment.h"
11#include "common/assert.h"
12#include "common/common_types.h"
13#include "common/logging/log.h"
14#include "video_core/vulkan_common/vulkan_device.h"
15#include "video_core/vulkan_common/vulkan_memory_allocator.h"
16#include "video_core/vulkan_common/vulkan_wrapper.h"
17
18namespace Vulkan {
19namespace {
20struct Range {
21 u64 begin;
22 u64 end;
23
24 [[nodiscard]] bool Contains(u64 iterator, u64 size) const noexcept {
25 return iterator < end && begin < iterator + size;
26 }
27};
28
29[[nodiscard]] u64 AllocationChunkSize(u64 required_size) {
30 static constexpr std::array sizes{
31 0x1000ULL << 10, 0x1400ULL << 10, 0x1800ULL << 10, 0x1c00ULL << 10, 0x2000ULL << 10,
32 0x3200ULL << 10, 0x4000ULL << 10, 0x6000ULL << 10, 0x8000ULL << 10, 0xA000ULL << 10,
33 0x10000ULL << 10, 0x18000ULL << 10, 0x20000ULL << 10,
34 };
35 static_assert(std::is_sorted(sizes.begin(), sizes.end()));
36
37 const auto it = std::ranges::lower_bound(sizes, required_size);
38 return it != sizes.end() ? *it : Common::AlignUp(required_size, 4ULL << 20);
39}
40
41[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) {
42 switch (usage) {
43 case MemoryUsage::DeviceLocal:
44 return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
45 case MemoryUsage::Upload:
46 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
47 case MemoryUsage::Download:
48 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
49 VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
50 }
51 UNREACHABLE_MSG("Invalid memory usage={}", usage);
52 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
53}
54} // Anonymous namespace
55
56class MemoryAllocation {
57public:
58 explicit MemoryAllocation(const Device& device_, vk::DeviceMemory memory_,
59 VkMemoryPropertyFlags properties, u64 allocation_size_, u32 type)
60 : device{device_}, memory{std::move(memory_)}, allocation_size{allocation_size_},
61 property_flags{properties}, shifted_memory_type{1U << type} {}
62
63 [[nodiscard]] std::optional<MemoryCommit> Commit(VkDeviceSize size, VkDeviceSize alignment) {
64 const std::optional<u64> alloc = FindFreeRegion(size, alignment);
65 if (!alloc) {
66 // Signal out of memory, it'll try to do more allocations.
67 return std::nullopt;
68 }
69 const Range range{
70 .begin = *alloc,
71 .end = *alloc + size,
72 };
73 commits.insert(std::ranges::upper_bound(commits, *alloc, {}, &Range::begin), range);
74 return std::make_optional<MemoryCommit>(this, *memory, *alloc, *alloc + size);
75 }
76
77 void Free(u64 begin) {
78 const auto it = std::ranges::find(commits, begin, &Range::begin);
79 ASSERT_MSG(it != commits.end(), "Invalid commit");
80 commits.erase(it);
81 }
82
83 [[nodiscard]] std::span<u8> Map() {
84 if (memory_mapped_span.empty()) {
85 u8* const raw_pointer = memory.Map(0, allocation_size);
86 memory_mapped_span = std::span<u8>(raw_pointer, allocation_size);
87 }
88 return memory_mapped_span;
89 }
90
91 /// Returns whether this allocation is compatible with the arguments.
92 [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const {
93 return (flags & property_flags) && (type_mask & shifted_memory_type) != 0;
94 }
95
96private:
97 [[nodiscard]] static constexpr u32 ShiftType(u32 type) {
98 return 1U << type;
99 }
100
101 [[nodiscard]] std::optional<u64> FindFreeRegion(u64 size, u64 alignment) noexcept {
102 ASSERT(std::has_single_bit(alignment));
103 const u64 alignment_log2 = std::countr_zero(alignment);
104 std::optional<u64> candidate;
105 u64 iterator = 0;
106 auto commit = commits.begin();
107 while (iterator + size <= allocation_size) {
108 candidate = candidate.value_or(iterator);
109 if (commit == commits.end()) {
110 break;
111 }
112 if (commit->Contains(*candidate, size)) {
113 candidate = std::nullopt;
114 }
115 iterator = Common::AlignUpLog2(commit->end, alignment_log2);
116 ++commit;
117 }
118 return candidate;
119 }
120
121 const Device& device; ///< Vulkan device.
122 const vk::DeviceMemory memory; ///< Vulkan memory allocation handler.
123 const u64 allocation_size; ///< Size of this allocation.
124 const VkMemoryPropertyFlags property_flags; ///< Vulkan memory property flags.
125 const u32 shifted_memory_type; ///< Shifted Vulkan memory type.
126 std::vector<Range> commits; ///< All commit ranges done from this allocation.
127 std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before.
128};
129
130MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_,
131 u64 end_) noexcept
132 : allocation{allocation_}, memory{memory_}, begin{begin_}, end{end_} {}
133
134MemoryCommit::~MemoryCommit() {
135 Release();
136}
137
138MemoryCommit& MemoryCommit::operator=(MemoryCommit&& rhs) noexcept {
139 Release();
140 allocation = std::exchange(rhs.allocation, nullptr);
141 memory = rhs.memory;
142 begin = rhs.begin;
143 end = rhs.end;
144 span = std::exchange(rhs.span, std::span<u8>{});
145 return *this;
146}
147
148MemoryCommit::MemoryCommit(MemoryCommit&& rhs) noexcept
149 : allocation{std::exchange(rhs.allocation, nullptr)}, memory{rhs.memory}, begin{rhs.begin},
150 end{rhs.end}, span{std::exchange(rhs.span, std::span<u8>{})} {}
151
152std::span<u8> MemoryCommit::Map() {
153 if (span.empty()) {
154 span = allocation->Map().subspan(begin, end - begin);
155 }
156 return span;
157}
158
159void MemoryCommit::Release() {
160 if (allocation) {
161 allocation->Free(begin);
162 }
163}
164
165MemoryAllocator::MemoryAllocator(const Device& device_)
166 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {}
167
168MemoryAllocator::~MemoryAllocator() = default;
169
170MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) {
171 // Find the fastest memory flags we can afford with the current requirements
172 const VkMemoryPropertyFlags flags = MemoryPropertyFlags(requirements.memoryTypeBits, usage);
173 if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) {
174 return std::move(*commit);
175 }
176 // Commit has failed, allocate more memory.
177 // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory.
178 AllocMemory(flags, requirements.memoryTypeBits, AllocationChunkSize(requirements.size));
179
180 // Commit again, this time it won't fail since there's a fresh allocation above.
181 // If it does, there's a bug.
182 return TryCommit(requirements, flags).value();
183}
184
185MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage) {
186 auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), usage);
187 buffer.BindMemory(commit.Memory(), commit.Offset());
188 return commit;
189}
190
191MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) {
192 auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), usage);
193 image.BindMemory(commit.Memory(), commit.Offset());
194 return commit;
195}
196
197void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) {
198 const u32 type = FindType(flags, type_mask).value();
199 vk::DeviceMemory memory = device.GetLogical().AllocateMemory({
200 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
201 .pNext = nullptr,
202 .allocationSize = size,
203 .memoryTypeIndex = type,
204 });
205 allocations.push_back(
206 std::make_unique<MemoryAllocation>(device, std::move(memory), flags, size, type));
207}
208
209std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements,
210 VkMemoryPropertyFlags flags) {
211 for (auto& allocation : allocations) {
212 if (!allocation->IsCompatible(flags, requirements.memoryTypeBits)) {
213 continue;
214 }
215 if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) {
216 return commit;
217 }
218 }
219 return std::nullopt;
220}
221
222VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const {
223 return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage));
224}
225
226VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask,
227 VkMemoryPropertyFlags flags) const {
228 if (FindType(flags, type_mask)) {
229 // Found a memory type with those requirements
230 return flags;
231 }
232 if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
233 // Remove host cached bit in case it's not supported
234 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
235 }
236 if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
237 // Remove device local, if it's not supported by the requested resource
238 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
239 }
240 UNREACHABLE_MSG("No compatible memory types found");
241 return 0;
242}
243
244std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const {
245 for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) {
246 const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags;
247 if ((type_mask & (1U << type_index)) && (type_flags & flags)) {
248 // The type matches in type and in the wanted properties.
249 return type_index;
250 }
251 }
252 // Failed to find index
253 return std::nullopt;
254}
255
256bool IsHostVisible(MemoryUsage usage) noexcept {
257 switch (usage) {
258 case MemoryUsage::DeviceLocal:
259 return false;
260 case MemoryUsage::Upload:
261 case MemoryUsage::Download:
262 return true;
263 }
264 UNREACHABLE_MSG("Invalid memory usage={}", usage);
265 return false;
266}
267
268} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h
new file mode 100644
index 000000000..53b3b275a
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h
@@ -0,0 +1,118 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <span>
9#include <utility>
10#include <vector>
11#include "common/common_types.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13
14namespace Vulkan {
15
16class Device;
17class MemoryMap;
18class MemoryAllocation;
19
20/// Hints and requirements for the backing memory type of a commit
21enum class MemoryUsage {
22 DeviceLocal, ///< Hints device local usages, fastest memory type to read and write from the GPU
23 Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads
24 Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks
25};
26
27/// Ownership handle of a memory commitment.
28/// Points to a subregion of a memory allocation.
29class MemoryCommit {
30public:
31 explicit MemoryCommit() noexcept = default;
32 explicit MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_,
33 u64 end_) noexcept;
34 ~MemoryCommit();
35
36 MemoryCommit& operator=(MemoryCommit&&) noexcept;
37 MemoryCommit(MemoryCommit&&) noexcept;
38
39 MemoryCommit& operator=(const MemoryCommit&) = delete;
40 MemoryCommit(const MemoryCommit&) = delete;
41
42 /// Returns a host visible memory map.
43 /// It will map the backing allocation if it hasn't been mapped before.
44 std::span<u8> Map();
45
46 /// Returns the Vulkan memory handler.
47 VkDeviceMemory Memory() const {
48 return memory;
49 }
50
51 /// Returns the start position of the commit relative to the allocation.
52 VkDeviceSize Offset() const {
53 return static_cast<VkDeviceSize>(begin);
54 }
55
56private:
57 void Release();
58
59 MemoryAllocation* allocation{}; ///< Pointer to the large memory allocation.
60 VkDeviceMemory memory{}; ///< Vulkan device memory handler.
61 u64 begin{}; ///< Beginning offset in bytes to where the commit exists.
62 u64 end{}; ///< Offset in bytes where the commit ends.
63 std::span<u8> span; ///< Host visible memory span. Empty if not queried before.
64};
65
66/// Memory allocator container.
67/// Allocates and releases memory allocations on demand.
68class MemoryAllocator {
69public:
70 explicit MemoryAllocator(const Device& device_);
71 ~MemoryAllocator();
72
73 MemoryAllocator& operator=(const MemoryAllocator&) = delete;
74 MemoryAllocator(const MemoryAllocator&) = delete;
75
76 /**
77 * Commits a memory with the specified requeriments.
78 *
79 * @param requirements Requirements returned from a Vulkan call.
80 * @param host_visible Signals the allocator that it *must* use host visible and coherent
81 * memory. When passing false, it will try to allocate device local memory.
82 *
83 * @returns A memory commit.
84 */
85 MemoryCommit Commit(const VkMemoryRequirements& requirements, MemoryUsage usage);
86
87 /// Commits memory required by the buffer and binds it.
88 MemoryCommit Commit(const vk::Buffer& buffer, MemoryUsage usage);
89
90 /// Commits memory required by the image and binds it.
91 MemoryCommit Commit(const vk::Image& image, MemoryUsage usage);
92
93private:
94 /// Allocates a chunk of memory.
95 void AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size);
96
97 /// Tries to allocate a memory commit.
98 std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements,
99 VkMemoryPropertyFlags flags);
100
101 /// Returns the fastest compatible memory property flags from a wanted usage.
102 VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const;
103
104 /// Returns the fastest compatible memory property flags from the wanted flags.
105 VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const;
106
107 /// Returns index to the fastest memory type compatible with the passed requirements.
108 std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const;
109
110 const Device& device; ///< Device handle.
111 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
112 std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations.
113};
114
115/// Returns true when a memory usage is guaranteed to be host visible.
116bool IsHostVisible(MemoryUsage usage) noexcept;
117
118} // namespace Vulkan