mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
efe58eac146b13dedf4d38476db47d3926bbbaa7
3179
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
efe58eac14 |
[MG_Backend, MG_Pipe, MG_Impl, Docs] (Disaggregated): P5e ra2 ID-136 - retire the indirect-binding pull instead of escalating for it, and withdraw escalation (iii)
- TOUCHING mv's FILE UNDER ID-136, deliberately and by ruling: MG_Backend/DirectGLES/
MultiDraw.cpp is mv's per ID-113, and the integrator overrode that boundary for this one
change because the two halves must not exist apart on any head. The boundary has not drifted.
- BoundDrawIndirectBufferId (MultiDraw.cpp) gets the handle arm its neighbour
ResolveBoundIndexBuffer already had, in mv's own idiom rather than a second one beside it:
the arm is `MG_Config::Transport != Monolith`, STATED (ID-81) and never inferred from a null
handle (ID-110); a null handle is "this verb bound no indirect buffer", the same 0 the
monolith arm returns for an empty slot; a missing backend resource is the named refusal
Fatal{RoleViolation, "multidraw-indirect-buffer-arm"}, a sibling of mv's
RefuseMissingIndexBufferRecord, never a quiet fall-back to the frontend. The monolith body
below it is unchanged token for token. The function moved down beside BoundIndexBufferId,
which is the same question about the other target.
- WHY A HANDLE ARM AND NOT A MIGRATION: the tier never reads a byte of the client's indirect
buffer. It binds its OWN scratch command buffer and wants to put back the name that was
there - a save/restore. With a transport the only other writer of this process's
GL_DRAW_INDIRECT_BUFFER is DirectGLES.cpp's DrawSyncBit::IndirectBuffer arm, which binds from
MGPipeApplier().VerbIndirectBuffer and nothing else, so "what was bound" IS "what the verb's
record named".
- Escalation (iii) withdrawn in the same commit - MGPipeBarriered's clause, the client's
verb-keyed half in ClientVerbIsBarriered, and the contract row. It was never wrong, it was
the wrong instrument: there is one draw opcode and the tier is chosen server-side per batch,
so the narrowest key both roles could compute charged EVERY plain glMultiDraw* on the
auto -> ext default - a per-batch rendezvous on the arm the phone ships, added to make a lane
green. THE CHECK ON AN ESCALATION IS "WHICH ARM PAYS FOR IT", NOT "WHICH LANE GOES GREEN";
the rule is recorded in PipeApply.h beside the three that remain and in CONTRACT-P5E §2.1.
- Red-once D: `if (false && ...)` on the new arm and the entry dies 3/3, deterministically, on
`Fatal{UnmigratedPipeInput, "GetBufferBindingSlot@DrawArrays"} [BARRIER-PULLED, UNBARRIERED,
the client did not fill it, retires in P8 (indirect), P9 (readback), P13 (transfer)]`.
Its counterpart is this head itself: the escalation is GONE and the lane is green, so nothing
needed it - and the strict census's admitted set drops from nine rows back to ID-128's eight,
because the pull no longer happens at all rather than being legalised.
- Gate on the flipped head: unit 2256/2256, isplit 179/179, gens rc=0, lane counts unchanged
(179 / 1357 / 2 / 2).
|
||
|
|
f286548010 |
[MG_Pipe, MG_Impl, MG_IntegrationTest, CI, Docs] (Disaggregated): P5e ra2 - ID-133's escalation, ID-134's lane, and the contract sentence that could not be checked
- ID-133: escalation (iii) in MGPipeBarriered - a draw_vbo with NumDraws > 1 that is NOT
kDrawIsIndirect is barriered. Espryt's indirect multi-draw tiers reach
MultiDrawImpl::RunIndirect, whose apply reads the client's GL_DRAW_INDIRECT_BUFFER binding
(BoundDrawIndirectBufferId, MultiDraw.cpp:87) - GetBufferBindingSlot, retiring in P8 - and
unbarriered that is Fatal{UnmigratedPipeInput, "GetBufferBindingSlot@DrawArrays"} on 18 lane
entries. Backtrace of the aborting thread, verbatim frames: CountBarrierPull <-
MultiDrawImpl::RunIndirect <- MultiDrawImpl::DrawElementsBatch <- ServerVerbSink::OnDrawVbo
<- PipeWireDecoder::DecodeAndApply <- PipeApplier::ApplyOne.
- TWO PREMISE CORRECTIONS, both measured, both in the report and in the contract. (a) There is
no indirect multi-draw OP: all twenty draw entry points collapse onto draw_vbo, and the tier
is resolved on the server per batch (ResolveTierForBatch), so the escalation cannot be keyed
on the tier and its cost is NOT confined to the opt-in tier lanes - a plain glMultiDraw* now
waits on the auto -> ext default too. NumDraws > 1 is the narrowest wire fact that contains
the reaching set; kDrawIsIndirect is excluded so Sodium's indirect draw, which resolves by
handle and pulls nothing, keeps running ahead. (b) The read is a SAVE/RESTORE of a GL binding
name around the tier's own scratch buffer, not a data dependency - so it can be retired
outright by giving BoundDrawIndirectBufferId the handle arm its neighbour
ResolveBoundIndexBuffer already has, and this escalation withdrawn. That is three lines in
MultiDraw.cpp, which ID-113 gives to mv, so it is reported rather than edited here.
- The client's half is verb-keyed because the fill decision is asked before the record exists:
{MultiDrawArrays, MultiDrawElements, MultiDrawElementsBaseVertex} is exactly the set whose
record can carry NumDraws > 1 without kDrawIsIndirect. The inclusion goes that way round on
purpose - a record the server calls barriered whose fields the client did not fill would be
an ADMITTED pull of a stale value, which is worse than the abort.
- ID-134: the two ClientVertexArrayScenario entries get the label integration-clientarrays-split
and an expected-red CI step that asserts the MARKER, shaped like gl's Magma lane. ID-131 heeded
by COUNTING, not by assuming: -L integration-split 181 -> 179, -L integration-gpu 1359 -> 1357,
the new label selects exactly 2. The step branches on whether run-ahead is actually ARMED,
because kMGPipeP5eRunAheadReady is a BUILD constant: on a head where it is false the refusal is
unreachable and the entries are legitimately green, so an unconditional demand for red would
fail for the one reason that is not a defect. The client says which world it is in in its own
log line; delete the green branch when the flip lands for good.
- CONTRACT-P5E §3.5 amended to the code: "outside a barriered fill" rested on the caller's claim
about the FUTURE, and a guard whose exemption cannot be false on the class it exists for is not
a guard - it never fired on any of the 70 red entries. The landed rule is "not while the applier
is inside, barriered fill or not", with the fill sites taking §2.5's forced wait first, twice
per filling verb because the block is written either side of the emitters.
- AND §3.2 IS MARKED UNLANDED, which nobody had noticed: "the server's stamp is server-private
… never into the shared block" describes storage the applier owns, and
MGPipeServerStampVerbBoundary still writes m_filled / m_currentVerb / m_serverStampedVerb into
gPipeInputs itself. That gap is what made the race possible. Splitting the stamp is the P11
item - but per-role stamps WITHOUT versioning the BARRIER_PULLED values would be worse than
today, because it turns a loud Fatal into a silent stale read.
- §2.1 carries escalation (iii) as a row, per its own rule that a new escalation is a ruling and
a row in the contract rather than a condition at a call site.
- Gate on the flipped head: unit 2256/2256, isplit 179/179 HARD GREEN, gens rc=0, doc citations
rc=0 (the three remaining are CONTRACT-P5.md's and pre-date this branch).
|
||
|
|
2d03565f87 |
[MG_Impl, MG_Remote, MG_Test] (Disaggregated): P5e ra2 - a barriered fill must be quiescent, not merely about to park
- ID-132's finding, diagnosed: with the flip thrown the 70 red lane entries are ONE race
and two debts. The race is the client's residual fill writing gPipeInputs while the apply
thread is inside an earlier UNBARRIERED record. Measured interleaving, verbatim:
`[MobileGLIntegra/ERROR] RA2-PROBE fill verb=DrawElements wireop=81 waitclass=-1
applierInside=1` then `[mgl-srv-apply/ERROR] RA2-PROBE unfresh field=IsCapabilityEnabled
verb=DrawElements serverStamped=0 ownership=RECORD-SUPPLIED` then
`[mgl-srv-apply/FATAL] MGPipe: Fatal{UnmigratedPipeInput, "IsCapabilityEnabled@DrawElements"}`.
The GL thread's MGPipeServerClearVerbBoundary withdrew the applier's own stamp mid-record.
- The marker's verb is the CLIENT's verb, which is what identifies the writer without a
probe at all: the applier can only ever put a verb of MGP_VERB_OP_LIST into the block, and
40 of the 54 unbracketed aborts named a verb that is not in it.
- A/B control on the same binary and the same records: MOBILEGL_IPC_RUN_AHEAD=0 is 8/8 green
where the default arm is 2/6 red, so the defect is in the wait rule and not in a record.
- RefusePipeInputsTouchWhileApplierOwnsIt could not fire on any of it: its run-ahead arm read
`if (isBarrieredFill) return;`, and that sentence is a claim about the FUTURE ("this thread
is about to park behind the record"). The order at the validate point is fill, then emit,
then park. So the arm now tests the fact - `isBarrieredFill && !ApplyThreadIsInsideApplier()`
- and the fill sites MAKE it true first with §2.5's forced wait (QuiesceApplierBeforeFill).
- TWICE PER VERB, because the block is written in two phases that straddle publication: the
serial bump / stamp withdrawal / verb rename run before the fourteen emitters and the
63-field walk of step 4 runs after them, and the records those emitters published are
records whose apply reads the block.
- ClientVerbIsBarriered: MGP_VERB_OP_LIST joins the whole draw family through one row
(DrawVbo -> DrawArrays), so inverting it verb-first answered kOpCount for DrawElements and
eighteen other draw verbs, and the "unknown verb answers barriered" default filled for each
while the record it published was a kWaitNone draw_vbo it never parked behind. The family's
row is applied to the family; every other unknown verb keeps the conservative answer, which
the wait above now makes honest. Measured alone it is worth 112 -> 133 isplit; with the wait
in place the lane is 161 either way, so this half is the PERFORMANCE half - without it every
indexed draw takes a forced drain and run-ahead is lockstep by another name on the draw path.
- Red-once A: put the guard's arm back to `if (isBarrieredFill) return;` and
RemoteGuards.ClientBarrieredFillWhileTheApplierIsInsideUnderRunAheadIsFatalByName exits 0
instead of aborting (`Value of: DiedOfAbort(child) Actual: false`), while its green control
keeps passing. Red-once B: delete the kDraw fallback and
RemoteRunAhead.AnIndexedDrawVerbIsUnbarrieredAndTouchesPipeInputsNotAtAll dies of signal 6
with `Fatal{RoleViolation, "gPipeInputs"} - the GL thread touched gPipeInputs
(MGPipeValidateForVerb) on a RUN-AHEAD session in a barriered fill that did not first wait
for the applier to catch up`.
- Both new cases run in the ordinary unit lane with run-ahead armed by the fixture, which is
what ID-132(1) asks for: the strict lane runs under lockstep and cannot see this class.
- Gate on the flipped head: unit 2256/2256, isplit 161/181 (was 112), gpu 1339/1359 (was 1290),
gens rc=0, flavours rc=0. The remaining 20 are two named debts, not this race: 18 are
`GetBufferBindingSlot@DrawArrays [UNBARRIERED]` on the two indirect multi-draw tiers, whose
retiring phase is P8, and 2 are ID-82's own refusal of client vertex arrays.
- The arm is stated and not inferred (ID-81): WaitForApplyToCatchUp's first test is
`m_runAheadArmed && m_started`, which reduces to Transport != Monolith. Under lockstep,
under monolith and under MOBILEGL_IPC_RUN_AHEAD=0 every line added here is a call that
returns, so those arms are unchanged.
|
||
|
|
985ae62dfa |
[MG_Backend, MG_Pipe] (Disaggregated): P5e - throw both run-ahead constants (NOT FOR MERGE YET)
kMGPipeP5eRunAheadReady (MG_Backend/Init.cpp) and kMGPipeP5eClientWaitRuleLanded (MG_Pipe/PipeApply.h) both to true. They are ONE switch with two spellings and the integration commit throws both. THIS BRANCH IS NOT MERGEABLE AS IT STANDS and is parked so the defect it exposes can be worked on against a reproducible head. Measured on this commit: build rc=0 · unit 2254/2254 · gens rc=0 · flavours rc=0 (pull 915 TUs, push 929 TUs) integration-split 112 / 181 (69 failed) integration-gpu 1290 / 1359 (69 failed) and the failures are FLAKY: 71 failed at -j 4, 68 at -j 1, and a single entry run alone passes and then aborts on a repeat. A census over all 181 entries under each entry's own command and ENVIRONMENT gives 70 red on 17 distinct <field>@<verb> pairs, over just FOUR fields: 27 GetRenderStateParametersVersion 25 GetTextureContextId 16 GetBufferBindingSlot 2 IsCapabilityEnabled spread across eleven verbs - Clear, DrawArrays, DrawElements, DrawElementsBaseVertex, DrawElementsIndirect, DrawElementsInstancedBaseVertex, EndTransformFeedback, GetIntegeri_v, GetSyncStatus, MultiDrawElementsBaseVertex, ReadPixels. Several of those verbs are BARRIERED, which is what makes this interesting: 16 of the 70 abort on the UNBARRIERED arm (why=UNBARRIERED) and the other 54 are the plain unfresh-read abort with no [BARRIER-PULLED] bracket at all, i.e. a RECORD-SUPPLIED field that the freshness stamp says was never filled. One field at one verb, consistently, would be a missing migration. Four fields scattered over eleven verbs, flaky run to run, including barriered ones, is a RACE - and the obvious candidate is that gPipeInputs is shared mutable state whose mutual exclusion WAS the lockstep. The client still fills it for barriered verbs while the apply thread is draining earlier unbarriered records, so the client's fill and the server's verb-boundary stamp can now overlap in time. That is consistent with P5d's own research conclusion, recorded in the roadmap, that retiring this barrier needs gPipeInputs VERSIONING or double-buffering, deferred to P11 after P3b/P4b's handle-keyed twin tables. The strict lane being hard green (181/181, zero Fatal) is necessary and NOT sufficient, and this commit is the proof: strict runs under lockstep, where ApplyOne stamps every record barriered, so it cannot see anything that only happens when the client stops waiting. The lane measures remaining DEBT; it does not measure whether run-ahead is correct. |
||
|
|
72bc4268ff |
[MG_Test] (Disaggregated): P5e wave 3 - the strict lane's expected-marker set, re-measured on the merge
The file carried gl's base measurement (
|
||
|
|
97a6a46c0d | Merge branch p5e/gl into feat/disaggregated (P5e gl) | ||
|
|
e5b74028b5 |
[Docs] (Disaggregated): P5e ID-120 - contract amendment 8's sticky-forward clause is marked UNLANDED
- CONTRACT-P5E section 8 item 8 says GetBufferBindingPointCount's sticky forward becomes FATAL under split. It is not in the code and must not be put there this phase: FieldOwnership.def:147 (field row) and :179 (forward row) both say BARRIER_PULLED, and the generator refuses a field/forward pair that disagrees - pinned by FieldOwnershipTest.TheSevenStickyForwardsAgreeWithTheirFieldRows. - Landing it as written would either trip the generator or drag the FIELD row to FATAL with it, and that second reading moves 23 pairs of the derived admitted set: the row's retiring phase is "P7/P13", which is exactly what admits it on every stamped verb under ID-125, and a FATAL row is admitted nowhere - so 23 reads this phase never promised to migrate would start aborting. - MEASURED, NOT INHERITED: ID-120 said 10 pairs; that count predates ID-125's retiring-phase disjunct. `--print-admitted | grep -c '^GetBufferBindingPointCount@'` says 23 on this head, and the note records the command as well as the number. - Annotated in place, struck through with the phase that lands it (P7/P13), for ID-105's reason: a contract clause that is arithmetically impossible against the code is a clause that changes, and the divergence is better stated than discovered from a red gate two phases from now. |
||
|
|
664cbf3ef8 |
[MG_IntegrationTest, CI, Docs] (Disaggregated): P5e - Magma's named-blit entries get a lane of their own
- Both DirectVulkan.Split.NamedBlit entries carried LABELS "integration-split" and abort under
strict on GetFramebufferBindingSlot@Clear (VulkanRenderer.cpp:7648). P5e does not unbarrier that
field on Magma - it retires there in P7 - so the Espryt lane could not go hard green with them
in it.
- ARGUED RATHER THAN ASSUMED, because the brief allowed either reading: admitting the row instead
is the WORSE option. FieldOwnership.def carries ONE retiring-phase string for both backends
("P5e (Espryt unbarriered), P7 (Magma)"), so a rule that admitted the pair for Magma would admit
it for Espryt too - and an Espryt Clear reading the frontend's binding slot again is exactly the
regression fb's handle arm exists to prevent. The lane split costs two entries; the admission
would cost the gate.
- THE LABEL IS `integration-magma-split`, NOT `integration-split-magma`, and the spelling is
load-bearing: `ctest -L` takes a REGEX, so `-L integration-split` matches a label spelled
`integration-split-magma` as a substring. Measured with the obvious name first: both DirectVulkan
entries were still listed by `ctest -N -L integration-split`, i.e. the split looked done in every
listing and had not happened.
- Its CI step asserts the run is RED **and** that a private log carries the named marker, so
"Magma is expected red" cannot decay into "Magma did not run". Measured: 0/2 under strict with
two logs carrying Fatal{UnmigratedPipeInput, "GetFramebufferBindingSlot@Clear"}, and 2/2 green
without the knob. integration-split is 117 and integration-magma-split is 2.
- CONTRACT-P5E section 7 states the split and the reason it is not an allowlist entry.
|
||
|
|
bc6e24ff30 |
[MG_IntegrationTest, MG_Util, MG_Backend, CI, Docs] (Disaggregated): P5e ID-119/ID-115 - the lane reads every log, counts, and has a positive control
Three measured defects in the strict lane, one commit each in spirit and one in fact because
they are the same step.
1. THE LOG SET CAME FROM A DIRECTORY. The CI step grepped MG_IntegrationTest/split-logs/, which
holds 90 of the lane's entries. The ones it could not see were the F1. readback block, both
NamedBlit pairs, the four Ct. entries and PersistentMapArm - precisely the readback population
the allowlist is ABOUT, plus the only DirectVulkan entries in the lane. split_log_paths.py
gains a `markers` mode that walks ctest's own {entry: MOBILEGL_LOG_FILE_PATH} map and
classifies every Fatal{ and Admitted{ line. Measured on this head: 113 of 113 entries read.
The lane is now a TWO-SIDED RATCHET - an unadmitted marker fails, an admitted pair not in
Harness/strict-expected-markers.txt fails, AND an expected pair that no longer appears fails,
so the lane cannot rot green by outliving its own debts.
2. THE `rsp` CHECK GREPPED FOR A COUNTER NO SPLIT ENTRY EMITS, and the pin behind it was vacuous:
CountBarrierPull's unbarriered arm is [[noreturn]] and runs BEFORE ++g_residualPulls, so
"rsp = 0 on unbarriered records" was true of every possible implementation. Replaced by an
assertion where the counter actually is - one entry, DirectGLES.Split.StrictArming., with
MOBILEGL_PIPE_STATS=1, MOBILEGL_PIPE_STATS_PERIOD=1 and a private log path of its own (a
whole-lane env flip races under -j: the library opens that path "w"). It asserts
rsp == 0 || rsp >= draws, which is the device evidence's shape - rsp ~= the draw count under
inproc, 0 under monolith - so the draw path's retirement is visible as rsp ceasing to scale.
3. THE LANE HAD NO POSITIVE CONTROL (ID-115). Of the seven entries that passed strict, three were
monolith transport - where nothing stamps a verb boundary, so the whole mechanism is
structurally unreachable - two self-skipped, one was a death test and one was a Python check.
Not one was a record-carrying split GL scenario, so "green" and "strict was never armed" were
the same observation. New push-only counter `vbs` (PipeStats::ServerVerbBoundaries), counted
at MGPipeServerStampVerbBoundary behind Enabled(), and the same entry asserts vbs > 0 on a
frame that drew. It is deliberately NOT rsp: rsp reaches zero when the phase SUCCEEDS, so a
control built on it would start failing on the day the debt is paid.
- Measured on this head (base
|
||
|
|
a0836568ac |
[MG_Pipe, MG_Remote, MG_Backend, MG_Test] (Disaggregated): P5e ID-128 - admission gains the escalation disjunct
- The integrator's census over the merged tree left two markers nothing admitted, both
DirectGLES.Split.ClientVertexArrayScenario: GetBoundVertexArray@DrawArrays. Disjunct 1 fails
(draw_vbo is kWaitNone) and disjunct 2 fails (that row's retiring phase names P5e, correctly,
for the ORDINARY draw path vi and mv retired). But these two entries pull the pair on a record
the client IS parked behind: MGPipeBarriered escalates a draw carrying client vertex arrays
(ID-83), ID-82 puts client arrays outside P5e entirely, and the read site
(SyncClientSideVertexArraysForDrawArrays) aborts by its own name if it is ever applied
unbarriered. The pull is legal; no static table could see it.
- Third disjunct: the record was barriered BY ESCALATION, i.e. wireSaysBarriered(op, payload,
applierState) && MGPipeWaitClassFor(op) == kWaitNone. Both halves were already computed on
every record - ApplyOne computes the predicate unconditionally for ID-103's reason - so this is
one more bit stamped beside the barriered stamp, not new plumbing.
- It is a SEPARATE flag rather than a refinement of the barriered stamp, because the two answer
different questions: the stamp answers "is the client parked" (the allocator and registry
guards ask that), this answers "and for a reason the static table cannot see" (only the strict
knob asks). Its default is false - the answer that admits nothing - which is the opposite of
the stamp's default and deliberately so.
- The marker says WHICH disjunct: ADMITTED when the generated table answered, ADMITTED-ESCALATED
when only the record did. That keeps the lane's comparison against --print-admitted exact
instead of widening the static list with every pair that could ever escalate - and the tag sits
in the slot the grammar already has for the reason, so Fatal{UnmigratedPipeInput filters are
untouched.
- Escalation also happens to be the truer justification for GetTransformFeedbackProgram@DrawArrays,
which ID-125 admits by phase. Both now hold; ID-125's disjunct is kept, as ruled.
- Red-once: AnEscalatedRecordsPullIsAdmittedAndSaysWhy. Dropping `&& !escalated` from the strict
arm kills it - "FieldOwnershipTest.cpp:938: Failure / Actual: false / signal 6" with
'Fatal{UnmigratedPipeInput, "GetBoundVertexArray@DrawArrays"} [BARRIER-PULLED,
MOBILEGL_IPC_STRICT_ERRORS=1, retires in P5e (Espryt unbarriered), P7 (Magma)]'. Its control is
StrictErrorsTurnsABarrierPulledReadIntoANamedAbort, which drives the identical pull with the
flag at its default and must keep aborting.
|
||
|
|
faf19a6aaa |
[MG_Pipe, MG_Test, CI] (Disaggregated): P5e ID-125 - admission gains the retiring-phase disjunct
- ID-116's static rule failed on a row no package in this phase may touch. pa retired the per-draw program pull, which unmasked GetTransformFeedbackProgram@DrawArrays (11 entries): BARRIER_PULLED, on kWaitNone draw_vbo, and ruled out of P5e entirely by CONTRACT-P5E section 5.7, with a retiring phase of "P3b/P4b (Espryt), P7 (Magma)". The rule was wrong, not the code. - A pull is now admitted if the field is BARRIER_PULLED, the verb has a stamp row, the field is in the verb class's may-read mask, and EITHER the verb's op is statically barriered OR the field's retiring phase does not name P5e. This table is only ever consulted on a record the server already stamped BARRIERED - CountBarrierPull's unbarriered arm is [[noreturn]] and fires first - so the pull is legal by construction and the only open question is whose debt it is. The retiring-phase column is exactly that answer and was already in the table. - A verb with NO stamp row still admits nothing under either disjunct: the server stamps only MGP_VERB_OP_LIST's ops, so any other bit would be a row nothing can exercise. 111 -> 174 pairs. - The phase column is matched as a TOKEN, not a substring, so a "P5" or a future "P5e2" cannot answer for "P5e". - All six of ID-125's survivors checked: GetFramebufferBindingSlot@ReadPixels and GetTextureUnitObject@CopyTexImage2D by disjunct 1; GetTransformFeedbackProgram@DrawArrays (P3b/P4b), GetTextureObject@CopyImageSubData (P7) and ValidateProgramName@ShaderStorageBlockBinding (P9) by disjunct 2. GetTextureObject@CopyImageSubData holds by BOTH, asserted separately. GetProgramForDraw@DrawArrays, GetBoundVertexArray@DrawArrays and GetProgramForDispatch@DispatchCompute stay rejected. - Magma's GetFramebufferBindingSlot@Clear is NOT admitted and cannot be: the retiring-phase column is one string for both backends and it names P5e for Espryt. The DirectVulkan entries get their own lane label instead (item 7), which keeps the Espryt allowlist about Espryt. - StrictErrorsAlsoPromotesTheStickyForwards is RENAMED to StrictErrorsReachTheStickyForwardsAndNameThemByField and restated. Not one of the seven sticky forwards names P5e (P7, P7/P9, P7/P13, P9), so on a stamped verb they are all admitted now. The claim that still has teeth is that the detector REACHES them - asserted by rsp moving and by the marker naming the field and its phase, so "did not abort" cannot pass for "never detected". The Fatal half keeps its own case next door on GetBoundVertexArray, a P5e row. - Six new derivation controls, including one that makes GetTransformFeedbackProgram this phase's debt and requires it to leave the unbarriered verb's allowlist - without which disjunct 2 could be a constant `true` and every other control would still hold. |
||
|
|
3c13fa4b4a |
[MG_Backend] (Disaggregated): P5e ID-124 - the pull and push flavours compile again
Three sites, all pre-existing at
|
||
|
|
d8b9848003 | Merge branch p5e/pa into feat/disaggregated (P5e pa) | ||
|
|
b4789e8ed4 | Merge branch p5e/mv into feat/disaggregated (P5e mv) | ||
|
|
a6ddeb0816 |
[MG_Pipe, MG_Test] (Disaggregated): P5e ID-118 - resource_copy_region becomes barriered
- CopyImageSubData's apply reads GetTextureObject, a BARRIER_PULLED row that retires in P7, not in P5e. resource_copy_region was kWaitNone, so after the flip that is a genuinely UNBARRIERED record reading client memory: an unconditional Fatal, no knob, standing for two phases. - The wait class moves rather than the field being special-cased into the allowlist. The allowlist is derived (ID-116), so an exception written into it would be a real defect kept quiet instead of a debt stated - and with the class moved the derivation admits the pair with no exception at all: 101 -> 111 pairs, CopyImageSubData joins the 12 barriered verbs. - kWaitApplied AND NOT kWaitReply: the row posts no reply slot, so the client parks until the record applies. That is exactly what copy_framebuffer_to_texture - the two framebuffer-sourced copies, same kBlitOrCopy verb class - already does. THE COST IS MEASURED: glCopyImageSubData is zero per frame in the Minecraft 26.3-rc-3 scene, so the wait is on no per-frame path. - PipeCatalogue's wait-class census moves with it, both halves in this commit: the named kWaitApplied set gains the row, the count goes 9 -> 10, and the kWaitNone offset 24 -> 25. - Red-once: putting the row back to kWaitNone drops CopyImageSubData out of the derived allowlist (111 -> 101 pairs, 0 @CopyImageSubData) and fails "FieldOwnershipTest.cpp:422: Failure / resource_copy_region is unbarriered again: after the flip, a copy record reading the client's texture object is an unconditional Fatal and P7 is two phases away", with PipeCatalogueTest red on three counts beside it. |
||
|
|
5c777b49e8 |
[MG_Test] (Disaggregated): P5e mv - the multi-draw tiers get gated entries of their own
- The resolved multi-draw tier is ONE process-wide read of MOBILEGL_ESPRYT_MULTIDRAW_MODE (MultiDraw.cpp's ResolveTierOnce) and the d1 block leaves it at "auto", which on this driver lands on "ext" - the one tier that asks the bound index buffer nothing. Three of the five sites the previous commit migrated therefore had no entry in the set the gate runs: ID-107's shape one level down, with the env knob in place of MG_Config::Transport. - Five blocks, one per tier "auto" cannot reach (basevertex, multiindirect, indirect, drawelements, compute), prefix DirectGLES.Split.MultiDrawTier<Tier>., both labels, and MOBILEGL_ESPRYT_MULTIDRAW_MODE pinned as a ctest ENVIRONMENT property rather than a job-level export - review finding N-6's reason, and the PushMonolithArm block's precedent. - "ext" is deliberately NOT registered: the d1 block already IS the ext lane. Every other tier was confirmed to resolve to ITSELF here rather than falling back to ext, so no entry in this set is a green that silently means "ext again". - The filter is the tier-sensitive workload: MultiDrawScenario (minus the client-indices case d1 already excludes by name) plus IndexedDrawFamilyScenario's three MultiDraw cases. 12 entries per tier, 60 in total. - SplitLogPaths.cmake.in gives each of them a private log path. That is the rule every DirectGLES.Split. entry follows (SplitLogPaths.PrivateAndDistinct), and for these it also repairs the census blind spot: a third name segment means strict_census2.sh would re-run them without the very pin under test, so the marker is read where the lane wrote it. - integration-split 116 -> 176, integration-gpu 1294 -> 1354, both fully green. Under the strict lane all 60 stop on GetProgramForDraw@DrawArrays, which is pa's row: PrepareForDraw runs before the tier dispatch, so no new marker class appears and mv's row stays at 0. |
||
|
|
4f492edbf3 |
[MG_Backend, MG_Test] (Disaggregated): P5e ID-117 - the strict knob gains an admitted state
- CountBarrierPull aborted under MOBILEGL_IPC_STRICT_ERRORS on EVERY barrier-pulled read,
admitted or not. The run's rc is therefore non-zero before CI ever reaches its allowlist
comparison, so that comparison was unreachable code and "hard green" was impossible with the
knob as written - the lane could not tell a debt this phase leaves standing from a defect.
- Third state, with the other two untouched: unbarriered stays an unconditional Fatal; barriered
and NOT admitted stays a Fatal under strict; barriered and admitted logs one MGLOG_W and the
entry completes. Admission is MGPipeBarrierPullAdmitted, generated from the three tables
(ID-116), so there is no list here to drift.
- The marker keeps the grammar and changes only the tag: Admitted{UnmigratedPipeInput,
"<F>@<V>"} [BARRIER-PULLED, ADMITTED, retires in <phase>]. Every filter written since P5c
matches Fatal{UnmigratedPipeInput and therefore still means exactly "red".
- Deduped per (field, verb) per process. An 852-draw frame reaches an admitted readback row once
per draw; undeduped, one frame writes 852 identical lines and the marker census is unreadable.
- CHECKED, NOT ASSUMED (the brief's instruction): StrictErrorsTurnsABarrierPulledReadIntoANamedAbort
and StrictErrorsAlsoPromotesTheStickyForwards both still go red. Both stamp DrawArrays, whose
op is kWaitNone, so neither pair is admitted.
- ID-119: the old "rsp = 0 on unbarriered records" comment at this function is withdrawn as
vacuous - the unbarriered arm is [[noreturn]] and runs BEFORE ++g_residualPulls, so the
sentence was true of every possible implementation.
- Red-once: AnAdmittedBarrierPullIsLoudOnceAndNotFatal is the same sticky forward as the case
above it under a barriered verb; making the strict arm unconditional again kills it -
"FieldOwnershipTest.cpp:867: Failure / Actual: false / signal 6".
|
||
|
|
dd89951062 |
[MG_Test] (Disaggregated): P5e pa - the strict lane gets four entries of its own
- MOBILEGL_IPC_STRICT_ERRORS=1 had no registered ctest entry anywhere: the phase's gate driver sets the knob by hand and pg's declared red-once for GetProgramForDraw never landed, so the two rows pa retires could come back and every gate would stay green. ID-109's finding, one scale down: the gap is a missing gated entry, not a missing case. - DirectGLES.Split.StrictProgramArm. registers the only two scenarios that are strict-CLEAN after pa - one per half. MultiDrawScenario.BaseVertexDrawsReject- MalformedArguments prepares a draw and asserts on GL errors rather than pixels; AtomicCounterScenario's three cases are the dispatch half. Everything else in the lane still ends in a readback, whose GetFramebufferBindingSlot@ReadPixels is an allowlisted row only P7 retires. - integration-split alone, and the prefix says which arm: the monolith arm of these scenarios is registered under DirectGLES. already and the knob is a no-op there, so labelling them integration-gpu would add entries that cannot fail for the reason they exist (R-16). - Negative control run: with the arm reverted all four abort, two with GetProgramForDraw@DrawArrays and two with GetProgramForDispatch@DispatchCompute. - Each entry takes a private log path through SplitLogPaths, for vi's reason: an entry that exists to carry a marker cannot share the file the marker is in. |
||
|
|
a211756aec |
[MG_Pipe, MG_Test, CI] (Disaggregated): P5e ID-116 - the section 7 allowlist is generated, not hand-kept
- ra landed a hand-kept 13-row copy of CONTRACT-P5E section 7 in .github/workflows/test.yml, and it was wrong in BOTH directions at once: it omitted GetFramebufferBindingSlot@ReadPixels (21 entries, the lane's largest survivor) and carried GetTextureObject@CopyImageSubData, which resource_copy_region's kWaitNone makes a genuine unbarriered read of client memory. Neither error is visible by reading the list. Both are obvious once the list is derived. - gen_pipe_field_ownership.py now joins the three tables ID-84 already states the rule in - kMGPipeFieldOwnership, kMGPipeWaitClasses (generated/PipeWire.inc) and kMGPipeClassFieldMask / kMGPipeVerbClass (generated/PipeFillPoints.inc) - and emits kMGPipeAdmittedPullMask plus MGPipeBarrierPullAdmitted(field, verb): 101 admitted pairs across 12 barriered verbs. - The three inputs are READ, never restated, so the allowlist moves with the code: retiring a verb's wait class narrows it with no edit anywhere, and a new BARRIER_PULLED row widens it only on verbs that wait. A verb with no stamp row admits nothing - only MGP_VERB_OP_LIST's ops can ever be the record a pull is reported against. - --print-admitted gives the CI shell the SAME table the C++ answers from, which is what makes the hand copy unnecessary rather than merely corrected. - Three new negative controls (each source renamed away must stop the script - a missing source yields a silently EMPTY allowlist, which reads as rigour and is blindness) and five derivation controls that move an input and read the allowlist back. - Red-once: dropping the wait-class conjunct from build_admitted fails the self-test by name - "3 admission-derivation control(s) did not hold: unbarriering ReadPixels' op removes every @ReadPixels pair; unbarriering ResourceCopyRegion removes every @CopyImageSubData pair; no pair is admitted on an unbarriered verb (DrawArrays)" - and the new unit case fails with "GetBoundVertexArray@DrawArrays is admitted on a verb no barriered op stamps". |
||
|
|
53b83469e6 |
[MG_Backend, MG_Remote] (Disaggregated): P5e pa - the draw and dispatch program rows leave the frontend
- PrepareForDraw's single GetProgramForDraw() and PrepareForCompute's GetProgramForDispatch() are the 68 red entries this package owns (61 + 7 in the base census; 62 + 7 once the PersistentMapArm entry is re-run under its own lane environment). Merely CALLING the accessor is what trips MGP_INPUT_CHECK, so the row retires by not making the call. - DrawProgramFromRecords() states the arm ONCE and as a CONJUNCTION: ProgramHandleArm() AND TextureImpl::UnitTexturesByHandle(). The hoisted value serves four callees and the arm has to name every one of them - the second conjunct is BindCurrentTextures', whose legacy memo key and whose sampledTargetForUnit lambda both read a real object. The family bits are independent A/Bs, so the |
||
|
|
f94752c01b |
[MG_Impl] (Disaggregated): P5e ID-112 - a compile-time trip wire on all seven pointer-backed residual rows
- EmittedCallSuppliesTheWholeField answers false for eight rows; seven of them are pointer-backed and this residual fill is Magma's ONLY source for them, dereferenced on its first draw (Magma is lockstep for the whole phase, ID-90). NoP4aFamilyFieldIsWhollySupplied is keyed on the EMITTER'S SUBSYSTEM, so it covered five of the seven: GetBoundVertexArray (P3a's, BindVertexElements) and GetBufferBindingPoint (sb's, SetShaderBuffers) sit outside kMGPipeP4aFamilySubsystems and had no compile-time protection at all. - A package that tidied one of those two away would not break the build - it would SIGSEGV Magma on a device, which is the shape that already cost this phase 38 scenarios (ID-107). The seven are now named in one list with their own assertion, so the most likely mistake of every remaining package is a build break instead. - The comment says where the fired assertion sends you: a row retires in the phase that stops the backend reading a frontend object (P7/P8), never in a consumer-side cleanup. - Red-once: deleting the GetBoundVertexArray case fails the build - "PipeFill.cpp:2720:23: error: static assertion failed due to requirement 'NoPointerBackedRowIsWhollySupplied()': a pointer-backed residual row stopped being pulled (ID-112)". The pre-existing P4a assertion stayed silent on the same edit, which is the gap ID-112 names. |
||
|
|
8df7861f07 |
[MG_Remote, MG_Test] (Disaggregated): P5e ID-111 - the barriered stamp asks the server's own capability
- ApplyOne stamped `kMGPipeP5eClientWaitRuleLanded ? wireSaysBarriered : true`, which reads a build constant and never asks which server this is. kCapRunAheadApply is never published on DirectVulkan (ID-90), yet draw_vbo / blit / clear / launch_grid are all kWaitNone - so the day the constant flips, a Magma server would stamp every draw record UNBARRIERED while its client is still lockstep. CountBarrierPull is an unconditional Fatal on an unbarriered pull, no knob involved, so Magma dies on its first draw and MagmaP7AllocatorDebtScope's exemption goes with it. - The stamp is now `MGPipeApplierStampsBarriered(waitRuleLanded, serverPublishesRunAhead, wireSaysBarriered)`, a pure function beside the applier, so ID-90 is something a unit case can hold AT THE POST-FLIP value of the constant rather than a claim that first becomes testable on integration day. That is MGPipeRunAheadCapBitsFor's shape, for MGPipeRunAheadCapBitsFor's reason. - Both predicates stay computed unconditionally, as arguments rather than short-circuit arms: ID-103's reason, extended to the capability probe. - `ServerPublishesRunAhead` is promoted out of the anonymous namespace as `MGPipeServerPublishesRunAhead` (body unchanged) so the red-once can state "this session lacks bit 10" in its own words instead of inferring it from the stamp under test (ID-102). - Red-once: ADrawOnASessionWithoutTheRunAheadBitStaysStampedBarriered drives a real DrawVbo through ApplyOne on a SetCapabilityBits(0) session. Deleting the new conjunct makes the post-flip assertion false - "RemoteClientControls.inc:143: Failure / Expected: true / exited 124". |
||
|
|
98ea16042a |
[MG_Backend] (Disaggregated): P5e mv - the multi-draw path stops reading the frontend VAO
- MultiDraw.cpp asked the bound element array buffer three questions (is one bound,
what GL name is bound and how big is its store, give me its bytes on the CPU) and
asked all three of MGB_CTX->GetBoundVertexArray() on the apply thread. That was the
last unguarded read of that field in the backend: package vi retired the ordinary
draw path's copy and this one survived only because these 9 entries used to die
earlier, on the framebuffer row fb has since retired.
- ResolveBoundIndexBuffer(question, entry) answers all three from
MGPipeApplier().IndexBuffer.Res on the record arm - EnsureBufferResourceForHandle
for the driver name, ResourceWidthForHandle for the size, the server's staged
shadow (hostBytes + RequireStagedCoverage) for the CPU bytes - and keeps the
monolith body token for token below it. No wire row was added.
- The arm is STATED, not inferred (ID-81 / ID-110): BufferImpl::VertexInputReadsRecords(),
the family's own selector, which reduces to Transport != Monolith AND the vertex-input
bit. Nothing here decides an arm from a null pointer or a noted handle.
- A missing record on the handle arm is a named refusal:
Fatal{RoleViolation, multidraw-index-buffer-arm}, the shape of
RefuseNullFrontendTextureOffTheHandleArm. The frontend element slot is not a second
opinion on this side - it answers for whatever the client has bound now.
- VertexInputReadsRecords() moved from DirectGLES.cpp to Managers.h, unchanged, so the
two translation units on this draw path share ONE selector rather than a copy each.
- Strict census: GetBoundVertexArray@DrawArrays 9 -> 0. The same 9 entries now stop on
GetProgramForDraw@DrawArrays, which is pa's row; no other row moved, no new row.
|
||
|
|
7c6f6886ff |
[Docs] (Disaggregated): P5e 进度、monolith 回归与设备验证写回
- CURRENT_STAGE_PROGRESS.md 新增 2.5 节:八包落地情况、设备验证发现的 monolith SIGSEGV 及其 根因/覆盖漏洞/修复(ID-107 -> fix1 -> ID-109/ID-110)、头 |
||
|
|
3cc4e1eca2 |
[MG_Backend] (Disaggregated): P5e ID-110 - the record arm's selector is stated, not inferred
fix1's report asked for a ruling on Managers.cpp's buffer-texture backing seam, which hands kNoFrontendBuffer to the lookup on the strength of a NOTED HANDLE and was unreachable under Transport == Monolith only because every writer of such a note happens to be transport-gated. That is safe by a chain rather than by its own statement, and it is exactly the shape that cost the monolith arm 137 scenarios across 38 files (ID-107): fix1's root cause was one seam reading "a handle was noted" as "the record arm is selected", which are different claims. - MGB_TEXTURE_RECORD_ARM_SELECTED() names the second claim once: Transport != Monolith AND TextureResourceSubsystemEnabled(), the two conjuncts every null-frontend-servable read already lives behind (CONTRACT-P5E 5.2 / 5.8). It is (false) in the pull build, so nothing moves there. - RefuseNullFrontendTextureOffTheHandleArm spells it instead of repeating it inline, which also removes its nested #if. - texBufferByRecord gains it as a third conjunct. Under a transport it is already implied by `pushedStorage != nullptr`, so no behaviour changes and no door closes; the arm now says what it depends on instead of depending on it silently. Gate on the integration head: build rc=0, unit 2250/2250, integration-split 116/116, gens all rc=0 (the CONTRACT-P5.md:524 doc-citation ambiguity is the standing pre-existing one). |
||
|
|
6662176733 |
[MG_Backend, MG_Test] (Disaggregated): P5e fix1 - the by-handle attachment sync must not run on the monolith arm
- FramebufferImpl::SyncAttachmentSurface selects its STORAGE sync by MG_Config::Transport
(ID-81 / CONTRACT-P5E §5.8) instead of routing both arms through tx2's by-handle seam. Under
a transport it is unchanged (SyncMipmapsToBackendByHandle / RBO SyncToBackendByHandle); under
Transport == Monolith it is the frontend sync it was at
|
||
|
|
44f91c7405 |
[MG_Backend, MG_Test] (Disaggregated): P5e wave 2 integration - the three seams where two packages met
- vi and sb both added a block at the same point of DirectGLES.cpp's buffer
section; both belong, and vi's arm used to borrow the `#endif` that follows the
hunk, so it gets one of its own and sb's block opens below it.
- pg and sb both rewrote the UBO loop's first lines: pg changed where `binding`
comes from (the archived block-binding tail, because glUniformBlockBinding moves
it after the link), sb changed what `binding` indexes (the applier's window
instead of the frontend binding-point array). The merge keeps pg's source and
sb's consumer, which is the boundary both reports named.
- pg wrote a comment stating the shape of the texture memo key because the line
belongs to tx2; tx2 wrote the fields. Both kept.
- PipeCatalogue's routed-row pin read 38 at the base, and sb (one generated row)
and pg (one escape) each landed 39 against it. The merged tree has 40, which is
what the count now asserts, with both rows named in the comment.
- Gates on the merged tree: unit 2250/2250, integration-split 113/113, gen_pipe
and gen_pipe_field_ownership --check/--self-test green, include closure 4 probes
0 problems, dirty surface --check/--self-test green, doc citations one
pre-existing CONTRACT-P5.md ambiguity. Strict is still the phase's two-sided
lane; its first-blocker markers fell from {GetTextureUnitObject@Clear 13,
GetFramebufferBindingSlot@Clear 6, GetTextureUnitObject@GenerateMipmap 2} to
{GetFramebufferBindingSlot@Clear 2, GetProgramForDraw@DrawArrays 1} plus the
allowlisted barriered rows (ReadPixels, CopyTexImage2D) that were always behind
them. Run-ahead is still OFF: kMGPipeP5eRunAheadReady and
kMGPipeP5eClientWaitRuleLanded are both false, so this is the lockstep split
with every family reading records instead of the frontend.
|
||
|
|
e7a66d2f24 | Merge branch 'p5e/ra' into feat/disaggregated (P5e ra) | ||
|
|
cae68992b0 | Merge branch 'p5e/fb' into feat/disaggregated (P5e fb) | ||
|
|
db2196fb08 |
Merge branch 'p5e/tx2' into feat/disaggregated (P5e tx2)
# Conflicts: # MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp |
||
|
|
30e385812d |
Merge branch 'p5e/pg' into feat/disaggregated (P5e pg)
# Conflicts: # MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp # MobileGL/MG_Test/Pipe/PipeCatalogueTest.cpp |
||
|
|
5b226f16ae |
Merge branch 'p5e/sb' into feat/disaggregated (P5e sb)
# Conflicts: # MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp |
||
|
|
e0f69e8791 | Merge branch 'p5e/vi' into feat/disaggregated (P5e vi) | ||
|
|
4d1499ef7e |
[MG_Test] (Disaggregated): P5e pg - four cases for the bindings record, the failed relink and the framed archive
- AUniformBlockBindingAfterTheLinkTravelsOnItsOwnRecord: the create unlatches the bindings and the first emission after it always goes out (the ordering the applier depends on), an unchanged binding set emits nothing, and a post-link unit assignment moves the latch. This is the case the package's first red-once is built on: the integration-split lane has no scenario with a named uniform block bound to a non-zero point, so emptying the tail there is invisible and the statement has to be made here. - TheThreeBindingTailsAreWholeSetsAndAReIssuedCreateDropsThem: whole-set replacement in all three tails, the override NAME copied rather than pointed at (rule C), BindingsSerial advancing, and the re-issued create dropping every tail because they index into the archive it just replaced. - AFailedRelinkReIssuesWithLinkStatusZeroAndNeverAnObjectDeath: ID-88's "iff the frontend reports it unlinked" is asserted against the frontend rather than assumed, so the day Link()'s prologue stops withdrawing LINK_STATUS this case says the ruling has to be re-decided instead of the server silently drawing a dead executable. - TheFramedArchiveRoundTripsWithTheStageOfEveryModule: two modules of the SAME stage - the shape MGPProgramDesc::StageMask cannot describe - survive the frame, the skipped member comes back null, and a truncated frame is refused with both outputs defined. |
||
|
|
ca1beb6958 |
[MG_Backend] (Disaggregated): P5e fb - revert the tx2 scaffold; the seam goes back to c0e's stub
- Reverts
|
||
|
|
48b2f56ef8 |
[MG_Remote] (Disaggregated): P5e ra - the run-ahead latch goes down with the session
- CONTRACT-P5E §1. The latch means "what THIS session's first caps adoption said", and ClientSession is a process singleton that Start/Stop can cycle. Left standing, a second Start would inherit a verdict about a server that is gone - and because a later snapshot may only turn run-ahead OFF, an inherited `true` could never be corrected by the honest answer arriving afterwards. - The present serial resets with it: a credit's id space belongs to the session that paces on it, and a server that never saw frame 1 cannot return its credit. |
||
|
|
1c8727102c |
[MG_Backend] (Disaggregated): P5e tx2 - name the two entries that actually stamp the sync handle
- ResolveTextureTwin is the lookup WITHOUT the sync and does not stamp it; the comment said it did. |
||
|
|
6ee4c0979b |
[CI, Docs] (Disaggregated): P5e ra - the strict lane becomes one step with two arms, and §11/§14/§17 say what landed
- CONTRACT-P5E §7. integration-split-strict was an EXPECTED-RED control: every entry aborts on its first BARRIER-PULLED read, so the step gated on the run failing for that reason. After the flip it is a HARD GREEN GATE - an unbarriered record's pull is Fatal whatever the knob says (§3.3), so strict adds only the barriered rows, and those are a short named allowlist of <field>@<verb>. A revert of any handle arm in vi/sb/pg/tx2/fb then goes red HERE, by field and verb, instead of in a picture. - THE ARM IS CHOSEN BY THE TREE. The step greps kMGPipeP5eRunAheadReady out of MG_Backend/Init.cpp, which is the one constant the integration commit flips and is exactly the fact the lane's expectation depends on. A lane whose flip is a separate human step is a lane that is wrong for as long as that step is outstanding - and the pre-flip arm is unchanged, so this commit moves no CI verdict today. - The allowlist names the package that retires each row: readbacks and the pack PBO (P8/P9), the CopyTex endpoint and GenerateMipmap (tx2), set_storage_block_binding (pg), XFB (P3b/P4b/P9), resource_copy_region's GL names (the declared P5b debt). A row leaves it by deletion, never by widening. The lane also warns on a non-zero rsp, which after P5e can only be a barriered row. - The allowlist is built with printf rather than a heredoc: a quoted heredoc's terminator has to start at column 0, which inside a YAML block scalar would end the scalar instead of the heredoc. - ARCHITECTURE §11.6: the wait rule is the generated WaitClass column now, not the call class, and it REPLACES the BatchWaits skip; glFinish stops being a no-op under run-ahead; the forced waits (EGL forwarders, the map/readback family) and glGetError's named window are listed. §11.7: the P9 plan is code - the producer blocks, the drain clears AND rings, the client's parks break on the latch, and two named refusals remain for the impossible cases. §14: the present credit is client-minted, 1-based, one per swap, paid before the encode. §17.7: what P5e is, in one section - the predicate, rule F, gPipeInputs' ownership, the latch and the control arms. - 附 A gains MOBILEGL_IPC_RUN_AHEAD and moves MOBILEGL_IPC_PRESENT_CREDIT out of the "P6+" line: a knob listed as not-yet-effective while it paces the frame is how an A/B stops being one. |
||
|
|
b004e5dcbb |
[MG_Backend] (Disaggregated): P5e sb - Espryt's four binding-point walks read the record
- SyncBufferBindingPointsByRecord(class, glTarget), file-local beside the walk it replaces: the window is the applier's ShaderBufferStart/Count, each entry resolves through EnsureBufferResourceForHandle(nullptr, Res), and whole-vs-range is `Offset == 0 && Size == kMGPipeWholeBuffer` - so obj->GetSize() leaves the function and the extent of a base binding is re-resolved HERE, against this server's own storage, which is what makes a glBufferData between the emission and the apply bind the new extent as GL does. The ES clamp stays exactly where it was: the device ceiling is a server question. - The arm is transport + bit 13, with bit 13 requiring bit 7 refused by name rather than half-run - ResolveVertexInputSubsystemArm's shape, and for its reason: without bit 7 every point would log once and `continue` WITHOUT unbinding. - SyncAtomicCounterBuffers reads the applier's window instead of GetBufferBindingPointCount's sticky forward; glBindings stays the backend program's own list. A binding outside the window takes the UNBIND arm rather than being skipped, because skipping would leave whatever the driver last had at that ESSL point. - The UBO loop's POINT half reads BoundShaderBuffers[Uniform][binding] in the same index space, with IsBufferDrawCleanByHandle / FindBufferResourceForHandle in place of the frontend probes. GetUniformBlockBinding above it is untouched - that line is package pg's. - MarkShaderStorageBuffersGpuWritten and SyncAtomicCounterBuffers' MarkBufferGpuWritten are DELETED under a transport. They walked client memory and wrote into a client object, which is the one thing that cannot survive run-ahead, and the client's own MarkGpuWritesForDraw has covered the same set since P5d. Withholding the client's two rows turns the atomic scenarios red (5 vs 13, 100 vs 116), which is what says the duplicate really is gone. - Every frontend arm is kept token for token under Transport=monolith (G1, ruling 1), as the `if (…) else` the indirect buffer already uses. |
||
|
|
bbfd4fef81 |
[MG_Test, MG_Impl, MG_Pipe] (Disaggregated): P5e ra - the wait rule's cases, and the two constants are one switch
- Fifteen cases, and every one of them arms run-ahead through the production latch
(publish kCapRunAheadApply into the caps mirror before Start) rather than through a
test-only setter - ID-102's lesson: a red-once whose probe is a branch beside the
production path is a red-once that proves the branch.
- RemoteRunAhead: the knob alone does not arm it; VERB_BARRIER=0 disarms it even with
the cap bit; a credit-1 client pays at every present after the first and a credit-3
client at none of its first three; an unbarriered verb fills nothing and is allowed;
a 3 MB event burst behind an unwaited sequence flow-controls instead of aborting and
arrives LOSSLESS; only the texture half of resource_subdata wants its reply.
- RemoteGuards: a gPipeInputs touch outside a barriered fill under run-ahead is
Fatal{RoleViolation, "gPipeInputs"} by name and the barriered fill is allowed; a
residual pull under an UNBARRIERED record is Fatal WITHOUT the strict knob while the
same pull under a barriered one still only counts (ruling 4, in executable form); a
deferred destroy from an unbarriered apply is the finding and from a barriered one is
still served (ruling 13).
- SessionTest: a full SEG_EVENT wakes a client parked on appliedSeq AND one parked on
the present credit, without touching either watermark; the drain clears the latch and
RINGS, once, and is silent when the latch was already down.
- The event burst's drainer starts 50 ms late on purpose. A drainer that starts at once
can keep a 256 KiB ring from ever being full, and a red-once that depends on the
scheduler losing a race is not a red-once.
- ClientVerbIsBarriered now takes a nullable context: the validate point asks the
question before it knows whether there is anything to fill, so the answer is about the
RECORD. A null context has no open XFB span, and treating it as "barriered" instead
made the fill red-once unreachable in any fixture without a live GLContext.
- kMGPipeP5eClientWaitRuleLanded flips WITH kMGPipeP5eRunAheadReady, in the integration
commit - an amendment to ID-103's wording ("the same commit that lands the wait rule").
The argument ID-103 makes is about whether the CLIENT is parked: RunAheadArmed() needs
the server's caps bit, so between ra landing and the bit being published the client
still blocks after every record, and a `false` stamp there would withdraw §4.4's
exemptions from probes that wait still makes safe. Two spellings, one switch.
|
||
|
|
99211a4807 |
[MG_Test] (Disaggregated): P5e tx2 - the ABA case asks the twin table, not the record
- MGPipeApplyResourceCreate declines a TEXTURE create on a process with no P4a consumer (NoP4aConsumer), which is every unit-lane process, so the record-driven shape of this case skipped instead of running. The ABA answer is the twin TABLE's - a forward generation resets the entry - so the case now drives g_backendTextureObjects directly and gates on EsprytSlotTablesEnabled() rather than on the family bit. - And it does NOT compare the successor's twin ADDRESS against the predecessor's: the reset released the predecessor, so the allocator hands the successor the same heap address, which is precisely why an address is never an identity here. "The slot came back empty" is the assertion. |
||
|
|
aa38f0a05b |
[MG_Test] (Disaggregated): P5e fb - the three constants on both sides, the server's point set, and the resize that must re-emit
- ImageEmit.TheThreeAccessConstantsArePinnedOnBothSidesOfTheWire: ruling 16 / ID-94 is that the encode and the decode are ONE table, so the case reaches both halves - the numbers, MGPipeEncodeImageAccess, MGPipeDecodeImageAccess, the validity predicate and the two read/write predicates - and then round-trips all three GL tokens through a real glBindImageTexture so what is pinned is the byte that actually crosses. The drift it catches (1 <-> 2) is a `writeonly` image the shader can read back, which no pixel test on a lockstep tree could see while the frontend value was answering. - FramebufferEmit.TheRecordsElevenSurfacesAreTheWholePointSetTheServerReads: the server twin of AnAttachmentPointAboveTheWireWidthIsRefusedNotTruncated. P5e spends that refusal as a licence - the by-handle attachment walk iterates Color[0..7] + Depth + Stencil instead of the frontend's 41 points - so both halves are pinned: a fully-populated framebuffer round-trips every point into the applier's record, and a point above the width leaves the record the server holds completely unmoved (hash and FramebufferSerial), rather than merely counting a refusal. - FramebufferEmit.ADefaultFramebufferResizeReEmitsTheRecordWithItsNewExtent: ruling 15, which S3 flagged as unsettled and said was load-bearing because under run-ahead there is no other extent source. The case drives the surface-changed consumer's own call - AllocateStorage on the attachment at the new extent, no rebind, no re-attach - which is exactly the shape the framebuffer shutter would have suppressed. - All three get their pull-build skip twin through the suites' X-macro lists (G2/G14). |
||
|
|
c5491ca675 |
[MG_Backend] (Disaggregated): P5e pg - the program twin is keyed by the ShaderCso handle
- BackendProgramObjectImpl::SyncToBackendByHandle is real. It and the frontend overload
feed ONE body (SyncToBackendFromSource) through ProgramArchiveSource, a view whose
accessors are ProgramObject's names one for one - so the body is written once and the
PULL build compiles the same text against a ProgramObject. In a pull build
ProgramBuildSource IS ProgramObject, the function keeps its own name and its null
guard, and G1's byte identity survives a rename and nothing else.
- The view answers the three POST-LINK MUTABLE reflection fields from the record's
set_program_bindings tails and everything else from the record's archive. The tails are
a whole-set replacement, so a location the sampler tail does not name has NO unit - not
whatever the link-time snapshot said - and "has a bindings record been applied at all"
is BindingsSerial != 0 rather than a non-empty tail.
- The nine-clause clean condition keeps its clause count and moves its inputs (D-H5):
record.Serial vs the m_syncedShaderCsoSerial stamp P4a landed and nothing has read
until now, BindingsSerial vs a new m_syncedBindingsSerial (a glUniform1i on an IMAGE
uniform is baked into the ESSL, so the record that carries it must force a rebuild),
record.Signature, Desc.LinkStatus and Desc.SpirvStatus.
- The signature has ONE authority per arm: the handle arm stamps and compares the
CLIENT's number verbatim, so the two commutative hashes never have to stay
bit-identical for a program with an override to stop rebuilding every draw.
- DirectGLES: SyncCurrentProgramByHandle, the stash re-keyed on the handle,
PrepareForDraw/PrepareForCompute reading MGPipeApplier().DrawProgram/DispatchProgram,
BindCurrentProgramWithResources and GetCurrentBackendProgram on the handle arm, the
block binding and the sampler unit from the record's tails, and the sampler-pass memo
on BindingsSerial instead of the frontend's backend-state version.
- MapUBO() has no run-ahead answer and now says so: every decline of
ResolveGlobalConstantsRecord under a transport is Fatal{UnmigratedVerb,
"set_global_constants"} rather than a torn read of the client's live globalUboScratch.
- The fragColor broadcast count takes a POLICY, because a dispatch is a third case: it
has no draw framebuffer and no fragment stage, PrepareForCompute runs no SyncCurrentFBO,
so it may neither fall back to a client row nor abort when the records are untrusted.
- Three of the family's five named frontend-keyed scope sites are now unreachable under a
transport (ResolveGlobalConstantsRecord, SyncCurrentProgram,
BindCurrentProgramWithResources). The other two are kept BY CONTRACT (§5.5):
GetBackendProgramId is a GL-thread entry point and ShaderStorageBlockBinding is reached
from the barriered set_storage_block_binding, which is pg's trailing item.
|
||
|
|
112c1d8dfc |
[MG_Test] (Disaggregated): P5e vi - the two source decisions, the window rule and the client-array lane
- SanityTest's DirectGLESVertexInputDraw pair drives the PRODUCTION decisions (ResolveDrawVertexBuffersFromRecord / ResolveDrawIndexBufferFromRecord) with the frontend VAO deliberately pointing at a buffer the record does not name - the state a run-ahead client makes ordinary and one process cannot otherwise produce. Both functions take the applier state and nothing else, so the only revert is a frontend read inside them and the cases then go red naming the field (ID-102's rule about what a red-once may test). G2/G14 skip twins in the pull arm. - VertexInputEmit pins ID-95 / ruling 19 on the producer side: set_vertex_buffers' window must cover every attribute create_vertex_elements declares ENABLED, with a sparse configuration so "covers the enabled set" is not "is as long as the array". The sink half is tx2's CheckUnitWindows. - ClientVertexArrayScenario is BRIEF-P5E's open item (a), answered: NO existing scenario exercises a client-memory vertex array under split (DoublePrecisionScenario uses the binding-model calls against real buffers), so the lane had no entry that could reach the refusal at all. Registered in the split lane with a private log path, and it is also the positive control for keeping the upload arm alive under lockstep. |
||
|
|
4fc93c1ab9 |
[MG_Remote] (Disaggregated): P5e vi - kDrawClientArrays on the wire, and the run-ahead refusal
- ReadDrawBindings answers ClientVertexArrays in the SAME single read of the bindings the
rest of the draw plan is made from: an enabled attribute with no buffer behind it, which is
exactly what EmitVertexBuffers publishes as Res == kMGPipeNullHandle.
- PlanDrawInfo sets kDrawClientArrays for every draw shape, not only the array ones - a
glDrawElements can fetch its VERTICES from client memory too - because MGPipeBarriered is
computed from the record on both sides and a null Res inside the window is also what a
disabled attribute publishes.
- EmitDrawRecord and the reduced DrawArrays path refuse such a draw under run-ahead with
Fatal{UnmigratedVerb, "DrawArrays+CLIENT_ARRAYS"} on the GL thread, before the hooks
(ID-82 / ruling 2). LOCKSTEP IS UNCHANGED: the client is parked in WaitForApplied for that
record, so the server's upload reads memory that is not moving (ruling 4). Staging the
bytes as a per-attribute {BindingIndex, MGHostSpan} tail is P8's.
- The armed predicate is spelled here from the three inputs MGPipeTypes.h names for
RunAheadArmed() rather than called, because ClientSession::RunAheadArmed is package ra's;
it is inert until the integration commit publishes the caps bit, and ra collapses this
body into that latch.
|
||
|
|
a04cf05c93 |
[MG_Backend] (Disaggregated): P5e vi - the VAO, its buffers and its index buffer come from the records
- ResolveVaoTwin takes st.BoundVertexElements under a transport (id's by-handle body); the
frontend overload survives as monolith glue and its MGPipeFrontendKeyedRegistryScope is
DELETED, which is what makes a revert of the twin key abort instead of being exempted
(CONTRACT-P5E §4.1, §4.4, §5.8). Two of this family's three scope sites go with it.
- SyncVaoAttributeBuffersByRecord beside SyncVaoAttributeBuffersByHandle: the walk is over
st.VertexBuffers[Start..+Count).Res, deduped on {slot, gen}, ensured with
EnsureBufferResourceForHandle(nullptr, Res) and probed with a null frontend. The
push-monolith arm keeps its frontend walk token for token (ruling 1 / ID-81).
- The IBO arm reads st.IndexBuffer.Res with st.IndexBufferSerial, and the serial JOINS the
identity compare because this arm has no live element slot to re-read: a re-emitted
set_index_buffer on the same handle would otherwise read clean. ResolvedDrawBuffers gains
iboSerial; Entry::frontend stays declared for the legacy arm and is never written here.
- PrepareForDraw stops reading GetBoundVertexArray and GetConfigVersion under a transport;
SyncCurrentVertexAttributeValues keys on {elementsHandle, ContentSerial, activeMask} and
reads rec->Attributes[i].Enabled. The two program reads there are pg's and stay.
- The DrawArrays / MultiDrawArrays client-array arms resolve the twin by handle and take the
frontend VAO only when the RECORD says an enabled attribute is client-sourced, which is
what retires GetBoundVertexArray@DrawArrays for every ordinary draw.
- The two source decisions are named functions over the applier state alone
(ResolveDrawVertexBuffersFromRecord / ResolveDrawIndexBufferFromRecord) so a unit case can
drive the production decision rather than a copy of it.
- BumpBufferMutationEpoch pins BRIEF-P5E's open question 2: a bump off the apply thread while
one is running is counted and Fatal under the strict lane. The measured answer is that the
only non-apply-thread bump in the tree is BackendObject_DirectGLES::Initialize's
RegisterBufferBackendOps, before the apply thread exists.
- The restart helpers refuse a frontend element-slot read under a transport by name; the
substitution's own body already resolved from the applier since P5c.
|
||
|
|
fcd7c5c87a |
[MG_Backend] (Disaggregated): P5e fb - the eager image-bind funnel stops reading the frontend binding
- The strict lane named GetImageTextureBinding@BindImageTexture on thirteen entries: bind_shader_image is an unbarriered row (CONTRACT-P5E §2.2) whose backend entry point receives GL arguments and a GL NAME - MGPImageBind::Res stops at the sink - so this funnel had no way to resolve the twin and read the frontend binding instead. - Under a transport it now records only the high-water mark and returns. Nothing is lost: the sweep at the next validate point re-binds every unit up to the mark from the applier's own MGPImageView array, and the sink bumps the texture shutter serial immediately after this call, which is one of the four values the sweep's gate is keyed on - so the sweep that follows a bind cannot be suppressed. An image unit has no reader but a shader, and no shader runs in between. - The mark itself must still be raised here, because it is the "no draw in this context can be reading an image" early-out. |
||
|
|
1ebe6abeb2 |
[MG_Pipe, MG_Impl, MG_State, MG_Remote, MG_Backend, MG_Test] (Disaggregated): P5e sb - set_shader_buffers is emitted, routed and applied
- MGPShaderBuffers::WritableMask is Uint32[3] and the POD grows 32 -> 40 (ID-104). One Uint32 could describe only the first 32 of an 84-point window, so a storage buffer bound at point 32 or above read as unwritable with nothing able to see it; the ruling widens the field rather than narrowing the window, which would change what an application may bind. MGPipeShaderBufferMaskHas / ...Set are the one spelling of the mask, so the emitter, the applier and the four backend sites cannot disagree about which word a point lives in. - MGPipeApplySetShaderBuffers has its body: per-class window, Start/Count validated against the capacity, the mask copied by word, ONE ShaderBuffersSerial advanced on every applied record and ADVANCED - never zeroed - by both resets, which also clear the three windows. - Route rows for opcode 38 on both arms (Mono_ and Wire_) and the server sink in the wire codec's decode switch, which is where a kCtxState row's sink lives - PipeApplier.cpp's ServerVerbSink has no method for it and the brief's "OnSetShaderBuffers" names no code. The codec also bounds Class and Start+Count where Count becomes a byte length. - New MG_Impl/Pipe/ShaderBufferEmit.h: one record per class, window = the touched high-water mark clamped to 84, zero early-out before any hash, a BASE binding travelling as kMGPipeWholeBuffer so the server re-resolves the extent at use, three suppressor slots (ruling 11), and the writable mask that replaces the backend's GPU-write walk. - BufferState gains a per-target bind-point generation (push-only, G1) and dirty bits 15/16/17 shutter on it instead of the buffer CONTENT aggregate. That aggregate answered the wrong question in both directions: any glBufferSubData fired all three, and glBindBufferBase fired none of them, because it mutates a binding point through a returned reference. Same defect class as P4a's glBindSampler hole. - Init.cpp publishes consumer bit 13 for DirectGLES and not for Magma (ID-106), in the same commit as the wired constant, and PipeFill's liveness gate asks R-8 for bit 13 in particular plus bit 13-requires-bit-7. - `Touch` joins the dirty-surface scanner's mutator prefixes; TouchBufferBindingPoint gets its row and SetNamedTransformFeedbackBinding's is rewritten now that bit 17 reads a generation a binding moves. Negative control 6 is re-pointed: the pair it used to refute is a live answer, and a control that has quietly become positive is what the self-test is for. |
||
|
|
f4675d1ac0 |
[MG_Backend] (Disaggregated): P5e fb - SCAFFOLD, REVERTED BEFORE HEAD: tx2's two by-handle seams, so fb can measure its own lane
- fb calls TextureImpl::SyncTextureToBackendByHandle and BackendTextureObject::SyncMipmapsToBackendByHandle from four places. tx2 owns those bodies and is being written in parallel, so with c0e's aborting stubs every integration-split entry that touches a texture dies on the seam and fb's own isplit and strict numbers cannot be read at all. - This commit bodies the two temporarily, in the one way that does not pre-empt tx2's design: the frontend texture is resolved from the record's own GL name through the BARRIER-PULLED sticky forward GetTextureObject, exactly as the copy-image sink resolves its endpoints, and the existing frontend overloads do the work. Correct under lockstep, and deliberately NOT the shape tx2 will write - tx2 drives the storage from the descriptor and holds no frontend object. - IT IS REVERTED BY THE NEXT-BUT-ONE COMMIT. It exists in history so the integrator can see exactly what fb assumed about the seam and what fb's measured numbers rest on. |
||
|
|
a08b337975 |
[MG_Test] (Disaggregated): P5e tx2 - the seven shape reads become pinned guard rows, and ABA gets its own case
- RemoteClientTest's MGL_TEXTURE_GUARD_TEST block gains the seven MipmapStorage shape
reads P5c deliberately exempted, with the sentence that exempted them ("the per-draw
binding walk reads them every draw") replaced by the reason it is no longer true.
- SanityTest gains ARecycledTextureSlotReMintsTheTwinOnTheHandleArm beside
UnitMemoRefusesToDriveATwinFromAnotherTexture: the borrowed-slot list needs a pointer
compare because its key is derived state; the by-handle list needs none, because a
recycled slot arrives as {s, g+1} and the forward-Gen reset gives the successor a fresh
twin while the predecessor's handle stops resolving. G2/G14 skip twin included.
|