diff --git a/MobileGL/MG_Backend/Init.cpp b/MobileGL/MG_Backend/Init.cpp index 047f79a3..5c226f4d 100644 --- a/MobileGL/MG_Backend/Init.cpp +++ b/MobileGL/MG_Backend/Init.cpp @@ -220,6 +220,32 @@ namespace MobileGL::MG_Backend { capBits |= MG_Pipe::kCapTimerQuery; } } + // P7 wave 2 package C, OQ-10 (CONTRACT-P7 §5.4): kCapResidentSubData, PUBLISHED + // OFF THE SERVER'S OWN WIRE RESOURCE TABLE AND NOTHING ELSE. + // + // The question the bit answers is the one BufferObject::LandBytesIntoResidentStore + // asks before it chooses between emitting `buffer_subdata_resident` (opcode 49) and + // falling back to the ordered in-place host memcpy: "does the side that will APPLY + // this record implement the resident arm at all?" Under a transport the client has + // no op table to probe - MGPipeResourceOpsHaveSubDataResident says so in as many + // words (MG_Impl/Pipe/PipeFill.cpp) and reads this bit instead - and until now + // nothing ever set it, so the answer was permanently "no" and BOTH backends fell + // back to the memcpy while both of them in fact register a SubDataResident arm + // (DirectGLES Managers.cpp's g_glesResourceOps, Magma VkBufferManager.cpp's + // g_vulkanWireResourceOps). Magma's implementation was dead code on the wire. + // + // BY THE TABLE, NEVER BY THE BACKEND ENUM, and that is ID-39's lesson rather than a + // style preference: a mask is a statement about THIS backend's registered table, so + // a bit derived from `ActiveBackendType == ...` would keep claiming the capability + // for a build, a flavour or a future backend whose table does not carry the arm - + // which is exactly how 66 uploads were lost to a consumer bit that nobody could + // have honoured. `g_resourceOpsAtStep2` is the pointer this function just pinned, + // and step 5 already refuses a run where that pointer was swapped underneath us, so + // reading the arm off it means the published bit and the table that has to answer + // for it cannot come apart. + if (g_resourceOpsAtStep2 != nullptr && g_resourceOpsAtStep2->SubDataResident != nullptr) { + capBits |= MG_Pipe::kCapResidentSubData; + } // Each backend has an independent implementation-readiness gate. // The runtime RunAhead knob can decline the feature, never create it. const Bool runAheadReady = MG_Config::ActiveBackendType == BackendType::DirectVulkan diff --git a/MobileGL/MG_Impl/Pipe/PipeFill.cpp b/MobileGL/MG_Impl/Pipe/PipeFill.cpp index 02d825ef..4f76483b 100644 --- a/MobileGL/MG_Impl/Pipe/PipeFill.cpp +++ b/MobileGL/MG_Impl/Pipe/PipeFill.cpp @@ -1077,6 +1077,14 @@ namespace MobileGL::MG_Pipe { #endif // The application's STAGING store, valid for the duration of the call only. MGPipeRouteBufferSubDataResident(record, base + (at - offset), length); + // P7 wave 2 package C, OQ-10: `rsd=`, counted HERE rather than at the call + // above so that one glBufferSubData cut into N chunk records counts N. See + // PipeStats.h's CallClass::ResidentSubDataEmissions for why the count is the + // only thing that can tell this arm from the in-place memcpy beside it. + if (MG_Util::PipeStats::Enabled()) { + MG_Util::PipeStats::AddCalls( + MG_Util::PipeStats::CallClass::ResidentSubDataEmissions, 1); + } }, MGPipeContentChunkCap()); if (!encodable) { MGLOG_E_ONCE("MGPipe: buffer_subdata_resident range [%llu, +%llu) on buffer %u cannot be encoded", @@ -1883,8 +1891,11 @@ namespace MobileGL::MG_Pipe { // // BACKEND-NEUTRAL FROM THE FIRST COMMIT, which is the whole point: before P3a's C-1 fix // the only thing that ever returned a VertexElementsCso slot was DirectGLES' - // StateObjectDeathOps table, so under a backend that installs none every VAO leaked a slot - // and a ~1.3 KB applier record for the life of the process. P4a mints SIX kinds and there + // StateObjectDeathOps table, so under a backend that installed none every VAO leaked a slot + // and a ~1.3 KB applier record for the life of the process. It stays the point now that + // BOTH backends install one (P7 wave 2 package C gives Magma its own, CONTRACT-P7 §5.5): + // Magma's table EMITS the death record and frees nothing, so the slot still comes back + // from here and from nowhere else. P4a mints SIX kinds and there // is no intermediate state in which a backend table is the only path for any of them. // // THE THREE-STEP ORDER IS FIXED and each position is load-bearing (see PipeMutation.h): diff --git a/MobileGL/MG_IntegrationTest/CMakeLists.txt b/MobileGL/MG_IntegrationTest/CMakeLists.txt index 59f2667b..4d4b9f11 100644 --- a/MobileGL/MG_IntegrationTest/CMakeLists.txt +++ b/MobileGL/MG_IntegrationTest/CMakeLists.txt @@ -2833,9 +2833,15 @@ endif() # case's child truncates whatever path it inherits. if (MOBILEGL_BUILD_DISAGGREGATED) target_sources(MobileGLIntegrationTest PRIVATE Scenarios/CtWireScenario.cpp) + # P7 wave 2 package C adds the fifth: the OQ-10 capability read. It belongs in THIS list + # rather than in a buffer lane because the thing it reads is a property of the SESSION - + # what the server published at handshake and the client's mirror received - and this list + # is the one that is replayed on DirectVulkan by tier 2, so one name here gives the bit a + # reading on both backends and all three transports. foreach(ctCase ApplierResetCrossesAtThePrimedEdgeInSerialOrder TextureDeathCrossesAndTheRecycledSlotAnswersTheNewObject FramebufferDeathCrossesAndTheRecycledSlotAnswersTheNewObject + TheServerPublishesTheResidentSubDataCapabilityFromItsOwnTable TheDirectApplierResetCallOnTheGLThreadIsRoleViolation) mgl_itest_register_split_arms("CtWireScenario.${ctCase}" "Ct.") endforeach() diff --git a/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.cpp b/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.cpp index 6f1f9dd6..1bbd66da 100644 --- a/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.cpp +++ b/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.cpp @@ -17,6 +17,7 @@ #if defined(MGITEST_SPLIT_RUNTIME_PEEK) && !defined(__ANDROID__) #include +#include #include #include #include @@ -140,6 +141,12 @@ namespace MGITest { const MobileGL::MG_Remote::Wire::PipeWireEncoder& encoder = session->Encoder(); state.emitSeq = encoder.EmitSeq(); state.runAheadArmed = session->RunAheadArmed(); + // P7 wave 2 package C, OQ-10: read through the SAME accessor the frontend's own + // MGPipeResourceOpsHaveSubDataResident reads (MG_Impl/Pipe/PipeFill.cpp), so a + // green case here and a resident emission there cannot disagree about the bit. + state.residentSubDataCap = + MobileGL::MG_Remote::Client::CapsMirrorInstance().HasCap( + MobileGL::MG_Pipe::kCapResidentSubData); state.presentCredit = MobileGL::MG_Config::Ipc.PresentCredit; state.presentCreditWaits = session->PresentCreditWaits(); if (const auto* control = session->Control()) { diff --git a/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.h b/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.h index 8e206b2f..a7a2630b 100644 --- a/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.h +++ b/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.h @@ -73,6 +73,16 @@ namespace MGITest { // Real session state/watermarks. Only the client thread reads this snapshot; // the scheduling observer below reads producerParked separately. bool runAheadArmed = false; + // P7 wave 2 package C, OQ-10 (CONTRACT-P7 §5.4): kCapResidentSubData, as the CLIENT's + // caps mirror received it. This is the wire-level half of the bit's proof - the unit + // pair asserts that the SERVER derives it from its own resource table, and this says + // the client got what the server published, which is the only thing the frontend's + // MGPipeResourceOpsHaveSubDataResident can read under a transport. + // + // FALSE where the peek cannot look, like every field here, so a reader must have + // passed SplitRuntimeSkipReason() first: "no mirror" and "the bit was withheld" are + // the same false and completely different facts. + bool residentSubDataCap = false; unsigned int presentCredit = 0; unsigned long long appliedSeq = 0; unsigned long long retiredSeq = 0; diff --git a/MobileGL/MG_IntegrationTest/Scenarios/CtWireScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/CtWireScenario.cpp index 2295dd01..03942b2e 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/CtWireScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/CtWireScenario.cpp @@ -206,6 +206,67 @@ namespace { << "Ct.TextureDeath.pixels - the recycled texture read back the dead twin's content"; } + // ------------------------------------------------------------------------------------ + // P7 wave 2 package C, OQ-10 (CONTRACT-P7 §5.4): the resident-sub-data capability, read + // on the CLIENT, on a real session, on both backends and all three transports. + // + // WHAT IT PINS AND WHY IT IS HERE RATHER THAN IN A BUFFER SCENARIO. `kCapResidentSubData` + // is the one thing the frontend can ask, under a transport, about whether the side that + // will APPLY a write implements the resident arm: the client has no op table of its own + // to probe (MGPipeResourceOpsHaveSubDataResident says so in as many words) and reads this + // bit instead. Until this package nothing ever ORed it into the server's mask, so the + // answer was permanently "no" - which silently pinned BOTH backends to the ordered + // in-place host memcpy and made Magma's own SubDataResident arm + // (VkBufferManager.cpp's g_vulkanWireResourceOps) dead code on the wire. A capability + // that is never published is indistinguishable from one that does not exist, and no + // pixel, GL query or error can tell the two apart - so the bit itself is the assertion. + // + // THE SERVER HALF IS ASSERTED ELSEWHERE, deliberately: RemoteClientTest's + // MagmaTransportPublishesRealBufferConsumersWithoutRunAhead pins that the bit is DERIVED + // FROM THE SERVER'S OWN RESOURCE TABLE (`ops->SubDataResident != nullptr`) and never from + // a backend enum, which is ID-39's lesson. This case pins the other half - that what the + // server published actually reached the client - and the two together are what make the + // frontend's read of it mean anything. + // + // WHAT IT DOES NOT PIN, AND THE REASON IS R-6 RATHER THAN AN OMISSION. This does not + // assert that a `buffer_subdata_resident` record (opcode 49) crossed, because under a + // transport none can yet: the resident arm is reached only from a store that is GPU + // RESIDENT, residency comes only from AdoptPersistentMap, and every persistent-map + // acquisition declines under split by R-6 (PipeApply.cpp's MGPipeApplyMapPersistent: + // "A SPLIT BUILD RUNS AT TIER T2 AND DECLINES EVERY ACQUISITION, ALWAYS"; T0/T1 are + // P11's and AdoptTierIsEmulate is a named Fatal for them). So the capability is wired + // AHEAD of its consumer on purpose, and `rsd=` on the PipeStats line is the counter that + // will show the records the day P11 lands a tier that adopts. See notes/p7/magma-c.md. + TEST_F(CtWireScenario, TheServerPublishesTheResidentSubDataCapabilityFromItsOwnTable) { + if (!Ready()) return; + // A REAL WORKLOAD FIRST, and it is not decoration: ScenarioFixture's arming rule + // (ScenarioFixture.h:90) fails a case that runs in an armed split lane without moving + // the client encoder's record ordinal, because a case that emits nothing cannot tell + // "the transport resolved and carried this session" from "the emit table resolved and + // then put nothing on the wire". The bit below is a property OF that session, so the + // session has to have carried something before the reading means anything. + glClearColor(0.25f, 0.5f, 0.75f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + GLubyte pixel[4]{}; + glReadPixels(1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "Ct.ResidentSubDataCap.workload"; + const int expected[4] = {64, 128, 191, 255}; + for (int i = 0; i < 4; ++i) EXPECT_NEAR(pixel[i], expected[i], 1) + << "Ct.ResidentSubDataCap.pixels"; + + const auto runtime = PeekSplitRuntime(); + if (!runtime.peekAvailable || !runtime.sessionActive) { + GTEST_SKIP() << "no split runtime peek or no live client session: 'could not look' " + "and 'the bit was withheld' are the same false"; + } + EXPECT_TRUE(runtime.residentSubDataCap) + << "the client's caps mirror did not receive kCapResidentSubData on a " + << runtime.transportName << " session with backend " << Gl().BackendName() + << ". The server publishes it in MG_Backend/Init.cpp's InitServerRoleCommon from " + "its own wire resource table; without it every resident write falls back to " + "the in-place memcpy and opcode 49 never crosses on either backend"; + } + TEST_F(CtWireScenario, FramebufferDeathCrossesAndTheRecycledSlotAnswersTheNewObject) { if (!Ready()) return; // Backend-agnostic for the texture case's reason (P7 wave 2 package C): both diff --git a/MobileGL/MG_Test/Wire/RemoteClientTest.cpp b/MobileGL/MG_Test/Wire/RemoteClientTest.cpp index c28ab529..3f6e24d4 100644 --- a/MobileGL/MG_Test/Wire/RemoteClientTest.cpp +++ b/MobileGL/MG_Test/Wire/RemoteClientTest.cpp @@ -705,6 +705,15 @@ TEST(CapsMirrorTest, TheReadAccessorsStillAnswerFromThePlaceholder) { CapsMirror mirror; EXPECT_FALSE(mirror.Valid()); EXPECT_EQ(mirror.Generation(), 0u); + // (F1 made ServerConsumes on a placeholder a refusal, so the two decision reads that used to + // sit here belong to the case above; the READ accessors below are what this case pins.) + // P7 wave 2 package C, OQ-10: the NEGATIVE half of the resident-sub-data pair. It used to + // be vacuous - nothing in the tree ever set kCapResidentSubData, so every reading of it was + // false - and MagmaTransportPublishesRealBufferConsumersWithoutRunAhead below is the + // positive half that makes this one mean "a mirror with no snapshot withholds the bit" + // rather than "the bit does not exist". Withholding is the SAFE direction: the client then + // keeps the ordered in-place host write (BufferObject::LandBytesIntoResidentStore) instead + // of emitting opcode 49 at a server that might have no arm for it - ID-39 in miniature. EXPECT_FALSE(mirror.HasCap(kCapResidentSubData)); EXPECT_EQ(mirror.CallMask(), 0u); EXPECT_EQ(mirror.Backend(), BackendType::Unknown); @@ -789,6 +798,24 @@ TEST(CapsMirrorTest, MagmaTransportPublishesRealBufferConsumersWithoutRunAhead) EXPECT_NE(ops->MapPersistent, nullptr); if (ops->MapPersistent) EXPECT_EQ(ops->MapPersistent({7, 1}, 64, nullptr), nullptr) << "Magma must not donate a server address as a client persistent map"; + // P7 wave 2 package C, OQ-10 (CONTRACT-P7 §5.4): THE TABLE DECIDES THE BIT, and + // this is the assertion that says so rather than asserting a constant. Magma + // registers a SubDataResident arm (VkBufferManager.cpp's g_vulkanWireResourceOps), + // so the published mask must carry kCapResidentSubData - and it must carry it + // BECAUSE of the arm, which is why the two sides are compared to each other + // instead of both to `true`. Written this way the case also covers the backend + // that does NOT have the arm, on the day one exists: no enum is consulted here, + // and deleting the `|=` in MG_Backend/Init.cpp reds exactly this line (red-once). + EXPECT_EQ((server.CallMask() & static_cast(kCapResidentSubData)) != 0, + ops->SubDataResident != nullptr) + << "kCapResidentSubData must be published from the server's own wire resource " + "table (ID-39: never from the backend enum). Without it " + "MGPipeResourceOpsHaveSubDataResident answers false under every transport " + "and both backends fall back to the in-place memcpy, which makes Magma's " + "SubDataResident arm dead code on the wire"; + EXPECT_NE(ops->SubDataResident, nullptr) + << "Magma's wire resource table lost its resident sub-data arm; the bit above " + "would then correctly go dark and opcode 49 would stop crossing"; } EXPECT_TRUE(ClientSessionInstance().RunAheadArmed()); ClientSessionInstance().Stop(); diff --git a/MobileGL/MG_Util/Metrics/PipeStats.cpp b/MobileGL/MG_Util/Metrics/PipeStats.cpp index 3f4c12db..a7b0da49 100644 --- a/MobileGL/MG_Util/Metrics/PipeStats.cpp +++ b/MobileGL/MG_Util/Metrics/PipeStats.cpp @@ -236,6 +236,7 @@ namespace MobileGL::MG_Util::PipeStats { "render-state-cso-mints", "render-state-cso-binds", "map-persistent-roundtrips", "framebuffer-emissions", "sampler-view-emissions", "sampler-state-emissions", "shader-image-emissions", "client-tex-upload-emissions", "tex-remint-pulls", + "resident-subdata-emissions", "residual-pulls", "server-verb-boundaries", "wire-records", #endif }; @@ -612,6 +613,14 @@ namespace MobileGL::MG_Util::PipeStats { // Espryt had already allocated and then had to re-mint image-bindable, replaying its // levels from the client's shadow, because ImageBindableHint reached it too late. line += " trp=" + std::to_string(calls[static_cast(CallClass::TextureRemintPulls)]); + // rsd is P7's resident sub-data emission count (OQ-10): one per buffer_subdata_resident + // record (opcode 49) the client put on the wire. It is the ONLY observable that + // separates the resident arm from the in-place memcpy beside it - the bytes in the + // buffer are identical either way - so it is what the white-box cases read. Zero under + // monolith by construction, and zero under split too until the server publishes + // kCapResidentSubData off its own wire resource table (MG_Backend/Init.cpp). + line += " rsd=" + + std::to_string(calls[static_cast(CallClass::ResidentSubDataEmissions)]); // rsp is P5's residual-pull count: reads, on the server side, of a PipeInputs field no // pushed record supplies, answered out of the client's residual fill under the verb // barrier. It is the SIZE OF THE P6/P7/P8 DEBT and it is published on this line rather diff --git a/MobileGL/MG_Util/Metrics/PipeStats.h b/MobileGL/MG_Util/Metrics/PipeStats.h index 0acf43f0..0c587bbe 100644 --- a/MobileGL/MG_Util/Metrics/PipeStats.h +++ b/MobileGL/MG_Util/Metrics/PipeStats.h @@ -190,6 +190,25 @@ namespace MobileGL::MG_Util::PipeStats { // sync is allocated image-bindable up front and never counts. `trp=` on the summary // line; the number that decides MOBILEGL_PIPE_TEXEL_RETAIN_MB's default. TextureRemintPulls, + // P7 wave 2 package C, OQ-10 (CONTRACT-P7 §5.4). `rsd=` - THE RESIDENT SUB-DATA + // EMISSION COUNT: one per `buffer_subdata_resident` record (opcode 49) the CLIENT + // actually put on the wire for a buffer whose store the server owns. + // + // IT EXISTS BECAUSE THE TWO ARMS ARE INDISTINGUISHABLE IN PIXELS AND IN GL. A write to + // a resident store either crosses as opcode 49 or lands in the ordered in-place host + // memcpy beside it (MG_State/.../BufferObject.cpp's LandBytesIntoResidentStore), and + // both produce identical bytes in the buffer - which is exactly how the capability + // stayed unpublished for a whole phase with every lane green: `kCapResidentSubData` + // was never ORed into the server's mask, MGPipeResourceOpsHaveSubDataResident + // therefore answered false under every transport, and Magma's SubDataResident arm was + // dead code on the wire that nothing could see. A counter is the only observable that + // tells the two apart from a scenario, so it is what the white-box cases assert on. + // + // Counted at the EMITTER (MG_Impl/Pipe/PipeFill.cpp's MGPipeEmitBufferSubDataResident), + // once per record rather than once per call, because a write larger than the content + // chunk cap is cut into several records and the number that matters is how many + // crossed. Push-only like the nine above: a pull build has no wire and no opcode 49. + ResidentSubDataEmissions, // P5's, and push-only for the same reason as the nine above. // // `rsp` - THE SIZE OF THE P6/P7/P8 DEBT. One per read, on the server side, of a diff --git a/docs/Disaggregated/notes/p7/magma-c.md b/docs/Disaggregated/notes/p7/magma-c.md index fd652eb1..8e6b298a 100644 --- a/docs/Disaggregated/notes/p7/magma-c.md +++ b/docs/Disaggregated/notes/p7/magma-c.md @@ -78,3 +78,76 @@ Restored and rebuilt: 8/8 green again. This is precisely the shape package L's t predicted (`notes/p7/magma-two-process-first-run.md` §6): spawn and tcp were already carried by `InstallClientWireTables`' emitter, and **inproc was the only hole** — which is the hole this table closes. + +--- + +## Slice 2 — OQ-10 `kCapResidentSubData` (§5.4) + +**What landed.** `InitServerRoleCommon` ORs `kCapResidentSubData` when +`g_resourceOpsAtStep2->SubDataResident != nullptr` — the pointer that function already pinned at +step 2 and that step 5 already refuses to see swapped. Never from `ActiveBackendType` (ID-39). +`rsd=` joins the `emit[]` bracket on the PipeStats summary line, counted at +`MGPipeEmitBufferSubDataResident` once per record. `SplitRuntimePeek` gains +`residentSubDataCap`, read through the same `CapsMirrorInstance().HasCap()` accessor the +frontend's `MGPipeResourceOpsHaveSubDataResident` reads. + +### BLOCKER FOR THE INTEGRATOR: §5.4's white-box case is not achievable at this tree + +§5.4 asks for a case in `integration-magma-buffers` and one in `integration-split` proving that +the emitted record is `BufferSubDataResident` (opcode 49). **Neither can exist today, and the +reason is R-6 rather than anything this package did or did not do.** The chain: + +| step | site | verdict under split | +|---|---|---| +| the resident emitter runs only from a resident store | `BufferObject.cpp` `UploadSubData` → `if (m_resource.IsGpuResident())` | — | +| residency comes only from `AdoptPersistentMap` | `PipeResource.h:181`, the only writer of `m_gpuMapped` | — | +| adoption needs a non-null `map_persistent` answer | `BufferObject.cpp` `TryAdoptLargeStorage` / `EnsureGpuResidentStorage` / `AcquireMemoryRange` | — | +| the answer is a decline | `PipeApply.cpp:2331` "**A SPLIT BUILD RUNS AT TIER T2 AND DECLINES EVERY ACQUISITION, ALWAYS**" | always | +| the tier gate has no other implemented value | `PersistentMapTracker.cpp:1033` `AdoptTierIsEmulate` — T0/T1 are P11's and are a named `Fatal{UnimplementedAdoptTier}` | always | + +So under any transport no buffer is ever GPU-resident, `LandBytesIntoResidentStore` is +unreachable, and opcode 49 cannot cross. The capability is therefore wired **ahead of its +consumer**, deliberately: when P11 lands a tier that adopts, the bit is already correct and +already derived from the table, and `rsd=` is the counter that will show the records. The +plan's phrasing ("both backends fall back to the in-place memcpy at `BufferObject.cpp:~711-716`") +is one step optimistic — under split the code does not reach that fallback either, it takes the +plain shadow `Memcpy` in `UploadSubData` above it. + +**What is proved instead, and it is both halves of the bit.** + +- *Server half* — `CapsMirrorTest.MagmaTransportPublishesRealBufferConsumersWithoutRunAhead` + (`RemoteClientTest.cpp`) now asserts + `(mask & kCapResidentSubData) != 0 == (ops->SubDataResident != nullptr)`. The two sides are + compared to each other rather than both to `true`, so the case also covers a backend WITHOUT + the arm on the day one exists. +- *Client half* — the new `CtWireScenario.TheServerPublishesTheResidentSubDataCapabilityFromIts + OwnTable`, registered through the `ctCase` list so tier 2 replays it: green on + `DirectGLES.{Split,Spawn,Tcp}` (`integration-{split,spawn,tcp}`) and + `DirectVulkan.{Split,Spawn,Tcp}` (`integration-magma-all-*`), 6/6. +- *Negative half* — `CapsMirrorTest.APlaceholderMirrorConsumesNothing` keeps the + no-snapshot-withholds-the-bit reading, which stopped being vacuous the moment anything set it. + +**RED-ONCE (executed, both levels).** `capBits |= MG_Pipe::kCapResidentSubData;` → `capBits |= 0;`: + +``` +RemoteClientTest.cpp:692: Failure +Expected equality of these values: + (server.CallMask() & static_cast(kCapResidentSubData)) != 0 + Which is: false + Which is: true + +25% tests passed, 6 tests failed out of 8 + DirectGLES.{Split,Spawn,Tcp}.Ct....ResidentSubDataCapability... ***Failed + DirectVulkan.{Split,Spawn,Tcp}.Ct....ResidentSubDataCapability... ***Failed +CtWireScenario.cpp:262: Failure +Value of: runtime.residentSubDataCap + Actual: false +``` + +Restored: 11/11 unit and 6/6 integration green again. + +**One incidental finding, recorded because it cost a cycle.** The first cut of the capability +case did no GL work and failed on `ScenarioFixture.h:90` — an armed split lane refuses a case +whose encoder ordinal did not move, because "emitted nothing" and "resolved the transport and +then put nothing on the wire" are the same observation. The case now clears and reads back +first. This is the same F1 arming rule package L hit on tier 3.