mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
c997ba9425e21838aaadcaed2a0d1574e55e57dc
3577
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. |
||
|
|
4db17d58c6 |
[MG_Backend, MG_IntegrationTest, scripts] (P7): a declined multisample depth/stencil shape is decided once before any aspect runs, so an erroring blit writes nothing, and the shader resolve arm gets a flipped knob twin
- BlitWireFramebuffers runs colour first. A scaled COLOR|DEPTH blit out of a multisample framebuffer scaled and wrote its colour and then recorded INVALID_OPERATION on its depth, and an X-mirrored one mirrored its colour and left its depth alone - partial effects GL 18.3.1 does not allow. The shape decline (scale, X mirror, cross-format Y mirror) moves into DeclineWireDepthStencilResolveShape, which BlitWireFramebuffers asks for every depth/stencil aspect that would take the multisample resolve arm before any aspect runs; ResolveWireDepthStencil asks it too for any other caller. Default read/draw framebuffers are left to the lambda (a default read is never multisampled; a default draw behind one is the region Fatal, and resolving it early would move its write index).
- The X decline and the cross-format Y decline get their own MGLOG_E_ONCE sites; one site used to cover both, so after the first X decline a cross-format Y decline logged nothing.
- An empty rectangle on one side only (64x48 onto 0x48) is a size mismatch and now takes the scale decline (INVALID_OPERATION) instead of returning silently; empty on both sides stays a silent no-op.
- DirectVulkan.{Split,Spawn}.MsFlip1. forces MGITEST_MAGMA_FORCE_SHADER_DEPTH_RESOLVE=1, so the shader arm min-corner rectangle under a flip runs on this host; split+spawn only and named in MAGMA_SERVER_ENV_KNOB_NO_TCP.
- spawn_lane_parity.py: every server-env-knob tail must match exactly one split key, so a dead or over-broad tail fails the gate instead of riding silently.
- The flipped-resolve case gains a COLOR|DEPTH scale leg (error, colour untouched, depth untouched) and an empty-destination leg (error, depth untouched).
- Red-once, each on split + spawn (+ tcp MsFlip.) and restored: pre-pass disabled -> all five MsFlip/MsFlip1 entries fail "the declined COLOR|DEPTH scale must not have written its colour either" (read {255,0,0,255}, want {0,0,255,255}); round-1 silent empty-side return -> all five fail the empty-destination leg (error 0, want 1282); shader arm handed sx0/sy0 instead of the min corner -> MsFlip1 fails on both arms ("768 of 768 depth values differ from 0.75", "928 of 3072 stencil values are wrong; first at (20, 4): got 255, want 99") while MsFlip. stays green; a dead ".MsFlip2." tail -> parity rc 1 "matches 0 split entrie(s)".
- The split once-sites have no lane red-once: the cross-format Y decline needs a device whose renderbuffer and texture D24S8 map to different VkFormats, which lavapipe is not.
|
||
|
|
1e46a67091 |
[MG_Backend, MG_IntegrationTest] (P7): the multisample depth/stencil copy-out pads its row stride to 4 bytes, so a narrow flipped stencil resolve puts every row region on a legal offset
- ResolveWireDepthStencil moved the resolved aspect through a tightly packed buffer and, for a vertical mirror, started one region per row at row * width * texelSize. A depth/stencil buffer->image copy needs every bufferOffset on a multiple of 4 (VUID-vkCmdCopyBufferToImage-dstImage-07978), so a stencil aspect (1-byte texel) whose width is not a multiple of 4, or a D16 aspect with an odd width, was illegal on every row after the first; scissor clipping produces such widths. - paddedWidth = width rounded up to 4 / texelSize texels; the buffer is paddedWidth * height * texelSize, both copies name it as bufferRowLength, and each row region starts at row * paddedWidth * texelSize. - The flipped-resolve case gains a stencil leg: stencil bands 11/99 in the multisample source, a 29x32 flipped STENCIL_BUFFER_BIT blit from (5,8) to (20,4), and every stencil value of the destination checked against the mirrored band or the untouched prime. - Red-once (split + spawn, under VK_LOADER_LAYERS_ENABLE=*khronos_validation): lavapipe does not enforce the VU and the stencil bytes came back right on the unpadded code, so the red is the validation layer: 10 x "pRegions[1].bufferOffset (29) must be a multiple 4 if using a depth/stencil format (VK_FORMAT_D24_UNORM_S8_UINT)" on each arm before, 0 validation errors of any kind after. |
||
|
|
0019643406 |
[MG_IntegrationTest] (P7): the flipped multisample resolve case asserts on the magma wire arm only and skips the monolith and espryt entries by name
- The suite is discovered whole on the monolith DirectVulkan arm and on every DirectGLES arm, and the case was red on all of them: the monolith refuses a flipped depth blit and scales a multisample one GL calls INVALID_OPERATION, and Espryt writes nothing for the flip and raises nothing for the scale.
- A backend gate first (Gl().BackendName() != "DirectVulkan", Espryt reason, P3b/P4b), then the lane marker (SplitLane::IsSplitLane(), monolith reason, P13/G1-bound), so a DirectGLES entry names the backend that owes the fix whichever lane it sits in.
- DirectVulkan.{Split,Spawn,Tcp}.MsFlip. stay armed (all carry MGITEST_SPLIT_LANE=1) and pass.
- Verified: DirectVulkan.DepthStencilReadbackMatrixScenario.AFlipped* and DirectGLES.{,ForcedDepthStencilEmulation.}DepthStencilReadbackMatrixScenario.AFlipped* report Skipped with those reasons.
|
||
|
|
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 | ||
|
|
38bfdf6d63 | [MG_IntegrationTest, MG_Pipe, ci] (P7): VERIFY_CORRUPT and POISON_OMIT each go red on the split arm, the poison through the server's own stamp with the ReadPixels:GetPixelStoreParameters pair, and verify stays inproc-only because its two arms share only that process | ||
|
|
64a2f4a300 | [MG_IntegrationTest, MG_Pipe, ci] (P7): every verify case runs on the split (inproc) arm of both backends under integration-verify-split, armed per entry, and the comparator's oracle for the pack half inside the server's read_pixels window is the ID-49 neutral pack | ||
|
|
e8678dfad9 | [MG_Test] (P7): the respecify-scope unit helpers are compiled only where the applier they read is, so the pull build builds again | ||
|
|
a1d17566f7 | [MG_Pipe, MG_Test] (P7): the respecify scope pin asks that a per-level producer cover the range it declares, so verify and split can be in the same build | ||
|
|
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. |
||
|
|
80b91346d1 |
[tools] (P7): a split retrace reds on any MGWIRE-FLOOR unsound-serial-complete line, so the frame-serial floor clamp has a lane
Review ID-P7-34, item 2. The primary B3 fix - the clamp in
VulkanRenderer::OnSubmitsCompletedUpTo that raises the completed-frame-
serial floor only to a serial no in-flight submission still carries - had
nothing behind it: deleting the clamp loop left every lane green and the
OpenRA picture gold on lavapipe (the host's barriers hide the tear). Its
only observable was the per-event MGLOG_W "MGWIRE-FLOOR
unsound-serial-complete".
run_trace_case.cmake now reads that line out of both roles' logs on every
split run, exactly as it already reads Fatal{: the count is printed pass
or fail and any nonzero count is a FATAL_ERROR. The R-16 counter (26 -> 0
on the OpenRA retrace) is thereby a gate.
Red/green on MobileGLTraceReplay.OpenRA.DirectVulkan.SPLIT / .SPAWN:
clamp loop deleted -> both FAIL at ssim 1.000000 / 0 px:
MGPipe split: OpenRA DirectVulkan transport=inproc, MGWIRE-FLOOR
unsound-serial-complete lines: 26
[mgl-srv-apply/WARN]: MGWIRE-FLOOR unsound-serial-complete #1: advancing
the completed frame-serial floor to 3, which submission 3 (serial 3)
still carries
(spawn: identical, 26)
clamp restored -> both PASS, 0 lines.
|
||
|
|
cba0c3a110 |
[MG_Backend, MG_IntegrationTest] (P7): the busy predicate's comments name the floor as the guarantee and the submission term as a probe, and the term is consulted and re-stamped wherever the serial is
Review ID-P7-34, item 3. 02bb5789 presented the submission term as the fix and still carried the every-8th-drain mechanism that magma-b3.md §2.4 refutes (the probe saw one successful drain and zero boundary works across the OpenRA replay). The term is not sound alone: lastUseSubmitIndex is stamped at AcquireWireSlice, but a mid-draw flush (BindProgramUniformBuffers -> SyncWireTextureShape's preserve path -> FlushWirePendingCommandsFor- TextureUpdate) can submit the stamped index without the draw, so the stamp names S1 while the draw rides S2. The guarantee is the floor in OnSubmitsCompletedUpTo; the term is a defence and a device probe. - WriteWireBuffer's block comment, the WireBufferResource field comment, the AcquireWireSlice stamp comment, the StreamedBufferSubData CMake comment and the F1WireScenario case comment are rewritten on that footing; the drain story is gone. The drain probe's comment now says it refuted the drain as the mechanism. - WaitForWireBufferHostAccess's early return also requires the submission term complete, so a caller with the serial term idle cannot skip a wait the term demands. - ReadWireBuffer re-stamps lastUseSubmitIndex beside lastUseSerial after its wait, preserving the pending draw's reservation in both halves. All disaggregated-only code. OpenRA DirectVulkan SPLIT / SPAWN retraces ssim 1.000000, 0 px, zero MGWIRE-FLOOR lines; integration-magma-split 90/90, -spawn 69/69, -tcp 70/70. |
||
|
|
b0c6af3168 |
[MG_IntegrationTest, tools] (P7): the stale-serial red-once is registered on split and spawn only, and the parity gate names it with the other server-side knobs
MGITEST_MAGMA_FORCE_STALE_BUFFER_SERIAL is read by the server. The tcp arm's server is the lane fixture (scripts/ci/tcp_server_fixture.py), started once from its own environment, so an entry-level ENVIRONMENT never reaches it: DirectVulkan.Tcp.StaleSerial.* ran the unforced case under a name that claimed the knob and could never red (review ID-P7-34, item 1). The entry is now registered only when the arm is Split or Spawn, and spawn_lane_parity.py grows a second named asymmetry beside MAGMA_INPROC_ONLY: MAGMA_NOT_OVER_TCP, key prefixes the reference arm has and the tcp arm does not by name. It comes off the reference for the tcp arm alone, so the same entry missing from spawn is still an error. B2 adds its own knob entries to the same tuple in its tree. integration-magma-tcp 71 -> 70 entries (68 cases + the two fixture entries); split 90, spawn 69 unchanged. spawn_lane_parity.py build-split rc 0; tcp_lane_tools_test.py 6/6. Integrator merge onto pipe: B2's round-1 mechanism (`mglItestServerEnvKnobArm` in CMake, `compare_arms(no_tcp=)` in the parity script) already covers this shape, so `.StaleSerial.` joins MAGMA_SERVER_ENV_KNOB_NO_TCP instead of the separate MAGMA_NOT_OVER_TCP the package tree introduced. |
||
|
|
b8d10d0730 |
[MG_Backend, tools] (P7): every WireDeclines.def row has a site and every site a warning-or-error line, and a tree-only audit fails the job when one does not
The B3 review (ID-P7-34) counted 12 of the 52 rows with no site at all - TexViewFormat, TexNoApplierRecord, TexRecordDeadOrStale and the nine Shape* rows were declared and never counted - and ten sites that counted with no (MGLOG_D included) line, so "a name, a tally and a decline line" did not describe the tree. - SyncWireTextureShape: all nine Shape* exits now count; the ViewOf exit and the preserve-flush exit were bare `return false` and are now MGL_WIRE_DECLINE_AT; the image-flags exit logged only MGLOG_D, which a Release build compiles away, and is promoted likewise. - The preserve-copy exit was the one Shape exit with no row: appended as ShapePreserveCopyFailed (rows are append-only), 53 rows. - SyncTextureResourceByHandle / UploadPendingWireLevels: the count-only sites without a log of their own are promoted to MGL_WIRE_DECLINE_AT; the ones already under a W/E _ONCE line keep that line and gain the count. - scripts/ci/wire_declines_audit.py: fails on a row with no site, a site naming no row, or a bare Count() with no MGLOG_W/E within 8 lines above it. WireDeclineTally.h (the macro's definition) is not a site. Wired into the job that runs the fatal census. Audit before: 52 rows, 40 with sites, 10 unlogged under the W/E rule (rc 1). After: 53 rows, 53 with sites, 0 unlogged (rc 0). Forced red (ShapeNoVkFormat made true for 1x1 textures, reverted): Split.Fm.F1WireScenario.VertexIdSampler... fails on pixels with ShapeNoVkFormat=2 / TexShapeSync=2 in the shutdown tally. |
||
|
|
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.
|
||
|
|
645dd96f69 |
[MG_Backend, MG_IntegrationTest] (P7): a flipped multisample depth or stencil resolve lands mirrored and a scaled one declines, and neither ends the session
ID-P7-31 review, should-fix 2.
ResolveWireDepthStencil required sx1 > sx0, sy1 > sy0 and identical signed spans, and
took the session with Magma:multisample-depth-resolve-region otherwise - so
glBlitFramebuffer(0, h, w, 0, 0, 0, w, h, COLOR|DEPTH) out of a multisample framebuffer
resolved its colour correctly and then died on its depth. GL 4.6 core 18.3.1 asks for
identical DIMENSIONS, not identical corners.
* spans are signed; everything downstream (renderArea, the shader arm's rectangle, the
buffer copy offsets, BlitDepthAcrossFormats' corners) works off the min corner;
* a SCALE is INVALID_OPERATION in 18.3.1 and now declines by rule I (a) with the colour
arm's three lines (MGLOG_E_ONCE, RecordTextureCopyError, return);
* a same-format Y mirror is done in the copy-out: one VkBufferImageCopy per row,
buffer row r written to destination row dyMin + height - 1 - r;
* an X mirror (a region per texel is not a copy) and a Y mirror across two formats
(BlitDepthAcrossFormats, shared with the monolith, has no flip) decline by name.
The region Fatal stays for a rectangle outside either image.
Scenario DepthStencilReadbackMatrixScenario.AFlippedMultisampleResolveMirrorsTheBandsAnd-
AScaleDeclines, registered as MsFlip. on all three magma arms (no knob): unequal depth
bands, COLOR|DEPTH in one flipped call with colour and depth asserted the same way up,
then a scaled depth blit that must be INVALID_OPERATION and leave the destination alone.
Red once (R-16, executed and reverted, split and spawn each):
* the old guard restored -> both arms die by
Fatal{UnmigratedVerb, "Magma:multisample-depth-resolve-region"};
* the scale decline turned back into that Fatal -> the flip half passes, both arms die
at the scale;
* the per-row copy-out bypassed (if (!mirrorY) forced true) -> both arms live and fail
"flipped resolve: the destination's bottom band: 768 of 768 depth values differ from
0.75; first bad value 0.25" (and the top band the other way).
|
||
|
|
9fed478dfd |
[MG_IntegrationTest, scripts] (P7): a server-side magma test knob is registered on split and spawn only, and the parity gate names the five entries tcp cannot carry
ID-P7-31 review, should-fix 1. Every MGITEST_MAGMA_FORCE_* knob is read BY THE SERVER (the getenv readers in WireFramebuffer.inc run on the apply thread), and a ctest ENVIRONMENT property only reaches the process ctest starts - the client. Inproc runs the server in that process and ServerSpawn.cpp copies ::environ into the spawned child, so both arms see the knob; the tcp arm's server is the lane-wide TcpServer.Start fixture, started once from scripts/ci/tcp_server_fixture.py's own os.environ, and nothing a per-case property says reaches it. The tcp copy of each knob=1 entry ran the DEFAULT arm under a name claiming otherwise. Measured, not deduced: a temporary fatal in each of the three readers killed all five knob=1 entries on split and on spawn and left all five green on tcp. So ShaderMip1, ShaderMip2, DepthMip, DefaultBlitShape1 and MsResolve1 are registered on split+spawn only (the first four are package B's pre-existing vacuity of the same shape). The knob=0 twins DefaultBlitShape0 / MsResolve0 keep their tcp entries: zero is the default and means the same thing on every arm. spawn_lane_parity.py names the exception, following ID-P7-14's MAGMA_INPROC_ONLY: MAGMA_SERVER_ENV_KNOB_NO_TCP, applied through a new compare_arms(no_tcp=) that drops the keys from the tcp comparison ONLY - folding them into inproc_only would stop requiring them on spawn, which is where they do most of their work. |
||
|
|
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. |
||
|
|
dbea871a06 | [docs] (P7): §12 - the one present per bsl-esc-menu replay is the trace's own two swaps and the harness's snapshot exit, so server reclaim must not assume a bounded frame | ||
|
|
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. |
||
|
|
511c1f1641 |
[MG_Backend] (P7): the completed-frame-serial floor only advances to a serial nothing in flight still carries
THE OpenRA DEVICE DIVERGENCE. "Frame-serial completion piggybacks on submission completion" holds only while a frame serial has ONE submission. On the wire arm it has two, measured on this trace: 73 submissions over 32 serials on inproc and on spawn, 30 over 32 on monolith. S1 is the per-frame palette glTexImage2D, which reaches UploadPendingWireLevels -> FlushWirePendingCommandsForTextureUpdate -> FlushPendingCommands at the first palette-sampling draw and submits under a POOLED fence. S2 is Present, and it carries the frame's draws AND the barriered vkCmdCopyBuffer that StagedWireRangeCopy records for every streamed glBufferSubData. RegisterSubmit stamps both with the serial current at submit time, so they share it. S1 is tiny and signals almost immediately. RefreshCompletedSubmits retires it alone, and OnSubmitsCompletedUpTo called NotifyFrameSerialComplete per retired record - advancing the floor to N-1 while S2(N-1), the submission actually carrying frame N-1's buffer copies, was still executing. GetCompletedSerial then returned N-1, so frame N's first write to that buffer failed WriteWireBuffer's busy predicate and took the unordered host memcpy at :268 - and S2(N-1)'s queued copies landed on top of it afterwards. That is why the wrong pictures are a PREFIX TEAR of one draw's vertex range rather than a missing draw: 318 vertices / 6 = 53 quads at 11448/53 = 216 bytes each, and 6696 = exactly 31 of them. 6696 and 11448 are both real write sizes of that same buffer (26 and 5 occurrences in this replay), so the damage is the previous frame's write list replayed over the new bytes, and its length is the largest write in whatever suffix of that list had not executed yet. It also explains the device's FRAMESINFLIGHT=4/8 result (6/6 whole-write loss): more frames in flight puts the GPU further behind, so the pending suffix contains the larger writes. The floor now advances only to a serial NO remaining in-flight submission still carries. NotifyFrameSerialComplete is monotone and already refuses the still-recording serial, so this can only advance the floor later, never further; the disaggregated build's monolith arm is behaviour-identical (one submission per serial, and its counter reads zero either way). The #else branch is the pull build's statement verbatim, which is what keeps G1 byte-identical. The counter is the red-once's reading, per lens C: an advance naming a serial some remaining submission still carries. Base 26 on inproc, 26 on spawn, 0 on monolith; zero on all three after this commit. It is logged per event rather than dumped at teardown because this trace's renderer is never Shutdown() - the process exits - so a teardown dump reports the start-up context's zero. |
||
|
|
a2e244c036 |
[MG_IntegrationTest] (P7): the streamed subdata-then-draw pair is a case on all three Magma arms
F1WireScenario.StreamedBufferSubDataBeforeEachDrawIsOrdered is OpenRA's frame reduced to four quads: one GL_DYNAMIC_DRAW VBO that is never orphaned, four batches written over the SAME offset-0 range and each drawn immediately, each in its own two-pixel column and its own colour. The case asserts PIXELS, because this path has no GL error and no log line to assert - a draw that read another batch's bytes still executes and still reports success. Two entries per arm. The plain Fm. entry is a regression gate that runs on every driver. The StaleSerial. entry carries MGITEST_MAGMA_FORCE_STALE_BUFFER_SERIAL=1 and is the rule-J red-once: on integration-magma-spawn, at the package base, batches 0..2 lose their own vertices and batch 3 - the last write - keeps them, which is the mechanism spelled out in pixels. Its own ENVIRONMENT rather than a lane-wide flip, for the reason the ShaderMip pair gives: the knob changes how every streamed write in the process is answered. |
||
|
|
c9164f25d3 |
[MG_Backend] (P7): a streamed buffer write waits for the submission that reads it, not for a frame count
WriteWireBuffer answers a resource_subdata either with an ordered vkCmdCopyBuffer or with an immediate host memcpy, and it chose on `lastUseSerial > GetCompletedSerial()` alone. GetCompletedSerial is max(m_frameSerial - frameCount, m_completedSerialFloor) and its first term is PURE COUNTING - it asserts that work from frameCount serials ago has finished with no fence behind it. On the wire arm m_frameSerial is advanced not only by Present but by TryDrainFrameTransients -> VkBufferManager::BeginFrame on every 8th drain, which happens MID-FRAME, and package B established that mid-frame pooled-fence submissions are not covered by frame.imageInFlightFence. So the predicate could answer "idle" about a buffer that a recorded-or-in-flight draw still reads; the write was then applied unsynchronised and the draw executed reading somebody else's vertices - drawing another batch's geometry, or nothing at all, with no log line and no GL error. That is OpenRA's frame: ~24 glBufferSubData -> glDrawArrays pairs over the SAME range of one GL_DYNAMIC_DRAW buffer that is never orphaned, and a picture missing exactly one batch's quads. The predicate now has a second, fence-backed term: WireBufferResource carries lastUseSubmitIndex, set at every draw bind and staged copy to GetWireNextSubmitIndex() (m_submitCounter + 1 - what RetireWireObjects already tags future objects with, and deliberately NOT GetSyncPointSubmitIndex, which answers m_submitCounter when nothing is recorded yet, exactly the hole a draw acquiring its vertex slices before BeginCommandRecording falls into), and IsSubmitIndexComplete reports an unsubmitted index as incomplete. The counting term is kept because it only ever over-reports busy; the two are OR'd, so the answer is monotonically safer. GetCompletedSerial itself is untouched - it also serves the monolith VkBufferResource path and changing it would move the pull build's .text. MGITEST_MAGMA_FORCE_STALE_BUFFER_SERIAL forces the counting term to answer "idle" (all writes, or the Nth one alone), which is the state a mid-frame idle drain puts the device into and which no host lane reaches by itself. It forces only the lie, not a bypass of the ordered copy, so it stays a valid red-once across this fix. |
||
|
|
76bbb6c300 |
[MG_Backend] (P7): every silent exit on the Magma wire draw path has a name, a tally and a decline line
Rule I admits two shapes for a refusal on the wire arm, a decline and a named Fatal. The draw path had a third one the rule does not allow: roughly forty bare `return false` statements with no log, no counter and no census entry, so a dropped draw left no trace anywhere - which is how the OpenRA device divergence survived four device windows with client and server logs that were line-identical between a passing and a failing run. WireDeclines.def names every one of them; WireDeclineTally.h expands that into a counter table plus MGL_WIRE_DECLINE_AT (the tally always moves, the log is once per site), in the shape of PipeApplier.h's ServerVerbSink tallies and for the same reason (R-16: a probe may not arm against a stub). Covered: SetupWireDraw and DispatchWireCompute (25), the three exits of PrepareWireTextureResources, the four of SyncTextureResourceByHandle, and UploadPendingWireLevels' five false exits PLUS the two that only `continue` - those report SUCCESS while leaving the entry pending, i.e. a draw that samples a level which was never uploaded, so they are counted apart. Also the pending-upload gauges (entries, bytes, peak bytes), sampled at the funnel, because a refused upload does not consume its entry and the level shadow behind it stays alive. Shutdown dumps the tally and is silent when it is all zero: a green lane stays quiet and a lane that dropped a draw cannot. Plus the drain probe behind MOBILEGL_MAGMA_WIREBUF_PROBE, which is what measured that a host replay reaches the mid-frame frame-boundary drain zero times. |
||
|
|
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.
|
||
|
|
a50d50df60 |
[MG_Backend, MG_Impl] (P7): the two Magma allocator debt scopes go, because the objects they were about are never built off the monolith arm
CONTRACT-P7 §5.2's "the two MagmaP7AllocatorDebtScope are deleted with it (prove
first, with a temporary assert, that they never arm under a non-monolith
transport)".
WHAT THE DEBT WAS. The hidden blit program, its two samplers and the hidden depth
-mipmap program are FRONTEND objects. Under an active transport the server backend
created them on the apply thread and destroyed them on it, where their destructors
run the client death helper and its lifetime-id probe - so P5c gave the two
teardowns a named exemption and P5e ruling 12 renamed it after the debt.
WHAT RETIRES IT IS NOT AN EXEMPTION BUT AN ABSENCE. P5f fv already gave
InitializeBlitResources and InitializeDepthMipmapResources an early return for
every non-monolith transport, so off the monolith arm neither resource ever holds
anything and `m_blitResources = {}` destroys nothing. The scopes have been
markers over a path that cannot run since that day.
PROVED, NOT ARGUED. A temporary probe in both teardowns failed the session by name
if either resource was non-null under a non-monolith transport. With it armed:
integration-magma-split 93, -spawn 72, -tcp 74, integration-magma-full-split 524,
-full-spawn 524, integration-split 187 and integration-magma-buffers 21 all green -
it never fired. The negative control is what makes that a measurement: dropping the
probe's transport test killed EVERY monolith `DirectVulkan.` entry at teardown
(8/8 aborted, after the case itself passed) while the split entries stayed green.
integration-magma-full-tcp's one red under the probe is the documented StreamLink x
Magma exposure texel (CONTRACT-P7 §2.5's named tcp entry,
IterationRPProgram203Scenario), not a probe death - it fails, it does not abort.
THE COUNT AGAINST P5e RULING 12. That ruling named FOUR Magma sites
(VulkanRenderer.cpp x3 and ResourceTracker.h x1). Two had already gone before P7
began - `git log -S` puts the ResourceTracker.h site's removal in P5f fv (
|
||
|
|
51fa513d73 |
[MG_Backend, MG_IntegrationTest, MG_Test] (P7): a multisample depth or stencil buffer resolves on a device with no resolve extension, and the sample-count shapes with no arm decline
CONTRACT-P7 §3.2's `multisample-blit-aspect@P7`, the last `@P7` refusal in the tree.
Two halves again, and neither is the other's fallback.
THE HALF WITH AN ARM. vkCmdResolveImage is COLOUR ONLY. The one way Vulkan
resolves a depth or stencil aspect without a shader is a render pass with a
depth/stencil resolve attachment, which needs VK_KHR_create_renderpass2 and
VK_KHR_depth_stencil_resolve - and a device with neither ended the session at
`Magma:multisample-depth-resolve-capability` on the first
glBlitFramebuffer(GL_DEPTH_BUFFER_BIT) out of a multisampled read framebuffer,
which is what every multisample depth resolve in GL is. §3.2 names the second arm
and this is it: WireMultisampleResolve.inc, three baked modules (one vertex stage,
one fragment stage per aspect), fetching SAMPLE ZERO - the same sample
VK_RESOLVE_MODE_SAMPLE_ZERO_BIT picks, so the two arms answer identically and the
lane can assert that.
The pass carries nothing: the scratch has the source's extent and coordinate
space, so the fragment stage indexes by gl_FragCoord and the rectangle is the
scissor. No push constants, no varyings, no frontend object. The stencil half
needs VK_EXT_shader_stencil_export to exist at all (a fragment shader cannot write
the stencil aspect otherwise); where it is absent the arm declines stencil rather
than leaving the aspect undefined, and the module is not even created, because
handing vkCreateShaderModule SPIR-V with an unsupported capability is undefined
rather than an error.
The two capability tests are deliberately not the same test: the subpass resolves
every aspect the FORMAT has and needs a mode for each, while the pass writes only
the aspect this blit asked for - which is safe because the copy-out reads exactly
that aspect.
THE HALF WITH NO ARM. Sample counts that differ with the DESTINATION multisampled:
one sample fanned out to N, or N reduced to M. vkCmdResolveImage only ever writes
a single-sample destination, vkCmdBlitImage refuses a multisampled side, and
vkCmdCopyImage needs the counts equal. Rule I (a): name both counts, record
INVALID_OPERATION, leave the destination alone.
Lanes. DepthStencilReadbackMatrixScenario.AResolvedMultisampleDepth… is PROMOTED
from the informational tier into the three gated Magma arms - it already owns the
observable (clear a 4x DEPTH24_STENCIL8, resolve with glBlitFramebuffer, read both
aspects back, check that a depth-only resolve left the destination's stencil alone
and vice versa), it just had no gated entry. Two entries over it per arm: plain,
and under MGITEST_MAGMA_FORCE_SHADER_DEPTH_RESOLVE, which is the only way the new
arm is reachable here at all - lavapipe carries BOTH resolve extensions, so the
extension arm answers every resolve on this host. The new F1 case is the decline.
It does NOT skip on this host: the shader arm resolves depth and stencil, because
lavapipe also carries VK_EXT_shader_stencil_export.
Three rows join MOBILEGL_BAKED_INTERNAL_SHADERS (8 -> 11).
Red once (R-16, executed and reverted), three times:
* restore MagmaWireFatal("multisample-depth-resolve-capability") for the
non-extension arm -> DirectVulkan.Split.MsResolve1.… dies by that name while
MsResolve0 stays green, which is also the proof that the knob routes;
* restore MagmaWireFatal("multisample-blit-aspect@P7") ->
DirectVulkan.Split.MsBlit.…FromASingleSampleSourceDeclines… dies by that name;
* fetch sample 1 instead of 0 in WireMultisampleStencilResolve.frag (no rebuild -
the gate reads the source at run time) -> WireMultisampleStencilResolveFragment
reds at word 158, naming the symbol, the header and the regeneration command.
Reverted, 15/15 green on the three arms and 11/11 on the freshness gate.
`grep -rn "@P7" MobileGL/ --include=*.cpp --include=*.inc --include=*.h` now
returns COMMENTS ONLY - 19 lines, every one of them prose about a retirement or a
red-once. No refusal in the tree carries the mark any more.
Gates: integration-magma-split 93 (+3), -spawn 72 (+3), -tcp 74 (+3),
integration-magma-full-split 524 (+1), ctest -L unit 2423 (+3), all green;
bake_internal_shaders.py --check reports all five headers fresh; fatal_census 79
abort sites / 44 family words / 0 unmarked; link_ratchet --assert-monotone
unchanged at 186; G1 pull .text 0xa52203, nm --defined-only identical to
p7-before/pull-syms.txt (30570 lines, 0 diffs) - every line lives under
`#if MOBILEGL_BUILD_DISAGGREGATED` or in the test tree.
Device debt: the shader arm is the arm the PHONE takes wherever
VK_KHR_depth_stencil_resolve is missing, and this host can only reach it through
the knob. Its geometry needs one look on the Redmi.
|
||
|
|
452d33b578 |
[MG_Backend, MG_IntegrationTest] (P7): a scaled multisample colour blit resolves into a scratch and blits that, and a multisample destination declines
CONTRACT-P7 §3.2's `multisample-blit-shape@P7`. The name covered two halves that
are not the same question.
The half with an arm: a multisample colour blit whose destination rectangle is a
different size from its source. vkCmdResolveImage carries one extent and one
offset per side, so it can express neither the scale nor the flip - which is why
this arm already resolved into a single-sample scratch at raw offsets and blitted
THAT for the flip. The two `width != |dx1-dx0|` clauses were the only thing
keeping the scale out of the same route, and the monolith arm reaches it the same
way (VulkanRenderer.cpp's `regionWasTransformed` branch). They are gone; the
scratch is still sized by the SOURCE rect, so only the second leg sees the scale.
The filter only started mattering with that scale - at 1:1 LINEAR and NEAREST read
the same texel - so the second leg now asks whether the format can be filtered
before it passes VK_FILTER_LINEAR, which is undefined behaviour on a format whose
optimalTilingFeatures lack SAMPLED_IMAGE_FILTER_LINEAR. GL ignores filter for a
multisample resolve anyway, so the fallback is NEAREST and not a refusal.
The half with no arm: a MULTISAMPLE DESTINATION. vkCmdCopyImage is the only
command that writes one, it takes a single extent, and GL 4.6 core 18.3.1 makes a
blit whose rectangles differ in size INVALID_OPERATION when either framebuffer is
multisampled. There is no picture to get right, so rule I (a): MGLOG_E_ONCE with
both rectangles and both sample counts, the error GL names, the destination left
alone, the session kept.
Two entries on each of the three Magma arms (`MsBlit.`), split-only because the
monolith arm drops the scale (its vkCmdResolveImage lands 1:1 at the destination
offset), so an equality between the arms would assert that the wire arm is as
wrong. The scale and the flip share one case over one source: dropping the scale
lands an 8x4 band in a 4x2 attachment, dropping the flip returns the right two
colours in the wrong order, and the second blit's expectation is the first's with
the rows swapped.
Red once (R-16, executed and reverted), twice:
* restore the two size-equality clauses ->
DirectVulkan.Split.MsBlit.…ScalesAndFlipsThroughTheResolveScratch dies as
Fatal{UnmigratedVerb, "Magma:multisample-resolve-region"};
* restore MagmaWireFatal("multisample-blit-shape@P7") ->
DirectVulkan.Split.MsBlit.…DeclinesAndKeepsTheSession dies by that name.
Reverted, 6/6 green on the three arms.
Measured on the way in: the decline's GL error reaches the client with a LATER
reply, not with the blit call, so the case's glFinish is load-bearing - without it
the INVALID_OPERATION surfaced attached to the 1:1 resolve twenty lines down.
Gates: integration-magma-split 90 (+2), -spawn 69 (+2), -tcp 71 (+2),
integration-magma-full-split 523 (+2), ctest -L unit 2420, all green;
fatal_census 79 abort sites / 44 family words / 0 unmarked; link_ratchet
--assert-monotone unchanged at 186; G1 pull .text 0xa52203 with an nm
--defined-only list identical to p7-before/pull-syms.txt (30570 lines, 0 diffs) -
every line of this commit is inside WireFramebuffer.inc's
`#if MOBILEGL_BUILD_DISAGGREGATED` or in the test tree.
|
||
|
|
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.
|
||
|
|
394df89237 |
[scripts, ci] (P7): the memo purity gate asserts its own anchors and allow-list, and stops calling a policy exemption an absence
Rework round 3 of 4 after the D2 review (fable, ID-P7-8). Three defects, each of a kind the gate could not have reported about itself. (a) TWO STRUCTURAL SELF-CHECKS. family_spans() returned whatever matched and nothing ever asserted that each of the five FAMILIES anchors found its declaration, so a renamed struct would drop a whole family while every rule kept passing. And an allow-list entry no rule can consult documents a carve-out that is not happening - a claim about the tree that nothing verifies. main() now fails on either, naming the anchor or the entry and saying which of the two repairs is wanted. Both are exercised: renaming an anchor and adding a dead entry each red the gate. (b) THE DOCSTRING AND THE test.yml COMMENT WERE WRONG about what the guards mean. MOBILEGL_PIPE_LEGACY_MEMOS defaults ON (CMakeLists.txt:43) and no job here clears it, so CI split builds DO compile those arms - TwinLookupMemo (DirectGLES.cpp:114) sits inside one and is compiled into every lane this repo runs. The rule is "the legacy arm MAY KEEP pointer keys", a policy exemption; only !MOBILEGL_BUILD_DISAGGREGATED is code a split build genuinely never sees. Both now say so. (c) FRONTEND HAD NO LEADING WORD BOUNDARY, so BackendSamplerObject* matched SamplerObject and the gate reported the memos' backend VALUES as frontend keys - which the first version answered with two allow-list entries, i.e. it documented a false positive instead of fixing it. (?<!\w) added, with controls both ways: the backend twins must stay green and the qualified MG_State::GLState::ProgramObject* must still match. The allow list therefore goes from six entries to ONE. Four were dead on arrival - two false positives now fixed at the source, one naming a typedef with no frontend type in it, and const void* program / Uint64 programLifetimeId, which no rule could ever consult because void is not in FRONTEND and neither line calls GetLifetimeId(). The ResolvedTextureBindingMemo monolith key those last two described is still deliberate and still there; it simply never needed an exemption, and saying it did was misleading. What remains is the twin registry's UnorderedMap<StateObject*, Entry>, which really is suppressed and really is the pull build's only arm. |
||
|
|
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.
|
||
|
|
b3334cf336 |
[MG_Test] (P7): SubsystemDepsTest skips its two resolver cases in the pull build, and says what keeps the sweep alive
Rework round 1 of 4 after the D2 review (fable, ID-P7-8). Mirrors the integrator's 6da19f71 so that later commits from this worktree apply cleanly, and takes the two review notes attached to the same file. THE GUARD. The four Resolve<Family>SubsystemArm() declarations live inside did not compile in the pull build (ID-P7-11). Both now GTEST_SKIP under !MOBILEGL_PIPE_PUSH the way the client-side case already did. Verified by re-running the file's own compile command with -DMOBILEGL_PIPE_PUSH=0 and MOBILEGL_BUILD_DISAGGREGATED undefined: -fsyntax-only rc 0. WHAT KEPT THE MASK SWEEP ALIVE, now asserted instead of assumed: every resolver ends in ClassifyPipeSubsystemArm, and a NoArm verdict calls StopOnArmlessPipeSubsystem (Managers.cpp:2928-2938), which ABORTS. The sweep deliberately drives masks that refuse a family's handle arm, so it survives only because MOBILEGL_PIPE_LEGACY_MEMOS defaults ON (Config.h:399) and leaves a legacy arm standing. ASSERT_TRUE on that, with the reason - the day the default flips, this file would have aborted with no explanation. THE FRAMEBUFFER RESOLVER IS RECORDED, NOT ASSERTED, and the asymmetry is the finding: ResolveFramebufferSubsystemArm folds D-C3's wire-caps refusal in beside the dependency rule (Managers.cpp:4323-4325), and that half asks the server's published caps, which a bare unit binary with no session cannot answer in the affirmative. So a false says nothing about D-K2 and asserting true would pin whatever the binary happens to have linked. The sweep still covers it in the direction that matters. Also adds the 0x5ff refusal lane beside 0x7ff. |
||
|
|
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. |
||
|
|
1c36987e84 |
[MG_Backend, MG_Pipe, MG_Impl] (P7): D-K2's dependency rule has one statement, and every reader reads it
P3b/P4b wave 2-D package D3, slice 2 - D2's two loose ends, now in-partition because D1 has
landed on feat/disaggregated and MG_Backend/DirectGLES/ is free.
THE STALE PROSE, in the header of the file that implements the refusal.
MG_Backend/DirectGLES/Managers.h's resolver block said "THREE OF THEM CARRY A DEPENDENCY"
(there are six rows; bits 8 and 13 were the two it did not know about) and listed
"10 without 11" among the mirror pairs that are FINE - when 10-without-11 is precisely D-K2's
fourth row (ID-14/ID-15), refused twenty lines below by ResolveTextureResourceSubsystemArm and
withheld by the client. MGPipe.h lost the identical pair of defects with R-5; this copy was
left behind because the file belonged to a package in flight. It does not restate the rows
again: the file that states them is the file the code reads.
BOTH READERS NOW READ MG_Pipe/SubsystemDeps.def. More than the two one-line changes D2
estimated, because the rule had six statements and the estimate counted two:
PipeFill.cpp P4aFamilyDependencyBits its own four-row kMGPipeP4aFamilyDependencies[]
-> MGPipeSubsystemRequires(); table deleted
PipeFill.cpp P5eFamilyIsLive hand-coded (pushMask & bit 7) == 0
-> MGPipeSubsystemDependenciesAreSet(bit 13, mask)
Managers.cpp PipeSubsystemDependencyMissing took a literal dependency BIT per call site
-> takes the FAMILY and asks the table
Managers.cpp the four resolvers five calls, five hand-written sentences
-> four calls, one per family, naming only the family
Managers.cpp ResolveVertexInputSubsystemArm hand-rolled bitSet && !resourcesBitSet
-> the table
DirectGLES.cpp ResolveBufferBindingSubsystemArm the same hand-rolled pair -> the table
MGPipe.h gains one accessor, MGPipeSubsystemDependencyWhy(), so a reader that refuses prints
the ROW'S OWN sentence instead of a copy of it. The .def has carried a Why column since R-5
and nothing read it; its "WHO READS IT" section is rewritten because "THEY ARE NOT SWITCHED
OVER IN THIS COMMIT" is no longer true of anything.
THE CLIENT'S ARGUMENT IS MASKED TO kMGPipeP4aFamilySubsystems, and that mask is the whole
difference between a rewrite and a behaviour change. The .def carries SIX rows; the table
deleted from PipeFill.cpp carried FOUR, and that predicate is wants()'s fourth conjunct for
P4a's families only. Reading all six unmasked makes the CLIENT withhold bit 8's vertex-input
emissions at a mask with bit 7 clear, which is P3a's own rule and not this gate's - caught,
not reasoned about, by TextureEmitTest's
EveryDKTwoDependencyRowGatesItsOwnFamilyAndTheMirrorPairsStayLive going red on exactly that
assertion ("bit 8 alone, with bit 7 clear: P3a's own rule, which this table must not touch").
Bit 13 is not narrowed there either; it asks the table for itself in P5eFamilyIsLive, because
bit 13's conjunct is bit 13's own. The ROWS come from one place; WHO each gate speaks for is
still each gate's.
ONE BEHAVIOUR IMPROVEMENT FALLS OUT rather than being designed in: the texture family is the
only row with two required bits and used to be two guarded calls, so a mask missing bit 11 but
carrying bit 7 was reported with the bit-7 sentence. One call now computes required & ~mask
from the row, so the refusal names whichever bit is actually missing.
WHAT THIS COSTS, said out loud. SubsystemDepsTest used to compare two COPIES of the rule; it
now compares two READERS of one table and cannot catch a typo in a row. It is kept and the
.def says why: both sides still fold in reasons of their own - the server's own family bit and
D-C3's wire-caps refusal, the client's consumer and wired conjuncts - and those can still
disagree at a mask. The compile-time half of D2's red-once (drop the fourth row from the .def
-> MGPipe.h:225/:230 fail) is untouched and is now the load-bearing one.
SubsystemDepsTest 5/5, TextureEmit's D-K2 case green, unit lane 2418/2418.
G1: .text 0xa52203, nm --defined-only 30570 symbols, 0 added / 0 removed, -D 0/0. Every line
is inside #if MOBILEGL_PIPE_PUSH or #if MOBILEGL_BUILD_DISAGGREGATED, and MGPipe.h's new
accessor is inline constexpr and unreferenced in the pull build.
|
||
|
|
1e1f4bad27 |
[MG_Backend, MG_IntegrationTest, MG_Remote, docs] (P7): an owner-side upload shows through a sampled texture view, because the split clean gate resolves to the storage record
P3b/P4b wave 2-D package D3, slice 1. The defect package D2 found with its new
TextureViewAliasScenario gate and excluded by name because the fix is outside D2's footprint.
THE HOLE. Once a glTextureView had been SAMPLED, a later glTexSubImage through the OWNER's
own name never became visible through the view, on inproc, spawn and tcp, green under
monolith. The client half was never at fault: the case's own emit-counter assertions pass on
the red run, so the records were emitted and the applier accepted them. A view owns no texels
and the client keeps ONE emission cursor per storage - an upload through a view's name is
remapped onto its storage owner before it is emitted (MG_Impl/Pipe/TextureEmit.h) - so every
resource_subdata for either name is keyed on the OWNER and ApplyTextureUpload moves the
OWNER's Serial and PendingUploads alone. BackendTextureObject::IsDrawSyncCleanByRecord read
the VIEW's Serial and PendingUploads, which nothing an upload does can move, so the gate
answered CLEAN for every owner write after the view's first sample and the sync that would
have drained the owner's pending uploads never ran.
THE FIX, AND WHY THIS ONE. The gate's two STORAGE clauses are asked of the STORAGE record -
this record for a texture that owns its texels, the record Desc.ViewOf names for a view -
through the new PipeTextureStorageRecordForRecord. The three PARAMETER clauses stay the
view's own record's, because a view has its own texture parameters and its own built-in
sampler, which is the whole reason the second name exists. SyncTextureViewToBackendByRecord
stamps that same storage serial at both its arms, so the stamp and the gate name one
quantity. This is the split equivalent of the monolith gate's GetContentVersion(), which
TextureObjectView forwards to its owner (TextureObjectView.cpp:100-102).
THE ALTERNATIVE WAS REJECTED. A reverse index owner->views, so an owner-side resource_subdata
bumps every viewing record's Serial, is the other shape D2 named. It is wrong here on three
counts: it puts new mutable state in MG_Pipe's applier, which both backends read, to answer a
question one backend's gate asks; it makes ApplyTextureUpload write a record other than the
one the call names, which breaks the applier's own serial discipline; and its index has to be
pruned on view death or an owner upload bumps a recycled slot's Serial - a silent
cross-texture hazard with no test that would catch it. The resolve direction needs no state,
no pruning, and is checked at every use: PipeTextureRecordForHandle tests Live and Gen.
DELETION ORDERING, WHICH DECIDED IT. A view holds a STRONG SharedPtr to its storage owner
(TextureObjectView::m_storageOwner) and the client emits resource_destroy from the DESTRUCTOR,
not from glDeleteTextures - "the last SharedPtr to this object dropping, not the glDelete*
that only marks the name and leaves a still-bound object very much alive"
(TextureObject.cpp:63). So an owner's record outlives every record that views it whatever
order the application deletes the two names in, and the forward edge cannot dangle. The walk
checks anyway, and a null answer is NOT clean - the direction that re-syncs.
VIEWS OF VIEWS. ONE hop always reaches storage: glTextureView composes a view-of-a-view onto
the ROOT at creation (the spec's additive min-level rule), the client's emitter says so and
TextureObjectView's invariant is that its owner is never a view. The walk is transitive
anyway and BOUNDED at four hops, because this side may not depend on a client invariant to
terminate; a cyclic chain the wire could forge ends as a null answer, which is "not clean",
not a refusal. No new abort site and no new Fatal (rule I): fatal_census abort_sites stays 79.
RED-ONCE (rule J), the case D2 excluded, with the exclusion lifted and nothing else changed,
at D2's head a7c06f4f:
ctest -R 'DirectGLES\.(Split|Spawn|Tcp)\.TextureViewAliasScenario' -j 3
-> 79% tests passed, 3 tests failed out of 14
4042 - DirectGLES.Split.TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary (Failed)
4046 - DirectGLES.Spawn.TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary (Failed)
4050 - DirectGLES.Tcp.TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary (Failed)
the first owner write, through the view: 11408 of 11408 pixels disagree;
first at (2, 2) is rgba(50,160,30,255), expected rgba(0,0,255,255) +/- 2
the second owner write, through the view: 11408 of 11408 pixels disagree;
first at (2, 2) is rgba(50,160,30,255), expected rgba(255,255,0,255) +/- 2
Only the PIXEL channel is red; the drain's emit counters pass, which is what places the
defect on the server. The monolith control DirectGLES.TextureViewAliasScenario.* is 4/4 in
the same run. With the fix: 14/14, 100%.
CONTRACT-P5E.md 5.2's clean gate is amended to state the rule the code now implements, and
says out loud that the rec->Serial form it carried through P5e was wrong rather than
restating it. The CMake exclusion is lifted; the scenario registers all four cases on all
three arms again (G14: the name never left any lane).
G1: .text 0xa52203, nm --defined-only 30570 symbols, 0 added / 0 removed, -D 0/0. Every line
touched in Managers.cpp is inside #if MOBILEGL_PIPE_PUSH (the gate and the resolver) or
#if MOBILEGL_BUILD_DISAGGREGATED (the view sync), and Managers.h's declaration is inside the
PIPE_PUSH block, so the pull build's preprocessed text is unchanged.
|
||
|
|
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. |