- 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.
- 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.