mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
394df89237fe6e2e4c0cdf45355c46b95e2cc1fd
137
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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.
|
||
|
|
07079c3c0e |
[docs] (P7): X1 verified on the phone, F1 verified but acquitted, OpenRA is a cold-cache timing race (ID-P7-18/19)
X1: 26.3 and create-instancing pass under inproc on both backends, zero declined faults, tracker line confirms tag=0xb4. F1: the R-8 line is gone in every run, yet OpenRA reads 0.976494 on a cold driver cache (three pm-clear runs identical), 1.0 warm, 1.0 cold monolith, and FRAMESINFLIGHT=1 flips it half the time - a GPU-timing race on the wire readback/present path, handed to package B as slice 0. |
||
|
|
3179c4972d |
[docs] (P7): F1's emitter, its three red-once arms and the one thing lavapipe cannot prove are on record
`notes/p7/f1-caps-placeholder.md`, per CONTRACT-P7 §9's "one red-once per package recorded in notes/p7/". It carries the call chain from `MobileGL::Initialize` down to `CapsMirror::ServerConsumes(0x400)`, the three corrections to the brief's reading (it is not a race; P6.5's fix already covers inproc; a bounded wait alone deadlocks), the three red-once arms, and the two things still owed to the integrator. The arm worth reading twice is A. On lavapipe the BASE build carries the R-8 line on every transport, with and without the delay knob, and OpenRA still scores ssim 1.000000 / mismatch 0. So the eleven dropped resource_creates cost nothing visible there, F1's red-once is the LOG LINE and the new Fatal rather than an SSIM, and this package must not be read as having explained the device's 0.976494 / 0.988998. That still needs the phone. |
||
|
|
206abdfa44 |
[docs] (P7): the gate-5 CTS base reading is on record - monolith x DirectVulkan x KHR-GL46 on the Redmi, five complete blocks
shader-image 63/56/6, ssbo 124/0/0, dsa 368/0/2, texture 815/221/18, packed-pixels 4732/0/0 (Pass/NotSupported/Fail); every block complete (no unrun, hung or crashed); library p7w1-d260f110, glcts from VK-GL-CTS 0b04c470e with the FBO colour-texture patch, API probe confirms GL 4.6. The AFTER reading compares inproc against this at 0.5 pp per block with new crashes as a separate hard red. |
||
|
|
5c4235d7ba | [docs, MG_Remote] (P7): ID-P7-12..17 - gate 3 per case against monolith, photon excluded, wave-0 naming ratified, X1 on the critical path, gate 5 is five blocks at GL46, E2/E0b folded into the F1 re-test | ||
|
|
4cbf78c269 |
[MG_Remote, MG_Test, docs] (P7): the persistent-map tracker answers faults in the kernel's address space, and a map hands back a range it can write
Wave 2 package X1. Three CI split-subset traces died on the Redmi under inproc and
passed under monolith on the same device and APK, on the client thread, in the
replayer's memcpy into a pointer MobileGL had handed it: SIGSEGV / SEGV_ACCERR on the
destination's FIRST byte, destination page-aligned.
The tombstone names the same address twice, in two different address spaces:
x0 b4000073ac9a5000 ... x2 0000000001800000
signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x00000073ac9a5000
x0 is the pointer the application writes through, carrying bionic's 0xb4 heap tag
(the same tombstone reports tagged_addr_ctrl PR_TAGGED_ADDR_ENABLE). fault addr is
si_addr, which the kernel reports UNTAGGED.
TrackWriteMap took shadowBase from MappedData() - a tagged heap pointer - kept the tag
through the page masking, mprotect()ed the mapped range PROT_READ and published the
TAGGED base/end into the slot. The handler compared si_addr against it:
if (base <= kSlotSettingUp || address < base) continue;
0x73ac9a5000 < 0xb4000073ac9a5000 is true for every fault, forever. Ownership test (2)
never matched, and every fault on a page the tracker had itself protected was chained
to debuggerd. mprotect was fine throughout - do_mprotect_pkey untags its own argument -
so the right pages really were read-only; only the recognition was broken. x86-64 has
no top-byte tag, which is why every host lane has always been green.
The fix is two rows and one ordering:
* UntagAddress(), applied to the shadow base at registration, to the shadow base in
PushBlocksFor (so trackedBase - shadowBase stays in one space) and to si_addr in
the handler. Written WITHOUT an #if defined(__aarch64__): masking bits 56-63 is
the identity for every valid userspace address off arm64, and an arch guard would
have put the only gate that can catch this on the one platform CI never runs.
Dereferences keep the tagged pointer they came from.
* SlotOwnsFault(), the ownership row the handler and the unit case both read.
* REGISTRATION ARMS NOTHING. AcquireMemoryRange registered and only then returned
Bytes() + range.start, so glMapBufferRange handed the application a PROT_READ
pointer whose writability depended on a handler reaching the writing thread - and
the application is free to write a mapped arena from a worker that blocks every
signal. Nothing is lost by waiting: every page bit is already set at registration,
so the first push ships the whole interior regardless, and it is that push which
arms the pages. 6144 pointless faults per 24 MB arena go with it, and the
invariant the file rests on (bit set <=> page writable) is now true from
registration onward instead of only from the first push.
Diagnostic, independent of the fix: one MGLOG_I_ONCE at the first registration naming
the mapping the way the device run needed and did not have (allocator, pointer, ITS
TAG, extent, range, published span, pages, alignment arm), and a decline record in the
handler - relaxed atomic stores plus one async-signal-safe write(2) - readable through
DeclinedFaults(). A non-zero decline count whose si_addr lies inside nearest-tracked
once the top byte is masked off is this defect's signature and nothing else's.
Not the mechanism, and checked: libsigchain (the tombstone's "F libc : Fatal signal 11"
is bionic's own handler, so the chain ran), the 16 KiB-page refusal (the device is a
4 KiB kernel and the protection demonstrably took), RefaultOfANonWriteAccess (matched
is false long before it is consulted), and the P5d shadow allocator (correct - it is
what makes the destination page-aligned in the tombstone).
Red-once, two and orthogonal (MG_Test/Wire/PersistentMapFaultTest.cpp, 6 cases):
dropping the two UntagAddress calls in SlotOwnsFault reds only
AKernelsUntaggedFaultAddressIsOwnedByATaggedShadowsSlot, which runs the device's own
two addresses through the real predicate on an x86-64 host; restoring the mprotect at
the end of TrackWriteMap reds only AMapHandsBackARangeWritableWithoutTheFaultHandler
(the child dies by SIGSEGV) and RegistrationLeavesEveryPageOfTheRangeReadWrite.
AnArmedPageIsStillAnsweredByTheHandlerAndMadeWritable is the anti-vacuity case.
Gates: ctest -L unit 2408/2408; integration-split 186/186; integration-spawn 102/102;
integration-magma-split 73/73 (PersistentCoherentMapScenario green in the Split, Spawn,
PersistentMapArm and SmallRing variants); fatal_census.py 0 unmarked. G1 holds - pull
.text 10822147 = 0xa52203 unchanged, symbol report 0 added / 0 removed / 0 resized /
0 renamed, nm --defined-only identical at 30570 and nm -D at 11933. Every line is under
MOBILEGL_BUILD_DISAGGREGATED (CMakeLists.txt:592-659), so the pull build sees none of it.
Evidence and the device experiment: docs/Disaggregated/notes/p7/x1-persistent-map-segv.md
|
||
|
|
4f2d4c4e83 |
[MG_Remote, docs] (P7): CONTRACT-P7 carries wave 0's numbers, and device window #1a is on record
CONTRACT-P7: the lane counts (magma-split 73 / spawn 52 / tcp 54; full-suite 510 x 3 with one StreamLink x Magma red), the census baseline (79 abort sites over 20 files, 43 family words, 0 unmarked), the ratchet baseline (186 = p7-magma 101 / espryt 14 / both 60 / core 11; espryt's clearable count is 9) and the @P7 count (15 sites / 4 files) replace the 待填 markers. Device window #1a (Redmi 2f7cbe2e, APK p7w1-d260f110, reboot-clean): E0a monolith 39/39 deterministic, three below threshold on monolith itself (create-indirect, 1.21.11 main-menu, iris-photon - Adreno refuses its pipelines with VK_ERROR_UNKNOWN); E0a inproc: OpenRA 0.976494 (run-ahead) / E1 lockstep 0.988998 - both historical numbers were OpenRA, and its client log shows a TextureResources verb answered from caps generation 0 (package F1); 26.3 / create-* SIGSEGV in the replayer's memcpy on BOTH backends and with the persistent-map push off (package X1); iterationt x2 die by name on Magma:mipmap-shader-format-or-shape (package B); bsl-esc-menu hits scudo OOM under inproc. Gate 3's denominator is 36; run-ahead and present credit are off the suspect list. |
||
|
|
8bb6309a1c |
[scripts, CI, docs] (P7): the 184-symbol link-closure ratchet lands as a gate
CONTRACT-P6.md 12.1 has carried "a ratchet recomputes the 184 in CI and asserts it only
ever falls" as an obligation since P6. The roadmap handed it to P6.5; the P6.5 line never
received it, and scripts/ci/ has never had anything that computes this number. ID-P7-7
gives it to P7 wave 0. This is it.
scripts/link_ratchet.py mechanises a6's hand experiment: partition the per-object output
of the single MobileGL target into SERVER / FRONTEND / SHARED, then compute
(SERVER undefined) - (SERVER defined) - (SHARED defined) intersect (FRONTEND defined),
which is the undefined-symbol list a real link of a server-only image would print. The
partition is an explicit table with a rationale per row, because it IS the module boundary
the build system does not have (SOURCE_FILES is one flat list; boundaries are P13's work).
An object matching no row is a hard failure, as is a build whose CMakeCache lacks the three
disaggregated options, or an empty role set - every way this gate could go green without
having measured anything is an explicit exit 2.
Measured on
|
||
|
|
1de8f02ebe |
[MG_IntegrationTest, docs] (P7): the whole integration binary runs under Magma split, and it is one red
Tier 2 replays the split-arm registrations - ~100 scenarios, the set Espryt's disaggregation work
has carried since P5 - and it measured green on Magma across all three transports. That is a real
result but it is NOT exit gate 1. The gate says the INTEGRATION BINARY is green under Magma split,
and the set that names is the one the monolith DirectVulkan. arm runs: the whole suite minus
MGL_ITEST_MONOLITH_MECHANISM_FILTER. 510 cases against tier 2's 102, and the readback matrices,
the same-texture copy shapes and the odd-offset uniform ranges are in it and nowhere else in the
disaggregated registry.
integration-magma-full-{split,spawn,tcp}, in no gating step, private log per entry through a list
of its own. THE RESULT: 453/0/57 on inproc, 453/0/57 on spawn, 454/1/57 on tcp - and not one of
the 1530 entries reaches any of the tree's fifteen @P7 named refusals. The single red is
IterationRPProgram203Scenario.FixedCompleteInputProducesFixedCompleteGoldenOutput on the TCP arm:
one texel of 262656, texel 0, which is the case's own exposure value
(IterationRPProgram203Scenario.cpp:371, actual half bits (0x357a, 0x4b65) against golden
(0x3a74, 0x4821)). Deterministic 5/5 on tcp; 5/5 GREEN on inproc, on spawn and on the monolith
control. spawn and tcp differ only in MOBILEGL_IPC_DATA, so the suspicion is the STREAM data path
and not "two processes"; the GLES tcp lane is green in the same run, so it is not the fixture.
Not fixed here - it is what the census is for.
TWO ENVIRONMENT DIFFERENCES FROM THE GATED TIERS, both measured rather than assumed:
* NO MGITEST_SPLIT_LANE=1. That marker arms ScenarioFixture's destructor assertion - a case in
an armed split lane must have moved the client encoder's record ordinal - which is right for
a curated lane and wrong for a whole-binary census. With it set, the first run produced 13
reds, all `EmitSeq was 20 at SetUp and is 20 now`: AdvertisedLimitsScenario (8),
SpirvShaderBinaryScenario (4) and ProgramPipelineScenario.AReservedName - a limits query is
answered from the client's CapsMirror and a shader-binary error surface never reaches a verb.
Those 13 would red on a DirectGLES census too. What it gives up is stated in the comment: this
tier does not prove its records crossed, and the tcp topology proof is inert in it.
* DualBlockScenario.* filtered out. Its green REQUIRES MOBILEGL_IPC_ROLE_SPLIT_STATE=1 and a
census must not set that knob; without it the case is red by name, which is its documented
negative-control behaviour. The monolith arm skips it for the same class of reason.
CtWireScenario gets MOBILEGL_TEST_SERVER_COUNTERS=1 in this tier - a switch that makes a counter
readable, not an oracle that asserts - because without it the spawn arm's ApplierReset case reds
on "server control-record telemetry missing", which says nothing about Magma. The strict-arming,
pmap and persistent-map-arm markers stay out.
The inproc-only exclusion in spawn_lane_parity.py is now PER TIER: in the gated tier the two
scenarios are registered on the inproc arm alone, in the census they are on every arm and simply
skip, so excluding them there would manufacture a spurious "extra".
Measured after the change: every pre-existing lane unchanged and green (magma-split 73,
spawn 52, tcp 54, all-* 102/102/104, GLES 186/102/104, buffers 19, runahead 14, caches 7), the
dual-block census 257 green / 2 skipped / 0 red with an empty ratchet, both new CI steps green,
parity RC=0 across all three tiers, G14 4155 -> 6095 with removed=0.
The note now carries the grouped red list, the 57 named skips by reason, the four pieces of
evidence that the tcp red is a defect and not plumbing, and six findings - the first being that
wave 2's clusters cannot be scheduled from this lane's red list, because on lavapipe there is
essentially none.
|
||
|
|
faca09d4ad |
[CI, docs] (P7): retrace-split exports the iterationrp knobs, and the two new Magma lanes gate
Three changes, one theme - making the two halves comparable, then gating what measured green.
retrace-split's "Retrace and validate under MOBILEGL_TRANSPORT" step exported no Magma knob at
all, while the monolith retrace job (:2183) exports three iterationRP repairs for
minecraft-1.21.4-fabric-iris-iterationrp-in-world x DirectVulkan. Those repairs are tri-state
quirks defaulting to device auto-detection and lavapipe is on no auto list, so that cell of the
split matrix (154 cells, and it contains that case x DirectVulkan x {inproc, spawn}) was being
compared against a monolith control running a different repair set. The same backend+case guard
is now copied verbatim into the split job.
Two gating steps for the lanes this wave added, and ONLY because they measured green on the
first run: integration-magma-{spawn,tcp} run like the existing magma-split step, and the
nineteen application-buffer cases get require_green BY NAME on both new arms - the tcp arm's
required list includes TcpServer.Start / Stop on purpose, because ctest lists the fixture
entries under the label and a lane whose supervisor failed to start has nothing to say about the
nineteen. The informational integration-magma-all-* tier is deliberately in no gating step.
Both steps were executed locally, not merely linted: spawn 19/19 and tcp 19/19 with
"require-green: N PASS, 0 skip, 0 failed, complete discovery". All 99 run: blocks in the
workflow pass bash -n.
The note records the first run in full: per-lane PASS/FAIL/SKIP, the two blocker groups the
first registration attempt produced (both lane plumbing, neither a server abort, wrong pixels
nor a hang), the arming evidence, both red-once demonstrations, the parity and G14 numbers,
what was NOT verified (no device, no retrace run, no G1) and six findings for the integrator -
the first of which is that P7 exit gate 1 has no host-side red list at all, which the wave 2
plan is built on top of.
|
||
|
|
c890f0ad1a |
[docs] (P7): the runbook spells the side-by-side package the way the flavour builds it, and its two shell traps are removed
The application id is top.mobilegl.plugin.p7w1.trace (flavour suffix after the id suffix), pgrep -f dex2oat matched the shell running the loop and never exited, and adb.exe cannot stat an MSYS /c/ path once MSYS_NO_PATHCONV is set. All three were hit on the first execution of section 3. |
||
|
|
1e2d076633 |
[docs] (P7): the wave-1 device window runbook
One reboot-clean session on Redmi 2f7cbe2e, copy-pasteable, with the shell each block runs in marked: pin and wake/Doze, APK install identity, P7-7 E0-E3, the P6.5 residual 39-case DirectGLES TCP golden matrix, the CTS $BASE for monolith x DirectVulkan, and the temp-environment restoration. Every step states in one line which result supports which hypothesis, and where the evidence is archived. Four things the runbook had to settle rather than restate: E1's proof. `running lockstep` is NOT the line that proves lockstep took effect: ClientSession.cpp:1873 guards it with `RunAhead != 0 && m_barrierArmed`, so it is the "the knob is set but the server lacks kCapRunAheadApply" warning and MOBILEGL_IPC_RUN_AHEAD=0 prints nothing at all from LatchRunAheadFromCaps. The proof is a line present and a line absent: `Config: IPC ... run-ahead=0 ...` (ConfigLoader.cpp:459) with zero `run-ahead ARMED` - the shape p5f/magma-runahead.md already recorded for the RA=0 arm. E1 will exit non-zero, by design. --transport inproc adds --require-inproc, whose gate demands run-ahead=1 in the Config: IPC line (trace-replay-ci.sh:601), and the proof runs AFTER result.json is pulled and checked. So the SSIM and the archive are usable and the arm is verified by hand against the table above; dropping the --env to make it green would delete the experiment. E0's budget. 39 cases x 2 transports x 3 repeats is 234 replays, roughly 3-4 h, which does not fit beside the TCP matrix and CTS in one window. It is split into a one-pass census that produces the divergent-case list and a three-repeat pass over that list plus two controls - the determinism question only has meaning for cases that already diverge. CTS is flagged, not promised. No caselist is in-tree, and the local VK-GL-CTS checkout has an empty external/openglcts/data with no mustpass file anywhere, so the five blocks cannot be cut in-tree; the exact pre-window procedure to fetch a tagged checkout and commit tools/cts/caselists/p7-*.txt is written out instead. And KHR-GL46 is unproven on the Android runner - every device reference result in the skill is KHR-GL33, the 4.5 numbers are desktop lavapipe, and the platform hands the package's version straight to eglCreateContext - so the runbook carries a five-minute pre-window probe and an explicit integrator decision: if 4.6 is unreachable the baseline is taken on KHR-GL33 and the AFTER reading must use the same version, because 0.5 pp between two different API versions means nothing. |
||
|
|
e19095269b |
[docs, MG_Remote] (P7): the roadmap rows say what the tree says, and CONTRACT-P7 is drafted
ROADMAP.md: the Ph / P3b-P4b / P7 rows carry the audit's verdicts and the factual
corrections it found - the @P7 refusal list is 15 sites in 4 files (the row printed 8),
the 29-commit DirectVulkan shape wave was never recorded, the device divergence is two
observations with no per-case attribution and no lockstep control, SetupDrawSnapshot /
content-addressed CSO are declined for this stage, "XFB scatter to client" is retired
in favour of the two real holes, readback/pack state splits into closed and open
halves, the NDK size pin is obsolete, PACK-PBO readback landed in
|
||
|
|
e8b2c4bd05 |
[docs, MG_Test] (P7): the Ph / P3b-P4b / P7 program plan, and the pull flavour builds its tests again
Plan for driving the roadmap to P7 close-out, from a per-item audit of the three
rows against
|
||
|
|
b95f5f1a20 | [docs] (P6.5): the first wave recorded as landed - review + fixes + representative re-validation, residual to CI | ||
|
|
fe28bdb5f3 | [MG_Remote, MG_Pipe, MG_Test, CI, tools, docs] (P6.5): the first wave completed and reviewed - archive v2, publication boundary, half-close, AcceptPair/role fixes, device re-validation | ||
|
|
35b3283bd9 |
[docs] (P6): the closeout - gates 7 and 8 are measured and P6 is closed
The device gate: Redmi 2f7cbe2e, reboot-clean, paired A/B in one thermal window (arms interleaved, best-of-3 then best-of-2), rd12 + DirectGLES. inproc vs spawn client CPU p50 7.880/7.889 vs 7.876/7.869 (-0.15%) and 7.987/7.989 vs 7.992/8.009 (+0.16%) - a tie inside the 0.2-4.2% within- arm spread, sign flipping with arm order, recorded and not gated. monolith is ~30% dearer. 22/22 runner exits clean, 44/44 PINNED, zero Fatal. The three mandatory numbers: socket doorbell vs condvar is a tie too - client 121.1 waits/frame identical across transports, server 10 866-11 963 (inproc) vs 10 415-10 416 (spawn) waits/frame, and the contract's 'converting that into ~4 blocking reads may be cheaper' did not pay off; the only column where the socket is worse is clipark/f (6.07 vs 5.60/5.75). SEG_STAGE bytes/frame: 130.8 KB median / 8.01 MB steady (OpenRA), 816.2 KB / 1.53 MB (rd12). Records/frame post-chunking: 211/206 and 7 497/7 504. maxrec stays 1808 - the 8 MiB chunk budget is never reached. Both framings reported throughout (the startup window runs ~7x the steady state) rather than the flattering one. MEASUREMENTS.md gains section 12 with the full protocol and tables; the 6.3 'no post-chunking distribution exists' gap now points there. CURRENT_STAGE_PROGRESS.md marks gates 7/8 met and records the closeout work plus two structural limits (spawn client windowed fields are zero by construction - OnPresent runs in the server process - though its run totals are valid stores; the stats dump is role-derived). ROADMAP marks P6 closed in the stage table, the header and the milestone. Evidence: notes/p6/gate7-device-ab.md, gate8-doorbell-device.md, gate8-wire-counters.md, git-worktree-fix.md. check_doc_citations: 82 citations in the three documents, 0 problems. |
||
|
|
4e7bf0fe65 |
[docs] (P6.5): the design for the first wave - all-TCP transport, WSL client to adb-device server
ROADMAP gains the section "the immediate next item": a design, not an implementation, for running the frontend on an x86_64 WSL process and the backend on the Redmi over TCP, with the integration lane and the CI retrace subset as the acceptance. The P6.5 row now points at it. What the design rests on, verified in the tree: the control plane already treats the rendezvous as a NAME with two ordered connections (SocketTransport), ClientSession::StartOverSocket already separates "how the socket came" from the handshake, MGPBlobRef addresses kSegStage only through one ResolveOrFatal, the reply header and the event heads are already wire-shaped, LogLine exists in CtrlMsg with no producer, goldens are shared between desktop and device, and the trace APK can already exec libMobileGLServer.so from nativeLibraryDir. What it does not rest on, stated rather than assumed: ILink is declared and unused beyond LinkProgress, ShmLink does not exist, both sessions hold SessionSegments directly - the lk seam body from the end-state review was never built and is this wave's first package (lk2). The manifest has no INTERNET permission. The mechanism: StreamLink as mirrored private segments replicated by messages - same sizes both ends, same offsets, so the codec, the watermark rules, the apply thread and the encoder do not change; RingControl becomes Progress frames sent on flush-on-idle (the recorded amendment to "appliedSeq is never lazy": late by at most one flush, never early); replies become seq-stamped messages; the event ring is mirrored with the P5e blocking rule intact. Cross-architecture makes the PipeFields.def layout digest mandatory (wf). Server logs on another machine make LogLine forwarding mandatory (lf) so the 34 server-log readers stay unchanged. A --serve supervisor forks one child per session on the device, started by a foreground Service in the trace APK. Packages lk2 -> wf -> ct -> sl -> lf -> sv -> t65, each with red-once; the gate list, the mandatory numbers (kWaitReply per frame x RTT, SEG_STAGE bytes/frame against the link, PRESENT_CREDIT 1..3), the two-sided configuration runbook and the risks (RTT per reply row, single large uploads, AP isolation) are in the section. check_doc_citations: 74 citations, 0 problems. |
||
|
|
872370ba9d |
[docs, MG_Remote] (P6->P6.5): the end state is TCP across machines, and the transport stack has two axes
The end state is re-ruled: client and server may sit on different machines, OSes and architectures, joined over TCP. The AVF pVM / AF_VSOCK end state the P6 end-state review assumed is withdrawn (retraction at its head, form D added to its table, the vsock probe and both external unknowns struck). The transport stack becomes two independently selectable axes, negotiated in the handshake and mixable: control plane (ITransport: fork / unix:<path> / tcp://host:port) and data plane (ILink: ShmLink / StreamLink). On one machine control over TCP with data over shared segments is a legal pair; ShmLink brings its own AF_UNIX aux rendezvous for SCM_RIGHTS, so ITransport narrows to control frames. `auto` is decided by a successful segment delivery, never by inspecting addresses, and a fall-back is named. Nothing above Transport/ may branch on the link kind. ARCHITECTURE.md gains 11.9 for this; CONTRACT-P6.md gains 10.3. ROADMAP: P6.5 and Ph get rows (both were referenced by the contract and absent from the stage table); the IPC track becomes P6 -> P6.5 -> Ph -> P12 -> P9 -> P10 -> P11(same-machine arm). Every later row is reconciled against the tree: items already landed are struck (client vertex arrays, client-minted query handles, credit default 1, SEG_EVENT overflow mechanism), the unreachable P7 gate (`nm | grep glslang` on a server image that IS libMobileGL.so) is replaced by the 184-symbol ratchet share, fixed-width wire form moves from P7 to P6.5, P12 is cut down per the review (server-owned display, no Surface passing), and the debt table's stale claims are corrected: class C is down to two slots, not 15 and not 511; PACK-PBO and the 2 MiB reply cap are P9's, not P6's. Verified against the tree and recorded honestly: the contract's three hs rows (LinkTerms, Refuse, the fingerprint split) never landed - protocol.fbs has neither - so CURRENT_STAGE_PROGRESS.md's claim that S6 ran red is withdrawn and the rows move to P6.5; the 184-symbol ratchet gate does not exist in CI; the server process never initialises PipeStats, so gate 8's `srv=0 srvpark=0` is a structural zero (fix in progress in the tree, not in this commit). check_doc_citations.py: 159 citations, one pre-existing basename ambiguity. |
||
|
|
75dd0cb1ca |
[docs] (P6): the roadmap and the current-stage doc record P6 as landed-not-closed
All ten P6 packages (a6/c6/lk/so/sm/cp/hs/dl/st/t6) are on feat/disaggregated
through
|