mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
7ae1c11de01b41dca08dab5fcaa9d59c727fc635
156
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c997ba9425 |
[docs] (P7): magma-b2.md carries review round 2 - where the flipped case asserts, the padded stride, one decision per blit, MsFlip1 - two new debt lines, and gates for this tree and the landed one
- §2.9 (new): the lane-marker/backend skips and their verified Skipped lines, the 4-byte row stride and its validation-layer red-once, the whole-call shape decision (the round-1 partial effect: a scaled or X-mirrored COLOR|DEPTH blit wrote its colour and then errored on its depth) with its red-once, the split once-sites, the empty-side decline, MsFlip1 and the exact-one parity tail check, each red-once as run. - §2.6: MsFlip goes red on a device whose renderbuffer and texture D24S8 map to different VkFormats (cross-format Y decline); §5 item 2 says to check it on the Redmi. - §2.8: the round-1 bufferRowLength=0 note and the per-aspect decline are marked superseded by §2.9. - §6: item 1 cites VulkanRenderer.cpp:10066 (the vkCmdBlitImage; :10060 is the vkCmdResolveImage) and states the scale partial effect as closed; item 5 states the X/cross-format-Y partial effect as closed and that its INVALID_OPERATION is a rule I (a) refusal of a legal blit; item 4 counts six knob entries; new item 6 (monolith DirectVulkan flipped/scaled multisample depth blit, VulkanRenderer.cpp:9594/:9608, a wire-vs-monolith divergence where the wire arm is the correct one, P13/G1-bound) and item 7 (Espryt flipped/scaled multisample depth resolve, P3b/P4b); stale WireFramebuffer.inc line numbers refreshed. - §7 is two labelled rows: this tree (p7/magma-b2@4442ea6d, measured) and the landed tree (pipe@3b66d1b1, integrator-measured), instead of numbers pinned to 5d896170, which is not an ancestor of the landed tree. |
||
|
|
1b9aa65145 |
[docs] (P7): the pipe commits the status page and ID-P7-35/36 cite are the ones without attribution trailers
- the sixteen unpushed integration commits after 1135c664 were rewritten to drop the Co-Authored-By trailer the project forbids (commit-style rule, user 2026-07-15 and 2026-09-09); their SHAs changed, so every citation of the old SHAs is remapped here - no content change beyond the SHA text |
||
|
|
542c5838e2 |
[docs] (P7): ID-P7-36 - the B3 rework lands with fixes, one knob-exception list survives the merge, and the OpenRA split retrace with its new red condition is part of every pipe gate
The status page's exit-gate table now says item (7) verify x split is reported and on pipe pending its gate and review, and the running table names the B3 fix round and the V1 review. |
||
|
|
75858bfd9c | [docs] (P7): notes/p7/verify-split.md records exit gate 2 met on the host - verify x {monolith, split(inproc)} green on both backends, both controls red once on the split arm, 8 verify traces x DirectVulkan x inproc at zero divergences - and CONTRACT-P7 §6 and §8 (7) say so | ||
|
|
75a64ce0da |
[tools, docs] (P7): the parity script keeps one server-side-knob list after the B3 rework merge, and magma-b3.md names it
The B3 rework's parity hunk and B2's round-1 hunk described the same asymmetry
under two names; the merge kept B2's compare_arms(no_tcp=) and added .StaleSerial.
to MAGMA_SERVER_ENV_KNOB_NO_TCP. One line of the package tree's other mechanism
(`not_on_arm = not_on_arm or {}`) survived outside the conflict hunks and would
have been a NameError on the first run; it goes, and the note's two mentions of
MAGMA_NOT_OVER_TCP now name the list that exists.
|
||
|
|
3f92d0f109 |
[docs] (P7): magma-b3 - the review round: every decline row is sited, the floor has a lane, the stamp is a probe, and the tcp arm carries no knob it cannot see
Records review ID-P7-34's four should-fix items as landed: - §1.5: the .def audit (52 rows / 40 sited / 10 unlogged, rc 1 -> 53 / 53 / 0, rc 0), the per-function distribution, and the forced ShapeNoVkFormat red; §1.2's "32 exits" is corrected against the tree. - §3.2: the submission term is a probe and a defence, not the fix - the stamp can name S1 while the draw rides S2 through the preserve flush - and the :273 early return and :432 re-stamp. - §4.1: the MGWIRE-FLOOR red condition in run_trace_case.cmake, with the red (clamp deleted: 26 lines, ssim 1.0, FAIL on SPLIT and SPAWN) and the green. - §4.2: StaleSerial. on split and spawn only; MAGMA_NOT_OVER_TCP. - §5: the review-round gate sweep; integration-magma-tcp 71 -> 70. - §7.2 (for CONTRACT-P7 §12): the disaggregated monolith arm is now sounder than the pull build on mid-frame flushes (OnSubData reads the same floor via IsResourceBusy; the pull build keeps the latent bug under G1 / P13), and the stale comments at WaitForFrameSerial and CollectAllDeferredReleases; the reviewer's verdict on the clamp and on B4. |
||
|
|
613ed05b98 |
[docs] (P7): ID-P7-35 - the B2 rework's first round is sent back, because its new case also lands on the monolith and Espryt entries and its per-row copy-out needs an aligned stride
The status page and the ROADMAP P7 row now say what is in flight after the six packages were relaunched on claude-opus-5-5: B2 rework round 2, B3 rework, M2, V1, F and E1. pipe stays at 1b5d97bd and is not pushed until round 2 lands. |
||
|
|
cc3c18c578 |
[scripts, docs] (P7): magma-b2.md counts the baseline as 73 + 11 + 2 + 2, carries the review round's §12 lines and gates, and the ratchet baseline names the tree that measured 173
ID-P7-31 review, note 3 and nits.
* §0's preamble: the +4 over package B's 84/63/65 is not "package A landing after
B" - A landed BEFORE B and is +2 (magma-a.md:151-153); package C is the other +2
(magma-c.md:59-61). 73 + 11 (B) + 2 (A) + 2 (C) = 88/67/69.
* §2.3 / §2.5: MsResolve1 is split+spawn only; the day-of +3 on tcp is marked as
superseded.
* §2.7 (the server-side knob has no tcp arm, measured) and §2.8 (flip mirrored,
scale declined, X / cross-format mirror declined; three red-once lines).
* §6: the lines for CONTRACT-P7 §12 - the scaled second leg is unclipped
(VUID-vkCmdBlitImage-dstOffset-00248/00249, shared with the monolith);
MagmaWireFatal("multisample-resolve-region") for a source rect outside the read
buffer, where GL promises undefined values rather than an error; the 1->N
identical-rect decline declines a legal shape (18.3.1 sample replication) because
the monolith's own observable is a VU violation; the tcp lane cannot carry a
server-side knob; X and cross-format Y mirrors of a depth/stencil resolve decline.
* §7: the review round's gates - magma 94/73/70, full-split 525, unit 2423, parity
RC 0 naming the five MAGMA_SERVER_ENV_KNOB_NO_TCP entries, census 79, ratchet 173,
G1 .text 0xa52203 with 0/0 symbol drift, @P7 19 comment lines.
* link_ratchet_baseline.txt: `base commit` points at 6af422b3, the tree that
measured 173, not 4bee1313.
The "@P7 = 24 lines" nit needed no edit: the note already reads 19 at 6af422b3, which
is what the tree measures.
|
||
|
|
3c80cd62fe |
[docs] (P7): ID-P7-34 - OpenRA passes 27/27 on the phone; CURRENT_STAGE_STATUS.md is the live stage page and the ROADMAP rows say where P7, Ph and P3b/P4b stand
p7w5 (B3 on top of B2 and D3) renders OpenRA to the golden on every arm the acceptance asked for - inproc and spawn, run-ahead and lockstep, cold and warm, FIF=8 and the dump-armed variant that failed 0/5 before - and the three regressions are bit-identical to p7w4. Gate 3's anchor case is closed; bsl-esc-menu (M2) and the three-repeat final form remain. docs/Disaggregated/CURRENT_STAGE_STATUS.md is new: the nine-item exit denominator with evidence, what landed, what is running, what is next. The ROADMAP's P7, Ph and P3b/P4b rows carry the wave-2 landing, the 6/9 checkpoint and the open items, and OQ-8/10/13 are marked answered. |
||
|
|
713bea9a8b | [docs] (P7): ID-P7-33 - B3 landed: the OpenRA divergence was an unsound frame-serial floor, proven by counter and closed on both submissions | ||
|
|
9a494b6f01 |
[docs] (P7): magma-b3 - the wire arm's frame-serial floor was raised on a fence it had not waited
Mechanism with file:line and the submission structure measured rather than assumed, the confirmation tallies (zero unforced, which refutes the silent-draw-drop hypothesis the investigation ranked first, and which the judge refuted independently on the prefix-tear forensics), the provable floor, the fence-backed second term in WriteWireBuffer, the red-once in three forms (a deterministic counter 26/26/0 -> 0/0/0, a two-process lane case, and forced OpenRA retraces), the gate numbers, and the device acceptance with three predictions H1 must satisfy. Records honestly: an earlier draft of this note blamed TryDrainFrameTransients' every-8th-drain frame boundary, which this package's own probe refutes (one successful drain in the whole replay, zero frame-boundary ones, all 33 serial bumps from Present); the GPU-drag knob the judge suggested was not built, with the reason; the aggregate-wait secondary fix is left to a separate package with the lane evidence that nothing in this tree's coverage regressed without it; and M1's "present fires once" observation does not hold for OpenRA, whose pbuffer replay presents every frame. |
||
|
|
b089e0de9b | [docs] (P7): ID-P7-31 carries B2's review verdict and the rework it sent back | ||
|
|
fe9105350c | [docs] (P7): ID-P7-32 - bsl-esc-menu is a dead-VkBuffer leak on the wire arm, and the 40 % checkpoint passed at 6/9 | ||
|
|
85ac7e7692 | [docs] (P7): ID-P7-31 - B2 landed: the six §3.2 retirements are complete, the ratchet re-baselines at 173, and the judge's Magma debts are recorded | ||
|
|
116dc48e80 |
[docs] (P7): W4 §3b - the judge's verdict: an unsound completed-frame-serial floor lets a streamed write take the unordered host path
Four lenses and an adversarial judge: the two wrong OpenRA pictures are prefix tears of one draw's vertex range, produced when frame N-1's first pooled-fence submission retires and raises the buffer manager's floor while the Present submission carrying the staged copies is still running, so frame N's first glBufferSubData takes WriteWireBuffer's host-memcpy path and the queued copies overwrite it. FIF=4/8 fail 6/6, as that predicts. The later device sessions' logs (spawn, texture dumps, bsl memory, FIF) join the evidence directory. |
||
|
|
3c2867d34d |
[MG_Backend, scripts, docs] (P7): in a disaggregated build the monolith arm blits and mips with the baked modules, and the ratchet's p7-magma bucket falls to 88
CONTRACT-P7 §5.2's (B'), which §4.2 needs.
WHAT MOVES. In a disaggregated build the monolith arm now drives the SAME baked
modules the wire arm does:
* InitializeBlitResources / InitializeDepthMipmapResources return true - there is
nothing to build on any transport;
* TryBlitToDefaultFramebufferWithShader turns its two BlitImageBindings into
WireImages and calls BlitWireColorToDefault (WireColorBlit.inc);
* GenerateDepthMipmapWithShader loops GenerateWireDepthMipLevel
(WireDepthMipmap.inc) and hands the image back in the finalLayout its caller
promised;
* GetOrCreateBlitPipeline, the three GLSL strings, the eight kHidden* ids and the
frontend members of BlitResources / DepthMipmapResources compile out.
The `#else` is the pull build's code, statement for statement.
WHY AN `#if` AND NOT A RUNTIME BRANCH - this is the whole of §4.2. P5f fv already
gave both initializers a non-monolith early return, and the ratchet counted the
symbols anyway, because a reference is a reference whether or not the branch runs.
And the MEMBER has to go with the construction: a SharedPtr member's destructor is
itself a reference to ~ProgramObject.
THE BUCKET TABLE (scripts/link_ratchet.py --bucket, build-split):
bucket before after a6
p6-core 11 11 6
p7-magma 101 88 101
p3b-p4b-espryt 13 18 14
both-backends 61 56 60
total 186 173 184
p3b-p4b-espryt's +5 is not a regression: those five MOVED out of both-backends
because DirectVulkan stopped referring to them and only DirectGLES still does.
Thirteen symbols went away outright, and they are exactly the three classes §4.2
names: ProgramObject::{AllocateLifetimeId, AttachShader, Link, ~ProgramObject},
SamplerObject::{SetLodRange, SetWrapR, SetWrapS, SetWrapT} and
ShaderObject::{Compile, DropCompileNode, JoinPendingCompile, ReleaseCompileNode,
SetShaderSource}. Re-baselined in this commit with --write-baseline; the 7 `# P13`
annotations carried over.
§4.2's "down to 0" DOES NOT HOLD, and the gap is not (B') left half-done - it is
that 88 of the 101 were never about the hidden programs. 62 come from
UniformManager.cpp.o and 14 more from VkSamplerManager.cpp.o: the MONOLITH
descriptor binder and sampler cache reading the application's own GL texture and
sampler objects (TextureObjectBase 35, TextureObjectWithOneMipmap 20 with its
vtable and typeinfo, SamplerObject's fourteen getters). Under a transport
SetupDraw branches to SetupWireDraw and not one of those lines runs - but that
branch is a RUNTIME one, which ID-P7-5 deliberately leaves to the optimisation
phase. notes/p7/magma-b2.md §4.3 names all eleven referring-object groups and what
each is.
NONE OF THEM IS ANNOTATED `# P13`, on purpose: that mark is for a symbol that
cannot fall without moving pull `.text`, and nothing above has shown that.
UniformManager's and VkSamplerManager's monolith arms are pull-build code, and
`#if`-ing them is the same operation this commit performed on VulkanRenderer.cpp.
They are wave 3's work, not P13's boundary problem, and marking them P13 early
would record an untried thing as a tried-and-impossible one.
Gates: integration-gpu 1705 (every monolith `DirectVulkan.` entry is in there),
integration-magma-split 93, -spawn 72, -tcp 74, integration-magma-full-split 524,
ctest -L unit 2423, all green; fatal_census 79 abort sites / 44 family words / 0
unmarked; spawn_lane_parity green; link_ratchet --assert-monotone unchanged at 173
against the re-baselined file, and --self-test OK.
RETRACE, which is this slice's load-bearing reading because the monolith arm
changed implementation: OpenRA.DirectVulkan (monolith) ssim 1.000000,
mismatchPixels 0, twice; OpenRA.DirectVulkan.SPLIT ssim 1.000000, 0. The whole
DirectVulkan matrix is 39/40 monolith and 38/39 split, and both reds are the SAME
case, minecraft-1.21.4-fabric-iris-iterationrp-in-world (ssim 0.027915), which is
PRE-EXISTING - it fails identically on ~/w7/p7-magma-b (this branch's parent),
because it needs CONTRACT-P7 §2.4's three iterationRP knobs and those are exported
by the CI job rather than by the ctest entry.
G1 pull .text 0xa52203, nm --defined-only identical to p7-before/pull-syms.txt
(30570 lines, 0 diffs).
Device debt: the monolith arm in the Android disaggregated package now runs the
baked blit, and its ROTATE_90/180/270 geometry has never executed - every surface
on this host is IDENTITY. notes/p7/magma-b2.md §5 lists it with the package's three
other device items.
|
||
|
|
22761a692d |
[MG_Pipe, MG_Backend, MG_Remote, docs] (P7): ID-P7-30 - D3's two review fixes: the bit-8 and bit-13 rows are pinned, and deletion ordering is guaranteed by the Live/Gen test
The vertex-input and buffer-bindings rows of SubsystemDeps.def became live behaviour when every reader started reading the table; two static_asserts now pin them the way the texture and programs rows already were. The "safe by construction" story about destroy ordering was inverted for the case where a view holds its owner's last reference - the owner's destroy reaches the wire first - so the three places that told it now say that the walk's Live/Gen test is the guarantee. |
||
|
|
498eb66a14 | [docs] (P7): ID-P7-29 - D2's rework landed on top of D3 | ||
|
|
a2d7fbd7ca |
[MG_IntegrationTest, docs] (P7): the census note carries its own gate readings and the right counts
Rework round 4 of 4 after the D2 review (fable, ID-P7-8).
COUNTS. The note said 71 cases per arm where its own table sums to 72, and "+37 entries
per split arm" - a number that matched nothing. The +37 came from differencing against
~/w7/pipe/build-split, the integrator's LIVE build directory, which had already moved past
this package's base (7ed5da52 -> 8c5dd6fc). Measured against the pinned
~/w7/p7-before/ctest-names-split.txt baseline this package adds 76 DirectGLES.{Split,Spawn,Tcp}.
entries per arm: 72 census + 3 TextureViewAlias + 1 TextureUploadShape. The raw diff shows
77; the extra one is CtWireScenario's kCapResidentSubData case from b56d70177, one of the
five P7 commits between that baseline and this package's base, and no CtWire file is
touched here. Both numbers and their provenance are now in the note.
GATES. The note pointed at espryt-d2.md for readings espryt-d2.md did not carry. It now
has its own table the way espryt-d1.md does: four lane totals, G1 .text and nm, the G14
name delta, and the five CI gates.
CMAKE. Drops the MGL_SPLIT_D2_FILTER_<Scenario> indirection, which was copied from the d1
and i1 blocks but never defined for any scenario here - a branch no run takes. Every D2
scenario is registered whole because the census found no case in any of them that needs
excluding; the d1/i1 shape is what to reach for the day one does.
And records that the TcpDevice arm gets the texture-view knob on its CLIENT half only. The
loopback Tcp arm's server is started by the TcpServer.Start fixture, which this package
also gives the knob; the TcpDevice arm's server is a supervisor already running on a
device, started outside this build, whose environment nothing here can reach. If it was
started without the knob every texture-view case in that arm green-skips - exactly what
the loopback arm did before the fixture entry got it. The arm is unconfigured in this repo
(empty endpoint, so it is not created at all), so this is a note for whoever configures it.
|
||
|
|
0f3b82725d |
[MG_IntegrationTest, docs] (P7): the upload-shape gate was measuring its own bounding box; inset the scatter so the box/rect decision is reachable
Rework round 2 of 4 after the D2 review (fable, ID-P7-8), which found that this gate's
numbers were right and its SUBJECT was wrong.
The scatter wrote 40 2x2 rects at x=((i*7)%32)*2, y=((i*5)%32)*2 into a 64-px atlas.
Both strides are surjective onto 0..31 over 40 iterations - rect 9 lands at x=62, rect 19
at y=62 - so the rects' union box covered the WHOLE LEVEL, and the server's subRectEligible
(Managers.cpp:8486-8491) requires !dirtyRegion.CoversWholeLevel, a BOUNDING-BOX test
(MipmapStorage.h:30-33). Both scatter textures were rect-INELIGIBLE on every arm before
:8649's unpack-ring policy was ever consulted. The box-versus-rect decision the scenario
exists to pin was never reached for the two textures it scatters into, and the only
texture that could move under any red-once was the contiguous control band - which is
exactly what the old substitute red-once's box=6 rect=3 jobs=12 was.
kScatterMargin insets the scatter by one rect: same 40 writes, same coarse stride, union
box bounded at (2,2)..(62,54). The gold row is UNCHANGED at emit=9 box=9 rect=0 jobs=9 on
all four arms - but it now means what it says.
THE RED-ONCE IS NOW ZERO-PATCH, which is stronger than the throwaway source edit it
replaces: MOBILEGL_ESPRYT_DISABLE_UNPACK_RING=1 alone gives box=3 rect=6 jobs=183 with
bytes/f[tex] falling from 104448 to 9024, while all 26 pixel cases stay green. BOTH
SCATTER TEXTURES moved - box=3 is the contiguous band alone, rect=6 is the plain atlas
AND the atlas written through the glTextureView, and jobs=183 = 3 + 6x30 confirms thirty
rects per scatter emission. The same knob was green before the inset, which is the
evidence that CoversWholeLevel was short-circuiting the decision.
The three prose sites are rewritten accordingly: the scenario's gold-table rationale, the
MEASUREMENTS.md 13.1 reading and the package note's red-once section now explain
attempts 1-3 as mutations of a decision the client already makes correctly, and attempt 4
by CoversWholeLevel rather than by the ring.
Also PipeStatsWindow gains LastFrom{Client,Server}LogSince, and the gate reads its two
role windows since a mark taken before the counted workload. The server writes an EMPTY
teardown window after the counted one, so "the last line in the file" is one shutdown
away from reporting four zeroes as the workload's shape. Moving to a marked read exposed
a second latent defect in the same assertion: ASSERT_TRUE(clientWindow.found) over the
whole file was satisfied by the "counters ON" BANNER in all three split arms, claiming a
window had been read when none had. The assertion is now on the window that actually
carries the tex[] bracket.
|
||
|
|
310d4e90d1 | [docs] (P7): ID-P7-28 - package D3 landed; the texture-view exclusion is retired | ||
|
|
00d0239696 |
[docs] (P7): package D3's note, and the census row its fix falsified
P3b/P4b wave 2-D package D3, slice 3. docs/Disaggregated/notes/p34b/espryt-d3.md, in espryt-d1.md's shape: the mechanism end to end, the decision between resolving the predicate and indexing the reverse edge with the argument that decided it (deletion ordering), the red-once evidence with the exact ctest invocations and the red and green lines per arm, the gate numbers, and six things that remain. The decision, stated once here because the note argues it at length: the gate resolves through Desc.ViewOf to the storage record; the reverse index was rejected because it needs pruning on VIEW death or an owner upload bumps a recycled slot's Serial, because it makes ApplyTextureUpload write a record other than the one the call names, and because VkTextureManager::ResolveWireTextureStorage already resolves the same way on the Magma side - an index would have given the two backends two answers to one question. notes/p34b/split-scenario-census.md is updated where the fix falsified it - the summary row, the texture-family row and the exclusion section - and NOT rewritten anywhere else. D2's text stands and D3's resolution sits above it in a quoted block, because a package note is a record of what that package did rather than a live statement of the tree. Two citation corrections taken while reading the device-window baseline: the CTS block is KHR-GL46 and not KHR-GL43, and texture_view is 1/7 fail there with the failing case view_sampling, NOT coherency - coherency reads Pass, which is what D2 claimed and is now checked per case against report-texture.json rather than against a summary line. espryt-d2.md itself is left alone and the note says why it is left alone. Gates on this head, all measured rather than carried over: G1 .text 0xa52203, nm --defined-only 30570 symbols 0 added / 0 removed, -D 0/0, ninja -C build-linux rc 0. fatal_census rc 0 (79 abort sites, 0 unmarked - unchanged, rule I). link_ratchet --assert-monotone rc 0 (unchanged at 186). spawn_lane_parity rc 0. Lanes: unit 2418/2418, integration-split 264/264, integration-spawn 180/180, integration-tcp 183/183, integration-magma-split 75/75, all 100%. Name sets against a real ctest -N at a7c06f4f: +1 on each of split/spawn/tcp (the un-excluded case), 0 elsewhere, 0 removed anywhere. |
||
|
|
550f81d5f8 |
[docs] (P7): W4 §3b - the OpenRA hole is one draw (call 30376) reading vertex bytes other than call 30366's
Lens D placed 100 % of the wrong pixels inside the screen-space union of a single glDrawArrays whose vertex data was the frame's first glBufferSubData into the streaming buffer; the palette respecify is not the mechanism. The candidates are WriteWireBuffer's serial-gated branch and the mid-frame drain that bumps the frame serial without a fence. |
||
|
|
ebd73da06c |
[docs] (P7): the W4 evidence files are plain files, not executables
Copied off a Windows mount, they came in as 100755; nothing in them runs. |
||
|
|
770f1a0b3a |
[docs] (P7): ID-P7-26/27 - D2 lands with a rework round, and bsl-esc-menu is a memory defect, not an exclusion
D2's fable review says land with fixes: the TUS gate's scatter workload has a union box that covers the whole level (rect=0 is geometry, not the unpack-ring policy), the memo purity script fails open on a renamed family, and the census numbers were not reproducible - all sent back to D2 as one rework round; the excluded TextureViewAlias case gets its §12 row (owner D3). bsl-esc-menu-854 on the spawn arm: monolith passes at 807 MiB peak RSS, the server process dies at ~1.2 GiB in scudo's secondary allocator with 8 GB of system memory free, inproc dies at 1.6 GiB. That is the wire arm holding about twice the bytes (or the mappings) of the monolith, a disaggregation defect under ID-P7-12's rule, so it stays in the gate-3 denominator; M1 attributes it. |
||
|
|
dd6c541af7 |
[docs] (P7): ID-P7-25 - W4 on p7w4: 34/36 match monolith, and OpenRA is a silent draw drop, not a cold cache
Device window 1 W4 (APK p7w4 = 8c5dd6fc): iterationt x2 pass, the 39-case inproc matrix has 34/36 of the denominator within 0.0005 of the monolith reading, bsl-esc-menu still OOMs under inproc (spawn arm pending). ID-P7-19's cold-driver-cache reading of OpenRA is withdrawn: 26 runs show a ~50 % per-run coin flip that ignores cache temperature, IPC_AUDIT and FIF=1, reproduces on the two-process spawn arm, and only ever yields three pictures (golden / 9836 px / 14658 px). The monolith snapshot at the previous swap is the golden and the palette texture is byte-correct at the snapshot, so the wrong pictures are frames with a bounded run of draws silently skipped (nine sprite blobs absent over intact terrain). Gate 3's acceptance for OpenRA is now six run-ahead and six lockstep runs all equal to the golden; the fix is package B3. |
||
|
|
519445f946 |
[MG_Pipe, MG_Impl, MG_Test, docs] (P7): D-K2's dependency rule in one table, with the case that fails when the two readers disagree
P3b/P4b R-5 (and R-3's cheap half), landing under the P7 program (PLAN-PH-P34B-P7.md
1.2, wave 2-D package D2).
The rule had SIX statements and nothing compared them, and two of the four prose ones
were wrong in the same way: MG_Pipe/MGPipe.h and MG_Backend/DirectGLES/Managers.h both
said "THREE OF THEM HAVE A DEPENDENCY" (there are six rows) and both said "the mirror
pairs (10 without 11, ...) are all fine" - when 10-without-11 is precisely D-K2's FOURTH
row (ID-14/ID-15), refused by the server and withheld by the client, with PipeFill.cpp
saying so out loud: "The brief's original 'bit 10 without 11 is fine' is WITHDRAWN for
P4a as built." Neither wrong comment could fail anything; Managers.h's copy sits in the
header of the file that implements the refusal.
The rows, once, in MG_Pipe/SubsystemDeps.def (X-macro, FatalFamilies.def's convention),
expanded by MGPipe.h into kMGPipeSubsystemDependencies[] / MGPipeSubsystemRequires() /
MGPipeSubsystemDependenciesAreSet() with four static_asserts. Six rows: 8->7, 9->10,
10->{7,11}, 11->10, 12->none (a ROW, not an absence), 13->7. Bits 8 and 13 are the two
the old comment did not know about; bit 13's row lived fifteen lines further down in the
same header, which is how "THREE OF THEM" survived two phases.
THE TWO READERS DO NOT YET READ IT, deliberately: MG_Impl/Pipe/PipeFill.cpp is the
contract package's file for the whole phase (TextureEmit.h states that rule) and
MG_Backend/DirectGLES/Managers.cpp belongs to wave 2-D package D1 while it is in flight.
Switching them is two one-line changes for the integrator. The anti-drift property does
not wait: MG_Test/Backend/DirectGLES/SubsystemDepsTest.cpp is the first thing in the tree
that calls BOTH readers at the same mask - sweeping the phase constants, the two
documented refusal lanes and one mask per row - and compares each against the .def. It
lives in that binary because that binary links both sides.
Both comparisons are one-directional, each for a measured reason. The server resolvers
also fold in the family's own bit and D-C3's wire-caps refusal, so a resolver may say NO
for its own reasons but never YES where the table refuses. MGPipeP4aFamilyEmits is a
QUADRUPLE and the first version of this case asserted equality and reddened at 0x1fff on
the sampler and program families because a bare unit binary registers no consumer - the
assertion being wrong, not the product, and recorded in the note because the temptation
to assert equality here is strong.
Red-once (R-16), twice: dropping the fourth row from the .def fails at COMPILE time
(MGPipe.h's static_asserts); making the server's ResolveSamplerSubsystemArm stop checking
bit 10 (throwaway, reverted) reds the runtime case and names the mask - 0x9ff, which is
exactly the lane CMakeLists.txt already pins for G12.
R-3's cheap half: CONTRACT-P5.md claimed the image-access, sampler-view-target and
draw-buffer encodings as contract by pointing at package-header LINE RANGES, and all
three had drifted (ImageEmit.h:146-159 no longer holds the encoding at all). All three
rows now state the encoding by enumerator and function name, and the two confusable
siblings are named. The remaining half - MGPSamplerView::Target still has no Count bound,
no ...IsValid and no Fatal for an out-of-range value - is stated in the row rather than
closed, being an MG_Pipe change outside this package's footprint.
Managers.h:690-695's copy of the wrong prose is NOT corrected here: it is package D1's
file, and its two defects are named in notes/p34b/espryt-d2.md so the integrator can take
them in one line.
G1 after this change: pull .text 10822147 = 0xa52203, nm --defined-only identical to
~/w7/p7-before/pull-syms.txt (30570 symbols) - the new inline constexpr table does not
reach the pull build.
|
||
|
|
9a69e08365 |
[MG_IntegrationTest, docs] (P7): twelve texture and program scenarios join the split arms, with the census that put them there
P3b/P4b deepening, landing under the P7 program (PLAN-PH-P34B-P7.md 1.2, "门:纹理 /
program 场景 split 覆盖", wave 2-D package D2). Each candidate was run once under
MOBILEGL_TRANSPORT=inproc on build-split and its first blocker recorded; the row per
scenario is in notes/p34b/split-scenario-census.md.
Registered (all green on inproc, spawn and tcp): TextureView,
TextureParamsWithoutASamplerView, SampledSetStaleness, ImageSizeAfterRespec,
ImageTargetKind, IntegerBorderColor, SwizzleAccessRoutine, PostLinkAttach,
RelinkStageSet, UniformInitializer, Glsl420Declaration, IoBlockNameCollision. 71 cases
per arm, 213 new gating entries; the recorded split-arm registration count the tier-2
Magma replay walks goes 33 -> 46.
TextureParamsWithoutASamplerView settles the debt table's 待核 row: all four G9 cases
pass under all three transports, so the P6 inspection-forwarder debt is closed for this
half - and it is now re-taken on every CI run rather than by inspection.
ProgramPipelineScenario beyond *StorageBlock* is NOT registered, and that is the rule
working rather than a blocker to retire: the other nine cases are pure name/state cases
that draw nothing, and an armed DirectGLES.Split. case whose workload produced no record
is red by design (ScenarioFixture.h:84-97 - the client encoder's record ordinal must
move). Registering them would assert "records crossed" about cases with no records to
send.
DirectVulkan arms default to none, deliberately, and the note says why: the
informational tier-2 block already replays every recorded registration on DirectVulkan
under integration-magma-all-{split,spawn,tcp}, so these scenarios do get a Magma x
three-transport reading - just not a gating one. Gating them would assert P7 wave 2's
exit gate 1 from inside a P3b/P4b package, on a backend whose own @P7 refusals are still
being retired in waves A/B/C.
D1 owns PixelStoreSweep, DepthStencilReadbackMatrix, PackedWordReadback and
LayeredTextureReadback; they are not in this census.
|
||
|
|
5617c5c3b3 |
[MG_IntegrationTest, docs] (P7): the texture upload shape becomes a gate, with the view remap inside the workload
P3b/P4b R-11 / D-D4, landing under the P7 program (PLAN-PH-P34B-P7.md 1.2, wave 2-D
package D2). P4a recorded the emission shape rather than gating it because the
per-storage-owner cursor with view/owner remapping was the next phase's and gating an
unfinished shape pins a non-answer. That work landed, so the RecordProperty block is
now EXPECT_EQ against a gold row MEASURED on this cursor, and the workload carries a
third texture written through a glTextureView so the remap is inside the counted
window.
The gold row, identical on monolith / inproc / spawn / tcp:
emit=9 box=9 rect=0 jobs=9, client emitter 9
keyed on the workload constants by static_assert. P4a's emit=6 box=6 rect=0 jobs=6
was a two-texture workload with no client emitter and is not comparable.
The client half is read from MGPipeTextureEmitter::SubDataCount() rather than from
ctu=, and that is a correction: under spawn and tcp the two roles are two processes
whose PipeStats windows close on different Present cadences (measured ctu=13 against
emit=9, and usually not yet written when the case reads), so ctu == emit is not a
well-posed comparison there. The log field is kept as a record and asserted only
where the transport says the two are window-aligned - asking the NUMBER was the first
mistake, because the server publishes a readable ctu=0 under spawn and the comparison
silently became 0 == 9. PipeStatsWindow gains LastFromClientLog/LastFromServerLog.
Split/spawn/tcp arms added as explicit per-arm blocks rather than through
mgl_itest_register_split_arms: the macro hands every arm the shared environment, and
this case needs a lane marker, PipeStats period 1, a push mask and above all a
MOBILEGL_LOG_FILE_PATH private to the arm - the library opens the log "w", so two
entries sharing a path truncate each other and the failure looks exactly like "the
counter was never emitted".
Red-once (R-16): the prescribed flip of summedArea*4 >= unionArea*3 leaves the gate
GREEN, and that is a finding. Two gates downstream dominate it - the server needs
dirtyRectCount >= 2, and UnpackRingAvailable() zeroes the rect list by design ("one
box, one job", the existing Mali-cliff mitigation). So rect=0 is the policy's
intended output, not an accident of the scatter pattern. The red-once that bites
splits the union box in two AND disables the unpack ring: box=6 rect=3 jobs=12
against the gold, while 26 pixel cases stay green. Four attempts and their reasons
are in the note.
The Mali frame-time delta D-D4 asks for beside the gold is NOT schedulable - one
Redmi, an Adreno, no Mali part - and is recorded as a declared deviation in the
scenario header and MEASUREMENTS.md 13.3 rather than modelled.
Also corrects the three places that said the client emitter had not landed: the
scenario header, the CMake probe's comment, and gates-v1.md's re-run table.
TextureEmit.h's EmitOneLevel has been incrementing it.
|
||
|
|
da30c1301e |
[MG_IntegrationTest, docs] (P7): the owner-keyed emission cursor gets the scenario that can see it
P3b/P4b deepening, landing under the P7 program (PLAN-PH-P34B-P7.md 1.2, wave 2-D
package D2). The code half of "按存储属主键控的发射游标与 view 索引重映射" landed in
P4a/P5e; this is the verification half it never had.
TextureViewAliasScenario, four cases: an upload through a layer-restricted view
sampled through the owner and the reverse, each once and again across draw
boundaries so the drain runs three times. Each case asserts on TWO channels at
once - sampled texels AND MGPipeTextureEmitter's SubDataCount / RefusedSubDataCount
/ DrainListSize, read through the new Harness/TextureEmitPeek. The counters are the
load-bearing half: a level that never made it onto the drain list is still uploaded
by the next whole-level respecify, by a glGenerateMipmap or by the monolith sync
path, so the picture comes out right one frame late and a pixel-only case goes green
over the regression on most trees.
Registered in the three GLES split arms, and MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1
added to MGL_ITEST_GLES_{SPLIT,SPAWN,TCP,TCPDEVICE}_ENVIRONMENT - views are Espryt's
opt-in and without it every case green-skips. The Tcp arm needed the knob on the
TcpServer.Start fixture as well: that lane's server is pre-started rather than
spawned from the client, so it withheld GL_ARB_texture_view and all four cases
SKIPPED while the identical Split and Spawn arms ran.
Red-once (R-16): TextureObjectView::MarkStorageDirtyRegion made to call
PipeNoteLevelDirty on ITSELF instead of forwarding to the storage owner - the
two-cursor shape. TheViewUploadDrainRunsAgainAfterADrawBoundary goes red on both
channels simultaneously (stale owner texels AND SubDataDelta() == 0). The plan
predicted case (a); it is (c) that reds, because the first drain of a texture's life
carries the whole level anyway - recorded in the note.
TheOwnerUploadDrainRunsAgainAfterADrawBoundary is EXCLUDED BY NAME from the split
arms: it is red on all three transports and green under monolith, because an
owner-side resource_subdata moves only the owner's record.Serial and nothing
propagates that to the records viewing it. Cause and the two candidate fix sites are
named in the CMake comment and in notes/p34b/espryt-d2.md. The name stays in every
lane (G14).
|
||
|
|
ae3f43ab56 | [docs] (P7): ID-P7-24 - package D1 landed, and the readback half was already closed | ||
|
|
ba81453f19 |
[MG_Backend, MG_IntegrationTest, docs] (P7): the readback family runs on the split arms, and it was already green
P3b/P4b espryt D1 slice 4, landing under the P7 program (notes/p7/PLAN-PH-P34B-P7.md
§1.2 row "回读 / pack state", §3 wave 2-D).
(a) THE CENSUS, AND IT CAME BACK CLEAN. Twenty-two cases per arm now run on the three split
arms: the two F1 pack-state cases, PixelStoreSweepScenario, FramebufferChurnScenario,
PackedWordReadbackScenario, LayeredTextureReadbackScenario and
DepthStencilReadbackMatrixScenario. The row expected reds and said so ("Expect reds - that
is the point"); the first run was 65/65 before any of this commit's code changes. That is
the finding: the 回读 half was assumed open because nothing had ever run it under a
transport, and the assumption was what was wrong - ScatterTightReadbackIntoPackState and the
server's neutral-pack read were already carrying it. No case needed a fix and no case needed
a named deferral.
DepthStencilReadbackMatrixScenario gets a per-arm block of its own, because the registration
macro cannot carry MOBILEGL_ESPRYT_FORCE_DS_READBACK_EMULATION=1 and without that pin the
scenario is unfalsifiable here: ES has no core depth/stencil readback, Mesa accepts the
reads anyway, and on llvmpipe every case goes green through a path the device does not have.
Its SeparateDepthAndStencilAttachmentsAreBothReadable SKIPS on all three arms and on monolith
too, naming the driver reason itself - a driver fact, not a transport one.
(b) THE TWO std::abort EMULATIONS, checked rather than assumed. copy-image-shadow-mirror
ALREADY cannot abort: MirrorCopyImageIntoDestinationShadow returns under a transport before
reaching MGPipeUnmigratedEmulation, and has since P5b package i1. get-tex-image-shadow is
retired here, and the reason is that its own premise did not hold: P4a wrote "a split server
holds no shadow to convert", but the level storage this converts IS the server's own, fed by
the staged texture store. When MapMipmapData answers, the conversion reads bytes that exist
on this side. So it converts when the shadow is there and DECLINES BY NAME when it is not
(rule I: MGLOG_E_ONCE + GL_INVALID_OPERATION, destination untouched). std::abort inside a
server answering a client's readback takes the session down for a level it happens not to
hold, and bypasses Session::Fail, which is the funnel the census gate watches. The funnel
keeps its teeth for the other four sites - generate-mipmap-storage,
generate-mipmap-cpu-fallback, generate-mipmap-cpu-filter, texture-remint-pull - which really
do reach into a client address space; fatal_census is unchanged at 79 abort sites because
the abort lives in MGPipeUnmigratedEmulation and that function stays. Stated plainly in the
notes: no case in the armed census reaches this site under a transport (the server answers
GetTextureImage with ReadTextureImageWire, never through the backend's GetTexImage slot), so
the change is defence in depth and has no integration red-once of its own.
(c) THE CASE NOTHING COVERED, and this slice's red-once:
PixelStoreSweepScenario.ReadPixelsIntoAPackBufferHonoursPackStateAndLeavesTheGapsAlone -
glReadPixels into a bound GL_PIXEL_PACK_BUFFER at a non-zero buffer offset with
PACK_ALIGNMENT/ROW_LENGTH/SKIP_ROWS/SKIP_PIXELS all non-default, read back with
glMapBufferRange. It is the only thing that drives EmitTables.cpp's `if (pbo)` branch, the
client servicing the pack PBO itself after the reply lands; the existing sweep case reads
into a client pointer, which is the other half and has unit cover. Every number makes a term
matter: ROW_LENGTH 5 at 4 bpp is 20, which ALIGNMENT 8 rounds up to a 24-byte stride;
SKIP_ROWS 1 and SKIP_PIXELS 2 put the first pixel at image byte 32; the read is issued at
buffer offset 16, so it lands at 48. The whole buffer starts 0xCD and the case asserts both
halves - the rows land where the pack state says, and every other byte is still poison.
Red-once (R-16): dropping just the buffer-offset term from EmitTables.cpp's `start` reds the
three split entries with "readback[at] Which is: '\xCD' (205)" - the destination still poison
because the rows landed 16 bytes early - while the ambient monolith entries pass, since the
driver services its own PBO there. EmitTables.cpp is not part of this commit.
(d) SplitReadbackPackBuffer's comment claimed "The client refuses PACK_BUFFER before
emission". It does not: it KEEPS the PBO and services it after the reply, and a refusal would
have been observable as a GL error. The conclusion (the server must see no pack buffer) was
right and the reason was backwards; rewritten to ID-49's actual split of the work.
Gates: integration-split 225/225 (base 186), integration-spawn 141/141 (base 102),
integration-tcp 144/144, ctest -L unit 2408/2408, integration-magma-split 73/73,
spawn_lane_parity / fatal_census (79 sites, 0 unmarked) / link_ratchet (186, unchanged)
green, G1 .text 0xa52203 with both nm lists byte-identical to ~/w7/p7-before.
|
||
|
|
834a8917ff |
[MG_Backend, docs] (P7): a transport with no record arm stops by name instead of dying mid-frame
P3b/P4b espryt D1 slice 5, landing under the P7 program (notes/p7/PLAN-PH-P34B-P7.md
§1.2 row "g_fboTextureSyncList", §3 wave 2-D).
ResolveFramebufferSubsystemArm and its three siblings classify a family's arm as Handles,
Legacy or NoArm and stop only on NoArm. Under an active transport Legacy is not a slower
answer, it is a postponed crash: all four pre-handle arms reach frontend state through
accessors that are BARRIER_PULLED rows (FieldOwnership.def), and under a transport that
state belongs to a client that is no longer parked behind this apply, so the accessor's
honest answer is Fatal{UnmigratedPipeInput, "<field>@<verb>"} on the first verb that reaches
it. That is the audit's own claim about g_fboTextureSyncList - "separation-safe, because the
record arm returns first" - which holds only while the record arm is ARMED, and nothing
checked that.
Each of the four now stops, under #if MOBILEGL_BUILD_DISAGGREGATED, when the transport is
not monolith and the verdict is not Handles, through one shared
StopOnTransportWithoutRecordArm(bit, detail) that formats both causes and hands them to
BufferImpl::StopOnArmlessPipeSubsystem - this file's existing single voice for "the
configuration left no arm to run". A transport with the record arm off IS that condition
with a different cause: the legacy arm is present but unreachable. Reusing the voice keeps
the census's family word and puts the cause in the message rather than in a new word the
census would have to learn.
Said precisely, this is a RESOLVE-TIME stop and not a startup one. The four resolvers are
function-local statics resolved lazily at first use, deliberately - Managers.cpp's own
comment says a stop inside eglMakeCurrent reads to the harness as "no usable GPU" and skips
- so the stop still lands in a scenario body where ctest reports it. What it no longer does
is wait for a verb to dereference the client's state and then blame the accessor.
Red-once (R-16), and it is the safety argument the audit asserted but never ran:
MOBILEGL_PIPE_PUSH=0x1dff (the Handles lane's 0x1fff minus 0x200,
kMGPipeSubsystemFramebuffer) with MOBILEGL_TRANSPORT=inproc, running
ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels. Both
runs abort; what changes is what the abort says. Without the stop:
Fatal{UnmigratedPipeInput, "GetFramebufferBindingSlot@Clear"} - mid-frame, naming an
accessor and a verb, with nothing pointing at the mask. With it:
Fatal{PipeLegacyMemosDisabled, "MOBILEGL_TRANSPORT is not monolith and
kMGPipeSubsystemFramebuffer (bit 9) is clear (or refused), so the record arm is off and the
legacy arm is unreachable: ..."}.
Gates: integration-split 202/202, integration-spawn 118/118, integration-tcp 121/121,
ctest -L unit 2408/2408, integration-magma-split 73/73, spawn_lane_parity / fatal_census (79
sites, 0 unmarked) / link_ratchet (186, unchanged) green, G1 .text 0xa52203 with both nm
lists byte-identical - the four blocks are inside MOBILEGL_BUILD_DISAGGREGATED and the pull
build compiles none of them.
|
||
|
|
767e6c8084 |
[MG_Pipe, MG_Test, docs] (P7): the reverse channel is nine callbacks, and the prose says what the tree does
P3b/P4b espryt D1 slice 3, landing under the P7 program (notes/p7/PLAN-PH-P34B-P7.md
§1.2 row "XFB scatter" item 3; CONTRACT-P7.md §139 already records the deletion).
Plan B section 7.1 named ten reverse-channel callbacks. The tenth, OnXfbScatterReady,
belonged to a design the tree does not have: plan B put the XFB scatter on the CLIENT - the
server would hand back the packed scratch and the client would run the patch loop over its
own shadow - so something had to tell the client the layout. P5c/P5f went the other way,
and the declaration was left behind with zero producers, zero consumers and no EventKind of
its own, while the static_assert beside kMGPipeCallbackCount made it look load-bearing.
Deleted; kMGPipeCallbackCount 10 -> 9.
Three pieces of prose that still described the old design go with it. MGPipeCallbacks.h's
own header said "ten callbacks", and now says nine and why the tenth went.
MGPipeTypes.h's MGPXfbAccounting comment said the scatter "is a read-modify-write of the
client's shadow and lives there"; it lives on the server's staged shadow, and nothing about
the scatter crosses in that record. ARCHITECTURE.md §8.5, titled "XFB scatter 搬到 client",
is rewritten to what the tree does: the pre-capture bytes are the server's own staged
shadow (rule C, since MappedData() on the apply thread is Fatal{RoleViolation,
"buffer-legacy-arm"}), the reconciled whole range comes back as one OnBufferWriteback
sliced against SEG_EVENT's capacity (slice 2), and an orphaned target is a legal one (slice
1). Its row in §8.1's callback table goes too - leaving it would be a new staleness rather
than an inherited one.
PipeCatalogue.ReverseChannelHasTenCallbacks now asserts 9. The CASE NAME IS DELIBERATELY
NOT RENAMED: G2/G14 say the ctest name set only ever grows, so renaming would delete a name
the gate watches. The count it asserts is what has to be right and the comment carries the
number.
Red-once (R-16): with the member and the count restored the case fails with
"kMGPipeCallbackCount Which is: 10 / 9u Which is: 9", which is what makes the struct's
shrink a measured fact - the static_assert beside the constant only proves the two agree
with each other.
G1 matters for this slice in particular, because MGPipeCallbacks.h is included from
MG_Backend and the struct is NOT behind MOBILEGL_BUILD_DISAGGREGATED, so
sizeof(gMGPipeCallbacks) really does shrink by 8 bytes in the pull build. The shrink is in
.bss and nothing indexes the table by count: .text is still 0xa52203 and both nm lists are
byte-identical to ~/w7/p7-before.
Gates: integration-split 202/202, integration-spawn 118/118, integration-tcp 121/121,
ctest -L unit 2408/2408, spawn_lane_parity / fatal_census / link_ratchet (186, unchanged)
green.
|
||
|
|
96fa569250 |
[MG_Backend, MG_Pipe, MG_Remote, MG_IntegrationTest, docs] (P7): the XFB writeback event is sliced, so a large capture stops killing the server
P3b/P4b espryt D1 slice 2, landing under the P7 program (notes/p7/PLAN-PH-P34B-P7.md
§1.2 row "XFB scatter", §3 wave 2-D).
Both XFB writeback producers posted ONE unbounded OnBufferWriteback covering the whole
bound range - ScatterCapturedRecords from its staged vector, ReadbackCapturedRanges from a
live glMapBufferRange mapping. ServerOnBufferWriteback turns each into one SEG_EVENT record
of sizeof(EventBufferWritebackHead) + bytes, and RingProducer::Reserve refuses any record
above Capacity()/2 outright - not "when full" but always, because above half a capacity a
record is placeable at some head offsets and not at others and waiting for room would be a
hang. EventRingBytes defaults to 256 KiB, so any capture target wider than ~128 KiB was
Fatal{EventRingOverflow} and the SERVER PROCESS DIED, on a buffer size, with nothing else
wrong. The producers' own Fatal text already said what to do: raise MOBILEGL_IPC_EVENT_KB
or slice the event at its producer, "as the writeback path already does" - which was true
of the client's request path and of nothing on this side.
MG_Pipe now carries the one number, published the way gMGPipeSegmentResolver is published:
gMGPipeEventRingCapacityBytes, installed by ServerSession::Accept beside the four reverse
producers and released at Close only if it is still ours, and
MGPipeBufferWritebackSliceBytes() which turns it into a per-record width - a quarter of the
ring with a 4 KiB floor, the same number and the same reasoning as the client-linkage
BufferWritebackSliceBytes one ring over. The backend can reach neither MG_Remote::Client
(wrong linkage) nor ServerSession (wrong layer), so the hook is the seam; 0 means "do not
slice", which is monolith, where the callback is a direct call with no ring under it.
Both producers loop against ONE helper, XfbWritebackSliceStep, because a second spelling of
the width is how one of them ends up still killing the server. The readback site slices
inside its mapping and still unmaps once; the scatter site slices the staged vector and
keeps the single glBufferSubData whole, since the ES store has no ring and cutting it would
change how many backend calls one application call makes. In-order delivery makes the last
slice's landing imply every earlier one, so the client needs nothing new.
Red-once (R-16): XfbAfterClipDistanceScenario.{AScatteredCapture,APlainCapture}
IntoAMegabyteRangeReachesTheApplication - a 1 MiB bound range, about eight times what one
record can ever be, with the same six capture records; glBindBufferBase over the whole
store is what makes the mirrored range a megabyte. Two cases for the two producers. With
the slicing reverted all six split entries go red on Fatal{EventRingOverflow}
("kEventBufferWriteback needs 1048600 bytes and SEG_EVENT caps ONE record at 131072") -
aborting the test process under inproc, losing the peer under spawn and tcp - while both
monolith controls stay green.
XfbAfterClipDistanceScenario and XfbRepeatedCaptureScenario now join the three split arms
in full; slice 1 could register only OrphanedCaptureTarget* because the rest could not
survive the unsliced event. XfbRepeatedCaptureScenario's gl_NextBuffer compacted-target
shape is the first thing outside the census to exercise the archive-served stride and
varyings (ProgramArchive->Link's xfb fields) on the server, and it passed on all three arms
with no further change.
Gates: integration-split 202/202, integration-spawn 118/118, integration-tcp 121/121,
ctest -L unit 2408/2408, the monolith and split XFB families 120/120, spawn_lane_parity /
fatal_census (79 sites, 0 unmarked) / link_ratchet (186, unchanged) green, G1 .text
0xa52203 with both nm lists byte-identical to ~/w7/p7-before.
|
||
|
|
faf0a1f902 |
[MG_Backend, MG_IntegrationTest, docs] (P7): an orphaned XFB capture target keeps its varyings
P3b/P4b espryt D1 slice 1, landing under the P7 program (notes/p7/PLAN-PH-P34B-P7.md
§1.2 row "XFB scatter", §3 wave 2-D).
ScatterCapturedRecords is a read-modify-write of the capture destination's pre-capture
bytes - a gl_SkipComponents hole has to come back holding whatever the application left
there - and under a transport those bytes are the server's staged shadow rather than a
frontend MappedData(). The split arm read a null shadow as "this target has no
pre-capture bytes, discard it". A null shadow is not an error: MGPResourceDesc's
HasDefinedContent == 0 is the store the application ORPHANED with glBufferData(size,
NULL), and Managers.cpp's own M-3 comment says every byte it has not staged since is
undefined by its own declaration. So the continue threw a whole capture away for the
ordinary streaming idiom (orphan, upload part of it, capture into it), leaving the
application's buffer holding its pre-draw bytes under GL_NO_ERROR. The partly uploaded
shape did not even reach the continue: it has a shadow, so it asked RequireStagedCoverage
for the whole bound range and died on Fatal{StageSnapshotTooNarrow,
"xfb_scatter_pre_capture"}.
Monolith has always been right here and is right by construction: `Vector<Uint8>
staged(rangeBytes)` is value-initialised, so scattering over an unreadable source scatters
over zeroes and uploads them, which is byte for byte what a fresh MappedData() hands back.
The split arm now skips only the Memcpy, never the target. Discard-with-log is kept for
splitResource == nullptr alone, which really is impossible and has no backendId to upload
into either. RequireStagedCoverage is guarded by ResourceContentIsDeclared, exactly as
Managers.cpp's pool-reuse ladder guards its own whole-store MGL_SERVER_STAGED_REQUIRE: a
declared store still Fatals on a coverage gap, an orphaned one does not.
ResourceContentIsDeclared moves out of Managers.cpp's anonymous namespace to namespace
scope beside RequireStagedCoverage and is declared in Managers.h. The two callers must not
be able to answer "did the application declare these bytes" differently. Both live inside
#if MOBILEGL_BUILD_DISAGGREGATED, so the pull build compiles neither.
Red-once (R-16): XfbAfterClipDistanceScenario.OrphanedCaptureTarget{WithAPartialUpload,
WithNoUploadAtAll}KeepsItsVaryings, registered on the three split arms under the Xfb.
prefix. With the fix reverted the six split entries go red - the partial-upload three on
Fatal{StageSnapshotTooNarrow, "xfb_scatter_pre_capture"} ([0, 696) of a buffer whose
staged coverage does not include it), the whole-orphan three on a captured varying that is
still zero - while the two ambient monolith entries pass, which is what makes it a
transport fact. Details and the lane numbers in docs/Disaggregated/notes/p34b/espryt-d1.md.
The narrow filter is deliberate: the rest of XfbAfterClipDistanceScenario is a DirectVulkan
allocation-pattern regression that has never run under a transport, and it joins the arms
in slice 2 once the writeback event it posts is sliced.
Gates: integration-split 188/188 (186 + 2), integration-spawn 104/104, integration-tcp
107/107, ctest -L unit 2408/2408, the monolith DirectGLES XFB + readback families 66/66,
spawn_lane_parity / fatal_census / link_ratchet (186, unchanged) all green, G1 .text
0xa52203 with both nm lists byte-identical to ~/w7/p7-before.
|
||
|
|
09ba88e70d | [MG_Remote, docs] (P7): CONTRACT-P7 §3.2 records package B (readback race, copy-image, default blit, depth mip bake, mip diagnostic), ID-P7-23 | ||
|
|
76b7469389 |
[MG_Backend, MG_IntegrationTest, docs] (P7): the wire readback waits for the frame it is reading, instead of inferring who wrote it
Exit gate 3, OpenRA on the Redmi. The mechanism, in two lines of this tree: WireFramebuffer.inc, ReadWirePixels: `if (HasPendingRecordedWork() && !FlushPendingCommands())` - FlushPendingCommands (VulkanRenderer.cpp:13534-13598) puts the frame's draws on the queue under a POOLED FENCE OF ITS OWN and returns without waiting; there is no vkWaitForFences on its success path. The vkCmdCopyImageToBuffer that follows is recorded into a fresh command buffer and becomes a LATER submission. VulkanRenderer.cpp:10707-10748, SubmitReadbackCommandsAndWait: it waited ONE fence, its own, and then called OnSubmitsCompletedUpTo(frame.lastSubmitIndex) - which RECORDED the earlier submission as complete and retired its fence, so afterwards nothing could observe that it had not been waited for. The comment beneath it said "the wait proved every submission complete". That is true of the monolith arm, which never flushes before a readback and therefore has its draws and its copy in one submission under one fence. It was not true of the wire arm. The only thing tying the two submissions together was the pipeline barrier that the readback's TransitionWireImage records - and that barrier derives its SOURCE scope from the image's tracked layout, through GetImageTransitionSourceState (VulkanRenderer.cpp:1784-1825). Two of its arms, VK_IMAGE_LAYOUT_UNDEFINED and VK_IMAGE_LAYOUT_PRESENT_SRC_KHR (:1787-1791), return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT with no access bits, which is Vulkan's spelling of an EMPTY first scope: wait for nothing. A just-presented default framebuffer is in the second of those layouts. The slower pipeline creation is, the longer the draws' submission runs, and the more of the frame's LAST-drawn objects the copy can miss - which is what the device's diff image shows. Measured on this host (OpenRA, DirectVulkan, inproc), at the moment the copy is recorded: submit=72 completed=71. The submission carrying the whole frame's draws is in flight and unwaited. That is structural, not a coin flip, and it reproduces every run. The fix is two halves, both wire-only: - RecordWireReadbackWriteBarrier (WireFramebuffer.inc, new): the readback records its own ALL_COMMANDS -> TRANSFER, MEMORY_WRITE -> TRANSFER_READ|MEMORY_READ barrier before the copy rather than inferring from a layout who it should wait for. A barrier's first scope is every command earlier in SUBMISSION order on the queue, so it reaches across the submission boundary. - SubmitReadbackCommandsAndWait now waits every in-flight fence at or below its own submission index. Under #if MOBILEGL_BUILD_DISAGGREGATED: the pull build has no wire arm, cannot flush before a readback, and is the image G1 pins - its single-fence wait is unchanged. The cost is nothing a readback does not already pay. THE COORDINATOR'S REPRODUCTION PATH DOES NOT WORK ON THIS HOST, and that is recorded rather than glossed. MOBILEGL_TEST_PIPELINE_CREATE_DELAY_MS is added as asked (split-only, inert unless set, on the pipeline-creation MISS path in GetOrCreatePipelineWithInput) and it does bite - the OpenRA DirectVulkan SPLIT retrace goes from 1.88 s to 4.53 s at 25 ms - but ssim stays 1.000000. The reason is in the probe above: this host's default framebuffer is tracked as VK_IMAGE_LAYOUT_GENERAL at the readback, which takes GetImageTransitionSourceState's `default:` arm (ALL_COMMANDS / MEMORY_WRITE) and orders the copy by accident. The device's is not GENERAL. Red once (executed, reverted) is therefore a COUNT, not a picture, and it is deterministic: instrumenting how many fences the readback actually waits on gives fences=2 upTo=73 completed=71 with the fix (submission 72, the draws, plus its own 73) and fences=1 without it, with completed=71 either way - i.e. 72 provably outstanding and provably unwaited before. Lane: three readback-after-work cases (VertexIdSamplerAndScalarUniformPixels, ComputeImageStoreFramebufferPixels, GenerateMipmapPixels) registered on the three Magma arms under MOBILEGL_TEST_PIPELINE_CREATE_DELAY_MS=25, prefix .PipelineDelay. They are a REGRESSION GATE, not the reproduction: they pin that a readback still returns the draw's pixels when every pipeline creation on the path costs 25 ms. Gates: integration-magma-split 84 (+3), -spawn 63 (+3), -tcp 65 (+3), 0 red; ctest -L unit 2410; informational integration-magma-full-split 516, 0 red, 59 skip; fatal_census green; link_ratchet --assert-monotone unchanged at 186, p7-magma 101; G1 pull .text 0xa52203, defined-symbol name list identical (30570); OpenRA DirectVulkan SPLIT retrace ssim 1.000000 with and without the delay, two runs each. Owed to the device: the three cold-cache repeats. If they still diverge, the next two suspects are named in notes/p7/magma-b.md §5.6 - the eleven silent `return false` drop sites in SetupWireDraw (one of which is `pipeline == VK_NULL_HANDLE`, on the same path as the device log's three "handing vkCreateShaderModule an INVALID module" errors), and SetImageContentDefined being set BEFORE the draw is recorded on the wire arm while the monolith sets it from EndRenderPass, which silences the "no write is recorded" warning exactly when it should fire. Neither is in this commit's scope. |
||
|
|
583e12de76 |
[MG_Backend, MG_IntegrationTest, docs] (P7): the default-framebuffer blit stops refusing shapes the arm beneath it can do, and the 3D bug that hid behind the refusal is fixed
CONTRACT-P7 §3.2's `default-color-blit-shape@P7`, retired in three pieces, because one
Fatal was covering three unrelated situations.
The shader pass it guarded is there for ROTATION and nothing else: it can only SAMPLE
its source - sampler2D, a float result, one of two 2D view types - so an RGBA8UI
attachment, a 3D slice or an array layer made it return false. The refusal then fired
EVEN ON A SURFACE WITH NO ROTATION, where the ordinary vkCmdBlitImage arm twenty lines
below would have done the blit and does handle those shapes.
- identity: fall through to that arm;
- a non-identity rotation with a source the pass cannot sample: move the region into an
owned 2D single-sample image of a sampled-float format in the SAME size-compatibility
class and shader-blit that;
- outside the four rotations (a mirror, or a mirror-rotation): decline by name, saying
which transform. Neither arm can express a reflection and this package has no device
that reports one to check a result against.
Reinterpretation is the point for the integer case and GL agrees: glBlitFramebuffer
between an integer and a normalized format is INVALID_OPERATION (4.6 core 18.3.1), so
there is no defined result to be wrong about - only a shape that used to end the session
and now produces the source's bits. For a float source of the wrong view type the copy
is a plain slice extraction and the result is exactly right.
THE RETIREMENT UNCOVERED A REAL BUG, which is the part worth reading. The ordinary blit
arm addressed a 3D source as `{aspect, level, source.layer, 1}` with z 0..1. A 3D image's
slice rides Z; its only subresource is layer 0 - which is how the copy endpoint's
CopyImageSliceMapping and the multisample resolve arm above already address one. So a
blit from slice 1 of a 3D texture read slice 0, out of a layer the image does not have.
It was invisible because that shape hit the Fatal first and never reached the arm: the
new scenario's first run had the scratch arm correct and the fall-through arm wrong in
all four quadrants, which is how it fell out. Both arms agree now.
The scenario's source is slice 1 of a 3D texture - ordinary GL, and exactly the shape the
shader pass cannot sample - with slice 0 filled in a colour that appears in no quadrant,
so a scratch copy that forgot the z origin would still be reading real texels and had to
be made distinguishable. It is registered twice on each arm: plain, taking the identity
fall-through, and under MGITEST_MAGMA_FORCE_DEFAULT_BLIT_SCRATCH, taking the rotated
arm's scratch path on an identity surface, where it must produce THE SAME PIXELS. That
equality is the assertion, and it is the only way the scratch arm is reachable from a
host lane at all: every surface this lane can create reports
VK_SURFACE_TRANSFORM_IDENTITY, as does the Redmi pbuffer this cluster's evidence comes
from. The knob does not fake a rotation - nothing downstream is told the surface turned.
Red once (executed, reverted): with MagmaWireFatal("default-color-blit-shape@P7")
restored, both entries die by that name; without it, 2/2 green.
Gates: integration-magma-split 81 (+2), -spawn 60 (+2), -tcp 62 (+2), 0 red;
ctest -L unit 2410; informational integration-magma-full-split 516, 0 red, 59 skip;
fatal_census green; link_ratchet --assert-monotone unchanged at 186, p7-magma 101;
G1 pull .text 0xa52203, defined-symbol name list identical (30570).
Owed to a device window: the scratch arm's geometry under a REAL ROTATE_90/180/270. The
knob proves it produces the fall-through's pixels on an identity surface, which exercises
every line of it except the rotation constant the vertex shader applies.
WireColorBlit.inc and VulkanRenderer.h are edited outside this package's named file list.
Both belong to this cluster (the colour blit's implementation, and four private
declarations beside the existing blit ones); flagged for the integrator.
|
||
|
|
4f857aa5c6 |
[MG_Backend, MG_IntegrationTest, docs] (P7): a same-image copy takes one GENERAL transition instead of the session
CONTRACT-P7 §3.2's `copy-image-in-place@P7`, retired.
glCopyImageSubData between two subresources of ONE texture is ordinary, defined GL -
4.6 core 18.3.2 spells out that a texture and a view of it are two objects over one
image and may be copied between. The wire arm took the session down for it, and the
reason it gave was true of the code beneath it and of nothing else: the
TRANSFER_SRC/TRANSFER_DST pair cannot describe one image, because both endpoints share
one tracked VkImageLayout and the second transition undoes the first.
VK_IMAGE_LAYOUT_GENERAL can describe it - both sides of vkCmdCopyImage accept it. So a
same-image copy now takes ONE transition of the whole image to GENERAL (the source level
and the destination level are both inside it), GENERAL on both sides of the command, and
ONE restore, with an access mask carrying both directions because that one barrier is
the edge for both. The monolith arm still declines every in-place copy a few lines up,
so the wire arm is the one that performs this - which is what §3.2 asks for rather than
parity. `inPlace` is constexpr false in the pull build so every branch it guards folds:
this function is in the monolith image and G1 pins that image's .text.
The one shape that stays a decline is same image + same level + overlapping slices +
overlapping rectangle, all three at once. GL leaves that undefined, and GENERAL makes
the command legal to RECORD rather than meaningful to execute - vkCmdCopyImage orders
nothing between its own reads and writes inside a region, so a tiler produces whichever
half of each texel it reached first. Level 0 to level 1, disjoint layers, and disjoint
rectangles of one level are all defined, and all performed.
Two split-only cases on the three Magma arms, over one 8x8x2 two-level array texture
whose every touched subresource is a different colour - a flat fill would let "copied to
the wrong level", "copied to the wrong layer" and "did not copy" all read as correct.
CopyImageInPlaceAcrossLevelsAndLayers does level 0 -> level 1 and then layer 0 -> layer 1
and checks the untouched subresources too; CopyImageInPlaceOverlapDeclinesAndLeavesTheLevelAlone
copies (0,0)-(4,4) onto (2,2) and pins every texel unchanged - an executed copy would
turn the texel at (4,4) from yellow to red, so the assertion can tell.
Red once (executed, reverted): with MagmaWireFatal("copy-image-in-place@P7") restored,
both entries die with Fatal{UnmigratedVerb, "Magma:copy-image-in-place@P7"}; without it,
2/2 green.
Gates: integration-magma-split 79 (+2), -spawn 58 (+2), -tcp 60 (+2), 0 red;
ctest -L unit 2410; informational integration-magma-full-split 515, 0 red, 59 skip;
fatal_census green (79 sites / 43 words / 0 unmarked); link_ratchet --assert-monotone
unchanged at 186, p7-magma 101; G1 pull .text 0xa52203 and the defined-symbol name list
identical (30570) - the constexpr-false branches fold as intended.
This tree's integration TCP endpoint moved to 127.0.0.1:40913: a sibling worktree's
libMobileGLServer was already listening on pipe's default 40613 and took the whole tcp
lane down at TcpServer.Start. It is a local configure option, not a lane semantic.
|
||
|
|
fdd611e83d |
[MG_Backend, MG_Test, MG_IntegrationTest, scripts, docs] (P7): the depth mip chain is baked, and every baked header in the tree now has to match its source
CONTRACT-P7 §5.1's (A) and (D), and (D) is what makes (A) safe to have.
(A) WireDepthMipmap.{vert,frag,inc}. VK_FORMAT_FEATURE_BLIT_DST is OPTIONAL for depth
formats, so a depth chain through a shader is the ordinary case and not the exotic
one - the monolith arm has carried such a fallback since before the split. The wire
arm had none, so a depth texture the device would not blit died on the first
disjunction of `mipmap-shader-format-or-shape`, and a DEPTH|STENCIL one died a guard
earlier as `depth-stencil-mipmap@P7` whether or not it could be blitted.
The monolith's fallback is not reusable, which is the whole of (A): it builds a GL
ProgramObject, two ShaderObjects and a GL sampler, binds through the uniform manager's
global UBO and writes uSrcTexelSize as a default-block uniform - four frontend objects
and a GL compiler on the server's apply thread. The baked program is the same shader
with the same arithmetic on a push constant and its own descriptor set. The fragment
stage is a PORT and not a rewrite: §3.2 retires a refusal by making the wire arm
observably the monolith's, so the 2x2 box lands where the monolith's lands, including
the half texel its ivec2(texCoord * srcTexelSize) sits off the destination centre.
`depth-stencil-mipmap@P7` therefore splits. DEPTH-only retires into the baked program.
DEPTH|STENCIL declines - MGLOG_E_ONCE and return, before the chain is grown, so a level
this verb will not fill is not created and then recorded as defined. A decline rather
than a Fatal because the monolith does no better: its fallback asserts depth-only, and
an assert is nothing in the release build both arms ship as. Nothing in GL promises a
stencil mip either, and this pass writes gl_FragDepth - it would leave the stencil
aspect of the level it just produced undefined.
(D) MOBILEGL_BAKED_INTERNAL_SHADERS. A baked header is a compiler output committed with
its input beside it and nothing connecting the two: edit the source and the build stays
green while the shader that runs is the one from before the edit. MG_Test's
BakedInternalShadersTest recompiles all eight arrays - WireColorBlit x2,
WireDepthMipmap x2, the iterationRP witness, the three probe stages - from their
committed GLSL with the in-tree glslang and compares word for word.
THE GATE IS THE BAKER. scripts/bake_internal_shaders.py compiles nothing; it runs this
binary with MOBILEGL_BAKE_INTERNAL_SHADERS and splices the words it emits. A gate that
used the glslang LIBRARY while the baker shelled out to a glslangValidator BINARY would
be two compilers with no rule about which is right, and the day their defaults diverge
the gate reds on a header nobody can regenerate.
What the first run measured, which is the one thing nobody could know beforehand:
WireColorBlitSpirv.h and all three arrays of PrimitivesGeneratedNoXfbProbeSpv.h were
already fresh - the hand-run glslangValidator words reproduce exactly. The iterationRP
witness was stale, as its own prose said; it is regenerated here (2142 -> 2143 words),
and the witness magic 0x50323033 is still there because it is a constant in the .comp
rather than a property of the words. All three provenance comments now point at the
script and the gate instead of at a tool on somebody's PATH.
Scenarios, on the three Magma arms: GenerateMipmapDepthWithoutNativeBlitPixels runs
DEPTH_COMPONENT24 and DEPTH_COMPONENT32F under MGITEST_MAGMA_FORCE_SHADER_MIPMAP=1 and
reads level one back through a real sampled attachment, asserting per half and leaving
row 3 - the straddle band - unpinned, because pinning it would assert the half-texel
offset rather than the mip. GenerateMipmapDepthStencilDeclinesAndKeepsTheSession takes
no knob (the decline is on the aspect, before blit support is asked about) and pins that
the session survives and level zero still holds what was cleared into it.
Red once, three of them, executed and reverted:
- a one-character edit to WireDepthMipmap.frag (no rebuild - the gate reads the source
at runtime) reds the WireDepthMipmapFragment row at word 279;
- with the depth arm disabled, the DepthMip entries die with Fatal{UnmigratedVerb,
"Magma:mipmap-shader-format-or-shape format=125 ... aspect=0x2 ...
missing=COLOR_ATTACHMENT"} - format 125 is X8_D24_UNORM_PACK32, which slice 1's
diagnostic names for us;
- with the Fatal restored, the decline case dies as
Fatal{UnmigratedVerb, "Magma:depth-stencil-mipmap@P7"}.
Gates: integration-magma-split 77 (+2), -spawn 56 (+2), -tcp 58 (+2), 0 red;
ctest -L unit 2410 (+8, the gate's rows); informational integration-magma-full-split
513, 0 red, 59 skip (base 510/0/57); fatal_census green (79 abort sites, 43 words, 0
unmarked); link_ratchet --assert-monotone unchanged at 186, p7-magma 101.
G1 holds and it was not obvious: the witness header is compiled into the pull build
too, and its word count changed. Pull .text is still 0xa52203 and the defined-symbol
name list is identical to ~/w7/p7-before/pull-syms.txt line for line (30570) - the
change lands in .rodata and widens no instruction encoding.
VulkanRenderer.h is edited outside this package's file list: the new pass needs four
private declarations next to the colour blit's. Flagged for the integrator.
|
||
|
|
c4be38005a |
[MG_Backend, MG_IntegrationTest, docs] (P7): the shader mip arm names the format it refused and renders through scratch when the image cannot be attached
Two Iris cases died on the Redmi (Adreno 830v2, DirectVulkan inproc) with
Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape"}, deterministically and
under both run-ahead and lockstep, while the monolith arm and the whole host lane
stayed green. That one line was the entire evidence, and five disjunctions share the
name, so the one thing the device could have told us - which format, at what shape,
missing which feature bit - was the one thing it did not.
The name stays first and unprefixed, because the census, the retrace refusal tally
and every log grep key on the leading word; the driver's own answer follows it:
format enum, the view's storage format when it reinterprets, extent, levels, aspect,
view type, usage bits, layer range, and the names of the optimalTilingFeatures bits
that are ABSENT. The next device run reads a line instead of re-deriving it.
The refusal also loses half its territory. The shader arm rendered STRAIGHT INTO the
next level, which needs the image to carry COLOR_ATTACHMENT usage - and a texture
whose format the device declines as a colour attachment never gets that bit, so every
sampled-only texture without BLIT support fell into the refusal rather than into a
fallback. When the FORMAT can be rendered and only the IMAGE cannot, an owned scratch
attachment of the destination level's size takes the pass and vkCmdCopyImage moves the
mip in: a raw texel move, no conversion, texels still from GPU contents rather than a
staged shadow, and the scratch retires with the submit that consumes it - the shape the
multisample resolve arm above it already uses.
Neither shader arm had ever executed on the gate machine. lavapipe reports
BLIT_SRC|BLIT_DST for every colour format this binary can create, so GenerateWireMipmap
takes its native arm for all of them; GenerateMipmapPackedFloatPixels next door looks
like it covers the shader arm and does not, because R11F_G11F_B10F blits natively here
too. MGITEST_MAGMA_FORCE_SHADER_MIPMAP drops the native arm instead, one tier per shape
(1 = render into the level, 2 = render into scratch and copy back), read once and
compiled only into the disaggregated build.
F1WireScenario.GenerateMipmapWithoutNativeBlitPixels paints two different halves rather
than one flat colour - a pass that loses GL's row order averages to the same number
everywhere, and a single-colour assertion would call that correct - and asserts level
one per half. It is registered on the three Magma arms twice, ONE ENVIRONMENT EACH: the
knob changes how every mipmap in the process is generated, so a lane-wide flip would
silently retarget the four green mipmap entries beside it.
Red once (executed, reverted): with the scratch arm forced off, the tier-2 entry dies
with Fatal{UnmigratedVerb, "Magma:mipmap-shader-format-or-shape format=37 extent=8x8x1
levels=4 aspect=0x1 view=1 usage=0x17 layers=0+1/1 missing=DEPTH_STENCIL_ATTACHMENT"},
by name and with the shape in it; restored, the same filter is 8/8 green.
Gates: integration-magma-split 75 (+2), -spawn 54 (+2), -tcp 56 (+2), 0 red;
ctest -L unit 2402/2402; informational integration-magma-full-split 511, 0 red, 58 skip
(the new case skips where the knob is unset); fatal_census green (79 abort sites, 43
family words, 0 unmarked - no site added, the funnel is unchanged); link_ratchet
--assert-monotone unchanged at 186, p7-magma bucket 101.
|
||
|
|
5937b4fcf2 | [MG_Remote, docs] (P7): CONTRACT-P7 §3.2 records package A (byte-tail retired, ranges clamped, alignment declines), ID-P7-21/22 | ||
|
|
2207b6d9ee |
[docs, MG_Backend, MG_IntegrationTest] (P7): package A's eight refusals are on record, and only real sites still carry @P7
notes/p7/magma-a.md: the per-site table (retired / declined / kept-fatal with the
reason), every red-once with its captured output, the gate readings, and what is
handed on.
The three places package A's own prose still named `uniform-buffer-byte-tail@P7`
lose the marker. They are comments, not refusal sites, and leaving the marker in
would have made a tree-wide `grep @P7` count them against packages B and C. The
census now reads 15 -> 7, and all seven are real: WireFramebuffer.inc x4 and
VulkanRenderer.cpp x1 (package B), WireDraw.inc x2 (package C).
Three findings worth the integrator's attention, all in the note:
1. Most of this cluster was unreachable, and that is provable rather than
assumed. The frontend validates buffer-range offsets against the very caps the
backend then re-checks - GL_{UNIFORM,SHADER_STORAGE,TEXTURE}_BUFFER_OFFSET_ALIGNMENT
and GL_MAX_UNIFORM_BLOCK_SIZE are all published from the same Vulkan limits -
so five of the eight sites could only ever have fired on a malformed peer. The
two that CAN fire are `uniform-buffer-byte-tail` (GL constrains a range's
offset but not its size) and `unaligned-atomic-buffer-range` (GL gives
GL_ATOMIC_COUNTER_BUFFER no queryable alignment, so the frontend only enforces
offset % 4 while the backend needs minStorageBufferOffsetAlignment).
2. fatal_census does not move for any of this, and no re-baseline is due. Wave 0
already routed these funnels through MGPipeSessionFailHook, so the abort lives
in FatalFunnel.cpp and retiring a WireDescriptorFatal call site changes no
census number: 79 abort sites / 43 family words, unchanged, RC=0. The brief
expected the count to fall because the sites were bare aborts before wave 0.
3. DirectVulkan.Split.Full.IterationRPProgram203Scenario.FixedCompleteInputProducesFixedCompleteGoldenOutput
is red on this worktree's build-split and is NOT ours: it reproduces 3/3 with
package A's backend diff fully reverted, and the mismatch bits are identical
to the S1 exposure texel in magma-two-process-first-run.md. The contract books
that one against the tcp arm, and ~/w7/pipe's own build-split passes it, so
whether it is a split-arm regression between 460a3a72 and 610fa281 or a
configure-dependent S1 needs the integrator.
Gates on the finished branch: integration-magma-split 75/75, -spawn 54/54, -tcp
56/56, -buffers 21/21, full-split 512 entries 454 PASS / 57 SKIP / 1 red (the
pre-existing one above; base was 453/57), ctest -L unit 2408/2408,
spawn_lane_parity RC=0, link_ratchet unchanged at 186, fatal_census RC=0, and G1
pull .text = a52203 with nm --defined-only identical to p7-before/pull-syms.txt
(added=0 removed=0).
|
||
|
|
cf7ca59fab | [MG_Remote, docs] (P7): CONTRACT-P7 records package C - vertex-layout split (seven masked reasons), the death-ops debt retired, the opcode-49 white-box deferred to P11, ID-P7-20 | ||
|
|
074a072739 |
[docs] (P7): package C's note carries the gate matrix and the three things the integrator has to decide
The per-slice red-once records were written with each slice. This adds what only exists once the four are together: the gate table (unit 2403, magma split/spawn/tcp 75/54/56 with the two death-case skips now gone, integration-split 187, integration-spawn 103, full-split 513 entries 0 red 55 skipped = 458 pass against the 453/57 bar, census unchanged, parity green, @P7 15/4 -> 13/3, ratchet 186 monotone, G1 .text 0xa52203 with every section +0 and the pull .so byte-identical), the ratchet bucket split with an honest note that I did not re-measure the buckets at the branch point, and a numbered list of the nine things this package changes that live outside its own files. Three of those nine need an integrator decision rather than a note: §5.4's white-box case is blocked by R-6 and not by this package; §5.3's g_programResourceCaches deletion is achieved in the disaggregated build only, because the archive member has to be guarded for G1; and buffer-window's family word is UnmigratedVerb where ProtocolCorruption is the honest one, which costs a row in MG_Pipe and an adapter arm that §3.3 deliberately has not spent. |
||
|
|
0acfa9aab9 |
[MG_State, MG_Backend, MG_Test, docs] (P7): the reflection archive publishes the storage-block index space, and both magma consumers read it
CONTRACT-P7 §5.3, OQ-8. MagmaProgramSource::EnsureStorageBlocks re-ran SPIRV-Reflect over
every stage module of the program PER DRAW: the class is a borrowed view that
WireDraw.inc's WireProgramSource constructs afresh for each wire draw, so its `mutable`
memo was cold every time it was read. The list it rebuilt is a pure function of the linked
program, so it is now built once at link time and travels in the archive.
LinkArtifacts gains storageBlocks{name, binding, dataSize} with its own VisitFields table,
the codec version goes 2 -> 3, and the schema fingerprint and wireFingerprint move with
it - which is expected, and is what makes a mixed-version pair refuse at the handshake
instead of at the first program.
FILLED IN PHASE A, and that is not where it looks like it belongs. The only function that
sees both finished halves is ProgramSpirvTask's L1 cache insert, but its writes reach the
TRANSLATION CACHE only: phase A's artifacts have already been moved into the ProgramObject
by then. A field filled there would be present on a cache hit and absent on every cold
link. So the fill is glslang-derived, and the equivalence is MEASURED rather than assumed:
ProgramTest.TheArchivesStorageBlockOrderIsTheOneSpirvReflectProduces runs the old
consumer's own algorithm over the real modules and compares element for element, on a
multi-stage program with an arrayed SSBO and an atomic-counter block - the three cases
that could have diverged (the instance array collapses to one entry, the synthesized
counter block IS in the backend's space although GL's enumeration excludes it, and the
cross-stage dedupe walks stages in linked order).
MEASURED, same binary and same window (frames=1 draws=1 acc=18, identical bytes),
DirectVulkan x inproc over the SSBO scenarios: spvReflectCreateShaderModule on the wire
draw path 4 -> 0. The 0 is structural: the call site is gone and MagmaProgramSource.h no
longer includes spirv_reflect.h at all, which is the gate a future edit breaks on purpose.
THE MONOLITH CONSUMER IS SWITCHED TOO, under #if symmetry, so the disaggregated build
retires g_programResourceCaches, ProgramResourceCache, StorageBlockResource,
BufferVariableResource, AddBufferVariablesRecursive, NormalizeDescriptorName,
GetProgramResourceCache and the rehash hazard - the open-addressed-map dangling reference
that was a reproducible segfault in ProgramPipelineScenario's two storage-block cases.
Nothing outside DirectVulkan.cpp read any of it (measured; GL_BUFFER_VARIABLE and
GL_ACTIVE_VARIABLES are answered by ProgramInterface.cpp out of a different index space),
so the two arms differ in exactly the two functions their callers use.
THE PULL BUILD KEEPS ALL OF IT, and the integrator should see why: the archive member is
MOBILEGL_BUILD_DISAGGREGATED-only because one more Vector in LinkArtifacts changes its
size, its implicit destructor and its move constructor, and G1 pins the pull build's .text
byte for byte. So §5.3's deletion is achieved in the disaggregated build and not in the
pull one; retiring the pull half is a one-line change the day that pin is retired or the
member moves. Filed as P13 in notes/p7/magma-c.md.
One improvement rides along: the monolith binding getter now asks the program for a
rebind on every read, over an immutable list, where it used to read a binding patched into
a mutable cache - so a rebind can no longer be lost to a cache rebuild that raced a
state-version bump.
G1: .text 0xa52203 unchanged, 27837 -> 27837 defined symbols, 0 added / 0 removed / 0
resized / 0 renamed, file byte-identical. An unguarded call site caught the guard rule
once mid-slice by failing the pull compile, which is the loud version.
RED-ONCE: publishing nothing reds the order-equivalence case (with the reference list
printed) and 25 of 60 storage-block scenarios - the MONOLITH DirectVulkan arm beside all
three transports, which is the evidence that the monolith consumer really was switched.
Restored: 61/61.
|
||
|
|
f1b04edfc6 |
[MG_Backend, MG_IntegrationTest, docs] (P7): the magma vertex-layout refusal splits into one protocol fatal and seven masked attributes
CONTRACT-P7 §3.2's `vertex-layout` and `vertex-format-conversion` rows.
BuildWireVertexInput answered eight different conditions with one `false`, and the single
caller answered that with a P7-marked MagmaWireFatal. So a Magma split session that met a
GL_FIXED array, a dvec on a device with native fp64, or any format without
VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT did not decline the draw: it killed the process. The
monolith arm on the same device has always masked the attribute out and then declined the
draw loudly and once.
The split is by what the reason is ABOUT, not by severity. `buffer-window` is a statement
about the RECORD - an enabled attribute the program reads whose slot lies outside the
window set_vertex_buffers published - so it stays a named Fatal through the hook; it is
what caught the Redmi VAO hash collision fixed in
|
||
|
|
1255caa2fb |
[MG_Backend, MG_Impl, MG_Util, MG_Test, MG_IntegrationTest, docs] (P7): the server publishes kCapResidentSubData off its own wire resource table
CONTRACT-P7 §5.4, OQ-10. InitServerRoleCommon computes the whole capability mask and
never ORed this bit, so MGPipeResourceOpsHaveSubDataResident answered false under every
transport and BOTH backends were pinned to the ordered in-place host memcpy - which made
Magma's SubDataResident arm (VkBufferManager.cpp's g_vulkanWireResourceOps) dead code on
the wire even though the arm has been there since P5f.
The bit is derived from g_resourceOpsAtStep2->SubDataResident, i.e. from the pointer this
function already pinned at step 2 and that step 5 already refuses to see swapped - never
from ActiveBackendType. That is ID-39's lesson and not a style preference: a mask is a
statement about THIS backend's registered table, and a bit derived from the enum would
keep claiming the capability for a build, a flavour or a future backend whose table does
not carry the arm.
`rsd=` joins the emit[] bracket on the PipeStats summary line, counted at the emitter once
per buffer_subdata_resident record rather than once per call, because a write past the
content chunk cap is cut into several records. It is the only observable that separates
the resident arm from the memcpy beside it: the bytes in the buffer are identical either
way, which is exactly how the capability stayed unpublished for a phase with every lane
green.
WHAT THIS DOES NOT YET PROVE, and R-6 is the reason rather than an omission. §5.4 asks for
a white-box case showing opcode 49 on the wire. None can exist at this tree: the resident
arm is reached only from a GPU-RESIDENT store, residency comes only from
AdoptPersistentMap, and every persistent-map acquisition declines under split by R-6
(PipeApply.cpp:2331 "A SPLIT BUILD RUNS AT TIER T2 AND DECLINES EVERY ACQUISITION,
ALWAYS"; T0/T1 are P11's and AdoptTierIsEmulate is a named Fatal for them). So the
capability is wired AHEAD of its consumer, deliberately, and `rsd=` is what will show the
records the day P11 lands a tier that adopts. Filed for the integrator in
notes/p7/magma-c.md.
What IS proved is both halves of the bit itself. RemoteClientTest's Magma bootstrap case
now asserts the published bit EQUALS `ops->SubDataResident != nullptr` - the two compared
to each other rather than both to true, so the case also covers a backend without the arm
on the day one exists - and a new CtWireScenario case reads the bit out of the client's
own caps mirror, through the same accessor the frontend reads, on both backends and all
three transports.
RED-ONCE, both levels. Deleting the OR reds
CapsMirrorTest.MagmaTransportPublishesRealBufferConsumersWithoutRunAhead
(RemoteClientTest.cpp:692, "Which is: false / Which is: true") and all SIX arms of
DirectGLES/DirectVulkan x {Split,Spawn,Tcp} Ct.TheServerPublishesTheResidentSubData
CapabilityFromItsOwnTable ("the client's caps mirror did not receive
kCapResidentSubData"). Restored: green.
Also carries the P4a death-helper comment from the previous commit's sweep: it no longer
says Magma installs no death table, because it now does and still frees no slot.
|
||
|
|
dce9953d36 |
[MG_Backend, MG_State, MG_Pipe, MG_Remote, MG_Impl, MG_IntegrationTest, docs] (P7): magma installs a state-object death table, and the inproc arm stops losing every object_death
CONTRACT-P7 §5.5. DirectVulkan had no StateObjectDeathOps at all, so under a Magma
inproc session NotifyStateObjectDestroyed reached nobody and the server was never told
that a frontend object had died. Package L measured the shape: with the WireTables
install condition made backend-agnostic, CtWireScenario's two death cases PASS on Magma
spawn and tcp - the remote client has no local backend, so the client-side emitter
installed there answers the notice - and FAIL on inproc, where the install condition
deliberately stands aside for "the backend's own dispatcher" and Magma had none.
g_magmaStateObjectDeathOps is that dispatcher. It mirrors g_glesStateObjectDeathOps
(DirectGLES/Managers.cpp:335) with ONE arm rather than two: Espryt's other arm destroys
a backend twin keyed by lifetime id, and Magma keeps no such twin - its two
client-minted kinds are keyed on {slot, gen} by MagmaPipeIdentityTables and the other
six are still reached from their frontend objects - so what is left is exactly the emit
of object_death off the apply thread. Installed from
BackendObject_DirectVulkan::Initialize(), i.e. ServerLoop::CreateBackend, step 1 of
InitServerRoleCommon: the one place BOTH Magma server roles pass through, and before any
client session exists. #if MOBILEGL_BUILD_DISAGGREGATED && MOBILEGL_PIPE_PUSH, so the
pull build compiles nothing new.
The two skip guards in CtWireScenario go with it. Every other "Magma installs none" site
the audit found is comment text whose ARGUMENT survives, and each is reworded rather than
deleted: the table emits the wire record and frees no client slot, so P4a's
backend-neutral NotifyAndFree is still the only thing that returns one, HandleRecycle's
C-1 case still measures the only protection the allocator has, and MagmaPipeArms' age
sweep is still the only thing that retires an identity slot.
The framebuffer death case now reads the client's allocator beside the server's tally.
That correction matters: the plan called this a slot leak provable with PipeSlotPeek, and
it is not - it has been backend-neutral since P4a. What leaked was the SERVER's twin, and
a framebuffer has no wire delete opcode at all, so object_death is its only delivery.
Two installers cannot race although the global is last-writer-wins with no stacking:
Magma's runs only in a process that owns a DirectVulkan backend, and
InstallClientWireTables' arm runs only under Transport == Spawn, which is by construction
the process that owns none.
RED-ONCE: `if (false) InstallStateObjectDeathOps();` reds the two Split cases alone
(`Expected: (afterCounters.deaths) > (deathsBefore), actual: 0 vs 0`, CtWireScenario.cpp:264)
and leaves spawn and tcp green - the exact split of responsibility the comment claims.
Restored: 8/8. Recorded in docs/Disaggregated/notes/p7/magma-c.md.
|