graphics: rate-limit the targeted preview-resolve readbacks
This commit is contained in:
parent
b4d018c405
commit
f8cbe94367
4 changed files with 56 additions and 1 deletions
|
|
@ -37,6 +37,7 @@ REXCVAR_DECLARE(std::string, readback_resolve);
|
||||||
REXCVAR_DECLARE(bool, readback_resolve_half_pixel_offset);
|
REXCVAR_DECLARE(bool, readback_resolve_half_pixel_offset);
|
||||||
REXCVAR_DECLARE(int32_t, native_render_force_resolve_readback_max_length);
|
REXCVAR_DECLARE(int32_t, native_render_force_resolve_readback_max_length);
|
||||||
REXCVAR_DECLARE(int32_t, native_render_force_resolve_readback_min_length);
|
REXCVAR_DECLARE(int32_t, native_render_force_resolve_readback_min_length);
|
||||||
|
REXCVAR_DECLARE(int32_t, native_render_targeted_readback_min_interval_ms);
|
||||||
REXCVAR_DECLARE(bool, readback_memexport);
|
REXCVAR_DECLARE(bool, readback_memexport);
|
||||||
REXCVAR_DECLARE(bool, readback_memexport_fast);
|
REXCVAR_DECLARE(bool, readback_memexport_fast);
|
||||||
REXCVAR_DECLARE(bool, occlusion_query_enable);
|
REXCVAR_DECLARE(bool, occlusion_query_enable);
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,19 @@ REXCVAR_DEFINE_INT32(native_render_force_resolve_readback_max_length, 0, "GPU",
|
||||||
.range(0, 16 * 1024 * 1024)
|
.range(0, 16 * 1024 * 1024)
|
||||||
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
||||||
|
|
||||||
|
REXCVAR_DEFINE_INT32(native_render_targeted_readback_min_interval_ms, 33, "GPU",
|
||||||
|
"Minimum interval between the targeted always-on resolve readbacks "
|
||||||
|
"(the non-gameplay Import-Skater/preview target) in milliseconds. "
|
||||||
|
"Each readback is a synchronous GPU drain plus a multi-megabyte CPU "
|
||||||
|
"copy; menu flows that re-resolve the preview target every frame "
|
||||||
|
"(trick guide demo pages) otherwise saturate the command processor "
|
||||||
|
"with hundreds of drains per second. Skipped copies leave the "
|
||||||
|
"previous CPU-visible contents in place (the consumer is a preview "
|
||||||
|
"image). 0 disables the throttle. Does not affect the app-armed "
|
||||||
|
"photo-grab window.")
|
||||||
|
.range(0, 1000)
|
||||||
|
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
||||||
|
|
||||||
REXCVAR_DEFINE_INT32(native_render_force_resolve_readback_min_length, 0, "GPU",
|
REXCVAR_DEFINE_INT32(native_render_force_resolve_readback_min_length, 0, "GPU",
|
||||||
"Lower bound companion to "
|
"Lower bound companion to "
|
||||||
"native_render_force_resolve_readback_max_length: when > 0, resolves "
|
"native_render_force_resolve_readback_max_length: when > 0, resolves "
|
||||||
|
|
|
||||||
|
|
@ -3616,10 +3616,30 @@ bool D3D12CommandProcessor::IssueCopy_ReadbackResolvePath() {
|
||||||
if (readback_mode == ReadbackResolveMode::kDisabled) {
|
if (readback_mode == ReadbackResolveMode::kDisabled) {
|
||||||
constexpr uint32_t kImportSkaterPreviewResolveAddress = UINT32_C(0x04911000);
|
constexpr uint32_t kImportSkaterPreviewResolveAddress = UINT32_C(0x04911000);
|
||||||
constexpr uint32_t kImportSkaterPreviewResolveLength = UINT32_C(0x2D0000);
|
constexpr uint32_t kImportSkaterPreviewResolveLength = UINT32_C(0x2D0000);
|
||||||
const bool force_scaled_resolve_cpu_copy =
|
bool force_scaled_resolve_cpu_copy =
|
||||||
!IsGameplayStateActive(kernel_state_) && is_scaled &&
|
!IsGameplayStateActive(kernel_state_) && is_scaled &&
|
||||||
written_address == kImportSkaterPreviewResolveAddress &&
|
written_address == kImportSkaterPreviewResolveAddress &&
|
||||||
written_length == kImportSkaterPreviewResolveLength;
|
written_length == kImportSkaterPreviewResolveLength;
|
||||||
|
// Rate-limit the targeted copies: each one is a synchronous GPU drain
|
||||||
|
// plus a multi-megabyte CPU copy, and menu flows that re-resolve the
|
||||||
|
// preview target every frame (trick guide demo pages) issue hundreds
|
||||||
|
// per second. A skipped copy leaves the previous CPU-visible contents
|
||||||
|
// in place, which a preview consumer tolerates. The app-armed
|
||||||
|
// photo-grab window below is deliberately NOT throttled (the display
|
||||||
|
// card composes from exact frames). Mirrors the Vulkan gate.
|
||||||
|
if (force_scaled_resolve_cpu_copy) {
|
||||||
|
const int32_t min_interval_ms =
|
||||||
|
REXCVAR_GET(native_render_targeted_readback_min_interval_ms);
|
||||||
|
if (min_interval_ms > 0) {
|
||||||
|
static uint64_t s_last_targeted_copy_ms = 0;
|
||||||
|
const uint64_t now_ms = rex::chrono::Clock::QueryHostUptimeMillis();
|
||||||
|
if (now_ms - s_last_targeted_copy_ms < uint64_t(min_interval_ms)) {
|
||||||
|
force_scaled_resolve_cpu_copy = false;
|
||||||
|
} else {
|
||||||
|
s_last_targeted_copy_ms = now_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// App-armed readback window (Skate 3 photo grab): the game CPU-reads the
|
// App-armed readback window (Skate 3 photo grab): the game CPU-reads the
|
||||||
// resolved 1152x640 PostFX screenshot target (also 0x04911000) while the
|
// resolved 1152x640 PostFX screenshot target (also 0x04911000) while the
|
||||||
// gameplay presence context is still 1 (photo missions), so the targeted
|
// gameplay presence context is still 1 (photo missions), so the targeted
|
||||||
|
|
|
||||||
|
|
@ -5824,6 +5824,27 @@ bool VulkanCommandProcessor::IssueCopy_ReadbackResolvePath() {
|
||||||
!IsGameplayStateActive(kernel_state_) && is_scaled &&
|
!IsGameplayStateActive(kernel_state_) && is_scaled &&
|
||||||
written_address == kImportSkaterPreviewResolveAddress &&
|
written_address == kImportSkaterPreviewResolveAddress &&
|
||||||
written_length == kImportSkaterPreviewResolveLength;
|
written_length == kImportSkaterPreviewResolveLength;
|
||||||
|
// Rate-limit the targeted copies: each one is a synchronous GPU drain
|
||||||
|
// plus a multi-megabyte CPU copy, and menu flows that re-resolve the
|
||||||
|
// preview target every frame (trick guide demo pages) issue hundreds
|
||||||
|
// per second, enough to halve the frame rate on their own. A skipped
|
||||||
|
// copy leaves the previous CPU-visible contents in place, which a
|
||||||
|
// preview consumer tolerates. The app-armed photo-grab window below is
|
||||||
|
// deliberately NOT throttled (the display card composes from exact
|
||||||
|
// frames).
|
||||||
|
if (force_scaled_resolve_cpu_copy) {
|
||||||
|
const int32_t min_interval_ms =
|
||||||
|
REXCVAR_GET(native_render_targeted_readback_min_interval_ms);
|
||||||
|
if (min_interval_ms > 0) {
|
||||||
|
static uint64_t s_last_targeted_copy_ms = 0;
|
||||||
|
const uint64_t now_ms = rex::chrono::Clock::QueryHostUptimeMillis();
|
||||||
|
if (now_ms - s_last_targeted_copy_ms < uint64_t(min_interval_ms)) {
|
||||||
|
force_scaled_resolve_cpu_copy = false;
|
||||||
|
} else {
|
||||||
|
s_last_targeted_copy_ms = now_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// App-armed readback window (Skate 3 photo grab): the game CPU-reads the
|
// App-armed readback window (Skate 3 photo grab): the game CPU-reads the
|
||||||
// resolved 1152x640 PostFX screenshot target (also 0x04911000) while the
|
// resolved 1152x640 PostFX screenshot target (also 0x04911000) while the
|
||||||
// gameplay presence context is still 1 (photo missions), so the targeted
|
// gameplay presence context is still 1 (photo missions), so the targeted
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue