[GPU] Simplify ZPD cvars

This commit is contained in:
bomabomabomaboma
2026-06-01 11:56:48 -07:00
committed by Radosław Gliński
parent fbd620c22b
commit 73945c06d7
8 changed files with 23 additions and 28 deletions

View File

@@ -58,7 +58,10 @@ DEFINE_string(
" though some effects may look slightly wrong.\n" " though some effects may look slightly wrong.\n"
" fast: Ask the GPU but don't wait for the answer. Writes a cached\n" " fast: Ask the GPU but don't wait for the answer. Writes a cached\n"
" result immediately and updates it when the GPU catches up.\n" " result immediately and updates it when the GPU catches up.\n"
" (default)\n" " Cached results bias toward visible when guessing. (default)\n"
" fast-alt: Variant of fast mode that keeps cached zero results for\n"
" unresolved reports. May improve effects relying on precise\n"
" visibility, but may be less stable for occlusion culling.\n"
" strict: Ask the GPU and wait for the real result before continuing.\n" " strict: Ask the GPU and wait for the real result before continuing.\n"
" Most accurate, but may be somewhat less performant.", " Most accurate, but may be somewhat less performant.",
"GPU"); "GPU");
@@ -130,6 +133,8 @@ ZPDMode GetZPDMode() {
return ZPDMode::kFake; return ZPDMode::kFake;
} else if (mode == "strict") { } else if (mode == "strict") {
return ZPDMode::kStrict; return ZPDMode::kStrict;
} else if (mode == "fast-alt") {
return ZPDMode::kFastAlt;
} }
return ZPDMode::kFast; return ZPDMode::kFast;
} }
@@ -968,7 +973,7 @@ bool CommandProcessor::BeginZPDReport(uint32_t report_address) {
PendingZPDSlot pending_slot = GetPendingZPDSlot(slot_base, end_record); PendingZPDSlot pending_slot = GetPendingZPDSlot(slot_base, end_record);
if (pending_slot.report_handle != kInvalidReportHandle) { if (pending_slot.report_handle != kInvalidReportHandle) {
if (GetZPDMode() == ZPDMode::kFast) { if (GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt) {
if (pending_slot.has_cached_delta) { if (pending_slot.has_cached_delta) {
carried_cached_delta = pending_slot.cached_delta; carried_cached_delta = pending_slot.cached_delta;
has_carried_cached_delta = true; has_carried_cached_delta = true;
@@ -1008,7 +1013,7 @@ bool CommandProcessor::BeginZPDReport(uint32_t report_address) {
// By default, BEGIN drops the cached value so an orphaned END doesn't replay // By default, BEGIN drops the cached value so an orphaned END doesn't replay
// something from a prior lifetime. The alternate fast path keeps it around // something from a prior lifetime. The alternate fast path keeps it around
// long enough for an async zero to help the next unresolved write. // long enough for an async zero to help the next unresolved write.
if (!cvars::occlusion_query_fast_preserve_cached_zero) { if (GetZPDMode() != ZPDMode::kFastAlt) {
fast_zpd_report_cached_values_.erase(end_record); fast_zpd_report_cached_values_.erase(end_record);
} }
@@ -1136,7 +1141,7 @@ bool CommandProcessor::EndZPDReport(uint32_t report_address,
WriteZPDReport(0, stored_end_record, 0, begin_value, false); WriteZPDReport(0, stored_end_record, 0, begin_value, false);
} }
if (GetZPDMode() == ZPDMode::kFast) { if (GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt) {
bool write_begin = begin_record && report_record_base && bool write_begin = begin_record && report_record_base &&
begin_record != report_record_base; begin_record != report_record_base;
// Unknown still means visible in fast mode. Reusing cached zeroes can help // Unknown still means visible in fast mode. Reusing cached zeroes can help
@@ -1147,8 +1152,7 @@ bool CommandProcessor::EndZPDReport(uint32_t report_address,
if (!resolved_immediately) { if (!resolved_immediately) {
speculative = 1; speculative = 1;
if (has_cached_delta && if (has_cached_delta &&
(cached_delta != 0 || (cached_delta != 0 || GetZPDMode() == ZPDMode::kFastAlt)) {
cvars::occlusion_query_fast_preserve_cached_zero)) {
speculative = cached_delta; speculative = cached_delta;
} }
} }
@@ -1201,7 +1205,7 @@ void CommandProcessor::OpenQuerySegment(bool can_close_submission) {
case QueryOpenResult::kDeferred: case QueryOpenResult::kDeferred:
return; return;
case QueryOpenResult::kPoolExhausted: { case QueryOpenResult::kPoolExhausted: {
if (GetZPDMode() == ZPDMode::kFast) { if (GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt) {
// Fast mode favors forward progress over accuracy. Keep a minimal // Fast mode favors forward progress over accuracy. Keep a minimal
// accumulated value instead of waiting for a slot to become available. // accumulated value instead of waiting for a slot to become available.
auto it = logical_zpd_reports_.find(zpd_active_segment_.report_handle); auto it = logical_zpd_reports_.find(zpd_active_segment_.report_handle);

View File

@@ -45,9 +45,10 @@ enum class ReadbackResolveMode {
// Occlusion queries - ZPD report mode. // Occlusion queries - ZPD report mode.
enum class ZPDMode { enum class ZPDMode {
kFake, // Fake sample counts, no real GPU queries (fake) kFake, // Fake sample counts, no real GPU queries (fake)
kFast, // Real queries with speculative cached writes (fast) kFast, // Real queries with speculative cached writes (fast)
kStrict, // Real queries, waits before writeback (strict) kFastAlt, // Fast queries, but preserves cached zeroes (fast-alt)
kStrict, // Real queries, waits before writeback (strict)
}; };
void SaveGPUSetting(GPUSetting setting, uint64_t value); void SaveGPUSetting(GPUSetting setting, uint64_t value);
@@ -324,8 +325,7 @@ class CommandProcessor {
uint32_t begin_value = 0; uint32_t begin_value = 0;
uint32_t pending_segments = 0; uint32_t pending_segments = 0;
// Last known delta. Carried forward on forced close so slot doesn't // Last known delta. Carried forward on forced close so slot doesn't
// briefly look fully occluded. 0 is a valid delta if the alternate fast // briefly look fully occluded. 0 is a valid delta for alternate fast path.
// cvar is enabled.
uint32_t cached_delta = 0; uint32_t cached_delta = 0;
bool has_cached_delta = false; bool has_cached_delta = false;
bool ended = false; bool ended = false;

View File

@@ -5312,7 +5312,7 @@ CommandProcessor::QueryOpenResult D3D12CommandProcessor::OpenZPDQuery(
bool waited_for_submission = false; bool waited_for_submission = false;
if (is_pool_exhausted) { if (is_pool_exhausted) {
if (GetZPDMode() == ZPDMode::kFast) { if (GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt) {
return QueryOpenResult::kPoolExhausted; return QueryOpenResult::kPoolExhausted;
} }

View File

@@ -71,13 +71,6 @@ DEFINE_int32(occlusion_query_fake_upper_threshold, 100,
"Keep this higher than occlusion_query_fake_lower_threshold.\n" "Keep this higher than occlusion_query_fake_lower_threshold.\n"
"Ignored if occlusion_query_fake_lower_threshold is -1.", "Ignored if occlusion_query_fake_lower_threshold is -1.",
"GPU"); "GPU");
DEFINE_bool(occlusion_query_fast_preserve_cached_zero, false,
"Alternate fast ZPD behavior that allows saved zero results to be "
"used for unresolved fast writes instead of forcing them visible.\n"
"This can materially improve flare accuracy in titles that might "
"otherwise need strict mode, but it also tends to break occlusion "
"culling in some titles.",
"GPU");
DEFINE_int32(occlusion_query_querybatch_range, 0, DEFINE_int32(occlusion_query_querybatch_range, 0,
"Range of fake sample count values to walk for titles using the " "Range of fake sample count values to walk for titles using the "
"D3D QueryBatch standard before wrapping back to " "D3D QueryBatch standard before wrapping back to "
@@ -86,7 +79,7 @@ DEFINE_int32(occlusion_query_querybatch_range, 0,
"unless necessary for a specific title.", "unless necessary for a specific title.",
"GPU"); "GPU");
DEFINE_double( DEFINE_double(
occlusion_query_sample_count_saturation, 1.0, occlusion_query_saturation, 1.0,
"Compress higher occlusion query sample counts before guest writeback.\n" "Compress higher occlusion query sample counts before guest writeback.\n"
"This can be useful if effects such as lens flares appear too strong.\n" "This can be useful if effects such as lens flares appear too strong.\n"
"1.0 = default behavior\n" "1.0 = default behavior\n"

View File

@@ -32,11 +32,9 @@ DECLARE_int32(occlusion_query_fake_lower_threshold);
DECLARE_int32(occlusion_query_fake_upper_threshold); DECLARE_int32(occlusion_query_fake_upper_threshold);
DECLARE_bool(occlusion_query_fast_preserve_cached_zero);
DECLARE_int32(occlusion_query_querybatch_range); DECLARE_int32(occlusion_query_querybatch_range);
DECLARE_double(occlusion_query_sample_count_saturation); DECLARE_double(occlusion_query_saturation);
DECLARE_int32(anisotropic_override); DECLARE_int32(anisotropic_override);

View File

@@ -1011,7 +1011,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_EVENT_WRITE_ZPD(
// No logical report is active for this slot, so this is likely an // No logical report is active for this slot, so this is likely an
// orphaned END. In fast mode, replay the last cached delta so polling // orphaned END. In fast mode, replay the last cached delta so polling
// code does not sit on the sentinel forever. // code does not sit on the sentinel forever.
if (GetZPDMode() == ZPDMode::kFast) { if (GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt) {
uint32_t cached_delta = 1; uint32_t cached_delta = 1;
auto cache_it = fast_zpd_report_cached_values_.find(report_record_base); auto cache_it = fast_zpd_report_cached_values_.find(report_record_base);
if (cache_it != fast_zpd_report_cached_values_.end()) { if (cache_it != fast_zpd_report_cached_values_.end()) {

View File

@@ -3318,7 +3318,8 @@ CommandProcessor::QueryOpenResult VulkanCommandProcessor::OpenZPDQuery(
is_pool_exhausted = !zpd_host_query_pool_->has_free_indices(); is_pool_exhausted = !zpd_host_query_pool_->has_free_indices();
} }
if (is_pool_exhausted && GetZPDMode() == ZPDMode::kFast) { if (is_pool_exhausted &&
(GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt)) {
return QueryOpenResult::kPoolExhausted; return QueryOpenResult::kPoolExhausted;
} }

View File

@@ -102,8 +102,7 @@ struct XenosZPDReport {
static uint32_t SaturateSampleCount(uint32_t sample_count) { static uint32_t SaturateSampleCount(uint32_t sample_count) {
double saturation = std::clamp( double saturation = std::clamp(
static_cast<double>(cvars::occlusion_query_sample_count_saturation), static_cast<double>(cvars::occlusion_query_saturation), 0.0, 1.0);
0.0, 1.0);
if (sample_count == 0 || saturation >= 1.0) { if (sample_count == 0 || saturation >= 1.0) {
return sample_count; return sample_count;