From 9c78d10044b95412fbdf1b9508e4029d51699187 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 16 Sep 2026 07:24:00 +0300 Subject: [PATCH] fix(agc): support unrestricted vulkan depth range --- .../libSceAgcDriver/Execution/src/VulkanDevice.cpp | 6 +++++- .../libSceAgcDriver/Graphics/include/Context.hpp | 1 + .../prx/libSceAgcDriver/Graphics/src/Pipeline.cpp | 2 ++ .../libs/prx/libSceAgcDriver/Graphics/src/State.cpp | 4 ++-- core/libs/prx/libSceAgcDriver/tests/Graphics.cpp | 13 +++++++++++-- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 06c46b470..872f5cd31 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -57,6 +57,7 @@ struct VulkanDevice::State { bool tessellationShader = false; bool meshShader = false; bool depthClipControl = false; + bool depthRangeUnrestricted = false; VkPhysicalDeviceMeshShaderPropertiesEXT meshLimits{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT}; template @@ -277,6 +278,8 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u meshFeatures.meshShader = state->meshShader; std::vector deviceExtensions; if (window != nullptr) deviceExtensions.assign(presentationExtensions.begin(), presentationExtensions.end()); + state->depthRangeUnrestricted = hasExtension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME); + if (state->depthRangeUnrestricted) deviceExtensions.push_back(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME); VkPhysicalDeviceDepthClipControlFeaturesEXT depthClipFeatures{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT}; if (hasExtension(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME)) { VkPhysicalDeviceFeatures2 features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &depthClipFeatures}; @@ -538,7 +541,8 @@ void VulkanDevice::DrawIndexed(const Graphics::State& graphics, const Pm4::Index state->tessellationShader, state->meshShader, state->meshLimits, - state->depthClipControl + state->depthClipControl, + state->depthRangeUnrestricted }; Graphics::DrawIndexed(context, graphics, draw, shaders); } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp index b73b9275e..d78aa4946 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp @@ -33,6 +33,7 @@ struct Context { bool meshShader = false; VkPhysicalDeviceMeshShaderPropertiesEXT meshLimits{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT}; bool depthClipControl = false; + bool depthRangeUnrestricted = false; template TFunction Function(const char* name) const { diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp index 7d1814e5b..f0a335945 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp @@ -22,6 +22,8 @@ Pipeline::Pipeline(const Context& context, const State& state, const RenderTarge Require(mesh.maxVertices <= context.meshLimits.maxMeshOutputVertices && mesh.maxPrimitives <= context.meshLimits.maxMeshOutputPrimitives && static_cast(mesh.ldsSizeDwords) * 4 <= context.meshLimits.maxMeshSharedMemorySize, "mesh output or LDS exceeds device limits"); } const auto& viewport = state.viewport; + Require(std::isfinite(viewport.minDepth) && std::isfinite(viewport.maxDepth), "non-finite viewport depth range"); + Require(context.depthRangeUnrestricted || (viewport.minDepth >= 0 && viewport.minDepth <= 1 && viewport.maxDepth >= 0 && viewport.maxDepth <= 1), "viewport depth outside [0, 1] requires VK_EXT_depth_range_unrestricted"); Require(std::isfinite(viewport.x) && std::isfinite(viewport.y) && std::isfinite(viewport.width) && std::isfinite(viewport.height), "viewport arithmetic overflow"); Require(viewport.width <= context.limits.maxViewportDimensions[0] && std::abs(viewport.height) <= context.limits.maxViewportDimensions[1], "viewport dimensions exceed device limits"); Require(viewport.x >= context.limits.viewportBoundsRange[0] && viewport.x + viewport.width <= context.limits.viewportBoundsRange[1], "viewport X exceeds device bounds"); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index 0c80ecd81..8c1bdbf9a 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -218,12 +218,12 @@ State DecodeState(const QueueState& queue) { const auto zo = readFloat(cx, 0x114); const auto minDepth = result.negativeOneToOne ? zo - zs : zo; const auto maxDepth = zo + zs; - if (!(xs > 0 && ys != 0 && minDepth >= 0 && minDepth <= 1 && maxDepth >= 0 && maxDepth <= 1)) { + if (!(xs > 0 && ys != 0 && std::isfinite(minDepth) && std::isfinite(maxDepth))) { std::ostringstream message; message << "AGC graphics: unsupported viewport transform: scale=(" << xs << ", " << ys << ", " << zs << "), offset=(" << xo << ", " << yo << ", " << zo << "), depth=(" << minDepth << ", " << maxDepth << "), negativeOneToOne=" << result.negativeOneToOne; throw std::runtime_error(message.str()); } - Require(readFloat(cx, 0xb4) == std::min(minDepth, maxDepth) && readFloat(cx, 0xb5) == std::max(minDepth, maxDepth), "viewport depth clamp differs from transform"); + Require(readFloat(cx, 0xb4) <= readFloat(cx, 0xb5), "inverted viewport depth clamp bounds"); result.viewport = {xo - xs, yo - ys, 2 * xs, 2 * ys, minDepth, maxDepth}; result.scissor = {{0, 0}, result.renderExtent}; intersect(result.scissor, cx, 0xc, true); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index b5887641a..c4b28011e 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -192,10 +192,19 @@ void DepthClipTests() { const auto reversed = AgcDriver::Graphics::DecodeState(queue); Require(reversed.viewport.minDepth == 1 && reversed.viewport.maxDepth == 0, "reversed depth transform is incorrect"); queue.context[0x113] = std::bit_cast(1.0f); + queue.context[0x114] = 0; + const auto unrestricted = AgcDriver::Graphics::DecodeState(queue); + Require(unrestricted.viewport.minDepth == -1 && unrestricted.viewport.maxDepth == 1, "unrestricted viewport depth was normalized"); + queue.context[0x113] = std::bit_cast(-1.0f); + const auto unrestrictedReversed = AgcDriver::Graphics::DecodeState(queue); + Require(unrestrictedReversed.viewport.minDepth == 1 && unrestrictedReversed.viewport.maxDepth == -1, "reversed unrestricted viewport depth changed"); + queue.context[0x113] = 0x7f7fffff; + queue.context[0x114] = 0x7f7fffff; expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "unsupported viewport transform"); + queue.context[0x114] = std::bit_cast(0.5f); queue.context[0x113] = std::bit_cast(0.5f); - queue.context[0xb4] = std::bit_cast(0.25f); - expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "viewport depth clamp differs"); + queue.context[0xb4] = std::bit_cast(2.0f); + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "inverted viewport depth clamp"); queue.context[0xb4] = 0; for (std::uint32_t bit = 0; bit < 32; ++bit) { if (bit == 19) continue;