diff options
| author | Shinmegumi <shinmegumi@eden-emu.dev> | 2025-10-10 19:24:20 +0200 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2025-10-10 19:24:20 +0200 |
| commit | 776958c79d2aa3e3e1c8545a700e8fe2bb3ca94a (patch) | |
| tree | 70bc4ac00a768a94459615ebc370b2bde05c4a88 /src | |
| parent | 3656253262fd1c22e5a1ab43ae373173e571e11d (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.cpp | 42 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_buffer_cache.h | 32 |
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; |
