[GPU] Double-buffer readback_resolve.

Introduce "fast" readback resolve that reads from previous frame's
resolve buffers, quick swap between buffers each frame to avoid
copies so minimal performance impact and mostly correct bahavior.
Checks if previous frame had a buffer for the current resolve and
falls through to the slow path, which also allows to support
"screenshot" features in the games that do that without stalling
on normal resolve operations.

Re-enabled readback_memexport as separate feature, was previously
bundled with readback_resolve (probably not intentionally) and
ensures destination address is writeable to avoid memory access
related crashes and unnecessary work.

readback_resolve cvar changes from bool to string ternary with
"none", "fast" and "full" options, defaulting to the new "fast"
mode.
This commit is contained in:
Herman S.
2025-10-31 21:20:37 +09:00
committed by Radosław Gliński
parent 93adb2bb95
commit e2c33686cc
8 changed files with 482 additions and 124 deletions

View File

@@ -48,11 +48,12 @@ DEFINE_bool(clear_memory_page_state, false,
"for 'Team Ninja' Games to fix missing character models)",
"GPU");
DEFINE_bool(
readback_resolve, false,
"Read render-to-texture results on the CPU. This may be "
"needed in some games, for instance, for screenshots in saved games, but "
"causes mid-frame synchronization, so it has a huge performance impact.",
DEFINE_string(
readback_resolve, "fast",
"Controls CPU readback of render-to-texture resolve results.\n"
" fast: Read from previous frame (1 frame delay, no GPU stall - default)\n"
" full: Wait for GPU to finish (accurate but slow, GPU-CPU sync stall)\n"
" none: Disable readback completely (some games render better without it)",
"GPU");
DEFINE_bool(
@@ -73,9 +74,6 @@ void SaveGPUSetting(GPUSetting setting, uint64_t value) {
case GPUSetting::ClearMemoryPageState:
OVERRIDE_bool(clear_memory_page_state, static_cast<bool>(value));
break;
case GPUSetting::ReadbackResolve:
OVERRIDE_bool(readback_resolve, static_cast<bool>(value));
break;
case GPUSetting::ReadbackMemexport:
OVERRIDE_bool(readback_memexport, static_cast<bool>(value));
break;
@@ -86,14 +84,28 @@ bool GetGPUSetting(GPUSetting setting) {
switch (setting) {
case GPUSetting::ClearMemoryPageState:
return cvars::clear_memory_page_state;
case GPUSetting::ReadbackResolve:
return cvars::readback_resolve;
case GPUSetting::ReadbackMemexport:
return cvars::readback_memexport;
}
return false;
}
ReadbackResolveMode GetReadbackResolveMode() {
const std::string& mode = cvars::readback_resolve;
if (mode == "full") {
return ReadbackResolveMode::kFull;
} else if (mode == "none") {
return ReadbackResolveMode::kDisabled;
} else {
// Default to "fast" for any unrecognized value
return ReadbackResolveMode::kFast;
}
}
void SetReadbackResolveMode(const std::string& mode) {
OVERRIDE_string(readback_resolve, mode);
}
using namespace xe::gpu::xenos;
CommandProcessor::CommandProcessor(GraphicsSystem* graphics_system,