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.h10
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h31
2 files changed, 39 insertions, 2 deletions
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 42d7ba066d..e90af3214d 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -279,8 +279,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
const DAddr diff = base_address - *cpu_src_address;
const DAddr new_base_address = *cpu_dest_address + diff;
tmp_intervals.push_back({new_base_address, size});
- if (AsyncBufferDownloads()) {
+ if (DownloadsAtFence(new_base_address, size)) {
uncommitted_gpu_modified_ranges.Add(new_base_address, size);
+ } else {
+ ArmReadTraps(new_base_address, size);
}
};
gpu_modified_ranges.ForEachInRange(*cpu_src_address, amount, mirror);
@@ -1603,8 +1605,10 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u3
}
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
gpu_modified_ranges.Add(device_addr, size);
- if (AsyncBufferDownloads()) {
+ if (DownloadsAtFence(device_addr, size)) {
uncommitted_gpu_modified_ranges.Add(device_addr, size);
+ } else {
+ ArmReadTraps(device_addr, size);
}
}
@@ -2090,6 +2094,8 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
if constexpr (requires { runtime.OnBufferDeleted(slot_buffers[buffer_id]); }) {
runtime.OnBufferDeleted(slot_buffers[buffer_id]);
}
+ read_trapped_ranges.Subtract(slot_buffers[buffer_id].CpuAddr(),
+ slot_buffers[buffer_id].SizeBytes());
bool dirty_index{false};
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> dirty_vertex_buffers;
const auto scalar_replace = [buffer_id](Binding& binding) {
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index 45a1836341..d8a61b7913 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -245,6 +245,12 @@ public:
std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(DAddr device_addr, u64 size);
+ /// Without downloads at every fence, a guest CPU read is the only way GPU-written buffer
+ /// data reaches guest memory, so reads must ask the buffer cache (GetFlushArea).
+ bool FlushesOnCpuRead() const {
+ return !AsyncBufferDownloads();
+ }
+
bool InlineMemory(DAddr dest_address, size_t copy_size, std::span<const u8> inlined_buffer);
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size);
@@ -536,6 +542,31 @@ private:
}
return gpu_memory == nullptr || !gpu_memory->UsesNativeTiling();
}
+
+ /// Whether a GPU write to this range is copied back at the next fence. Ranges the guest CPU
+ /// has read (Preflushable, set by the first reactive flush) always are: later reads skip the
+ /// flush and rely on these downloads, e.g. collision meshes skinned by compute shaders.
+ bool DownloadsAtFence(DAddr device_addr, u64 size) noexcept {
+ return AsyncBufferDownloads() || memory_tracker.IsRegionPreflushable(device_addr, size);
+ }
+
+ /// GPU writes that are not downloaded at fences: trap the guest CPU's next read of their
+ /// pages, which flushes them once and marks them Preflushable (then DownloadsAtFence).
+ void ArmReadTraps(DAddr device_addr, u64 size) {
+ const DAddr begin = Common::AlignDown(device_addr, Core::DEVICE_PAGESIZE);
+ const DAddr end = Common::AlignUp(device_addr + size, Core::DEVICE_PAGESIZE);
+ u64 armed_bytes = 0;
+ read_trapped_ranges.ForEachInRange(begin, end - begin, [&](DAddr lower, DAddr upper) {
+ armed_bytes += upper - lower;
+ });
+ if (armed_bytes == end - begin) {
+ return;
+ }
+ read_trapped_ranges.Add(begin, end - begin);
+ device_memory.ArmReadTraps(begin, end - begin);
+ }
+
+ Common::RangeSet<DAddr> read_trapped_ranges;
Common::RangeSet<DAddr> gpu_modified_ranges;
std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges;