From c8f7fc06e01a4e58600655c81aafcb31c6ce1432 Mon Sep 17 00:00:00 2001 From: Johan David Date: Fri, 16 Oct 2026 01:59:25 -0500 Subject: [PATCH] fix(agc): compare the depth write base only when the depth plane is written decodeDepth required DB_Z_WRITE_BASE to equal DB_Z_READ_BASE whenever the depth plane was not read-only, also for draws that never write it. PPSA31327 01.015.000 stops there on a stencil-only draw: DB_DEPTH_CONTROL 0x00000701 (Z_ENABLE and Z_WRITE_ENABLE clear), DB_RENDER_CONTROL 0, DB_DEPTH_VIEW 0, Z read base 0xed11340000 and Z write base 0, with equal stencil read and write bases. The bases are now compared only when the depth plane can be written: a depth clear, a draw with both Z_ENABLE and Z_WRITE_ENABLE (the condition that sets depthWrite), or an expand pass with DEPTH_COMPRESS_DISABLE or STENCIL_COMPRESS_DISABLE in DB_RENDER_CONTROL, which still throws. The surface keeps the read base. The stencil check is unchanged. DepthStencilTests accepts a stencil-only draw, Z_WRITE_ENABLE without Z_ENABLE and a depth test without writes at another write base, and still rejects a written plane, a depth clear and the compress-disable bits there. --- .../prx/libSceAgcDriver/Graphics/src/State.cpp | 3 ++- .../libs/prx/libSceAgcDriver/tests/Graphics.cpp | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index b767129e68..39c64fb196 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -285,7 +285,8 @@ void decodeDepth(const Registers& cx, std::uint32_t depthControl, bool testValEx depth.lastSlice = (view >> 13u) & 0x7ffu; depth.address = zFormat != 0 ? base(0x012, 0x01a) : 0; depth.stencilAddress = stencil ? base(0x013, 0x01b) : 0; - Require(zFormat == 0 || depthReadOnly || base(0x014, 0x01c) == depth.address, "depth read and written at different addresses is unsupported"); + const bool depthWritten = zFormat != 0 && !depthReadOnly && (depthClear || (depthControl & 6u) == 6u || (read(cx, 0x000) & 0x60u) != 0); + Require(!depthWritten || base(0x014, 0x01c) == depth.address, "depth read and written at different addresses is unsupported"); Require(!stencil || stencilReadOnly || base(0x015, 0x01d) == depth.stencilAddress, "stencil read and written at different addresses is unsupported"); if (depth.samples != 1u) { depth.pipeBankXor = static_cast(depth.address & 65535u); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 2e1d26e11f..63bccd07a9 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -1741,6 +1741,23 @@ void DepthStencilTests() { const auto rejection = AgcDriver::Graphics::DrawRejection(queue, false); Require(rejection.empty(), "precheck rejected a stencil draw with a surface: " + rejection); Require(state.depth && state.depth->address == 0x10000 && state.depth->stencilAddress == 0x20000 && state.depth->format == VK_FORMAT_D32_SFLOAT_S8_UINT && state.depth->clearStencil == 7 && state.depth->htileAddress == 0x10000030000ull && !state.depth->htileStencil, "depth surface decode changed"); + { + auto split = queue; + split.context[0x014] = 0; + for (const auto control : {0x00700711u, 0x00700715u, 0x00700713u}) { + split.context[0x200] = control; + const auto splitState = AgcDriver::Graphics::DecodeState(split); + Require(splitState.depth && splitState.depth->address == 0x10000 && !splitState.depthWrite, "a depth plane the draw does not write rejected another write address"); + Require(AgcDriver::Graphics::DrawRejection(split, false).empty(), "precheck rejected a depth plane the draw does not write at another write address"); + } + split.context[0x200] = 0x00700717u; + expectFailure([&] { AgcDriver::Graphics::DecodeState(split); }, "depth read and written at different addresses"); + split.context[0x200] = 0x00700711u; + for (const auto control : {0x01u, 0x20u, 0x40u, 0x60u}) { + split.context[0x000] = control; + expectFailure([&] { AgcDriver::Graphics::DecodeState(split); }, "depth read and written at different addresses"); + } + } for (const auto clear : {0u, 1u, 2u, 3u}) { auto retained = queue; retained.context[0x8e] = retained.context[0x8f] = retained.context[0x203] = 0;