[GPU] Implement ZPD occlusion queries

Implements EVENT_WRITE_ZPD handling across shared CommandProcessor, D3D12, and Vulkan backends.

This adds a shared report lifecycle. Reports can span submissions/render passes, split into multiple segments, and asynchronously retire. Reports can also survive same-slot reuse, which is critical for titles that aggressively recycle against a small pool.

RTV/FBO use their respective host occlusion queries, whereas ROV/FSI use counter buffers instead, with shader helpers that are called from the translated PS, accumulating surviving MSAA sample coverage into active slots. Regardless of approach, results are copied to small, dedicated readbacks and fed through shared retirement.

Fast mode writes speculative values and patches in resolved results later. Strict mode waits for real results with a small retire backstop so it doesn't spin forever when a report gets weird. Fake mode's fundamentals are unchanged, it just now uses XenosZPDReport memory helpers like all the modes do.

Also adds optional sample count saturation for instances when attenuation isn't a one-for-one with real hardware, an optional alternate fast behavior that preserves cached zeroes (that can improve flare-heavy titles but might regress occlusion culling), and normalization for upscaling.

QueryBatch is still unsupported other than a fake path that's enabled when a range is set via cvar.
This commit is contained in:
bomabomabomaboma
2026-05-01 21:09:16 +00:00
committed by Radosław Gliński
parent 29311ddcdd
commit fbd620c22b
33 changed files with 4113 additions and 89 deletions

View File

@@ -5,6 +5,7 @@ XE_UI_VULKAN_FUNCTION(vkAllocateMemory)
XE_UI_VULKAN_FUNCTION(vkBeginCommandBuffer)
XE_UI_VULKAN_FUNCTION(vkBindBufferMemory)
XE_UI_VULKAN_FUNCTION(vkBindImageMemory)
XE_UI_VULKAN_FUNCTION(vkCmdBeginQuery)
XE_UI_VULKAN_FUNCTION(vkCmdBeginRenderPass)
XE_UI_VULKAN_FUNCTION(vkCmdBindDescriptorSets)
XE_UI_VULKAN_FUNCTION(vkCmdBindIndexBuffer)
@@ -16,12 +17,16 @@ XE_UI_VULKAN_FUNCTION(vkCmdClearColorImage)
XE_UI_VULKAN_FUNCTION(vkCmdCopyBuffer)
XE_UI_VULKAN_FUNCTION(vkCmdCopyBufferToImage)
XE_UI_VULKAN_FUNCTION(vkCmdCopyImageToBuffer)
XE_UI_VULKAN_FUNCTION(vkCmdCopyQueryPoolResults)
XE_UI_VULKAN_FUNCTION(vkCmdDispatch)
XE_UI_VULKAN_FUNCTION(vkCmdDraw)
XE_UI_VULKAN_FUNCTION(vkCmdDrawIndexed)
XE_UI_VULKAN_FUNCTION(vkCmdEndQuery)
XE_UI_VULKAN_FUNCTION(vkCmdEndRenderPass)
XE_UI_VULKAN_FUNCTION(vkCmdFillBuffer)
XE_UI_VULKAN_FUNCTION(vkCmdPipelineBarrier)
XE_UI_VULKAN_FUNCTION(vkCmdPushConstants)
XE_UI_VULKAN_FUNCTION(vkCmdResetQueryPool)
XE_UI_VULKAN_FUNCTION(vkCmdSetBlendConstants)
XE_UI_VULKAN_FUNCTION(vkCmdSetDepthBias)
XE_UI_VULKAN_FUNCTION(vkCmdSetScissor)
@@ -42,6 +47,7 @@ XE_UI_VULKAN_FUNCTION(vkCreateImage)
XE_UI_VULKAN_FUNCTION(vkCreateImageView)
XE_UI_VULKAN_FUNCTION(vkCreatePipelineCache)
XE_UI_VULKAN_FUNCTION(vkCreatePipelineLayout)
XE_UI_VULKAN_FUNCTION(vkCreateQueryPool)
XE_UI_VULKAN_FUNCTION(vkCreateRenderPass)
XE_UI_VULKAN_FUNCTION(vkCreateSampler)
XE_UI_VULKAN_FUNCTION(vkCreateSemaphore)
@@ -58,6 +64,7 @@ XE_UI_VULKAN_FUNCTION(vkDestroyImageView)
XE_UI_VULKAN_FUNCTION(vkDestroyPipeline)
XE_UI_VULKAN_FUNCTION(vkDestroyPipelineCache)
XE_UI_VULKAN_FUNCTION(vkDestroyPipelineLayout)
XE_UI_VULKAN_FUNCTION(vkDestroyQueryPool)
XE_UI_VULKAN_FUNCTION(vkDestroyRenderPass)
XE_UI_VULKAN_FUNCTION(vkDestroySampler)
XE_UI_VULKAN_FUNCTION(vkDestroySemaphore)

View File

@@ -0,0 +1,3 @@
// VK_EXT_host_query_reset functions used in Xenia.
// Promoted to Vulkan 1.2 core.
XE_UI_VULKAN_FUNCTION_PROMOTED(vkResetQueryPoolEXT, vkResetQueryPool)

View File

