aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/buffer_cache
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/buffer_cache')
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h33
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h6
-rw-r--r--src/video_core/buffer_cache/memory_tracker_base.h2
-rw-r--r--src/video_core/buffer_cache/usage_tracker.h17
4 files changed, 53 insertions, 5 deletions
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index f30c85911f..ad2e200438 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -9,6 +9,9 @@
#include <algorithm>
#include <memory>
#include <numeric>
+#include <stdexcept>
+#include <vector>
+#include <fmt/ranges.h>
#include "common/range_sets.inc"
#include "video_core/buffer_cache/buffer_cache_base.h"
@@ -78,6 +81,20 @@ void BufferCache<P>::TickFrame() {
return;
}
runtime.TickFrame(slot_buffers);
+ if (static u64 frames{}; ++frames % 600 == 0) {
+ u64 total{};
+ size_t count{};
+ std::vector<std::pair<u64, VAddr>> largest;
+ for (const auto [buffer_id, buffer] : slot_buffers) {
+ total += buffer->SizeBytes();
+ ++count;
+ largest.emplace_back(buffer->SizeBytes(), buffer->CpuAddr());
+ }
+ std::partial_sort(largest.begin(), largest.begin() + std::min<size_t>(6, largest.size()),
+ largest.end(), std::greater<>{});
+ LOG_INFO(HW_GPU, "Buffer cache: {} buffers {} MiB; largest {}", count, total >> 20,
+ fmt::join(largest.begin(), largest.begin() + std::min<size_t>(6, largest.size()), ", "));
+ }
// Calculate hits and shots and move hit bits to the right
const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(),
@@ -1194,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);
@@ -1372,7 +1396,12 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
const auto& array = maxwell3d->regs.vertex_streams[index];
const auto& limit = maxwell3d->regs.vertex_stream_limits[index];
const GPUVAddr gpu_addr_begin = array.Address();
- const GPUVAddr gpu_addr_end = limit.Address() + 1;
+ const bool native = maxwell3d->GetNativeMacroEngine().native_turing;
+ const u64 native_size = native ? maxwell3d->regs.NativeVertexStreamSize(index) : 0;
+ if (native && array.enable != 0 && native_size > (std::numeric_limits<u32>::max)()) {
+ throw std::runtime_error("Native vertex stream exceeds supported 32-bit buffer size");
+ }
+ const GPUVAddr gpu_addr_end = native ? gpu_addr_begin + native_size : limit.Address() + 1;
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
u32 size = address_size; // TODO: Analyze stride and number of vertices
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index 26bf3dd9ed..57011752cc 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -21,6 +21,7 @@
#include <boost/container/small_vector.hpp>
#include "common/common_types.h"
+#include "common/spin_recursive_mutex.h"
#include "common/div_ceil.h"
#include "common/literals.h"
#include "common/lru_cache.h"
@@ -339,7 +340,7 @@ public:
} while (channel_state->has_deleted_buffers);
}
- std::recursive_mutex mutex;
+ Common::SpinRecursiveMutex mutex;
Runtime& runtime;
bool any_buffer_uploaded = false;
@@ -544,7 +545,8 @@ private:
bool immediately_free = false;
#endif
- std::array<BufferId, ((1ULL << 34) >> CACHING_PAGEBITS)> page_table;
+ // Covers the device address space (MaxwellDeviceTraits::device_virtual_bits).
+ std::array<BufferId, ((1ULL << 36) >> CACHING_PAGEBITS)> page_table;
Common::ScratchBuffer<u8> tmp_buffer;
};
diff --git a/src/video_core/buffer_cache/memory_tracker_base.h b/src/video_core/buffer_cache/memory_tracker_base.h
index b8547f3149..8936da023f 100644
--- a/src/video_core/buffer_cache/memory_tracker_base.h
+++ b/src/video_core/buffer_cache/memory_tracker_base.h
@@ -22,7 +22,7 @@ namespace VideoCommon {
template <typename DeviceTracker>
class MemoryTrackerBase {
- static constexpr size_t MAX_CPU_PAGE_BITS = 34;
+ static constexpr size_t MAX_CPU_PAGE_BITS = 36; // device addresses (MaxwellDeviceTraits)
static constexpr size_t HIGHER_PAGE_BITS = 22;
static constexpr size_t HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS;
static constexpr size_t HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL;
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