aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShinmegumi <shinmegumi@eden-emu.dev>2025-10-10 19:24:20 +0200
committercrueter <crueter@eden-emu.dev>2025-10-10 19:24:20 +0200
commit776958c79d2aa3e3e1c8545a700e8fe2bb3ca94a (patch)
tree70bc4ac00a768a94459615ebc370b2bde05c4a88 /src
parent3656253262fd1c22e5a1ab43ae373173e571e11d (diff)
[vk] Introduce Ring Buffers for Uniform Buffer (#2698)
Create 3 ring buffers which rotates between buffers each frame to avoid GPU/CPU conflicts BindMappedUniformBuffer first tries to allocate from the ring buffer and falls back to staging pool only if allocation is too large. Note to testers:- please test the performance since it is primarily a performance optimization and also look for visual bugs. Co-authored-by: wildcard <wildcard@eden-emu.dev> Co-authored-by: MaranBr <maranbr@outlook.com> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2698 Reviewed-by: MaranBr <maranbr@eden-emu.dev> Co-authored-by: Shinmegumi <shinmegumi@eden-emu.dev> Co-committed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp42
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h32
2 files changed, 72 insertions, 2 deletions
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 55565e3d79..0f0d27c6f7 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -337,6 +337,11 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
uint8_pass = std::make_unique<Uint8Pass>(device, scheduler, descriptor_pool, staging_pool,
compute_pass_descriptor_queue);
}
+ const u32 ubo_align = static_cast<u32>(
+ device.GetUniformBufferAlignment() //check if the device has it
+ );
+ // add the ability to change the size in settings in future
+ uniform_ring.Init(device, memory_allocator, 8 * 1024 * 1024 /* 8 MiB */, ubo_align ? ubo_align : 256);
quad_array_index_buffer = std::make_shared<QuadArrayIndexBuffer>(device_, memory_allocator_,
scheduler_, staging_pool_);
quad_strip_index_buffer = std::make_shared<QuadStripIndexBuffer>(device_, memory_allocator_,
@@ -355,6 +360,42 @@ void BufferCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) {
staging_pool.FreeDeferred(ref);
}
+void BufferCacheRuntime::UniformRing::Init(const Device& device,
+ MemoryAllocator& alloc,
+ u64 bytes, u32 alignment) {
+ for (size_t i = 0; i < NUM_FRAMES; ++i) {
+ VkBufferCreateInfo ci{
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .size = bytes,
+ .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .queueFamilyIndexCount = 0,
+ .pQueueFamilyIndices = nullptr,
+ };
+ buffers[i] = alloc.CreateBuffer(ci, MemoryUsage::Upload);
+ mapped[i] = buffers[i].Mapped().data();
+ }
+ size = bytes;
+ align = alignment ? alignment : 256;
+ head = 0;
+ current_frame = 0;
+}
+
+std::span<u8> BufferCacheRuntime::UniformRing::Alloc(u32 bytes, u32& out_offset) {
+ const u64 aligned = Common::AlignUp(head, static_cast<u64>(align));
+ u64 end = aligned + bytes;
+
+ if (end > size) {
+ return {}; // Fallback to staging pool
+ }
+
+ out_offset = static_cast<u32>(aligned);
+ head = end;
+ return {mapped[current_frame] + out_offset, bytes};
+}
+
u64 BufferCacheRuntime::GetDeviceLocalMemory() const {
return device.GetDeviceLocalMemory();
}
@@ -375,6 +416,7 @@ void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noe
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
it->ResetUsageTracking();
}
+ uniform_ring.BeginFrame();
}
void BufferCacheRuntime::Finish() {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index efe960258c..86bce01596 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -124,8 +127,15 @@ public:
void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings);
- std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
- [[maybe_unused]] u32 binding_index, u32 size) {
+ std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t /*stage*/,
+ [[maybe_unused]] u32 /*binding_index*/,
+ u32 size) {
+ u32 offset = 0;
+ if (auto span = uniform_ring.Alloc(size, offset); !span.empty()) {
+ BindBuffer(*uniform_ring.buffers[uniform_ring.current_frame], offset, size);
+ return span;
+ }
+ // Fallback for giant requests
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload);
BindBuffer(ref.buffer, static_cast<u32>(ref.offset), size);
return ref.mapped_span;
@@ -153,6 +163,24 @@ private:
void ReserveNullBuffer();
vk::Buffer CreateNullBuffer();
+ struct UniformRing {
+ static constexpr size_t NUM_FRAMES = 3;
+ std::array<vk::Buffer, NUM_FRAMES> buffers{};
+ std::array<u8*, NUM_FRAMES> mapped{};
+ u64 size = 0;
+ u64 head = 0;
+ u32 align = 256;
+ size_t current_frame = 0;
+
+ void Init(const Device& device, MemoryAllocator& alloc, u64 bytes, u32 alignment);
+ void BeginFrame() {
+ current_frame = (current_frame + 1) % NUM_FRAMES;
+ head = 0;
+ }
+ std::span<u8> Alloc(u32 bytes, u32& out_offset);
+ };
+ UniformRing uniform_ring;
+
const Device& device;
MemoryAllocator& memory_allocator;
Scheduler& scheduler;