aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/buffer_cache
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 01:26:52 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 01:26:52 -0400
commitc01459347405591446d50858d7b075d617a58be9 (patch)
treee2b31109ab0ee66ca4b12612619bdeab03506be9 /src/video_core/buffer_cache
parente8bc734d1040044abe117a369a9f80a792e51df4 (diff)
video_core: cheaper small GPU reads, buffer usage tracking and compute uniforms
- MemoryManager::ReadBlockUnsafe reads within one 4 KiB page through a single translation (as Read<T> does) instead of the page walk: texture and sampler descriptors, several per draw (2.6% -> 0.1% of title 1's GPU thread). - UsageTracker skips ranges it tracked just before (until Reset): draws bind the same multi-megabyte storage buffers again and again, and each Track wrote a word per 4 KiB (2.0% -> 0.7%). - Small compute uniform buffers the GPU has not written are streamed, as graphics ones are; synchronizing them re-protected pages an inline upload had just unprotected, two VirtualProtect calls per dispatch (1.9% -> 0.7%, page protection 3.2% -> 2.3%).
Diffstat (limited to 'src/video_core/buffer_cache')
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h9
-rw-r--r--src/video_core/buffer_cache/usage_tracker.h17
2 files changed, 25 insertions, 1 deletions
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 4df855c0fe..ad2e200438 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -1211,8 +1211,15 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
return alignment > 1 && (offset % alignment) != 0;
}
}();
+ // Small buffers the GPU has not written are streamed, as graphics ones are: synchronizing
+ // them made every dispatch re-protect pages an inline upload had just unprotected (two
+ // VirtualProtect calls per dispatch, ~4,000 dispatches a second in title 1).
+ const bool use_fast_buffer =
+ needs_alignment_stream ||
+ (has_host_buffer && size <= channel_state->uniform_buffer_skip_cache_size &&
+ !memory_tracker.IsRegionGpuModified(binding.device_addr, size));
if constexpr (!IS_OPENGL) {
- if (needs_alignment_stream) {
+ if (use_fast_buffer) {
const std::span<u8> span =
runtime.BindMappedUniformBuffer(0, binding_index, size);
device_memory.ReadBlockUnsafe(binding.device_addr, span.data(), size);
diff --git a/src/video_core/buffer_cache/usage_tracker.h b/src/video_core/buffer_cache/usage_tracker.h
index 6f84e463e3..3e2da8bc0b 100644
--- a/src/video_core/buffer_cache/usage_tracker.h
+++ b/src/video_core/buffer_cache/usage_tracker.h
@@ -6,6 +6,10 @@
#pragma once
+#include <array>
+#include <utility>
+#include <vector>
+
#include "common/alignment.h"
#include "common/common_types.h"
@@ -24,9 +28,19 @@ public:
void Reset() noexcept {
std::ranges::fill(pages, 0ULL);
+ recent.fill({~u64{0}, 0});
}
void Track(u64 offset, u64 size) noexcept {
+ // Tracking is idempotent until Reset: skip ranges tracked just before. Draws bind the
+ // same (often multi-megabyte) storage buffers again and again, and each Track wrote a
+ // word per 4 KiB of the range.
+ for (const auto& [recent_offset, recent_size] : recent) {
+ if (recent_offset == offset && recent_size == size) {
+ return;
+ }
+ }
+ recent[recent_cursor++ % recent.size()] = {offset, size};
const size_t page = offset >> BUFFER_PAGE_SHIFT;
const size_t page_end = (offset + size) >> BUFFER_PAGE_SHIFT;
if (page_end < page || page_end >= pages.size()) {
@@ -83,6 +97,9 @@ private:
private:
std::vector<u64> pages;
+ /// Ranges tracked last, (offset, size).
+ std::array<std::pair<u64, u64>, 4> recent{{{~u64{0}, 0}, {~u64{0}, 0}, {~u64{0}, 0}, {~u64{0}, 0}}};
+ size_t recent_cursor{};
};
} // namespace VideoCommon