Commit Graph
333 Commits
Author SHA1 Message Date
swung0x48 6e2e4c295d [Fix] (Espryt): resolve the framebuffer record by the handle it names and refuse before binding, so a DSA blit or clear configures the framebuffer it is about to write instead of one that never got its attachments 2026-09-08 16:57:36 -04:00
swung0x48 c64913307a [Refactor] (Espryt): take the sampler parameters and the program artefacts from the applier instead of the frontend objects 2026-09-08 16:57:36 -04:00
swung0x48 6b7475fea4 [Refactor] (Espryt): sync the driver framebuffer from the pushed record and answer the read buffer from the resolved read surface 2026-09-08 16:57:36 -04:00
swung0x48 e4208541e7 [Refactor] (Espryt): allocate renderbuffer storage from the pushed descriptor and keep the deferred out-of-memory report where it is 2026-09-08 16:57:36 -04:00
swung0x48 24c54bf4d7 [Refactor] (Espryt): drive texture storage, parameters and uploads from the pushed descriptors and take the upload strides from the record 2026-09-08 16:57:36 -04:00
swung0x48 86cfc379de [Refactor] (Espryt): key the texture, renderbuffer, framebuffer, sampler and program twins on the client-minted handle instead of the frontend object 2026-09-08 16:57:36 -04:00
swung0x48 83c8101a9b [Fix] (DirectGLES): record the fp64 narrowing's dropped SyncGpuWrites as a ruled P3a deviation at the site, with its blast radius 2026-09-08 07:00:28 -04:00
swung0x48 2a5e0195b8 [Fix] (DirectGLES): stop on an armless P3a subsystem verdict now that every LEGACY_MEMOS=0 lane pins an explicit 0x1ff arm - warning and continuing drew through an unconfigured driver VAO 2026-09-08 07:00:28 -04:00
swung0x48 afda613bd9 [Fix] (DirectGLES): publish GLESBufferResource::hostBytes under pendingMutex - it was the one member the off-thread SubData/FlushRange arm wrote with neither a lock nor an atomic 2026-09-08 07:00:28 -04:00
swung0x48 9ba5d7ba1e [CI] (scripts, test.yml, DirectGLES): hash FlushPendingRangesFrom as G5's eleventh row - a push build compiles no FlushPendingRangesNow, so the tenth row protected text the shipping build never sees 2026-09-08 06:54:25 -04:00
swung0x48 78ff014547 [Fix] (MG_Impl, MG_State, DirectGLES): give the vertex-elements CSO a backend-neutral death path - the client minted every VAO's slot and only Espryt ever freed one, so under Magma each VAO leaked a slot and a 1.3 KB applier record for the life of the process 2026-09-08 06:49:59 -04:00
swung0x48 3c55e02797 [Fix] (Espryt): republish the live shadow base on the ensure path so a lazily twinned store is not left with none for the readers that hold no frontend object 2026-09-08 05:27:19 -04:00
swung0x48 75ea7ee2df [Fix] (Espryt): ask the frontend object whether the shadow the ensure path is about to upload holds bytes, instead of a descriptor no content call refreshes 2026-09-08 05:27:03 -04:00
swung0x48 13b380feda [Fix] (Espryt): resolve an attribute's pushed vertex buffer by its attribute index, not by the GL binding point the wire attribute carries 2026-09-08 05:26:48 -04:00
swung0x48 df055eab13 [Fix] (Espryt): treat an unbound vertex-elements record as a memo miss and record the applier-reset serial rule the twin's gate depends on 2026-09-08 04:52:15 -04:00
swung0x48 511b3752c0 [Fix] (Espryt): make the vertex-input bit require the resource bit and give both new subsystems a named verdict when the knobs leave no arm 2026-09-08 04:52:15 -04:00
swung0x48 b28058f37c [Fix] (Espryt): walk all 32 attribute slots so a vertex-elements record that shrinks still disables the arrays it dropped 2026-09-08 04:52:15 -04:00
swung0x48 14137bc9a6 [Fix] (Espryt): refuse a backwards generation and an out-of-range slot in the handle-keyed GetOrCreate instead of adopting them 2026-09-08 04:52:15 -04:00
swung0x48 39933613ae [Fix] (Espryt): re-read the client's shadow base at every use and forget it at the two events that free the allocation it names 2026-09-08 04:52:15 -04:00
swung0x48 816373ffd9 [Fix] (Espryt): keep the live-map question in the handle arm's draw-clean probe instead of a record field P3a pins false 2026-09-08 04:52:15 -04:00
swung0x48 7047331a41 [Fix] (Espryt): clamp the handle readback to the backend store past its offset, memoise the buffer handle lookup, and write down the two contracts the ops owed their callers 2026-09-08 04:52:15 -04:00
swung0x48 7ef8cb93fc [Fix] (Espryt): leave FlushPendingRangesNow as this file's only definition and call the one shared range ladder from both arms 2026-09-08 04:52:15 -04:00
swung0x48 b97a228cea [Fix] (Espryt): carry the adopted-store respecify's buffer-id generation bump into the handle arm as well 2026-09-08 04:52:15 -04:00
swung0x48 68db6db7f8 [Refactor] (Espryt): key the narrowed fp64 vertex stream on the buffer's handle instead of its lifetime id 2026-09-08 04:52:15 -04:00
swung0x48 cd05de504e [Refactor] (Espryt): drive the driver VAO from the pushed vertex-elements record and retire the wrapping index-slot version and its identity patch 2026-09-08 04:52:15 -04:00
swung0x48 ce24e2a734 [Refactor] (Espryt): answer a readback through the reverse channel and bump the mutation epoch after it, never before 2026-09-08 04:52:15 -04:00
swung0x48 4e44650199 [Refactor] (Espryt): take the buffer ops by handle and payload instead of by frontend object reference 2026-09-08 04:52:15 -04:00
swung0x48 9951961d9c [Refactor] (Espryt): give the buffer resource its own {slot, gen} table instead of hanging it off the frontend object 2026-09-08 04:52:15 -04:00
swung0x48 5cb826b01e [Merge] (dev): bring dev@9eae9858 into feat/disaggregated - the adopted-store respecify VAO rebind fix (d7655247) and the persistent-buffer ordering POST probe 2026-09-08 03:15:31 -04:00
swung0x48 df784c6752 [Fix, Test] (Espryt): deliver a death notice to every holder of a kind, retire the twin table's last sweep, and pin the bring-up call site under the armless knob pair
- BackendSlotTable links every instance into a per-type holder list; OnFrontendObjectDestroyed resolves the handle once, drops the twin in each holder by handle and frees the slot once, last - a notice delivered to one registry left the fixture's saved copy holding a dead twin, and its driver storage, for the life of the process (review v4 minor 2)
- ReclaimDeadSlots and CollectGarbageNow leave the slot table: nothing called them, so the header's "backstop" was a claim without a caller; the registry's pre-P2 CollectGarbageNow is a no-op on the handle arm (minor 1)
- HandleOf no longer memoises a null answer, which a second holder's acquire could never refresh (minor 8)
- EnsureProcessTeardownSentinel is armed by the slot table's first insertion, as D13 says; the registry arms it only on the legacy arm (minor 9)
- new SanityTest cases: OneDeathNoticeDropsTheTwinInEveryHolderOfTheKind, ASavedCopyOfARealRegistryDropsTheTwinOnTheSameNotice, ANegativeLookupIsNotCachedAcrossAnotherHoldersAcquire, and EglBringUpUnderTheArmlessKnobPairReturnsInsteadOfStopping, which runs InitPbufferSurface under the pair in a forked child and fails naming both knobs if InitDisplayAndContext ever stops there again (minor 4)
- AnArmlessKnobCombinationStopsInsteadOfSkippingTheLane writes a per-process, per-case log path and restores MOBILEGL_LOG_FILE_PATH and MG_Config::Features through RAII guards on every exit path (minor 5)
2026-09-07 23:18:09 -04:00
swung0x48 7c97fcfee3 [Fix] (Espryt): stop the armless knob pair inside the test that needs an arm, not inside the EGL bring-up a forked pre-flight swallows
- Fatal{PipeLegacyMemosDisabled} was raised from InitDisplayAndContext(), i.e. from inside
  eglMakeCurrent. The integration harness pre-flights that exact sequence in a forked child
  (MG_IntegrationTest/Harness/HeadlessGL.cpp) and reports a child that dies on a signal as
  "no usable GPU/display/ICD", so every scenario SKIPPED and ctest called the lane 100%
  passed while running nothing - on the very pair of env vars the D14/D18 A/B is driven
  with. ROADMAP.md:7 forbids a gate that cannot go red for the reason it exists.
