- VulkanRenderer::LookupVaoDrawMemo's legacy arm read MG_Config::Features.PipeHandleAbaControl
a fourth time, bypassing the one-question accessor MagmaPipeAbaControlDefeatsIdentity that
55d2af9b's own message says all the control's sites go through. It could never be false
there: the control's early return fires ahead of BOTH arms, so a run that reaches the legacy
compare has the knob off. The compare is unconditional again, with a note saying where the
question is answered and to route through the accessor if that early return is ever narrowed
- which is the trap the dead line was, since narrowing it would have silently restored D18's
retired semantics at this one site.
- HandleRecycleScenario's vertex-array case still said "BOTH the VAO and the buffer are
recycled here so that a key built out of raw addresses matches", which the body has
contradicted since 55d2af9b: both buffers are created before the ABA window and neither is
deleted inside it, deliberately, so that no buffer traffic moves VkBufferManager's
slice-epoch counter inside the window. Only the VAO is recycled, and only its GL name - the
heap block is not handed back, which is why the knob rather than the allocator constructs the
AbaControl arms' collision.
- 55d2af9b claimed - in its message, in MagmaPipeArms.h, in VertexInputStateFactory.cpp and
in MG_IntegrationTest/CMakeLists.txt - that the AbaControlHandles lane defeats the
GENERATION in {slot, gen}. It does not, and no lane of that shape can. Magma's mint has no
death notification (nothing in MG_Backend/DirectVulkan consumes NotifyStateObjectDestroyed)
and returns a slot only through OnFrameBoundary's age sweep, kSweepInterval 256 /
kRetireAgeBoundaries 1024; HandleRecycleScenario issues five frame boundaries, so the
replacement VAO acquires against an empty free list and gets a BRAND-NEW slot at Gen 1
(measured: redVao slot=2 gen=1, greenVao slot=3 gen=1). The knob-off FRESH verdict there is
decided by the SLOT alone, and deleting ++m_entries[index].Gen leaves all 32 HandleRecycle
entries green - re-measured this round.
- What the lane does defeat is the object identity that SELECTS the slot, which IS the key the
handle arm ships, and that is what the three code sites now say. The two requirements are
mutually exclusive for the pixel-visible memo: a genuine slot reuse needs >= 1024 idle
boundaries after the dead object's last draw, which necessarily puts the two draws in
different frames, and ResolvedVertexBindings - the only memo carrying a GPU slice rather
than a layout - declines across frames by design.
- So the generation is covered where it IS expressible. MG_Test/Pipe/MagmaPipeIdentityTest.cpp
drives the mint's real retire -> reuse (1280 boundaries, with a keep-alive object holding the
first allocatable slot so the reuse is not the slot the control aliases onto) and asserts
four things: the retired slot comes back with Gen+1; with the knob OFF a memo stamped at
{slot, gen=N} is NOT served at {slot, gen=N+1}; with the knob ON it IS, out of one uncleared
and unclaimed entry; and a live object keeps its slot, its generation and its memo across two
sweeps, so the generation cannot be "fixed" by bumping it on every acquisition.
- The claim rule itself moves into MagmaPipeArms.h as MagmaPipeClaimSlotMemos so the suite
exercises production code rather than a copy of it. VertexInputStateFactory::MemosFor is now
one call to it and is otherwise unchanged, on both the knob-on and the knob-off path.
- Load-bearing, measured: with ++m_entries[index].Gen commented out, ctest -L unit in
build-push goes 1563/1566 - three of the four new cases red, one of them naming the inherited
0xDEAD payload out of the same slot - while ctest -R HandleRecycle stays 32/32. Restored, all
four pass in build-push and build-verify and skip visibly in the pull build, so the ctest name
sets stay identical (G2).