From 0ace5f5d3db86e3f98e421c0ed3495ffe39a02fc Mon Sep 17 00:00:00 2001 From: CrossVR Date: Mon, 21 Jul 2025 05:48:04 +0900 Subject: [PATCH] Vulkan: Add support for unrestricted depth range. --- Source/Core/VideoBackends/Vulkan/VKPipeline.cpp | 9 ++++++++- Source/Core/VideoBackends/Vulkan/VulkanContext.cpp | 11 +++++++++++ Source/Core/VideoCommon/BPFunctions.cpp | 13 ++++--------- Source/Core/VideoCommon/VertexShaderManager.cpp | 5 +++++ Source/Core/VideoCommon/VideoCommon.h | 6 ++++++ Source/Core/VideoCommon/VideoConfig.h | 1 + 6 files changed, 35 insertions(+), 10 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp b/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp index be3aae1390..1b8dd0eb9a 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp @@ -368,12 +368,19 @@ std::unique_ptr VKPipeline::Create(const AbstractPipelineConfig& con GetVulkanColorBlendState(config.blending_state, blend_attachment_states.data(), static_cast(blend_attachment_states.size())); + static const VkDepthClampRangeEXT clamp_range = {0.0f, MAX_EFB_DEPTH}; + static const VkPipelineViewportDepthClampControlCreateInfoEXT depth_clamp_state = { + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLAMP_CONTROL_CREATE_INFO_EXT, nullptr, + VK_DEPTH_CLAMP_MODE_USER_DEFINED_RANGE_EXT, // VkDepthClampModeEXT depthClampMode + &clamp_range // const VkDepthClampRangeEXT* pDepthClampRange + }; + // This viewport isn't used, but needs to be specified anyway. static const VkViewport viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; static const VkRect2D scissor = {{0, 0}, {1, 1}}; static const VkPipelineViewportStateCreateInfo viewport_state = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - nullptr, + &depth_clamp_state, 0, // VkPipelineViewportStateCreateFlags flags; 1, // uint32_t viewportCount &viewport, // const VkViewport* pViewports diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index c6ff5e148d..60580aff40 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -459,6 +459,7 @@ void VulkanContext::PopulateBackendInfo(BackendInfo* backend_info) backend_info->bSupportsDynamicVertexLoader = true; // Assumed support. backend_info->bSupportsVSLinePointExpand = true; // Assumed support. backend_info->bSupportsHDROutput = true; // Assumed support. + backend_info->bSupportsUnrestrictedDepthRange = false; // Dependent on features. } void VulkanContext::PopulateBackendInfoAdapters(BackendInfo* backend_info, const GPUList& gpu_list) @@ -663,6 +664,16 @@ bool VulkanContext::SelectDeviceExtensions(bool enable_surface) AddExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false); AddExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, false); + if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_D32F_CLEAR)) + { + // Unrestricted depth range is one of the few extensions that changes the behavior + // of Vulkan just by being enabled, so we rely on lazy evaluation to ensure it is + // not enabled unless depth clamp control is supported. + g_backend_info.bSupportsUnrestrictedDepthRange = + AddExtension(VK_EXT_DEPTH_CLAMP_CONTROL_EXTENSION_NAME, false) && + AddExtension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, false); + } + return true; } diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index 0b20bcfd8a..444b1dbc4f 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -216,17 +216,12 @@ void SetScissorAndViewport() height *= -1; } - // The maximum depth that is written to the depth buffer should never exceed this value. - // This is necessary because we use a 2^24 divisor for all our depth values to prevent - // floating-point round-trip errors. However the console GPU doesn't ever write a value - // to the depth buffer that exceeds 2^24 - 1. - constexpr float GX_MAX_DEPTH = 16777215.0f / 16777216.0f; if (!g_backend_info.bSupportsDepthClamp) { // There's no way to support oversized depth ranges in this situation. Let's just clamp the // range to the maximum value supported by the console GPU and hope for the best. - min_depth = std::clamp(min_depth, 0.0f, GX_MAX_DEPTH); - max_depth = std::clamp(max_depth, 0.0f, GX_MAX_DEPTH); + min_depth = std::clamp(min_depth, 0.0f, MAX_EFB_DEPTH); + max_depth = std::clamp(max_depth, 0.0f, MAX_EFB_DEPTH); } if (VertexShaderManager::UseVertexDepthRange()) @@ -235,13 +230,13 @@ void SetScissorAndViewport() // Taking into account whether the depth range is inverted or not. if (xfmem.viewport.zRange < 0.0f && g_backend_info.bSupportsReversedDepthRange) { - min_depth = GX_MAX_DEPTH; + min_depth = MAX_EFB_DEPTH; max_depth = 0.0f; } else { min_depth = 0.0f; - max_depth = GX_MAX_DEPTH; + max_depth = MAX_EFB_DEPTH; } } diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index 976cff00f6..4dba5432bc 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -131,6 +131,11 @@ void VertexShaderManager::SetProjectionMatrix(XFStateManager& xf_state_manager) bool VertexShaderManager::UseVertexDepthRange() { + // Backend has full support for unrestricted depth ranges including the ability to clamp the + // final depth value to MAX_EFB_DEPTH. + if (g_backend_info.bSupportsUnrestrictedDepthRange) + return false; + // We can't compute the depth range in the vertex shader if we don't support depth clamp. if (!g_backend_info.bSupportsDepthClamp) return false; diff --git a/Source/Core/VideoCommon/VideoCommon.h b/Source/Core/VideoCommon/VideoCommon.h index 517e8b3e9b..abc79a847d 100644 --- a/Source/Core/VideoCommon/VideoCommon.h +++ b/Source/Core/VideoCommon/VideoCommon.h @@ -15,6 +15,12 @@ constexpr u32 EFB_WIDTH = 640u; constexpr u32 EFB_HEIGHT = 528u; +// The maximum depth that is written to the depth buffer should never exceed this value. +// This is necessary because we use a 2^24 divisor for all our depth values to prevent +// floating-point round-trip errors. However the console GPU doesn't ever write a value +// to the depth buffer that exceeds 2^24 - 1. +constexpr float MAX_EFB_DEPTH = 16777215.0f / 16777216.0f; + // Max XFB width is 720. You can only copy out 640 wide areas of efb to XFB // so you need multiple copies to do the full width. // The VI can do horizontal scaling (TODO: emulate). diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index a268278eca..a0baaa666c 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -180,6 +180,7 @@ struct BackendInfo bool bSupportsVSLinePointExpand = false; bool bSupportsGLLayerInFS = true; bool bSupportsHDROutput = false; + bool bSupportsUnrestrictedDepthRange = false; }; extern BackendInfo g_backend_info;