You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
readbacks_mode ships off by default because the implementation has a known perf and stability cost that has been called out across PR #2668, #3178, #3305 ("currently slow and will most likely be rewritten"), #4085, #4120, and issues #3322, #3346, #3826, #4215. Every one of those threads argues from hand-measured numbers (fps deltas, "12 fps on AMD", "significant stuttering") collected with ad-hoc methodology. There is no shared measurement framework, which means successive PRs are debated on incomparable data. PR #3404's fence-detection rewrite stalled on correctness concerns that, in hindsight, could have been narrowed if a common harness existed.
I built one to investigate a separate issue (DriveClub race-start dim, CUSA00003) and it landed me on concrete numbers that I'd like to share, and on a proposal for what would actually reduce the structural cost. I can only validate on one GPU, so I want feedback before going further.
Proposed Change
Two independent pieces, in order of risk.
1. Land a measurement harness upstream — src/common/readback_metrics.{h,cpp} + ~30 lines of instrumentation across vk_scheduler, buffer_cache, texture_cache, fault_manager, page_manager, vk_presenter. Atomic counters always live (~5 ns / op), wall-clock timing gated on SHADPS4_READBACKS_METRICS=<csv path>. Zero behavioural change. CSV dump on exit (atexit, at_quick_exit, SIGTERM chain).
Counters: Scheduler::Finish / Wait / Submit counts and cumulative ns; BufferCache::ReadMemory + DownloadBufferMemory bytes + ns; TextureCache::DownloadImageMemory; FaultManager::ProcessFaultBuffer dispatches; PageManager::Protect syscalls + ns; bounded frame-time ring for p50/p95/p99.
Rationale: fills the "everyone measures differently" gap. Lets future readback PRs be A/B'd against a common baseline. Small, self-contained, opt-in at runtime.
2. Propose GpuReadbacksMode::Batched — coalesce BufferCache::ReadMemory calls within a fault window into a single Scheduler::Finish flushed at the next PM4 sync packet (WaitRegMem / fence EventWrite). Ordering guard: a guest read targeting a range with a pending batched download force-flushes first.
Rationale: per-call measured at ~68 µs on RTX 5090, ~106 ReadMemory/frame in Bloodborne gameplay = 7.23 ms/frame of cumulative CPU stall. One Finish per batch collapses that regardless of per-Finish-wait cost, which is the axis that scales poorly on AMD (see #3322, #4120).
Explicitly not proposing: flipping the global default, removing Relaxed, or reviving PR #3404's fence-detection prepass. Those are separate calls.
Bloodborne gameplay p99 is identical under Precise and Disabled (50.00 ms both). The tail-hitch is shader-compile, not readback-related.
Relaxed matches Precise on Finish cost in our clean-cell data. Didn't reproduce the "Precise stutters during asset loads" symptom that motivated PR Low readbacks mode #4085. May be hardware-specific, may need a longer-session repro.
FaultManager::ProcessFaultBuffer dispatches zero times in both games. The fault-buffer compute path is unused for these titles — a potential per-game opt-out.
Explicit non-conclusion: the data above does not justify flipping the global default. It only says Precise is viable on one high-end Nvidia config for two games. The default-flip question needs exactly the kind of data that a landed harness would let others produce.
Feedback Period
2 weeks, or until a maintainer weighs in on whether the harness is welcome and whether the batching direction aligns with the internal roadmap (especially vs PR #3404's approach).
Questions
Would you merge the measurement harness as a standalone PR, behind the opt-in env var? Or do you prefer this routed through Tracy zones instead?
Is there an existing contributor with AMD RDNA 2 / 3 / 4 hardware who'd be willing to run the harness against their setup? That's the only way to close the "does batching actually fix the AMD regression" loop.
Any game-specific repro cases you'd want run through the harness before deciding? (I still have Bloodborne + DriveClub set up.)
Any Other Things
Full research dossier that seeded this work: docs/readbacks-perf/research/readbacks-research.md. Covers PR/issue timeline, per-frame cost breakdown from the source, failure modes, comparable techniques from yuzu / Ryujinx / Dolphin / PCSX2 / PPSSPP.
Related prior work on this fork: the same harness was how I traced DriveClub CUSA00003's race-start blackout back to readbacks_mode being off (issue: guest auto-exposure is a GPU→CPU histogram feedback loop that fails silently under Disabled). Write-up at docs/driveclub-investigation/phase-31-readbacks-mode-fix.md on the gamma-debug branch.
All benchmarks are reproducible via scripts/benchmark_readbacks.sh <mode> <label> <seconds> plus scripts/analyze_readbacks.py <run-dir> on the readbacks-perf branch.
Motivation
readbacks_modeships off by default because the implementation has a known perf and stability cost that has been called out across PR #2668, #3178, #3305 ("currently slow and will most likely be rewritten"), #4085, #4120, and issues #3322, #3346, #3826, #4215. Every one of those threads argues from hand-measured numbers (fps deltas, "12 fps on AMD", "significant stuttering") collected with ad-hoc methodology. There is no shared measurement framework, which means successive PRs are debated on incomparable data. PR #3404's fence-detection rewrite stalled on correctness concerns that, in hindsight, could have been narrowed if a common harness existed.I built one to investigate a separate issue (DriveClub race-start dim, CUSA00003) and it landed me on concrete numbers that I'd like to share, and on a proposal for what would actually reduce the structural cost. I can only validate on one GPU, so I want feedback before going further.
Proposed Change
Two independent pieces, in order of risk.
1. Land a measurement harness upstream —
src/common/readback_metrics.{h,cpp}+ ~30 lines of instrumentation acrossvk_scheduler,buffer_cache,texture_cache,fault_manager,page_manager,vk_presenter. Atomic counters always live (~5 ns / op), wall-clock timing gated onSHADPS4_READBACKS_METRICS=<csv path>. Zero behavioural change. CSV dump on exit (atexit,at_quick_exit, SIGTERM chain).Counters:
Scheduler::Finish/Wait/Submitcounts and cumulative ns;BufferCache::ReadMemory+DownloadBufferMemorybytes + ns;TextureCache::DownloadImageMemory;FaultManager::ProcessFaultBufferdispatches;PageManager::Protectsyscalls + ns; bounded frame-time ring for p50/p95/p99.Rationale: fills the "everyone measures differently" gap. Lets future readback PRs be A/B'd against a common baseline. Small, self-contained, opt-in at runtime.
2. Propose
GpuReadbacksMode::Batched— coalesceBufferCache::ReadMemorycalls within a fault window into a singleScheduler::Finishflushed at the next PM4 sync packet (WaitRegMem/ fenceEventWrite). Ordering guard: a guest read targeting a range with a pending batched download force-flushes first.Rationale: per-call measured at ~68 µs on RTX 5090, ~106
ReadMemory/frame in Bloodborne gameplay = 7.23 ms/frame of cumulative CPU stall. One Finish per batch collapses that regardless of per-Finish-wait cost, which is the axis that scales poorly on AMD (see #3322, #4120).Explicitly not proposing: flipping the global default, removing Relaxed, or reviving PR #3404's fence-detection prepass. Those are separate calls.
Data
Single hardware: RTX 5090 + Ryzen 7950X3D + Arch Linux + Nvidia proprietary. Branch
akitaonrails/shadPS4:readbacks-perf, commit133e3d7c. Methodology + raw CSVs atdocs/readbacks-perf/.Observations (this hardware only):
FaultManager::ProcessFaultBufferdispatches zero times in both games. The fault-buffer compute path is unused for these titles — a potential per-game opt-out.Explicit non-conclusion: the data above does not justify flipping the global default. It only says Precise is viable on one high-end Nvidia config for two games. The default-flip question needs exactly the kind of data that a landed harness would let others produce.
Feedback Period
2 weeks, or until a maintainer weighs in on whether the harness is welcome and whether the batching direction aligns with the internal roadmap (especially vs PR #3404's approach).
Questions
GpuReadbacksMode::Batcheda direction you'd consider, or does it overlap too much with PR video_core: Readback optimizations #3404? If so, I'd rather rebase on video_core: Readback optimizations #3404's work than start fresh.Any Other Things
docs/readbacks-perf/research/readbacks-research.md. Covers PR/issue timeline, per-frame cost breakdown from the source, failure modes, comparable techniques from yuzu / Ryujinx / Dolphin / PCSX2 / PPSSPP.readbacks_modebeing off (issue: guest auto-exposure is a GPU→CPU histogram feedback loop that fails silently under Disabled). Write-up atdocs/driveclub-investigation/phase-31-readbacks-mode-fix.mdon thegamma-debugbranch.scripts/benchmark_readbacks.sh <mode> <label> <seconds>plusscripts/analyze_readbacks.py <run-dir>on thereadbacks-perfbranch.