- The arm decision becomes a pure function of the two knobs, ClassifyEsprytSlotArm(), with
  three verdicts. Bring-up now calls DiagnoseEsprytSlotArm(), which names both knobs at
  ERROR and RETURNS; the stop stays in ResolveEsprytSlotTablesArm(), which the inline latch
  reaches at the first twin lookup - a scenario body, where a crash is a test failure.
- A process that never looks a twin up never needs an arm and is no longer stopped by one
  it would not have used. That is the only behaviour this moves.
- SanityTest gains an always-on case: the four knob combinations of the pure classifier,
  that the diagnosis does not stop, and that the stop is SIGABRT whose log line names
  PipeLegacyMemosDisabled, MOBILEGL_PIPE_PUSH, MOBILEGL_PIPE_LEGACY_MEMOS=0 and the bit -
  the message and not merely the signal, because "Subprocess aborted" alone tells an
  operator nothing. It skips visibly in the pull and no-legacy builds (G2 name parity).
2026-09-07 23:18:09 -04:00
swung0x48 eb81705130 [Fix] (Espryt): tell the backend when a frontend object dies instead of discovering it in a garbage sweep
- P2 step e2, as far as the file-ownership table lets one package take it. New frontend
  header MG_State/GLState/StateObjectDeathNotice.h carries BufferBackendOps' shape for the
  other six kinds: an ops table the backend fills in, and one entry point that takes
  {kind, lifetimeId} rather than the object, because by the time the last SharedPtr has
  dropped there is no object left to pass and the lifetime id is exactly what the client
  slot allocator resolves a handle from. Declared only under MOBILEGL_PIPE_PUSH, so the
  pull build's symbol set is untouched.
