Merge pull request #3702 from oneandonlydean/up4/depth-tests-without-planes

fix(agc): leave the depth surface out when no enabled test has its plane
This commit is contained in:
Adrià 2026-10-11 23:52:46 +02:00 committed by GitHub
commit 4bde70b4e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 37 additions and 4 deletions

View File

@ -138,6 +138,14 @@ bool depthSurfaceBound(const Registers& cx) {
return (z != cx.end() && (z->second & 3u) != 0) || (stencil != cx.end() && (stencil->second & 1u) != 0);
}
std::uint32_t planeTests(const Registers& cx, std::uint32_t depthControl) {
const auto z = find(cx, 0x010);
const auto stencil = find(cx, 0x011);
if (z != cx.end() && (z->second & 3u) == 0) depthControl &= ~2u;
if (stencil != cx.end() && (stencil->second & 1u) == 0) depthControl &= ~1u;
return depthControl & 0xbu;
}
bool depthPlanesAbsent(const Registers& cx) {
const auto z = find(cx, 0x010);
const auto stencil = find(cx, 0x011);
@ -583,9 +591,10 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) {
const auto depthControl = read(cx, 0x200);
const auto renderControl = find(cx, 0x000);
const bool clear = renderControl != cx.end() && (renderControl->second & 3u) != 0;
if (clear || ((depthControl & 0xbu) != 0 && depthSurfaceBound(cx))) {
const auto tests = planeTests(cx, depthControl);
if (clear || (tests != 0 && depthSurfaceBound(cx))) {
decodeDepth(cx, depthControl, result);
} else if (depthPassThrough(depthControl) || ((depthControl & 0xbu) != 0 && depthPlanesAbsent(cx))) {
} else if (depthPassThrough(depthControl) || ((depthControl & 0xbu) != 0 && (tests == 0 || depthPlanesAbsent(cx)))) {
static bool reported = false;
if (!reported) {
reported = true;
@ -1013,8 +1022,9 @@ std::string DrawRejection(const QueueState& queue, bool indexed) {
std::uint32_t z = 0, view = 0;
if ((value(cx, 0x010, z) && (z & 3u) == 0) || (value(cx, 0x002, view) && (view & 0x01000000u) != 0)) return require(false, "depth clear requires a writable depth plane");
}
const bool surface = ((word & 0xbu) != 0 || depthClear || stencilClear) && depthSurfaceBound(cx);
if (!surface && !((word & 0xbu) != 0 && depthPlanesAbsent(cx)) && !depthPassThrough(word) && !IgnoreDepthTest() && (effectiveDepthControl(word) & DepthControlMask) != 0) return zeroMessage(0x200, word, "depth, stencil or conditional color writes");
const auto tests = planeTests(cx, word);
const bool surface = (tests != 0 || depthClear || stencilClear) && depthSurfaceBound(cx);
if (!surface && !((word & 0xbu) != 0 && (tests == 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 || depthPlanesAbsent(cx), "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;
}

View File

@ -275,6 +275,29 @@ void stateTests() {
queue.context[0x200] = 0x007007bbu;
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "depth bounds without a depth plane");
queue = makeState();
queue.context[0x010] = 0x80000183;
queue.context[0x011] = 0x20000180;
queue.context[0x012] = 0x00001000;
queue.context[0x014] = 0x00001000;
queue.context[0x007] = 0x0001001f;
queue.context[0x000] = 0;
queue.context[0x002] = 0;
queue.context[0x00a] = 0;
queue.context[0x00b] = 0x3f800000;
queue.context[0x200] = 0x007007b1;
queue.context[0x1b3] = 2;
queue.context[0x1b4] = 2;
state = AgcDriver::Graphics::DecodeState(queue);
Require(!state.depth.has_value() && !state.stencilTest && state.renderExtent.width == state.color.extent.width && state.renderExtent.height == state.color.extent.height, "a stencil test without a stencil plane bound the depth plane it does not test");
Require(AgcDriver::Graphics::DrawRejection(queue, false).empty(), "a stencil test without a stencil plane beside an untested depth plane was rejected");
queue.context[0x200] = 0x00700231;
state = AgcDriver::Graphics::DecodeState(queue);
Require(!state.depth.has_value() && !state.stencilTest && state.renderExtent.width == state.color.extent.width && state.renderExtent.height == state.color.extent.height, "a failing stencil test without a stencil plane bound the depth plane it does not test");
Require(AgcDriver::Graphics::DrawRejection(queue, false).empty(), "a failing stencil test without a stencil plane beside an untested depth plane was rejected");
queue.context[0x200] = 0x007007b3;
state = AgcDriver::Graphics::DecodeState(queue);
Require(state.depth.has_value() && state.depthTest && !state.stencilTest && state.renderExtent.width == 32 && state.renderExtent.height == 2, "a depth test on a bound depth plane lost its surface");
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