diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp index 253aaf7a8..6f54f0fff 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp @@ -83,6 +83,7 @@ std::shared_ptr CachedPipeline(const Context& context, const State& st void ClearCachedPipelines(VkDevice device); // Device limit checks of the viewport, which is dynamic state and so no longer checked by Pipeline. void ValidateViewport(const Context& context, const VkViewport& viewport); +void ValidateDepthBounds(const Context& context, const State& state); void ValidateShaderPair(const ShaderRecompiler::RecompileResult& vertex, const ShaderRecompiler::RecompileResult& fragment); // Returns the color attachment locations the pixel shader writes. diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp index 2519fd3a8..317abb34b 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp @@ -778,6 +778,7 @@ DrawInputs prepareDrawInputs(const Context& context, const State& state, const P if (state.stages.tessellation) Require(draw.indexCount % state.stages.tessellation->inputControlPoints == 0, "incomplete tessellation patch"); // Viewport and scissor are dynamic pipeline state, so their limits are checked here per draw. ValidateViewport(context, state.viewport); + ValidateDepthBounds(context, state); timer.phase(PhaseValidate); inputs.maxIndex = draw.indexed ? 0u : draw.firstVertex + draw.indexCount - 1u; if (draw.indexed) { diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp index 677de2619..63e659174 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp @@ -32,6 +32,10 @@ Framebuffer::~Framebuffer() { if (framebuffer) context.Function("vkDestroyFramebuffer")(context.device, framebuffer, nullptr); } +void ValidateDepthBounds(const Context& context, const State& state) { + Require(!state.depthBoundsTest || context.depthRangeUnrestricted || (state.minDepthBounds >= 0.0f && state.minDepthBounds <= 1.0f && state.maxDepthBounds >= 0.0f && state.maxDepthBounds <= 1.0f), "depth bounds outside [0, 1] require VK_EXT_depth_range_unrestricted"); +} + void ValidateViewport(const Context& context, const VkViewport& 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"); @@ -285,8 +289,7 @@ void Pipeline::Continue(VkCommandBuffer commands, const State& state) const { context.Resolved(&DeviceFunctions::cmdSetScissor, "vkCmdSetScissor")(commands, 0, 1, &state.scissor); if (depthBias) context.Resolved(&DeviceFunctions::cmdSetDepthBias, "vkCmdSetDepthBias")(commands, state.depthBiasConstant, state.depthBiasClamp, state.depthBiasSlope); if (!depthBounds) return; - const auto bound = [&](float value) { return context.depthRangeUnrestricted ? value : std::clamp(value, 0.0f, 1.0f); }; - context.Resolved(&DeviceFunctions::cmdSetDepthBounds, "vkCmdSetDepthBounds")(commands, bound(state.minDepthBounds), bound(state.maxDepthBounds)); + context.Resolved(&DeviceFunctions::cmdSetDepthBounds, "vkCmdSetDepthBounds")(commands, state.minDepthBounds, state.maxDepthBounds); } void Pipeline::PushConstants(VkCommandBuffer commands, VkShaderStageFlags stages, std::span bytes) const { diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index a6bd877ad..cf0f5a8fc 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -1702,6 +1702,15 @@ int main() { draw.indexAddress = 0; draw.flags = 1; expectFailure([&] { AgcDriver::Graphics::Draw(context, state, draw, {}); }, "draw modifiers"); + auto bounded = state; + bounded.depthBoundsTest = true; + bounded.minDepthBounds = 0.25f; + AgcDriver::Graphics::ValidateDepthBounds(context, bounded); + bounded.minDepthBounds = 1.5f; + expectFailure([&] { AgcDriver::Graphics::ValidateDepthBounds(context, bounded); }, "depth bounds outside [0, 1]"); + auto unrestricted = context; + unrestricted.depthRangeUnrestricted = true; + AgcDriver::Graphics::ValidateDepthBounds(unrestricted, bounded); } stateTests(); hardwareScreenOffsetTests();