diff --git a/docs/Disaggregated/notes/p34b/espryt-d3.md b/docs/Disaggregated/notes/p34b/espryt-d3.md new file mode 100644 index 00000000..448fa761 --- /dev/null +++ b/docs/Disaggregated/notes/p34b/espryt-d3.md @@ -0,0 +1,380 @@ +# espryt D3 — the Espryt (DirectGLES) stream, part 3 + +The defect package D2 found and could not fix, plus the two D-K2 loose ends D2 left for +whoever owned `MG_Backend/DirectGLES/` next. Base `p7/espryt-d2 @ a7c06f4f`, worktree +`~/w7/p7-espryt-d3`, branch `p7/espryt-d3`. + +Per-slice red-once notes (R-16 / rule J). A slice is not done until the case it turns on has +been seen RED against the tree without that slice's fix and GREEN with it, on the arms the fix +is for — and for this package that means the two-process arms, not only inproc. + +Four commits, in cherry-pick order: + +| # | files | what is true after it | +|---|---|---| +| 1 | `MG_Test` | the pull build compiles again — `SubsystemDepsTest`'s server half is `MOBILEGL_PIPE_PUSH`-only | +| 2 | `MG_Backend`, `MG_IntegrationTest`, `MG_Remote`, `docs` | an owner-side upload shows through a sampled texture view, because the split clean gate resolves to the storage record | +| 3 | `MG_Backend`, `MG_Pipe`, `MG_Impl` | D-K2's dependency rule has one statement, and every reader reads it | +| 4 | `docs` | this note, and the census row the fix falsified | + +Commit 1 is not a deliverable and is first for a reason: without it `ninja -C build-linux` exits +1 at D2's head, so every later commit would be measured on a tree whose pull build does not +build. + +--- + +## slice 0 — the pull build compiles again + +Not a deliverable; a prerequisite for this package's own G1, and worth stating first because it +means D2's head does not pass gate C as it stands. + +D2's new `MG_Test/Backend/DirectGLES/SubsystemDepsTest.cpp` guards its CLIENT case with +`#if MOBILEGL_PIPE_PUSH` + `GTEST_SKIP` and does not guard its two SERVER cases, which take the +addresses of `ResolveFramebufferSubsystemArm` / `ResolveTextureResourceSubsystemArm` / +`ResolveSamplerSubsystemArm` / `ResolveProgramSubsystemArm`. `Managers.h` declares all four +inside `#if MOBILEGL_PIPE_PUSH` and `Managers.cpp` defines them inside it, so with the pull +flags the file does not compile: + +``` +../MobileGL/MG_Test/Backend/DirectGLES/SubsystemDepsTest.cpp:200:11: + error: use of undeclared identifier 'ResolveFramebufferSubsystemArm' +... 9 errors generated. +ninja: build stopped: subcommand failed. +``` + +`libMobileGL.so` has already linked by then, so G1's `.text` and symbol numbers are still +readable — but `~/w7/logs/p7-gate-c-inner.sh` reports `=== linux build rc= ===` and it was 1. +The two cases get the same guard and the same skip the client case already carries. Nothing +else moves; `ninja -C build-linux` rc 0. + +--- + +## slice 1 — an owner-side upload is visible through a sampled view + +### The hole + +`TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary`, registered by D2 and +excluded by name from the three split arms with the cause in the CMake comment: once a +`glTextureView` has been SAMPLED, a later `glTexSubImage` through the OWNER's own name never +becomes visible through the view. Red on inproc, spawn and tcp; green under monolith. + +The mechanism, end to end: + +* A view owns no texels, and the client keeps **one emission cursor per storage**. An upload + through a view's own name is remapped onto its storage owner before it is emitted + (`MG_Impl/Pipe/TextureEmit.h`, `MGPipeTextureEmitter`; `TextureEmitTest.cpp`'s + `AnUploadThroughAViewKeysOnTheStorageOwner` pins it). So every `resource_subdata` for either + name is keyed on the OWNER. +* `MG_Pipe/PipeApply.cpp`'s `ApplyTextureUpload` accumulates into that record and does + `++stored->Serial` on it — the owner's. Nothing moves the view's. +* `BackendTextureObject::IsDrawSyncCleanByRecord` (`MG_Backend/DirectGLES/Managers.cpp`) read + `record.Serial` and `record.PendingUploads` **of the record it was handed**, which for a bound + view is the view's. A view's `Serial` is moved by nothing an upload does, so after the view's + first sample the gate answered CLEAN forever and `SyncTextureViewToBackendByRecord` — the only + thing that drains the owner's pending uploads on a view's behalf, because it syncs the storage + twin by handle at its head — never ran again. +* Under monolith the same gate reads `GetContentVersion()` THROUGH the view, which + `TextureObjectView` forwards to its owner (`TextureObjectView.cpp:100-102`). The split gate + had no equivalent of that forwarding. +* `CONTRACT-P5E.md` §5.2 stated the split gate in full, `rec->Serial` and all, so the code was + faithful to a wrong contract. **The contract is amended, not restated.** + +The client half was never at fault, and the red run proves it rather than arguing it: the +case's own `MGPipeTextureEmitter::SubDataCount()` / `RefusedSubDataCount()` / `DrainListSize()` +assertions PASS on the red run. Only the pixel channel is red. + +### The decision: resolve the predicate, not index the reverse edge + +**Chosen (a): the gate's two STORAGE clauses resolve through `Desc.ViewOf` to the storage +record.** `PipeTextureStorageRecordForRecord` (new, `Managers.cpp`, beside +`PipeTextureRecordForHandle`) answers with the record itself for a texture that owns its texels +and with the record `Desc.ViewOf` names for a view. `IsDrawSyncCleanByRecord` asks it for +`Serial` and `PendingUploads`; the three PARAMETER clauses and `Desc.StorageKind` stay the +view's own record's, because a view has its own texture parameters and its own built-in sampler +and that is the whole reason the second name exists. `SyncTextureViewToBackendByRecord` stamps +that same storage serial at both its arms — the steady one and the one after the native view +call — so the stamp and the gate name one quantity rather than two. + +**Rejected (b): a reverse index owner → views, so an owner-side `resource_subdata` bumps every +viewing record's `Serial`.** Three reasons, in increasing order of how much they would have +cost to discover later: + +1. It puts new mutable state in `MG_Pipe`'s applier, which BOTH backends read, to answer a + question one backend's gate asks. Magma does not need it (below). +2. It makes `ApplyTextureUpload` write a record other than the one the call names. The + applier's serial discipline is stated in that function's own comment — the twin stamps its + synced serial from inside the sync that reads THIS record — and a bump arriving from a + different record's apply is exactly the shape that comment exists to forbid. +3. **Deletion ordering, which is what actually decided it.** The index has to be pruned when a + VIEW dies, or an owner upload bumps a slot that has since been recycled to an unrelated + texture — a silent cross-texture serial skew with no assertion anywhere that would catch it. + The resolve direction needs no pruning at all, and it is checked at every use: + `PipeTextureRecordForHandle` tests `Live` and `Gen`, so a recycled slot answers null rather + than a stranger's record. + +**Deletion ordering for the chosen shape, checked rather than assumed.** A view may outlive its +owner's GL NAME — `glDeleteTextures(owner)` while the view is alive is legal and the storage +must survive. It cannot outlive the owner's RECORD, and that is by construction on the client: +`TextureObjectView::m_storageOwner` is a strong `SharedPtr`, and +`MGPipeEmitTextureDestroyAndFree` is called from `TextureObjectBase`'s DESTRUCTOR, whose own +comment says why — *"the last SharedPtr to this object dropping, not the glDelete* that only +marks the name and leaves a still-bound object very much alive"* (`TextureObject.cpp:63`). So +`Desc.ViewOf` cannot dangle while the record that carries it is live, whatever order the +application deletes the two names in. The walk validates anyway, and **a null answer is NOT +clean** — the direction that re-syncs, never the direction that shows stale texels. It is not a +refusal and raises nothing (rule I: no new abort site, no new `Fatal` family word). + +**Views of views resolve transitively.** One hop always reaches storage in practice — +`glTextureView` composes a view-of-a-view onto the ROOT at creation, which is what the spec's +additive min-level rule means; the client's emitter says so in as many words +(`TextureEmit.h EmitResourceRespecify`) and `TextureObjectView`'s own invariant is that its +owner "is never a view itself". The walk is written transitively anyway and BOUNDED, because +this side may not depend on a client invariant to terminate: a chain the wire could forge into a +cycle ends as a null answer rather than as a hang. + +**The tree already agreed with (a), on the other backend.** `VkTextureManager:: +ResolveWireTextureStorage` (`MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp:2603`) is the +same walk — follow `Desc.ViewOf`, bound the hops, check `Live`/`Gen`, return null on failure — +and its comment states the same two reasons: *"Production views point directly to their root. +The bound also makes a corrupt or future multi-hop record graph terminate without ever following +client data."* Magma therefore never had this defect: it resolves the storage at every use +instead of memoising a per-view serial. Picking (b) would have given the two backends two +different answers to one question. + +### Red-once (rule J) + +At D2's head `a7c06f4f`, with the CMake exclusion lifted and **nothing else changed**: + +``` +$ ctest -R 'DirectGLES\.(Split|Spawn|Tcp)\.TextureViewAliasScenario' -j 3 +79% tests passed, 3 tests failed out of 14 + +The following tests FAILED: + 4042 - DirectGLES.Split.TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary (Failed) integration-gpu integration-split + 4046 - DirectGLES.Spawn.TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary (Failed) integration-gpu integration-spawn + 4050 - DirectGLES.Tcp.TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary (Failed) integration-gpu integration-tcp +``` + +and the failure itself, verbatim from the Split entry: + +``` +../MobileGL/MG_IntegrationTest/Scenarios/TextureViewAliasScenario.cpp:328: Failure +the first owner write, through the view: 11408 of 11408 pixels disagree; + first at (2, 2) is rgba(50,160,30,255), expected rgba(0,0,255,255) +/- 2 +../MobileGL/MG_IntegrationTest/Scenarios/TextureViewAliasScenario.cpp:328: Failure +the second owner write, through the view: 11408 of 11408 pixels disagree; + first at (2, 2) is rgba(50,160,30,255), expected rgba(255,255,0,255) +/- 2 +``` + +`rgba(50,160,30,255)` is the LAYER SEED — what the view was minted holding. Both writes are +lost, not one, which is the signature of a gate that latched rather than of a record that got +dropped. The drain's counter assertions are silent in the failure list: they passed. + +The MONOLITH control in the same tree is 4/4: + +``` +$ ctest -R 'DirectGLES\.TextureViewAliasScenario' +100% tests passed, 0 tests failed out of 4 +``` + +With the fix, the same invocation on the three arms: + +``` +$ ctest -R 'DirectGLES\.(Split|Spawn|Tcp)\.TextureViewAliasScenario' -j 3 +100% tests passed, 0 tests failed out of 14 +``` + +Rule J is satisfied on spawn AND tcp, not only inproc: entries 4046 and 4050 are the +two-process arms and both went red and then green. + +--- + +## slice 2 — D2's D-K2 loose ends, now in-partition + +D1 has landed on `feat/disaggregated`, so `MG_Backend/DirectGLES/` is free and the two items +D2 listed as the integrator's are taken here. + +### The stale prose in the header of the file that implements the refusal + +`MG_Backend/DirectGLES/Managers.h`'s resolver block said **"THREE OF THEM CARRY A DEPENDENCY"** +(there are six rows; bits 8 and 13 were the two it did not know about) and listed +**"10 without 11"** among the mirror pairs that are FINE — when 10-without-11 is precisely +D-K2's fourth row (ID-14/ID-15), refused twenty lines below by +`ResolveTextureResourceSubsystemArm` and withheld by the client. `MGPipe.h` lost the identical +pair of defects with R-5; this copy was left behind because the file belonged to a package in +flight. It does not restate the rows again: the file that states them is the file the code +reads. + +### Both readers read the table + +More than the two one-line changes D2 estimated, because the rule had **six** executable and +prose statements and the estimate counted two: + +| reader | was | now | +|---|---|---| +| `MG_Impl/Pipe/PipeFill.cpp` `P4aFamilyDependencyBits` | its own `kMGPipeP4aFamilyDependencies[]`, four rows | `MGPipeSubsystemRequires()`; the local table is deleted | +| `MG_Impl/Pipe/PipeFill.cpp` `P5eFamilyIsLive` | hand-coded `(pushMask & kMGPipeSubsystemResources) == 0` | `MGPipeSubsystemDependenciesAreSet(bit 13, mask)` | +| `MG_Backend/DirectGLES/Managers.cpp` `PipeSubsystemDependencyMissing` | took a literal dependency BIT per call site | takes the FAMILY and asks the table | +| …its four `ResolveSubsystemArm` call sites | five calls with five hand-written sentences | four calls, one per family, naming only the family | +| `MG_Backend/DirectGLES/Managers.cpp` `ResolveVertexInputSubsystemArm` | hand-rolled `bitSet && !resourcesBitSet` | the table | +| `MG_Backend/DirectGLES/DirectGLES.cpp` `ResolveBufferBindingSubsystemArm` | hand-rolled `bitSet && !resourcesBitSet` | the table | + +`MGPipe.h` gains one accessor, `MGPipeSubsystemDependencyWhy()`, so a reader that refuses +prints the ROW'S OWN sentence instead of a copy of it — the `.def` has carried a `Why` column +since R-5 and nothing read it. + +**One behaviour improvement falls out of it rather than being designed in.** The texture family +is the only row with two required bits, and it used to be two guarded calls here, so a mask +missing bit 11 but carrying bit 7 was reported with the bit-7 sentence. One call now computes +`required & ~mask` from the row, so the refusal names whichever bit is actually missing. + +**What this does to the anti-drift property, said out loud because it is a real weakening.** +`SubsystemDepsTest` used to compare two COPIES of the rule; it now compares two READERS of one +table, which cannot catch a typo in a row. It is kept, and the `.def`'s prose now says why: the +two sides still fold in reasons of their own — the server's own family bit and D-C3's wire-caps +refusal, the client's consumer and wired conjuncts — and those can still disagree at a mask. The +compile-time half of D2's red-once (drop the fourth row from the `.def` → `MGPipe.h:225`/`:230` +fail) is untouched and is now the load-bearing one. + +`SubsystemDepsTest` 5/5 after the switch, including D2's two refusal-lane cases at `0x1fff` +and `0x7ff`. + +--- + +## Gates + +Measured on `p7/espryt-d3 @ HEAD`, `build-split` configured by `p7_worktree.sh`, `build-linux` +configured with the same compilers and `DISAGGREGATED=OFF PIPE_PUSH=OFF PIPE_LEGACY_MEMOS=ON` +to match `~/w7/pipe/build-linux`. + +### G1 — the pull build is byte-identical + +| | value | +|---|---| +| `readelf -S -W build-linux/libMobileGL.so` `.text` | **`0xa52203`** (required `0xa52203`) | +| `nm --defined-only` symbols | 30570 | +| vs `~/w7/p7-before/pull-syms.txt` | **0 added / 0 removed** | +| `nm --defined-only -D` vs `pull-dynsyms.txt` | **0 added / 0 removed** | +| `ninja -C build-linux` | rc 0 (rc 1 at base — slice 0) | + +Measured three times: after slice 1, after slice 0, and after slice 2. Identical every time. + +**Why nothing this package touches reaches the pull build**, per file: + +| file | where the change sits | +|---|---| +| `Managers.cpp` `PipeTextureStorageRecordForRecord`, `PipeSubsystemDependencyMissing`, the four resolvers, `ResolveVertexInputSubsystemArm` | inside `#if MOBILEGL_PIPE_PUSH` | +| `Managers.cpp` `IsDrawSyncCleanByRecord` | inside `#if MOBILEGL_PIPE_PUSH` | +| `Managers.cpp` `SyncTextureViewToBackendByRecord` | inside `#if MOBILEGL_BUILD_DISAGGREGATED` | +| `Managers.h` the declaration and the prose | inside `#if MOBILEGL_PIPE_PUSH` | +| `DirectGLES.cpp` `ResolveBufferBindingSubsystemArm` | inside `#if MOBILEGL_BUILD_DISAGGREGATED` | +| `MGPipe.h` `MGPipeSubsystemDependencyWhy` | `inline constexpr`, unreferenced in the pull build | +| `PipeFill.cpp` | `MOBILEGL_PIPE_PUSH`-only translation unit content | +| `SubsystemDepsTest.cpp`, `MG_IntegrationTest/CMakeLists.txt`, the two `.md` files | not `libMobileGL.so` | + +The numbers are the proof; the table is what makes them checkable. + +### The three scripts + +``` +python3 scripts/ci/fatal_census.py rc 0 + 79 abort sites over 20 files of the split server image, 44 distinct family words, + 3 refusal words, 0 unmarked (79 is D1's number; this package adds none — rule I) +python3 scripts/link_ratchet.py --build-dir build-split \ + --baseline scripts/data/link_ratchet_baseline.txt --assert-monotone rc 0 + ratchet: unchanged at 186 symbol(s) +python3 scripts/ci/spawn_lane_parity.py build-split rc 0 +``` + +### Lanes + +All five at 100%, each run on its own so that "The following tests FAILED" belongs to the lane +that printed it: + +| lane | base (`a7c06f4f`) | D3 | delta | +|---|---|---|---| +| `unit` | 2418 | **2418 / 2418** | 0 | +| `integration-split` | 263 | **264 / 264** | +1 | +| `integration-spawn` | 179 | **180 / 180** | +1 | +| `integration-tcp` | 182 | **183 / 183** | +1 | +| `integration-magma-split` | 75 | **75 / 75** | 0 | + +**Names only grow, and the growth is named.** The base column is a real `ctest -N -L` taken on +this worktree with `MG_IntegrationTest/CMakeLists.txt` reverted to `a7c06f4f` and nothing else, +not an arithmetic guess. Set difference, per lane: + +``` +unit added=0 removed=0 +integration-split added=1 removed=0 + + DirectGLES.Split.TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary +integration-spawn added=1 removed=0 + + DirectGLES.Spawn.TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary +integration-tcp added=1 removed=0 + + DirectGLES.Tcp.TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary +integration-magma-split added=0 removed=0 +``` + +Three names, one per arm, and they are the un-exclusion. Nothing was removed from any lane. + +**The DirectVulkan arms of the same scenario: 30 / 30.** `ctest -R +'DirectVulkan\..*TextureViewAliasScenario'` covers the monolith entry plus the six informational +Magma tiers (`integration-magma-{all,full}-{split,spawn,tcp}`), four cases each, and every one +passes on this head — as they did at base. Magma never had this defect, because +`VkTextureManager::ResolveWireTextureStorage` resolves the storage at every use instead of +memoising a per-view serial. They are informational, not gating: `integration-magma-split` (75) +does not carry this scenario. + +**One tcp-lane flake, already on record and not this package's.** The FIRST full gate run had +`integration-tcp` at 182/183; the lane is clean on re-run and clean in the final run above. +`split-scenario-census.md`'s own "`SampledSetStalenessScenario` on tcp" section records the same +shape from D2's measurements and attributes it to the tcp lane serialising through one +supervisor (`RESOURCE_LOCK mobilegl-tcp`) that serves one session at a time. Recorded here so the +count is not quietly a best-of-two. + +--- + +## What remains + +**1. `KHR-GL46.texture_view.coherency` has no SPLIT reading.** It is exactly slice 1's test +(`tools/cts/caselists/p7-texture-gl46.txt:1050`), and the device-window baseline confirms D2's +claim case by case — `report-texture.json`'s results map has +`"KHR-GL46.texture_view.coherency": "Pass"`. That is a **monolith** reading; 門 5's split +comparison is the device window's, so on host hardware the integration case is the only thing +standing behind this fix. When the window runs, `texture_view.coherency` is the row to read +first. Two corrections to the citation while it is being read: + +* the block is `KHR-GL46`, not `KHR-GL43` as `espryt-d2.md` and `split-scenario-census.md` write + it. The caselist, the baseline and `CONTRACT-P7.md` §7.3 all say GL46; +* `texture_view` is **1/7 fail** in that baseline (`report-texture.txt:24`) and the failing case + is `KHR-GL46.texture_view.view_sampling`, NOT `coherency`. It is a pre-existing monolith + Magma conformance failure, on the list `CTS-base/README.md` calls "monolith Magma 的既有一致性 + 状态", and it is not this defect — worth naming so that a 6/7 reading after the fix is not + mistaken for a regression this package caused. + +**2. The Magma arms of `TextureViewAliasScenario` are informational, not gating.** Magma has no +defect to fix, but nothing in `integration-magma-split` watches the property on that backend +either. Promoting the four cases by name into the gating tier is a `test.yml` `require_green` +edit, which is not this package's file. + +**3. `SubsystemDepsTest` compares two READERS now, not two copies.** Stated in slice 2 and in the +`.def`, repeated here because it is the one thing D3 made weaker: a typo in a `.def` row can no +longer be caught by that case. The compile-time half of D2's red-once (`MGPipe.h:225`/`:230`) is +what carries the anti-drift weight, and the four `static_assert`s over the table are what stand +behind the rows themselves. + +**4. The remaining half of D2's R-3, untouched.** `MGPSamplerView::Target` still has no `Count` +bound, no `...IsValid` predicate and no `Fatal{ProtocolCorruption}` for an out-of-range value, +unlike `MGPipeResourceTarget`. It is an `MG_Pipe/MGPipeValueTypes.h` change and nothing in this +package needed it. + +**5. D2's own note is not rewritten.** `espryt-d2.md` still says `Managers.h:690-695` "is NOT +corrected here" and that switching the readers "is two one-line changes for the integrator". Both +were true of D2 and are the record of what D2 did; a package note is not a live statement of the +tree. The tree's own live statements — `SubsystemDeps.def`'s "WHO READS IT", `Managers.h`'s +resolver block and `split-scenario-census.md`'s exclusion row — are the ones corrected, and +`split-scenario-census.md` says so in a quoted block above D2's original text rather than by +overwriting it. + +**6. The seven unguarded frontend-pointer-keyed containers under `MG_Backend/DirectVulkan`** that +D2's memo-purity sweep listed are still there. They are wave 2 A/B/C's territory and this package +did not look at them. diff --git a/docs/Disaggregated/notes/p34b/split-scenario-census.md b/docs/Disaggregated/notes/p34b/split-scenario-census.md index 4a4b6dd3..7e73120c 100644 --- a/docs/Disaggregated/notes/p34b/split-scenario-census.md +++ b/docs/Disaggregated/notes/p34b/split-scenario-census.md @@ -15,12 +15,16 @@ Package D1 owns `PixelStoreSweep`, `DepthStencilReadbackMatrix`, `PackedWordRead | | registered | excluded by name | blocked, not registered | |---|---|---|---| -| texture family | 7 scenarios | 1 case | 0 | +| texture family | 7 scenarios | ~~1 case~~ **0** (package D3 fixed the defect) | 0 | | program family | 5 scenarios | 9 cases (one scenario's remainder) | 0 | **12 scenarios newly carry `integration-split` / `-spawn` / `-tcp` entries**, 71 cases per arm. -Plus `TextureViewAliasScenario` (this package's own, 3 of 4 cases). The split-arm registration -count recorded for the tier-2 Magma replay went from 33 to 46. +Plus `TextureViewAliasScenario` (this package's own, ~~3 of 4 cases~~ **all 4 since D3**). The +split-arm registration count recorded for the tier-2 Magma replay went from 33 to 46. + +**Updated by package D3** (`notes/p34b/espryt-d3.md`): the one excluded case is the only row of +this census that has moved, and it moved because the defect behind it was fixed rather than +because the exclusion was widened or narrowed. Everything else below is as D2 measured it. ## Texture family @@ -33,7 +37,7 @@ count recorded for the tier-2 Magma replay went from 33 to 46. | `ImageTargetKindScenario` | 29 | pass (24 run, 5 skip) | pass | pass | — the 5 skips are `*Texture2DMultisample*` capability skips that also skip under monolith | | `IntegerBorderColorScenario` | 6 | pass | pass | pass | — | | `SwizzleAccessRoutineScenario` | 4 | pass | pass | pass | — | -| `TextureViewAliasScenario` (this package) | 4 | 3 pass, 1 red | 3 pass, 1 red | 3 pass, 1 red | **`TheOwnerUploadDrainRunsAgainAfterADrawBoundary`**, excluded by name — see below | +| `TextureViewAliasScenario` (this package) | 4 | 4 pass | 4 pass | 4 pass | ~~`TheOwnerUploadDrainRunsAgainAfterADrawBoundary`, excluded by name~~ — **fixed by package D3**, see below | ### The debt-table 待核 row: is the P6 inspection forwarder debt closed? @@ -45,7 +49,18 @@ white-box half (`PipeApplyPeek`) agrees with Espryt's applied value in every arm now registered in the three arms, so the answer is re-taken on every CI run rather than by inspection. -### `TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary` — excluded by name +### `TextureViewAliasScenario.TheOwnerUploadDrainRunsAgainAfterADrawBoundary` — ~~excluded by name~~ FIXED (package D3) + +> **D3's resolution, at the head of D2's own diagnosis because the diagnosis was right.** The +> fix is at the GATE: `IsDrawSyncCleanByRecord`'s two STORAGE clauses resolve through +> `Desc.ViewOf` to the storage record (`PipeTextureStorageRecordForRecord`), and +> `SyncTextureViewToBackendByRecord` stamps the same storage serial, so the stamp and the gate +> name one quantity. The three PARAMETER clauses stay the view's own record's. The publisher-side +> reverse index was rejected: it needs pruning on VIEW death or an owner upload bumps a recycled +> slot's `Serial`, and `VkTextureManager::ResolveWireTextureStorage` already resolves the same way +> on the Magma side, so the index would have given the two backends two answers to one question. +> The case is un-excluded and green on all three arms; `CONTRACT-P5E.md` §5.2 now states the rule +> the code implements. Everything from here to the end of this section is D2's finding as written. Red on inproc, spawn AND tcp; green under monolith. **Not a lane problem — a product defect this package found.** After a view has been SAMPLED once, a later `glTexSubImage` through the OWNER's @@ -69,6 +84,11 @@ footprint), and the publisher form needs an O(#texture records) scan or a revers `MGPipeResourceRecord`, plus a `CONTRACT-P5E` amendment. `KHR-GL43.texture_view.coherency` is exactly this test and reads Pass in the CTS baseline, which is a **monolith** reading. +> **D3**: taken at the gate. `KHR-GL43.texture_view.coherency` still has no SPLIT reading on this +> tree — the CTS baselines are monolith and the split comparison is the device window's (門 5) — +> so the integration case is the only thing standing behind the fix on host hardware. Listed in +> `espryt-d3.md`'s "what remains". + ### `SampledSetStalenessScenario` on tcp `AQueuedClearIsMaterialisedWhenAFilterChangeCompletesTheTexture` aborted once in a full