diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index 22373ebce..06e66cb9e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -86,6 +86,12 @@ bool depthSurfaceBound(const Registers& cx) { return (z != cx.end() && (z->second & 3u) != 0) || (stencil != cx.end() && (stencil->second & 1u) != 0); } +bool depthPlanesAbsent(const Registers& cx) { + const auto z = find(cx, 0x010); + const auto stencil = find(cx, 0x011); + return z != cx.end() && stencil != cx.end() && (z->second & 3u) == 0 && (stencil->second & 1u) == 0; +} + VkStencilOpState stencilFace(std::uint32_t compare, std::uint32_t ops, std::uint32_t refMask, bool readOnly) { VkStencilOpState face{}; face.compareOp = static_cast(compare); @@ -144,8 +150,8 @@ void decodeDepth(const Registers& cx, std::uint32_t depthControl, State& result) const auto zFormat = read(cx, 0x010) & 3u; const bool stencil = (read(cx, 0x011) & 1u) != 0; Require(zFormat != 2, "Z_24 depth is unsupported"); - Require(zFormat != 0 || (depthControl & 2u) == 0, "depth test without a depth plane"); - Require(stencil || (depthControl & 1u) == 0, "stencil test without a stencil plane"); + if (zFormat == 0) depthControl &= ~6u; + if (!stencil) depthControl &= ~1u; const auto base = [&](std::uint32_t low, std::uint32_t highOffset) { const auto high = find(cx, highOffset); return (high == cx.end() ? 0ull : static_cast(high->second & 0xffu) << 40u) | (static_cast(read(cx, low)) << 8u); @@ -445,7 +451,7 @@ State DecodeState(const QueueState& queue) { const auto depthControl = read(cx, 0x200); if ((depthControl & 0xbu) != 0 && depthSurfaceBound(cx)) { decodeDepth(cx, depthControl, result); - } else if (depthPassThrough(depthControl)) { + } else if (depthPassThrough(depthControl) || ((depthControl & 3u) != 0 && depthPlanesAbsent(cx))) { static bool reported = false; if (!reported) { reported = true; @@ -732,7 +738,7 @@ std::string DrawRejection(const QueueState& queue, bool indexed) { if (auto reason = nonzero(cx, 0x207, ~LayerExports, "clip distances, layer, viewport or auxiliary vertex exports"); !reason.empty()) return reason; if (value(cx, 0x200, word)) { const bool surface = (word & 0xbu) != 0 && depthSurfaceBound(cx); - if (!surface && !depthPassThrough(word) && !IgnoreDepthTest() && (effectiveDepthControl(word) & DepthControlMask) != 0) return zeroMessage(0x200, word, "depth, stencil or conditional color writes"); + if (!surface && !((word & 3u) != 0 && depthPlanesAbsent(cx)) && !depthPassThrough(word) && !IgnoreDepthTest() && (effectiveDepthControl(word) & DepthControlMask) != 0) return zeroMessage(0x200, word, "depth, stencil or conditional color writes"); if (auto reason = require((word & 8u) == 0 || surface, "depth bounds without a depth surface"); !reason.empty()) return reason; if (auto reason = require((word & 0xc0000000u) == 0, "depth-conditional color writes are unsupported"); !reason.empty()) return reason; } diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index b74c63b80..7f9042726 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -148,6 +148,29 @@ void stateTests() { queue.context[0x200] = 0x007007b6; expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "depth"); queue = makeState(); + queue.context[0x010] = 0x80000180; + queue.context[0x011] = 0x20000180; + queue.context[0x200] = 0x007007b3; + queue.context[0x1b3] = 2; + queue.context[0x1b4] = 2; + state = AgcDriver::Graphics::DecodeState(queue); + Require(!state.depthTest && !state.stencilTest, "tests on absent depth and stencil planes were kept"); + Require(AgcDriver::Graphics::DrawRejection(queue, false).empty(), "tests on absent depth and stencil planes were rejected"); + queue.context[0x011] = 0x20000181; + queue.context[0x012] = 0x00001000; + queue.context[0x013] = 0x00002000; + queue.context[0x015] = 0x00002000; + queue.context[0x007] = 0x003f003f; + queue.context[0x000] = 0; + queue.context[0x002] = 0; + queue.context[0x00a] = 0; + queue.context[0x00b] = 0x3f800000; + queue.context[0x10b] = 0; + queue.context[0x10c] = 0x01ffff00; + queue.context[0x10d] = 0x01ffff00; + state =AgcDriver::Graphics::DecodeState(queue); + Require(!state.depthTest && state.stencilTest, "a depth test on an absent depth plane was kept beside a stencil plane"); + queue = makeState(); queue.context[0x10f] = 0x7fc00000; expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "non-finite"); // The register facade: every register the decoders read is in DrawKeyRegisters (a wrong hit of