mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-12 20:43:23 +09:00
[Fix] (Magma): size the {slot, gen} mint by the live working set instead of by a capacity, and give it to the renderer that uses it
- MagmaPipeIdentityTable was a FIXED 2048/8192-entry, 2-way set-associative LRU. Above capacity it evicted LIVE objects, and every memo keyed on the handle died with them: a verbatim transcription of the previous Acquire lost 54% of uses' handles at 2500 live VAOs against 2048 entries, and 20% at 1024 live VAOs once the lifetime ids are sparse (an app that creates and destroys VAOs - the Minecraft chunk shape this exists for). - Two of the three memos it fed had NO capacity before this package: the content-hash memo and the resolved-state memo were unbounded mutable fields on VertexArrayObject. Eviction there turns one ComputeHash per VAO reconfiguration into one per DRAW; once the buffer table thrashes too, the vertex-input content hash becomes a per-draw value that inserts a fresh heap-allocated BackendVertexInputState into an unbounded map on every draw, swept only every 256 frame boundaries. That is a worse leak than the one the fixed table was introduced to avoid. - So the mint grows on demand and reclaims by AGE: a lifetime-id map with a one-entry front memo, a free list, and an OnFrameBoundary sweep on the same cadence and retirement age as the cache entries those slots key. Footprint tracks the live DRAWN working set instead of objects ever created, which is the property MG_Impl/Pipe/SlotAllocator cannot have here (nothing in P2 can call its Free). Re-run of the same workloads: handle churn is 0.0% at 512, 1024, 2048, 2500, 3000, 4000, 8192, 10000 and 16000 live objects, consecutive and sparse ids alike, at 1 and 5 acquisitions per use. - VertexInputStateFactory::m_vaoMemos follows the mint with no capacity of its own, through a chunked table whose entry addresses never move - which is what the fixed table's only real guarantee was, and D12.4's grow-on-demand ask without a relocating Vector. - VulkanRenderer::m_vaoDrawMemoTable deliberately keeps the base ref's 2048 entries and the base ref's older-frameSerial victim rule, and changes only its KEY. It is the one memo of the three that had a capacity before P2, a VaoDrawMemo is ~450 B, and losing one costs one vertex-binding re-resolve. Measured steady-state miss rate against the base ref's address-hashed table: 0.0% vs 6.4% at 512 live VAOs, 0.0% vs 24.0% at 1024, 0.0% vs 60.0% at 2048, 36.2% vs 69.5% at 2500, 63.5% vs 79.1% at 3000; both are ~100% at 4096 (2x capacity), where an LRU on a cyclic pattern cannot win. - The two tables are now a MagmaPipeIdentityTables member of VulkanRenderer, handed to its VertexInputStateFactory, instead of two function-local statics that outlived every context and shared one reclamation clock across two. - A Gen that reaches 2^32-1 retires its slot for good rather than wrapping. MOBILEGL_ASSERT is compiled out of every build P2 runs, and a DEBUG-level build of this tree does not compile at all (MG_Util/Types.h uses MOBILEGL_ASSERT before MGLOG_F is declared - untouched since the base ref, and not this package's file), so the defence has to be on the release path to exist. - The no-CSO pipeline-memo fallback stops using MGLOG_W_ONCE. MOBILEGL_LOG_ONCE_INTERNAL is an unconditional std::atomic_flag::test_and_set - a locked xchg per evaluation, not "one static bool test" - and this site is on the per-draw path in exactly the configuration that reaches it. A plain per-renderer bool replaces it, and the comment now says what the warning's absence does and does not prove (nothing at all while bit 0 is clear). - MOBILEGL_PIPE_LEGACY_MEMOS=0 with kMGPipeSubsystemRenderState clear still runs the pre-handle state hash - there is a correct answer there and bit 0 is not Track H, so it is not fatal - but it is no longer silent: the startup gate names the combination. - The D12.3 static_assert block now names D19's DynamicChunksCoverMagmasDynamicTailKey, whose ctest entry lives in package A's file, so the integrator can see which half is missing.
This commit is contained in:
@@ -61,7 +61,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// identity while its neighbours keyed on the handle.
|
||||
if (MagmaPipeTrackHArmIsHandles(MG_Pipe::kMGPipeSubsystemMagmaVertexInput)) {
|
||||
const MG_Pipe::MGPipeHandle handle =
|
||||
MagmaPipeHandleOf(MG_Pipe::MGPipeKind::Buffer, attr.Buffer->GetLifetimeId());
|
||||
m_identity->HandleOf(MG_Pipe::MGPipeKind::Buffer, attr.Buffer->GetLifetimeId());
|
||||
bufferKey = static_cast<Uint64>(handle.Slot) | (static_cast<Uint64>(handle.Gen) << 32);
|
||||
} else if (MG_Config::Features.PipeHandleAbaControl) {
|
||||
// Negative control C (P2 brief D18), and it applies to the PRE-HANDLE arm
|
||||
@@ -83,18 +83,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
VertexInputStateFactory::VaoBackendMemos& VertexInputStateFactory::MemosFor(
|
||||
const MG_State::GLState::VertexArrayObject& vao) const {
|
||||
static_assert(kVaoMemoSlotCount == kMagmaVaoIdentityEntries,
|
||||
"this table is indexed directly by MagmaPipeSlotIndex, so it has to hold "
|
||||
"exactly one entry per slot the VAO identity table can mint");
|
||||
if (m_vaoMemos.empty()) {
|
||||
m_vaoMemos.resize(kVaoMemoSlotCount);
|
||||
}
|
||||
const MG_Pipe::MGPipeHandle handle =
|
||||
MagmaPipeHandleOf(MG_Pipe::MGPipeKind::VertexElementsCso, vao.GetLifetimeId());
|
||||
// One entry per mintable slot - see the static_assert on kVaoMemoSlotCount - so this
|
||||
// index is exact and two live VAOs cannot share an entry. There is no probe in front
|
||||
// of it because the mint itself is one: an array index and at most two Uint64
|
||||
// compares, which is less than the address hash the pre-handle arm ran.
|
||||
m_identity->HandleOf(MG_Pipe::MGPipeKind::VertexElementsCso, vao.GetLifetimeId());
|
||||
// One entry per mintable slot, grown on demand: the mint has no capacity, so neither
|
||||
// does this, and no two live VAOs can share an entry however large the working set is.
|
||||
// There is no probe in front of it because the mint itself is one - a one-entry memo
|
||||
// hit for every acquisition after this draw's first, and a hash probe otherwise.
|
||||
VaoBackendMemos& memos = m_vaoMemos[MagmaPipeSlotIndex(handle)];
|
||||
if (!(memos.Owner == handle)) {
|
||||
// A slot whose Gen moved because the identity table recycled it for a different
|
||||
|
||||
Reference in New Issue
Block a user