- BackendSlotTable::DestroyByLifetimeId drops the twin and returns the slot at the moment
  the object goes, instead of at the next sweep - which for a renderbuffer or a texture
  atlas is the difference between freeing the driver allocation now and freeing it 64
  creations from now. It returns the slot only when THIS table holds it: two holders of one
  kind already exist (the ScopedDirectGLESTextureBindings fixture; Magma's subsystem-4
  table shares the VertexElementsCso kind), and a table that never twinned the object must
  not free a slot the other one still names. The legacy arm keys on the frontend heap
  address, cannot answer a notice at all, and keeps the sweep - which is the announced-
  versus-discovered half of the A/B the compile-time arm exists for.
- Managers.cpp registers one dispatcher for all six kinds from ResolveEsprytSlotTablesArm(),
  i.e. exactly when the arm that can answer a notice is the arm that runs, and drops a
  notice that arrives after exit() has begun.
- FIRING it needs a destructor per class, and the P2 ownership table gives
  {Texture,Framebuffer,Sampler,VertexArray}State/* to other packages, so only ProgramObject
  and RenderbufferObject raise it here. The other four still rely on the sweep; their four
  one-line calls retire it entirely.
- Three cases pin the three halves: the slot comes back with no sweep and the notice is
  idempotent and does not free another holder's slot; a program and a renderbuffer announce
  their own death when the last SharedPtr drops and not before; and the handle arm actually
  installs a consumer, rather than the two halves each being fine on their own.
2026-09-07 23:18:09 -04:00
swung0x48 9a8369296e [Fix] (Espryt): sweep the twin table on object churn again, refuse to run an arm the operator disabled, and give the hot lookups back their array probe
- The handle arm took only ONE of the registry's two sweep drivers. The map arm sweeps
  every 64 first-time insertions BECAUSE object churn, not draw count, is what makes the
  sweep urgent: a CTS-shaped case runs ~10 per-draw ticks, so the 1024-tick draw-path
  driver alone spans ~100 cases' worth of dead, gigabyte-sized twins. BackendSlotTable now
  carries the same kCreationGCInterval = 64 creation tick, swept before the entry reference
  exists for the same reason the map arm sweeps there. Without this the slice REGRESSED the
  memory it was supposed to leave unchanged.
- Fatal{PipeLegacyMemosDisabled} now aborts. It logged and then returned false, which fell
  straight into the legacy arm the operator had just made unreachable: a green run measured
  on the wrong arm, and the exact lever HandleRecycleScenario's arms are selected with. It
  is also resolved at backend context creation now, not on the first twin lookup, so a
  process that twins nothing still learns its knobs leave it with no arm at all.
- EsprytSlotTablesEnabled() becomes an inline latch over an out-of-line resolver. It is
  consulted on every Find/GetOrCreate/HandleOf/ForEachLive/CollectGarbage*, i.e. several
  times per draw, and as a cross-TU call with no LTO that was a PLT call per lookup.
- HandleOf keeps a one-entry lifetimeId -> handle memo, so the three per-draw resolution
  paths whose TwinLookupMemos this slice deleted go back to an integer compare plus an
  array index instead of the allocator's ByLifetimeId hash - which is the "direct slot
  indexing" the memo removal was traded for. It cannot serve a stale answer: a lifetime id
  is never handed out twice, and FindByHandle compares Gen anyway.
- GetOrCreate(nullptr) returns a parking slot instead of dereferencing null in a release
  build; the map arm inserted a null key and SyncTextureObjectToBackend documents relying
  on that tolerance.
- ReclaimDeadSlots moves the twin out before it writes the entry, so a twin destructor that
  re-entered GetOrCreate and grew m_slots could not make the writes land in freed memory.
- The framebuffer binding-slot cache is gated on kMGPipeSubsystemEsprytSlots rather than on
  the compile-time MOBILEGL_PIPE_PUSH, so MOBILEGL_PIPE_PUSH=0 stays the faithful
  all-subsystems-pull control ConfigLoader.cpp documents. The poison bypass stays closed on
  the arm that ships.
- MGB_TWIN_KIND_PARAM/ARG stop leaking into every TU that includes Managers.h: the twelve
  declaration and definition sites name a TwinRegistry alias template that swallows the
  kind in the pull build, and the one remaining macro is #undef'd after the class.
- The twin lookup inside BindCurrentFBO stops shadowing the framebuffer binding slot in a
  function whose whole subject is which "slot" is meant.
2026-09-07 23:18:09 -04:00
Swung0x48 f4dbea2300 [Feat] (Espryt): give the backend a dense {slot, gen} twin table beside the address-keyed registry
- SlotTables.h: BackendSlotTable<StateObject, BackendObject, kKind>, indexed by
  MGPipeHandle::Slot and validated by Gen, with the handle minted by the client
  MGPipeSlotAllocator off the frontend object GetLifetimeId(). A lookup is one bounds
  check plus one array index, and unlike the registry Find it never mutates the table,
  so a returned BackendPtr* is not invalidated by the next call on it.
- StateBackendObjectRegistry keeps its name, its signature and all ~40 call sites, and
  becomes the two-arm facade ARCHITECTURE.md 9.6 asks for: the pre-handle map under
  MOBILEGL_PIPE_LEGACY_MEMOS, the slot table under MOBILEGL_PIPE_PUSH, chosen once per
  process by EsprytSlotTablesEnabled() off kMGPipeSubsystemEsprytSlots. A clear bit with
  MOBILEGL_PIPE_LEGACY_MEMOS=0 leaves no arm at all and is Fatal{PipeLegacyMemosDisabled}.
- The kind is a template parameter only in the push build (MGB_TWIN_KIND_ARG): a third
  template argument would rename every instantiation and G1 wants the pull build byte
  identical. Pull-build symbol report is 0 added / 0 removed / 0 renamed and adds no
  resize beyond the three RenderState symbols the contract commit already moved.
- ForEachLive replaces begin()/end() under push and hands the callee a strong reference
  to the state object instead of the map key, which was the raw frontend address.
- Nothing switches over yet: the tables are built and reachable, and the twins still go
  through whichever arm the bit selects.
2026-09-07 23:18:09 -04:00
swung0x48 d7655247f7 [Fix, Test] (DirectGLES, Integration): rebind VAOs when an adopted buffer is respecified - the immediate retire path forgot the buffer-id generation, so cached vertex and element bindings kept the deleted store
Advance the buffer-id generation when Ops_Respecify retires immutable storage on the context thread, matching the existing adoption and deferred-retirement paths.

Add pixel regression coverage for unchanged VBO/IBO attachments across same-size redefinition, growth and shrinkage, including bound and unbound VAOs sharing a vertex arena and an index arena returning to shadow storage.
2026-09-06 22:55:06 -04:00
swung0x48 d4504e30f8 [Refactor] (Espryt): route every frontend read through MGB_CTX - 113 arrow sites sed'd, 9 non-arrow lines converted (the fb-slot cache keys on MGB_CTX_IDENTITY); pull build byte-identical
- P1 package B (BRIEF-P1 C.2): the four DirectGLES TUs include <MG_Pipe/PipeInputsSwitch.h> right after
  their MG_State/GLState/Core.h include (Managers.cpp after its Managers.h include) and spell every
  frontend read MGB_CTX->Accessor(...). In the pull build MGB_CTX is MG_State::pGLContext, so the
  code is the tree before this commit token for token; in the push build it is &gPipeInputs, the block
  the frontend fills at every verb boundary.
- 113 arrow occurrences on 113 lines went through the mechanical sed (DirectGLES.cpp 91, Managers.cpp 15,
  MultiDraw.cpp 5, Utils.cpp 2). The 9 non-arrow lines follow D9: Managers.cpp's five bare/compound
  guards and three ternary conditions become MGB_CTX_LIVE (UniquePtr::operator bool spelled out, so the
  pull build does not move); DirectGLES.cpp's GetFramebufferBindingSlotFast keys its static cache on
  MGB_CTX_IDENTITY (a const void* compare) instead of pGLContext.get().
- The cache refill loop dereferences MGB_CTX once into a local reference and reads the slots through it,
  instead of &MGB_CTX->GetFramebufferBindingSlot(i) per iteration as D9 spells it: a store into
  g_fbSlotCache (a pointer) may alias the unique_ptr's own pointer under clang's TBAA, so the per-iteration
  spelling re-reads pGLContext inside the loop and grows the three functions the loop is inlined into
  (SyncCurrentProgram +16, ForceBindCurrentFBO +9, BlitNamedFramebuffer +1; .text +32). Hoisting the
  dereference restores the single load and a zero .text delta.
- grep -rc pGLContext MobileGL/MG_Backend/DirectGLES reports 0 in every file; symbol_report --threshold 0
  against the 087685d1 baseline: 0 added / 0 removed / 0 resized / 0 renamed, .text +0, nm --defined-only
  set identical. The only bytes that move are __LINE__ immediates in RecordError sites after the inserted
  include line. The 8 SyncPersistentMappedRange and 3 SyncGpuWrites sites and the PipeStats AddCalls
  literals are untouched.
2026-09-06 04:41:28 -04:00
swung0x48 d380a01f32 [Fix] (Metrics, DirectGLES, DirectVulkan): stop the summary line printing window totals under a per-frame label, and wire the six staging paths the site inventory claimed were covered or absent
- A window with no Present divided by a faked 1 and printed the window TOTALS under
  "bytes/f[...]": the *MultiDraw* slice (47 draws, no present) reported 1,404,550 bytes as a
  PER-FRAME figure, a 47x overstatement of exactly the SEG_STAGE sizing input plan B section
  8.2 / section 11 P0 asks this package to produce. FormatWindowLine now relabels the bracket
  to "bytes[...]" and prints draws/f=n/a when the window holds no frame; acc/draw=n/a follows
  the same rule, because "0.00" beside a non-zero acc= is the same lie. Pinned by
  PipeStatsTest.SummaryLineSurvivesZeroFrames and the reworked SummaryLineSurvivesZeroDraws.
- Every per-frame and per-draw field now goes through one FormatFixed2 helper. draws/f read 1
  for 26 draws over 14 frames (1.86) and buf read 97 for 1360 bytes (97.14): a systematic
  downward truncation of up to a whole unit on the figures the package exists to produce.
  Pinned by PipeStatsTest.PerFrameFieldsKeepTwoDecimals.
- FormatSummaryLine rewrote the window bases as a side effect of formatting, so any second
  reader silently zeroed the next window. Split into a pure FormatWindowLine() and an explicit
  AdvanceSummaryWindow(); EmitSummaryLine calls both. Pinned by
  PipeStatsTest.FormattingTwiceDoesNotConsumeTheWindow.
- Init() called ResetForTesting(), against the header's own "not used by any shipping path".
  Both now forward to an internal ResetCounters().
- TracyPlot published only the MISS half of each gate under the gate's unqualified name, so
  the headline output channel of section 11 P0 carried no denominator. Two series per gate now
  ("...-hit" / "...-miss") from static literal arrays. The payload histogram stays unplotted
  and says why: it is a run-total distribution over draws (section 4.5.7), not a per-frame
  scalar, and it reaches the operator through the JSON dump.
- GetOrCreatePipeline's 15-call tally sat ABOVE the list-topology primitive-restart refusal,
  so a declined draw added ten reads it never made - an OVER-count, which breaks the lower
  bound contract every other tally keeps. Moved to immediately before the payload build, and
  the enumeration reconciled with the constant: the excluded read is the sample-shading
  capability, short-circuited by m_sampleRateShadingFeatureEnabled.
- Six real staging paths were uncounted while the inventory claimed coverage. The inventory
  claim "DirectVulkan's own buffer staging ... has no second copy to count" was simply false.
  Now wired: MultiDraw.cpp's UploadScratch/UploadScratchRing (indirect commands, the compute
  tier's draw-info array, the rebased index stream - the class is passed in, so a new tier
  cannot forget it); Managers.cpp's pool-recycle reseed and the VBO-backed Float64 narrowing;
  VkBufferManager's eight host->device copies of buffer contents plus UploadTransient, the
  single chokepoint for Magma's per-draw vertex/index/indirect staging; VkTextureManager's
  packed staging slice, with the same box/rect SHAPE split Espryt already reported.
- New byte class stage-indirect-cmd, for draw PARAMETER bytes a backend synthesises and
  stages. Kept out of stage-index-client because these are the population that becomes MGPipe
  command-record payload (section 4.5.7), not resource bytes. A name addition, not a rename:
  no recorded baseline is invalidated.
- stage-ubo-named moved past the zero-copy direct-bind decision in ResolveUniformBufferPayload
  (a direct bind repacks nothing, so counting it there reported a copy that never happened),
  and Magma's default-uniform-block image now feeds stage-ubo-global the way Espryt's does,
  counted after the per-frame slice memo.
- The site inventory in PipeStats.cpp is rewritten to name every unwired path by file and
  function. An inventory that overstates coverage is worse than a missing counter, because
  the zero is then read as an answer.
- Evidence, lavapipe/llvmpipe, MOBILEGL_PIPE_STATS=1: the MultiDraw slice now prints
  "window=0 draws/f=n/a bytes[buf=1404550 ...]"; the indirect tiers
  (MOBILEGL_ESPRYT_MULTIDRAW_MODE=indirect|multiindirect) move icmd 0 -> 660; Magma's GuiBatch
  line moves from buf=0 tex=0 ubog=0 to buf=685.71 tex=41.14 ubog=157.71 with tex[emit=9
  box=9 rect=0 jobs=9]. Off-path A/B against the base tree with the env unset, 9 runs each of
  a 40-scenario draw slice, sorted totals in ms: Espryt base 389..794 (median 399) vs branch
  384..403 (median 394); Magma base 406..472 (median 410) vs branch 408..761 (median 414) -
  the always-compiled guard is below this harness's noise on both backends.
- 1410 unit tests green (15 PipeStatsTest). integration-gpu 860/860, 860/860, 859/860; the one
  failure is DirectVulkan.PointSizeDemotion...TheDemotionIsActuallyArmedWhenTheEnvironmentPins-
  ItOn, a member of the load-dependent *IsActuallyArmed* flake family already present in the
  untouched base tree, and it passes 8/8 standalone here. stdio gate and gen_pipe check green.
2026-09-05 20:16:49 -04:00
swung0x48 7566a0b002 [Feat] (DirectGLES, DirectVulkan): instrument the six memo gates, the staging byte paths and both Present hooks with the MGPipe counters
- The sites of plan B section 2.3.1, verified against dev@81b17c0b (the plan's own line
  numbers for DirectGLES drift by 4-9 lines; the DirectVulkan ones are exact):
  SyncRenderState is DirectGLES.cpp:1994 with the version read at :1998 and the
  early-out at :2007-2010 (plan says 2003 / 2007 / 2016-2018); SyncNeccessaryTextures
  at :1511 (plan :1520); CurrentUnitBindingsEpoch at :1412-1435 (plan :1418-1436);
  PrepareForDraw at :2907-2968 (plan :2916-2976); the global-UBO upload at :3355-3397
  (plan :3369-3392); TrySetupDrawFastPath :5994, GetOrCreatePipeline :4948-4993,
  ApplyDynamicDrawStateTail :5871-5893 and UniformManager ResolveUniformBufferPayload
  :2022/:2052 all as cited.
- Accessor counting is STATIC TALLIES at ten hot entry points, not a wrapper around the
  293 pGLContext-> sites: each instrumented function adds the number of accessor calls
  its own body made on the path taken. Reads inside callees, and every conditional read
  (the sRGB capability in SyncRenderState, the XFB probe and the version-gated parameter
  fetch in TrySetupDrawFastPath, the cull-mode/logic-op/tessellation reads in the
  pipeline payload builder) are excluded, so the number is a consistent LOWER bound. The
  full inventory of what is and is not counted is the header comment of PipeStats.cpp.
- The Magma fast-path gate is counted from SetupDraw, not from inside
  TrySetupDrawFastPath: that function has 27 decline returns and one success return, and
  counting at the caller is the only shape that cannot miss one.
- The texture upload counts the SHAPE (union box vs N-rect list) separately from the
  bytes, because SSIM is blind to the shape and the +6 ms/frame Mali regression of
  section 7.3 was a shape regression, not a byte one.
- Frame boundary: DirectGLES::Present after the ring upkeep, and DirectVulkan's backend
  Present rather than VulkanRenderer::Present - the latter has an early return for the
  no-usable-swapchain case, and a suspended frame is still a frame the counters close.
- Measured on lavapipe/llvmpipe with MOBILEGL_PIPE_STATS=1, GuiBatchScenario, 14 frames
  and 26 draws: Espryt 20.65 accessor calls per draw (gates ers 22/18, etl 71/9, eub
  71/9), Magma 15.54 (mfp 12/14, mpm 0/14, mdt 12/14). Both land inside the 10-25 band
  section 2.3.1 predicted and far below the 124/169 static counts, which is the
  correction that section was written to force.
2026-09-05 20:16:49 -04:00
swung0x48 d7f66722d1 [Fix] (ShaderTranspiler, Link, DirectGLES, DirectVulkan): demote tessellation/geometry gl_PointSize to an ordinary varying where the device cannot host the built-in - the value survives for gl_in reads and by-name capture, both backends' declines stay for shapes the pass refuses, and the verdict rides the L1 key 2026-08-28 06:21:30 -04:00
swung0x48 0ee3384b22 [Fix] (Espryt): land a SubData into an adopted store as a GPU-ordered copy - the in-place coherent write tore the frames still reading the old section bytes 2026-08-28 04:50:07 -04:00
swung0x48 3327784fd0 [Fix] (Espryt): adopt mesh-arena-sized stores into coherent persistent maps at definition, and stop the flush tiers from re-synchronizing them 2026-08-28 04:19:11 -04:00
swung0x48 5d51372c44 [Chore] (Config): triage backend-scoped env toggles under MOBILEGL_ESPRYT_ and MOBILEGL_MAGMA_ prefixes 2026-08-28 00:38:17 -04:00
swung0x48 29599dcf90 [Fix] (DirectGLES): flush queued buffer ranges through a range-invalidating map first, so a partial write into a busy store is priced by the range 2026-08-27 23:54:19 -04:00
swung0x48 8282eecbfa [Fix] (Espryt): store RGB565/RGB5_A1/RGBA4 as 8-bit channels where a POST probe measures the Mali packed16 array-mip field-order mirror 2026-08-27 22:46:12 -04:00
swung0x48 a28da07641 [Fix] (DirectGLES): queue app buffer updates and flush them through a staged-copy upload ring instead of stalling in glBufferSubData 2026-08-27 22:19:44 -04:00
swung0x48 525ffe0f14 [Test] (Review): pin the inverted override mapping, the probe's controls, and that the pinned-on lane is really armed 2026-08-27 21:06:01 -04:00
swung0x48 ad28d2b744 [Fix, Test] (Review): strip a block's member-level locations too, restore the probe's colour mask, and let the POST verdict follow the override 2026-08-27 20:15:31 -04:00
swung0x48 0d0ef13619 [Fix] (DirectGLES): run the interface-block location strip last, where its deliberately Vulkan-invalid module reaches no validator 2026-08-27 19:44:18 -04:00
swung0x48 5dc26e3e2c [Fix] (DirectGLES, ShaderTranspiler): drop the location qualifier from inter-stage interface blocks on a driver that loses their payload 2026-08-27 19:18:35 -04:00
swung0x48 7520607d47 [Fix] (Review): decline a point-size program the device cannot run, bound the compile-failure source dump, and correct the pass-through rationale 2026-08-27 15:42:04 -04:00