[Fix] (MG_Backend): two DirectVulkan draw memos trusted more than they proved

Two correctness holes from the round-7/8 fast-path work, found by
bisecting the retrace matrix after corruption reports on device.

Cross-frame slice trust: the vertex-binding and EBO memos skipped the
acquire - the frame's content-sync point - whenever their recorded
slice epochs still matched, trusting the BumpSliceEpoch inventory to
cover every way a buffer's GPU copy can go stale. At least one mutation
path escapes it: journeymap and common-mods retraces shipped visibly
corrupted, and Sodium on an Adreno device rendered random triangles
from stale vertex data. A memo recorded in an earlier frame now
declines, so the first draw of each (VAO, frame) re-runs the full
acquire; the same-frame paths (layout memo, factory-chase elimination,
one-compare rescue) are untouched. The cross-frame idea can return once
the bump-site inventory is proven complete against exactly these traces.

Transform-flags memo key: GetShaderTransformFlags reads the swapchain
pre-transform AND whether the bound draw framebuffer is the default one
- only a presenting pass gets the Y-flip/rotation bits. The memo
declared it pure in the pre-transform, so after any render-to-texture
pass the next default-framebuffer pass inherited the FBO's unflipped
flags: 1.17-main-menu retraced as a perfectly rendered, perfectly
upside-down frame (SSIM 0.052, deterministic), and cloud passes
flickered on device. The memo now keys on (preTransform, isDefaultFbo).

DirectVulkan retraces for 1.17-main-menu, journeymap, common-mods,
sodium and xaero-world-map all pass on lavapipe; unit tests 421/421.
This commit is contained in:
BZLZHH
2026-08-06 21:55:41 -04:00
parent 72ee7c439c
commit d7976326fa
2 changed files with 33 additions and 59 deletions
@@ -648,11 +648,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint32 m_pipelineStateHashColorCount = 0;
Uint64 m_pipelineStateHash = 0;
Bool m_pipelineStateHashValid = false;
// GetShaderTransformFlags(preTransform) memo: a pure function of the swapchain
// pre-transform, re-evaluated only when that value changes (surface rotation).
// No other invalidation input exists.
// GetShaderTransformFlags memo. NOT pure in the pre-transform alone: the
// function also reads whether the bound DRAW framebuffer is the default one
// (only the default framebuffer gets the Y-flip and rotation bits - an FBO
// pass renders unflipped). Keyed on BOTH inputs; missing the FBO bit shipped
// an upside-down default-framebuffer pass after any render-to-texture
// (minecraft-1.17-main-menu retrace, whole frame flipped).
VkSurfaceTransformFlagBitsKHR m_baseTransformFlagsPreTransform =
VK_SURFACE_TRANSFORM_FLAG_BITS_MAX_ENUM_KHR;
Bool m_baseTransformFlagsIsDefaultFbo = false;
Bool m_baseTransformFlagsKeyValid = false;
Uint32 m_baseTransformFlagsCache = 0;
Uint32 GetBaseTransformFlagsRaw();
// Drops every memoized pipeline handle. Required at command-buffer