@@ -175,6 +175,7 @@ std::unique_ptr<VulkanDevice> VulkanDevice::CreateIfSupported(
}
bool ext_1_2_KHR_sampler_mirror_clamp_to_edge = false;
bool ext_1_2_EXT_host_query_reset = false;
bool ext_1_1_KHR_maintenance1 = false;
bool ext_1_2_KHR_shader_float_controls = false;
bool ext_EXT_fragment_shader_interlock = false;
@@ -195,6 +196,7 @@ std::unique_ptr<VulkanDevice> VulkanDevice::CreateIfSupported(
XE_UI_VULKAN_STRUCT_PROMOTED_EXTENSION(KHR_sampler_ycbcr_conversion, 1, 1)
// #198. Also must be enabled for VK_KHR_spirv_1_4.
XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION(KHR_shader_float_controls, 1, 2)
XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION(EXT_host_query_reset, 1, 2)
// #252.
XE_UI_VULKAN_LOCAL_EXTENSION(EXT_fragment_shader_interlock)
// #277.
@@ -282,6 +284,9 @@ std::unique_ptr<VulkanDevice> VulkanDevice::CreateIfSupported(
VulkanFeatures<VkPhysicalDeviceVulkan12Features,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES>
features_1_2;
VulkanFeatures<VkPhysicalDeviceHostQueryResetFeatures,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES>
features_EXT_host_query_reset;
VulkanFeatures<VkPhysicalDeviceVulkan13Features,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES>
features_1_3;
@@ -310,6 +315,9 @@ std::unique_ptr<VulkanDevice> VulkanDevice::CreateIfSupported(
if (get_physical_device_properties2_supported) {
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 2, 0)) {
features_1_2.Link(supported_features_2, device_create_info);
} else if (ext_1_2_EXT_host_query_reset) {
features_EXT_host_query_reset.Link(supported_features_2,
device_create_info);
}
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 3, 0)) {
features_1_3.Link(supported_features_2, device_create_info);
@@ -631,12 +639,18 @@ std::unique_ptr<VulkanDevice> VulkanDevice::CreateIfSupported(
XE_UI_VULKAN_FEATURE_2(features_1_2, samplerMirrorClampToEdge);
XE_UI_VULKAN_FEATURE_2(features_1_2, uniformBufferStandardLayout);
XE_UI_VULKAN_FEATURE_2(features_1_2, scalarBlockLayout);
XE_UI_VULKAN_FEATURE_2(features_1_2, hostQueryReset);
}
} else {
if (ext_1_2_KHR_sampler_mirror_clamp_to_edge) {
XE_UI_VULKAN_FEATURE_IMPLIED(samplerMirrorClampToEdge)
}
if (ext_1_2_EXT_host_query_reset && with_gpu_emulation) {
XE_UI_VULKAN_FEATURE_2(features_EXT_host_query_reset, hostQueryReset);
}
}
device->extensions_.ext_1_2_EXT_host_query_reset =
ext_1_2_EXT_host_query_reset;
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 3, 0)) {
if (with_gpu_emulation) {
@@ -743,6 +757,9 @@ std::unique_ptr<VulkanDevice> VulkanDevice::CreateIfSupported(
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
#include "xenia/ui/vulkan/functions/device_1_1_khr_bind_memory2.inc"
#include "xenia/ui/vulkan/functions/device_1_1_khr_get_memory_requirements2.inc"
}
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 2, 0)) {
#include "xenia/ui/vulkan/functions/device_1_2_ext_host_query_reset.inc"
}
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 3, 0)) {
#include "xenia/ui/vulkan/functions/device_1_3_khr_maintenance4.inc"
@@ -763,6 +780,11 @@ std::unique_ptr<VulkanDevice> VulkanDevice::CreateIfSupported(
#include "xenia/ui/vulkan/functions/device_1_1_khr_bind_memory2.inc"
}
}
if (properties.apiVersion < VK_MAKE_API_VERSION(0, 1, 2, 0)) {
if (device->extensions_.ext_1_2_EXT_host_query_reset) {
#include "xenia/ui/vulkan/functions/device_1_2_ext_host_query_reset.inc"
}
}
if (properties.apiVersion < VK_MAKE_API_VERSION(0, 1, 3, 0)) {
if (device->extensions_.ext_1_3_KHR_maintenance4) {
#include "xenia/ui/vulkan/functions/device_1_3_khr_maintenance4.inc"

View File

@@ -126,6 +126,10 @@ class VulkanDevice {
bool scalarBlockLayout = false;
// VK_EXT_host_query_reset (promoted to 1.2)
bool hostQueryReset = false;
// VK_KHR_portability_subset (#164)
bool constantAlphaColorBlendFactors = false;
@@ -181,6 +185,7 @@ class VulkanDevice {
bool ext_1_1_KHR_bind_memory2 = false; // #158
bool ext_1_2_KHR_spirv_1_4 = false; // #237
bool ext_EXT_memory_budget = false; // #238
bool ext_1_2_EXT_host_query_reset = false; // promoted to 1.2
// Has optional features not implied by this being true.
bool ext_1_3_KHR_maintenance4 = false; // #414
#if XE_PLATFORM_WIN32
@@ -204,6 +209,8 @@ class VulkanDevice {
#include "xenia/ui/vulkan/functions/device_1_1_khr_get_memory_requirements2.inc"
// VK_KHR_bind_memory2 (#158, promoted to 1.1)
#include "xenia/ui/vulkan/functions/device_1_1_khr_bind_memory2.inc"
// VK_EXT_host_query_reset (promoted to 1.2)
#include "xenia/ui/vulkan/functions/device_1_2_ext_host_query_reset.inc"
// VK_KHR_maintenance4 (#414, promoted to 1.3)
#include "xenia/ui/vulkan/functions/device_1_3_khr_maintenance4.inc"
#undef XE_UI_VULKAN_FUNCTION_PROMOTED