mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MG_Backend, MG_Impl, MG_Util, MG_Test, MG_IntegrationTest, docs] (P7): the server publishes kCapResidentSubData off its own wire resource table
CONTRACT-P7 §5.4, OQ-10. InitServerRoleCommon computes the whole capability mask and
never ORed this bit, so MGPipeResourceOpsHaveSubDataResident answered false under every
transport and BOTH backends were pinned to the ordered in-place host memcpy - which made
Magma's SubDataResident arm (VkBufferManager.cpp's g_vulkanWireResourceOps) dead code on
the wire even though the arm has been there since P5f.
The bit is derived from g_resourceOpsAtStep2->SubDataResident, i.e. from the pointer this
function already pinned at step 2 and that step 5 already refuses to see swapped - never
from ActiveBackendType. That is ID-39's lesson and not a style preference: a mask is a
statement about THIS backend's registered table, and a bit derived from the enum would
keep claiming the capability for a build, a flavour or a future backend whose table does
not carry the arm.
`rsd=` joins the emit[] bracket on the PipeStats summary line, counted at the emitter once
per buffer_subdata_resident record rather than once per call, because a write past the
content chunk cap is cut into several records. It is the only observable that separates
the resident arm from the memcpy beside it: the bytes in the buffer are identical either
way, which is exactly how the capability stayed unpublished for a phase with every lane
green.
WHAT THIS DOES NOT YET PROVE, and R-6 is the reason rather than an omission. §5.4 asks for
a white-box case showing opcode 49 on the wire. None can exist at this tree: the resident
arm is reached only from a GPU-RESIDENT store, residency comes only from
AdoptPersistentMap, and every persistent-map acquisition declines under split by R-6
(PipeApply.cpp:2331 "A SPLIT BUILD RUNS AT TIER T2 AND DECLINES EVERY ACQUISITION,
ALWAYS"; T0/T1 are P11's and AdoptTierIsEmulate is a named Fatal for them). So the
capability is wired AHEAD of its consumer, deliberately, and `rsd=` is what will show the
records the day P11 lands a tier that adopts. Filed for the integrator in
notes/p7/magma-c.md.
What IS proved is both halves of the bit itself. RemoteClientTest's Magma bootstrap case
now asserts the published bit EQUALS `ops->SubDataResident != nullptr` - the two compared
to each other rather than both to true, so the case also covers a backend without the arm
on the day one exists - and a new CtWireScenario case reads the bit out of the client's
own caps mirror, through the same accessor the frontend reads, on both backends and all
three transports.
RED-ONCE, both levels. Deleting the OR reds
CapsMirrorTest.MagmaTransportPublishesRealBufferConsumersWithoutRunAhead
(RemoteClientTest.cpp:692, "Which is: false / Which is: true") and all SIX arms of
DirectGLES/DirectVulkan x {Split,Spawn,Tcp} Ct.TheServerPublishesTheResidentSubData
CapabilityFromItsOwnTable ("the client's caps mirror did not receive
kCapResidentSubData"). Restored: green.
Also carries the P4a death-helper comment from the previous commit's sweep: it no longer
says Magma installs no death table, because it now does and still frees no slot.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#if defined(MGITEST_SPLIT_RUNTIME_PEEK) && !defined(__ANDROID__)
|
||||
#include <Config.h>
|
||||
|
||||
#include <MG_Remote/Client/CapsMirror.h>
|
||||
#include <MG_Remote/Client/ClientSession.h>
|
||||
#include <MG_Remote/Client/EmitTables.h>
|
||||
#include <MG_Remote/Server/ServerLoop.h>
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Uint64>(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();
|
||||
|
||||
@@ -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<Uint32>(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<Uint32>(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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Uint64>(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.
|
||||
|
||||
Reference in New Issue
Block a user