From 1190d5de7ddcec8ae7ee54e2d6231ee3a76dd325 Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 20 Aug 2026 09:47:03 +0200 Subject: [PATCH] graphics: honor read-only stencil targets --- src/graphics/host_gpu/renderer/debug.cpp | 8 +--- .../host_gpu/renderer/depthRenderTarget.cpp | 19 +++++--- tests/ShaderRecompilerComputeTests.cpp | 48 +++++++++++++++++-- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/graphics/host_gpu/renderer/debug.cpp b/src/graphics/host_gpu/renderer/debug.cpp index 50f556603..4289f907e 100644 --- a/src/graphics/host_gpu/renderer/debug.cpp +++ b/src/graphics/host_gpu/renderer/debug.cpp @@ -567,13 +567,9 @@ static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc, } EXIT_NOT_IMPLEMENTED(!z.depth_view.depth_write_disable && z.z_read_base_addr != z.z_write_base_addr); - EXIT_NOT_IMPLEMENTED(z.stencil_read_base_addr != z.stencil_write_base_addr); + EXIT_NOT_IMPLEMENTED(!z.depth_view.stencil_write_disable && + z.stencil_read_base_addr != z.stencil_write_base_addr); EXIT_NOT_IMPLEMENTED(!z.depth_view.depth_write_disable && z.z_write_base_addr == 0); - if (z.stencil_info.format != Prospero::StencilFormat::kInvalid && - z.stencil_write_base_addr == 0) { - LOGF("\t warning: stencil format is set without a stencil base address, continuing " - "without stencil attachment\n"); - } // EXIT_NOT_IMPLEMENTED(z.pitch_div8_minus1 != 0x000000ff); // EXIT_NOT_IMPLEMENTED(z.height_div8_minus1 != 0x0000008f); // EXIT_NOT_IMPLEMENTED(z.slice_div64_minus1 != 0x00008fff); diff --git a/src/graphics/host_gpu/renderer/depthRenderTarget.cpp b/src/graphics/host_gpu/renderer/depthRenderTarget.cpp index 334fbc2a9..f8d457a19 100644 --- a/src/graphics/host_gpu/renderer/depthRenderTarget.cpp +++ b/src/graphics/host_gpu/renderer/depthRenderTarget.cpp @@ -185,8 +185,9 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB if (has_stencil) { if (z.stencil_info.format != Prospero::StencilFormat::k8UInt || !htile_stencil_compat || z.stencil_read_base_addr == 0 || - z.stencil_write_base_addr != z.stencil_read_base_addr || - (z.stencil_read_base_addr & 0xffffu) != 0 || z.depth_view.stencil_write_disable) { + (!z.depth_view.stencil_write_disable && + z.stencil_write_base_addr != z.stencil_read_base_addr) || + (z.stencil_read_base_addr & 0xffffu) != 0) { DepthFatal("unsupported stencil attachment state"); } } else if (z.stencil_read_base_addr != 0 || z.stencil_write_base_addr != 0 || @@ -287,18 +288,22 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB r.depth_min_bounds = hw.GetDepthBoundsMin(); r.depth_max_bounds = hw.GetDepthBoundsMax(); - r.stencil_clear_enable = has_stencil && rc.stencil_clear_enable; + r.stencil_clear_enable = + has_stencil && rc.stencil_clear_enable && !z.depth_view.stencil_write_disable; r.stencil_clear_value = hw.GetStencilClearValue(); r.stencil_test_enable = has_stencil && dc.stencil_enable; if (r.stencil_test_enable) { - const uint8_t front_write_mask = rc.stencil_clear_enable ? 0 : sm.stencil_writemask; - const uint8_t back_write_mask = rc.stencil_clear_enable ? 0 : sm.stencil_writemask_bf; + const bool stencil_ops_disabled = + rc.stencil_clear_enable || z.depth_view.stencil_write_disable; + const uint8_t front_write_mask = stencil_ops_disabled ? 0 : sm.stencil_writemask; + const uint8_t back_write_mask = stencil_ops_disabled ? 0 : sm.stencil_writemask_bf; if (dc.stencilfunc > static_cast(vk::CompareOp::eAlways) || (dc.backface_enable && dc.stencilfunc_bf > static_cast(vk::CompareOp::eAlways)) || - (UsesStencilOpValue(sc.stencil_fail, sc.stencil_zpass, sc.stencil_zfail) && + (front_write_mask != 0 && + UsesStencilOpValue(sc.stencil_fail, sc.stencil_zpass, sc.stencil_zfail) && sm.stencil_opval != sm.stencil_testval) || - (dc.backface_enable && + (dc.backface_enable && back_write_mask != 0 && UsesStencilOpValue(sc.stencil_fail_bf, sc.stencil_zpass_bf, sc.stencil_zfail_bf) && sm.stencil_opval_bf != sm.stencil_testval_bf)) { DepthFatal("unsupported stencil compare or replacement state"); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 80c04f061..86e05c193 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -7651,27 +7651,69 @@ public: auto read_only_depth_target = phased_depth_target; read_only_depth_target.z_write_base_addr = 0; + read_only_depth_target.stencil_write_base_addr = 0; read_only_depth_target.depth_view.depth_write_disable = true; + read_only_depth_target.depth_view.stencil_write_disable = true; registers.SetDepthRenderTarget(read_only_depth_target); auto read_only_depth_control = phased_depth_control; read_only_depth_control.z_write_enable = false; + read_only_depth_control.zfunc = + static_cast(vk::CompareOp::eAlways); + read_only_depth_control.stencil_enable = true; + read_only_depth_control.stencilfunc = + static_cast(vk::CompareOp::eAlways); registers.SetDepthControl(read_only_depth_control); + HW::StencilControl read_only_stencil_control{}; + read_only_stencil_control.stencil_zpass = + static_cast(Prospero::StencilOp::kReplaceOp); + registers.SetStencilControl(read_only_stencil_control); + HW::StencilMask read_only_stencil_mask{}; + read_only_stencil_mask.stencil_testval = 0x12; + read_only_stencil_mask.stencil_mask = 0xff; + read_only_stencil_mask.stencil_writemask = 0xff; + read_only_stencil_mask.stencil_opval = 0x34; + registers.SetStencilMask(read_only_stencil_mask); RenderDepthInfo read_only_depth{}; RenderExecutorTestAccess::ResolveRenderDepthTarget( executor, 1, scheduler.Current(), read_only_depth); Require( - name, "read-only depth target without write address", + name, "read-only depth/stencil target without write addresses", read_only_depth.image_id == phased_depth.image_id && read_only_depth.depth_buffer_vaddr == phased_depth_address && + read_only_depth.stencil_buffer_vaddr == phased_stencil_address && !read_only_depth.depth_write_enable && + !read_only_depth.stencil_clear_enable && + read_only_depth.stencil_test_enable && + read_only_depth.stencil_dynamic_front.writeMask == 0 && + read_only_depth.stencil_dynamic_back.writeMask == 0 && + read_only_depth.stencil_static_front.passOp == + vk::StencilOp::eKeep && !read_only_depth.AttachmentWriteAspects() && depth_attachment_layout(read_only_depth) == vk::ImageLayout::eDepthStencilReadOnlyOptimal, - "a PS5 read-only depth target required a depth write address or " - "selected a writable host layout"); + "a PS5 read-only depth/stencil target required write addresses or " + "retained host writes"); + + auto read_only_render_control = phased_render_control; + read_only_render_control.stencil_clear_enable = true; + registers.SetRenderControl(read_only_render_control); + RenderDepthInfo read_only_clear_depth{}; + RenderExecutorTestAccess::ResolveRenderDepthTarget( + executor, 1, scheduler.Current(), read_only_clear_depth); + Require( + name, "read-only stencil clear suppression", + read_only_clear_depth.image_id == phased_depth.image_id && + !read_only_clear_depth.stencil_clear_enable && + !read_only_clear_depth.AttachmentWriteAspects() && + depth_attachment_layout(read_only_clear_depth) == + vk::ImageLayout::eDepthStencilReadOnlyOptimal, + "a PS5 target-level stencil write disable retained a host clear"); registers.SetDepthRenderTarget(phased_depth_target); registers.SetDepthControl(phased_depth_control); + registers.SetStencilControl({}); + registers.SetStencilMask({}); + registers.SetRenderControl(phased_render_control); RenderColorInfo no_color{}; auto phased_rendering = RenderExecutorTestAccess::AcquireRenderTargets( executor, scheduler.Current(), &no_color, 0, phased_depth);