[MG_Backend, MG_IntegrationTest, scripts] (P7): a declined multisample depth/stencil shape is decided once before any aspect runs, so an erroring blit writes nothing, and the shader resolve arm gets a flipped knob twin

- BlitWireFramebuffers runs colour first. A scaled COLOR|DEPTH blit out of a multisample framebuffer scaled and wrote its colour and then recorded INVALID_OPERATION on its depth, and an X-mirrored one mirrored its colour and left its depth alone - partial effects GL 18.3.1 does not allow. The shape decline (scale, X mirror, cross-format Y mirror) moves into DeclineWireDepthStencilResolveShape, which BlitWireFramebuffers asks for every depth/stencil aspect that would take the multisample resolve arm before any aspect runs; ResolveWireDepthStencil asks it too for any other caller. Default read/draw framebuffers are left to the lambda (a default read is never multisampled; a default draw behind one is the region Fatal, and resolving it early would move its write index).
- The X decline and the cross-format Y decline get their own MGLOG_E_ONCE sites; one site used to cover both, so after the first X decline a cross-format Y decline logged nothing.
- An empty rectangle on one side only (64x48 onto 0x48) is a size mismatch and now takes the scale decline (INVALID_OPERATION) instead of returning silently; empty on both sides stays a silent no-op.
- DirectVulkan.{Split,Spawn}.MsFlip1. forces MGITEST_MAGMA_FORCE_SHADER_DEPTH_RESOLVE=1, so the shader arm min-corner rectangle under a flip runs on this host; split+spawn only and named in MAGMA_SERVER_ENV_KNOB_NO_TCP.
- spawn_lane_parity.py: every server-env-knob tail must match exactly one split key, so a dead or over-broad tail fails the gate instead of riding silently.
- The flipped-resolve case gains a COLOR|DEPTH scale leg (error, colour untouched, depth untouched) and an empty-destination leg (error, depth untouched).
- Red-once, each on split + spawn (+ tcp MsFlip.) and restored: pre-pass disabled -> all five MsFlip/MsFlip1 entries fail "the declined COLOR|DEPTH scale must not have written its colour either" (read {255,0,0,255}, want {0,0,255,255}); round-1 silent empty-side return -> all five fail the empty-destination leg (error 0, want 1282); shader arm handed sx0/sy0 instead of the min corner -> MsFlip1 fails on both arms ("768 of 768 depth values differ from 0.75", "928 of 3072 stencil values are wrong; first at (20, 4): got 255, want 99") while MsFlip. stays green; a dead ".MsFlip2." tail -> parity rc 1 "matches 0 split entrie(s)".
- The split once-sites have no lane red-once: the cross-format Y decline needs a device whose renderbuffer and texture D24S8 map to different VkFormats, which lavapipe is not.
This commit is contained in:
2026-09-22 20:49:45 -04:00
parent 1e46a67091
commit 4db17d58c6
5 changed files with 179 additions and 38 deletions
+17 -1
View File
@@ -76,8 +76,10 @@ MAGMA_INPROC_ONLY = ("MagmaRunAheadScenario.", "MagmaWireCacheScenario.")
# P7 wave 2-B3 (ID-P7-34) adds `.StaleSerial.`: MGITEST_MAGMA_FORCE_STALE_BUFFER_SERIAL is read by
# the server too (VkBufferManager.cpp), so the streamed subdata-then-draw red-once has the same
# two arms and the same tcp absence. One list, one mechanism, for every server-side knob.
# `.MsFlip1.` (B2 review round 2) is the shader-resolve knob twin of the knob-free `.MsFlip.`,
# which keeps its tcp entry.
MAGMA_SERVER_ENV_KNOB_NO_TCP = (".ShaderMip1.", ".ShaderMip2.", ".DepthMip.",
".DefaultBlitShape1.", ".MsResolve1.", ".StaleSerial.")
".DefaultBlitShape1.", ".MsResolve1.", ".StaleSerial.", ".MsFlip1.")
def lane_names(build_dir, label):
@@ -130,6 +132,20 @@ def compare_arms(build_dir, tier, labels, inproc_only=(), no_tcp=()):
f"from the comparison by name ({', '.join(inproc_only)})")
if no_tcp:
dropped = {k for k in comparable if any(only in k for only in no_tcp)}
# EVERY TAIL MUST NAME SOMETHING (review round 2). A tail that matches no split key is
# silently inert - a renamed prefix would leave the exception in the list and the
# entry it was about riding under some other name - so an unmatched tail is an error,
# and so is a tail that suddenly matches a different number of entries than the one
# knob case it was written for.
for tail in no_tcp:
hits = sorted(k for k in comparable if tail in k)
if len(hits) != 1:
print(f"::error::{tier}: the server-env-knob tail {tail!r} matches {len(hits)} "
f"split entrie(s) {hits}, not exactly one. Each tail names ONE knob case; a "
f"tail that names none is an exception about nothing, and one that names "
f"more has let a second case ride off the tcp arm unexamined.",
file=sys.stderr)
return True
if dropped:
print(f"{tier}: {len(dropped)} server-env-knob entrie(s) excluded from the TCP "
f"comparison only ({', '.join(no_tcp)}) - the knob is read on the server and "