From c9c136bea7567c825be19c1dcb481a47ee6536f6 Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 20 Feb 2026 00:52:07 +0100 Subject: [texture_cache, buffer_cache] Added TLS handling + changed command queue for GPU threading. (#3579) (Merge of #3495 + #3108) This PR works around to simplify math operations on hot pointers inside the access and requests to the cache of buffers and texture cache, removing previous logic of indirection and replaced by a PoD approach. This will ensure less CPU times spended on the same request and flow directly into another chain of the render, in the same way, command queue currently uses an internal mutex that constraints the flow of data within the GPU threads, we're moving over a single command, I verified to keep using mutexes instead of internal mutex + mutex per operation, which are resolved by themselves. In simplier words, this aims to improve performance on those games and devices where the waits for next orders on GPU commands were heavier than a single verification. Co-Authored-by: @CamilleLaVey Co-Authored-by: @Lizzie Co-authored-by: CamilleLaVey Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3579 Reviewed-by: CamilleLaVey Co-authored-by: lizzie Co-committed-by: lizzie --- src/video_core/buffer_cache/buffer_cache.h | 115 ++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 33 deletions(-) (limited to 'src/video_core/buffer_cache/buffer_cache.h') diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index b368ffea05..7b92edecaa 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project @@ -382,6 +382,10 @@ void BufferCache

::BindHostComputeBuffers() { BindHostComputeUniformBuffers(); BindHostComputeStorageBuffers(); BindHostComputeTextureBuffers(); + if (any_buffer_uploaded) { + runtime.PostCopyBarrier(); + any_buffer_uploaded = false; + } } template @@ -763,45 +767,85 @@ void BufferCache

::BindHostIndexBuffer() { } } +template +void BufferCache

::BindHostVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, + u32 stride) { + if constexpr (IS_OPENGL) { + runtime.BindVertexBuffer(index, buffer, offset, size, stride); + } else { + runtime.BindVertexBuffer(index, buffer.Handle(), offset, size, stride); + } +} + +template +Binding& BufferCache

::VertexBufferSlot(u32 index) { + ASSERT(index < NUM_VERTEX_BUFFERS); + return v_buffer[index]; +} + +template +const Binding& BufferCache

::VertexBufferSlot(u32 index) const { + ASSERT(index < NUM_VERTEX_BUFFERS); + return v_buffer[index]; +} + +template +void BufferCache

::UpdateVertexBufferSlot(u32 index, const Binding& binding) { + Binding& slot = VertexBufferSlot(index); + if (slot.device_addr != binding.device_addr || slot.size != binding.size) { + ++vertex_buffers_serial; + } + slot = binding; + if (binding.buffer_id != NULL_BUFFER_ID && binding.size != 0) { + enabled_vertex_buffers_mask |= (1u << index); + } else { + enabled_vertex_buffers_mask &= ~(1u << index); + } +} + template void BufferCache

::BindHostVertexBuffers() { - HostBindings host_bindings; - bool any_valid{false}; auto& flags = maxwell3d->dirty.flags; - for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { - const Binding& binding = channel_state->vertex_buffers[index]; + u32 enabled_mask = enabled_vertex_buffers_mask; + HostBindings bindings{}; + u32 last_index = std::numeric_limits::max(); + const auto flush_bindings = [&]() { + if (bindings.buffers.empty()) { + return; + } + bindings.max_index = bindings.min_index + static_cast(bindings.buffers.size()); + runtime.BindVertexBuffers(bindings); + bindings = HostBindings{}; + last_index = std::numeric_limits::max(); + }; + while (enabled_mask != 0) { + const u32 index = std::countr_zero(enabled_mask); + enabled_mask &= (enabled_mask - 1); + const Binding& binding = VertexBufferSlot(index); Buffer& buffer = slot_buffers[binding.buffer_id]; TouchBuffer(buffer, binding.buffer_id); SynchronizeBuffer(buffer, binding.device_addr, binding.size); if (!flags[Dirty::VertexBuffer0 + index]) { + flush_bindings(); continue; } flags[Dirty::VertexBuffer0 + index] = false; - - host_bindings.min_index = (std::min)(host_bindings.min_index, index); - host_bindings.max_index = (std::max)(host_bindings.max_index, index); - any_valid = true; - } - - if (any_valid) { - host_bindings.max_index++; - for (u32 index = host_bindings.min_index; index < host_bindings.max_index; index++) { - flags[Dirty::VertexBuffer0 + index] = false; - - const Binding& binding = channel_state->vertex_buffers[index]; - Buffer& buffer = slot_buffers[binding.buffer_id]; - - const u32 stride = maxwell3d->regs.vertex_streams[index].stride; - const u32 offset = buffer.Offset(binding.device_addr); - buffer.MarkUsage(offset, binding.size); - - host_bindings.buffers.push_back(&buffer); - host_bindings.offsets.push_back(offset); - host_bindings.sizes.push_back(binding.size); - host_bindings.strides.push_back(stride); + const u32 stride = maxwell3d->regs.vertex_streams[index].stride; + const u32 offset = buffer.Offset(binding.device_addr); + buffer.MarkUsage(offset, binding.size); + if (!bindings.buffers.empty() && index != last_index + 1) { + flush_bindings(); } - runtime.BindVertexBuffers(host_bindings); + if (bindings.buffers.empty()) { + bindings.min_index = index; + } + bindings.buffers.push_back(&buffer); + bindings.offsets.push_back(offset); + bindings.sizes.push_back(binding.size); + bindings.strides.push_back(stride); + last_index = index; } + flush_bindings(); } template @@ -1205,17 +1249,20 @@ void BufferCache

::UpdateVertexBuffer(u32 index) { u32 size = address_size; // TODO: Analyze stride and number of vertices if (array.enable == 0 || size == 0 || !device_addr) { channel_state->vertex_buffers[index] = NULL_BINDING; + UpdateVertexBufferSlot(index, NULL_BINDING); return; } if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) { size = static_cast(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); } const BufferId buffer_id = FindBuffer(*device_addr, size); - channel_state->vertex_buffers[index] = Binding{ + const Binding binding{ .device_addr = *device_addr, .size = size, .buffer_id = buffer_id, }; + channel_state->vertex_buffers[index] = binding; + UpdateVertexBufferSlot(index, binding); } template @@ -1528,12 +1575,12 @@ void BufferCache

::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { template bool BufferCache

::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size) { - boost::container::small_vector copies; + upload_copies.clear(); u64 total_size_bytes = 0; u64 largest_copy = 0; - DAddr buffer_start = buffer.CpuAddr(); + const DAddr buffer_start = buffer.cpu_addr_cached; memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { - copies.push_back(BufferCopy{ + upload_copies.push_back(BufferCopy{ .src_offset = total_size_bytes, .dst_offset = device_addr_out - buffer_start, .size = range_size, @@ -1544,8 +1591,9 @@ bool BufferCache

::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si if (total_size_bytes == 0) { return true; } - const std::span copies_span(copies.data(), copies.size()); + const std::span copies_span(upload_copies.data(), upload_copies.size()); UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); + any_buffer_uploaded = true; return false; } @@ -1735,6 +1783,7 @@ void BufferCache

::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { auto& binding = channel_state->vertex_buffers[index]; if (binding.buffer_id == buffer_id) { binding.buffer_id = BufferId{}; + UpdateVertexBufferSlot(index, binding); dirty_vertex_buffers.push_back(index); } } -- cgit v1.2.3