[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"
" 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"
" (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"
" Most accurate, but may be somewhat less performant.",
"GPU");
@@ -130,6 +133,8 @@ ZPDMode GetZPDMode() {
return ZPDMode::kFake;
} else if (mode == "strict") {
return ZPDMode::kStrict;
} else if (mode == "fast-alt") {
return ZPDMode::kFastAlt;
}
return ZPDMode::kFast;
}
@@ -968,7 +973,7 @@ bool CommandProcessor::BeginZPDReport(uint32_t report_address) {
PendingZPDSlot pending_slot = GetPendingZPDSlot(slot_base, end_record);
if (pending_slot.report_handle != kInvalidReportHandle) {
if (GetZPDMode() == ZPDMode::kFast) {
if (GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt) {
if (pending_slot.has_cached_delta) {
carried_cached_delta = pending_slot.cached_delta;
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
// something from a prior lifetime. The alternate fast path keeps it around
// 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);
}
@@ -1136,7 +1141,7 @@ bool CommandProcessor::EndZPDReport(uint32_t report_address,
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 &&
begin_record != report_record_base;
// 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) {
speculative = 1;
if (has_cached_delta &&
(cached_delta != 0 ||
cvars::occlusion_query_fast_preserve_cached_zero)) {
(cached_delta != 0 || GetZPDMode() == ZPDMode::kFastAlt)) {
speculative = cached_delta;
}
}
@@ -1201,7 +1205,7 @@ void CommandProcessor::OpenQuerySegment(bool can_close_submission) {
case QueryOpenResult::kDeferred:
return;
case QueryOpenResult::kPoolExhausted: {
if (GetZPDMode() == ZPDMode::kFast) {
if (GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt) {
// Fast mode favors forward progress over accuracy. Keep a minimal
// accumulated value instead of waiting for a slot to become available.
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.
enum class ZPDMode {
kFake, // Fake sample counts, no real GPU queries (fake)
kFast, // Real queries with speculative cached writes (fast)
kStrict, // Real queries, waits before writeback (strict)
kFake, // Fake sample counts, no real GPU queries (fake)
kFast, // Real queries with speculative cached writes (fast)
kFastAlt, // Fast queries, but preserves cached zeroes (fast-alt)
kStrict, // Real queries, waits before writeback (strict)
};
void SaveGPUSetting(GPUSetting setting, uint64_t value);
@@ -324,8 +325,7 @@ class CommandProcessor {
uint32_t begin_value = 0;
uint32_t pending_segments = 0;
// 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
// cvar is enabled.
// briefly look fully occluded. 0 is a valid delta for alternate fast path.
uint32_t cached_delta = 0;
bool has_cached_delta = false;
bool ended = false;

View File

@@ -5312,7 +5312,7 @@ CommandProcessor::QueryOpenResult D3D12CommandProcessor::OpenZPDQuery(
bool waited_for_submission = false;
if (is_pool_exhausted) {
if (GetZPDMode() == ZPDMode::kFast) {
if (GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt) {
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"
"Ignored if occlusion_query_fake_lower_threshold is -1.",
"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,
"Range of fake sample count values to walk for titles using the "
"D3D QueryBatch standard before wrapping back to "
@@ -86,7 +79,7 @@ DEFINE_int32(occlusion_query_querybatch_range, 0,
"unless necessary for a specific title.",
"GPU");
DEFINE_double(
occlusion_query_sample_count_saturation, 1.0,
occlusion_query_saturation, 1.0,
"Compress higher occlusion query sample counts before guest writeback.\n"
"This can be useful if effects such as lens flares appear too strong.\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_bool(occlusion_query_fast_preserve_cached_zero);
DECLARE_int32(occlusion_query_querybatch_range);
DECLARE_double(occlusion_query_sample_count_saturation);
DECLARE_double(occlusion_query_saturation);
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
// orphaned END. In fast mode, replay the last cached delta so polling
// code does not sit on the sentinel forever.
if (GetZPDMode() == ZPDMode::kFast) {
if (GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt) {
uint32_t cached_delta = 1;
auto cache_it = fast_zpd_report_cached_values_.find(report_record_base);
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();
}
if (is_pool_exhausted && GetZPDMode() == ZPDMode::kFast) {
if (is_pool_exhausted &&
(GetZPDMode() == ZPDMode::kFast || GetZPDMode() == ZPDMode::kFastAlt)) {
return QueryOpenResult::kPoolExhausted;
}

View File

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