- 144 commits from785fed0b0onward were rewritten to drop the Co-Authored-By trailer the project forbids; origin/feat/disaggregated moved from 1135c664 to3c80cd62(same trees) - the old->new map is ~/w7/logs/sha-map-20260922.txt; every 7..40-hex citation in the Disaggregated docs and the MG_Remote contracts that matched an old SHA is replaced by the same-length prefix of its rewrite; no other content changes
27 KiB
P7 wave 2 package C — Magma object death, resident sub-data, the reflection archive, the vertex-layout split
For the integrator, first
Nine things this package changes that live outside its own files or outside its own slices.
CONTRACT-P7.md is the integrator's file (§0 "怎么改"), so none of these edit it.
- §12's debt row is retired. "Magma 两进程 client 在
StateObjectDeathOps落地前ObjectDeaths=0;CtWireScenario的死亡用例在 Magma 臂按名 skip" — the table landed, both skip guards are deleted, and the two cases are green onDirectVulkan.{Split,Spawn,Tcp}. §2.3's "Magma 自己的StateObjectDeathOps表(wave 2-C)落地前…按名 skip" can go with it. - §3.1's pinned census moves: 15 sites / 4 files → 13 / 3.
Renderer/WireDraw.incleaves the list entirely.scripts/ci/fatal_census.pyis unchanged (79 / 43 / 3 / 0) — the funnels were already converted in wave 0, so retiring aMagmaWireFatalCALL moves no abort count. - §3.2 says "the other six" for
vertex-layout; there are seven.offset-overflowpost-dates the audit. It is masked with the rest; the reasoning is in slice 4 below. - §5.4's white-box case (opcode 49 on the wire) is not achievable at this tree, and the
blocker is R-6, not this package. The full chain is in slice 2. The capability is wired
ahead of its consumer on purpose;
rsd=on the PipeStats line is what will show the records the day P11 lands an adoption tier. - §5.3's
g_programResourceCachesdeletion is achieved in the disaggregated build only. The archive member has to be#if MOBILEGL_BUILD_DISAGGREGATEDfor G1, so the pull build keeps the cache. One-line change the day that pin is retired. P13. - A family-word question.
buffer-windowis now a permanent protocol error going throughMagmaWireFatal, whose family isUnmigratedVerb. The honest word isProtocolCorruption;MG_Pipe::MGPipeFatalFamilycarries only two words and its own comment says a third is where the choice gets argued. §3.3 keeps the funnels onUnmigratedVerbdeliberately so the refusal census stays comparable to P5b. Flagged, not decided. - Gating lane counts grow (grow-only, G14-legal):
integration-magma-split73 → 75,-spawn52 → 54,-tcp54 → 56,integration-split186 → 187,integration-spawn102 → 103,ctest -L unit2402 → 2403. - One ratchet symbol changes bucket without changing the total (186, monotone green):
p3b-p4b-espryt14 → 13 andboth-backends60 → 61, because slice 3's monolith consumer makesDirectVulkan.cppreference a frontend symbol onlyDirectGLESreferenced before.p7-magma— P7's own target bucket — is unchanged at 101. Nothing in this package was aimed at gate 4; the §4.2 path to 101 → 0 is package B's(B'). - The worktree tool leaves
MOBILEGL_ITEST_VK_ICDempty and CMake warns about it; every Magma entry then runs whatever ICD the loader finds first. This package reconfiguredbuild-splitwith/usr/share/vulkan/icd.d/lvp_icd.json, the pin~/w7/pipe/build-splitalready carries. Worth adding top7_worktree.shso the next package does not have to notice it.
Base: p7/magma-c off feat/disaggregated @ 4f2d4c4e (pipe HEAD at branch time).
Build: build-split = -DMOBILEGL_BUILD_DISAGGREGATED=ON -DMOBILEGL_BUILD_DISAGGREGATED_INPROC=ON -DMOBILEGL_PIPE_PUSH=ON -DMOBILEGL_BUILD_INTEGRATION_TEST=ON, ICD pinned to
/usr/share/vulkan/icd.d/lvp_icd.json (the same lavapipe ICD ~/w7/pipe/build-split pins; the
worktree tool leaves MOBILEGL_ITEST_VK_ICD empty and CMake warns about it).
Contract rows: CONTRACT-P7 §5.5 (StateObjectDeathOps), §5.4 (OQ-10), §5.3 (OQ-8), §3.2
(vertex-layout, vertex-format-conversion). Discipline: rule I (no std::abort() on a Magma
wire arm), rule J / R-16 (every fix red once on a two-process arm first).
Gates, on the four slices together
| gate | base (4f2d4c4e) |
after | |
|---|---|---|---|
ctest -L unit |
2402 | 2403 / 2403 | +1 = the OQ-8 order case |
integration-magma-split |
73 (71 P + 2 S) | 75 / 75, 0 skipped | +2 vertex-layout; the 2 skips were the death cases |
integration-magma-spawn |
52 (50 + 2) | 54 / 54, 0 skipped | |
integration-magma-tcp |
54 (52 + 2) | 56 / 56, 0 skipped | |
integration-split |
186 | 187 / 187 | +1 = the OQ-10 capability case |
integration-spawn |
102 | 103 / 103 | |
integration-magma-full-split |
453 P / 57 S / 0 red | 513 entries, 0 red, 55 skipped → 458 P / 55 S | better on both counts |
scripts/ci/fatal_census.py |
79 / 43 / 3 / 0 | 79 / 43 / 3 / 0 | unchanged, as expected |
scripts/ci/spawn_lane_parity.py |
green | green | all tiers equal after normalisation |
grep -rn "@P7" |
15 sites / 4 files | 13 / 3 | WireDraw.inc leaves the list |
link_ratchet.py --assert-monotone |
186 | 186, "unchanged" | green |
G1 (build-linux, pull) |
.text 0xa52203 |
.text 0xa52203 (10822147, +0) |
.data/.bss/.rodata/total all +0; 27837 → 27837 defined symbols, 0 added / 0 removed / 0 resized / 0 renamed; the .so is byte-identical on disk (19143824) |
Ratchet buckets. AFTER: p7-magma 101, p3b-p4b-espryt 13, both-backends 61,
p6-core 11, total 186. CONTRACT-P7 §4.1 records the 8bb6309a baseline as
101 / 14 / 60 / 11. So one symbol sits in both-backends that the baseline recorded under
p3b-p4b-espryt; the total, the monotone assertion and P7's own target bucket (101) are all
unchanged. HONESTY NOTE: I did not separately re-measure the buckets at 4f2d4c4e, so I cannot
attribute that single move to this package rather than to the two commits between the baseline
and this branch point. Nothing here was aimed at gate 4 — §4.2's path from 101 to 0 is package
B's (B'). (An attempt to identify the mover by rebuilding one object at the base failed to
compile and was abandoned; the clean way is --bucket --verbose-buckets on a base build.)
Slice 1 — Magma StateObjectDeathOps (§5.5)
What landed. MG_Backend/DirectVulkan/DirectVulkan.cpp gains
g_magmaStateObjectDeathOps and InstallStateObjectDeathOps(), installed from
BackendObject_DirectVulkan::Initialize() — i.e. from ServerLoop::CreateBackend, step 1 of
InitServerRoleCommon, which both Magma server roles (inproc and the spawn/TCP child) run and
which is before any client session exists. Guarded #if MOBILEGL_BUILD_DISAGGREGATED && MOBILEGL_PIPE_PUSH, so the pull build compiles nothing new (G1).
The one arm, and why it is only one. Espryt's g_glesStateObjectDeathOps has two jobs: emit
the wire object_death record off the apply thread, and otherwise destroy a backend twin keyed
by lifetime id. Magma has no lifetime-id-keyed twin at all — its two client-minted kinds
(VertexElementsCso, Buffer) are keyed on {slot, gen} by MagmaPipeIdentityTables and the
other six are still reached from their frontend objects — so the Magma table is exactly the EMIT
arm and nothing else.
Correction to the plan's wording (worth the integrator's attention). Plan §1.3 and the task
brief describe the gap as a client SLOT leak provable with PipeSlotPeek. It is not, and has not
been since P4a: MG_Impl/Pipe/PipeFill.cpp's NotifyAndFree frees the client slot
unconditionally and backend-neutrally, so PipeSlotPeek never saw a Magma leak and does not see
a change now. What leaked was the server's twin. A framebuffer has no wire delete opcode at
all (BRIEF-P4A D-I2), so object_death is its only death delivery; with no consumer installed
under Magma inproc, the server's record stayed Live for the life of the session. The
CtWireScenario framebuffer case now reads BOTH numbers — the server's deaths tally and the
client's PipeSlotKind::Framebuffer live count — so a future regression in either half is
attributable.
Cleanup of the Magma special cases. The two executable ones are the CtWireScenario skip
guards (:153 / :209), both deleted. Everything else the audit counted is comment text, and it
is reworded rather than deleted because the underlying argument survives: PipeSlotPeek.h,
MagmaPipeArms.h (the age-sweep-instead-of-death-notice paragraph — still true, because the new
table never touches the per-renderer identity tables), PipeMutation.h, PipeFill.cpp,
VertexArrayObject.cpp, DirectGLES/Managers.cpp, HandleRecycleScenario.cpp,
WireTables.cpp.
The install-order question, answered rather than assumed. SetStateObjectDeathOps is a bare
last-writer-wins pointer with no stacking, so two installers could in principle race.
They cannot meet: Magma's runs only in a process that OWNS a DirectVulkan backend, and
InstallClientWireTables' arm runs only under Transport == Spawn, which is by construction the
process that owns no backend (InitSplitRoles builds a BackendObject_Remote there). The
red-once below measures exactly that split of responsibility.
Proof (green). ctest -R "DirectVulkan\.(Split|Spawn|Tcp)\.Ct\..*Death" — 6/6 pass
(integration-magma-all-{split,spawn,tcp}), plus the two TcpServer fixtures. DirectGLES
control ctest -R "DirectGLES\.(Split|Spawn|Tcp)\.Ct\..*Death" — 6/6 pass, unchanged.
RED-ONCE (executed). BackendObject_DirectVulkan::Initialize()'s call changed to
if (false) InstallStateObjectDeathOps();, rebuilt, lane re-run:
1/8 DirectVulkan.Split.Ct.CtWireScenario.TextureDeath... ***Failed
2/8 DirectVulkan.Spawn.Ct.CtWireScenario.TextureDeath... Passed
4/8 DirectVulkan.Tcp.Ct.CtWireScenario.TextureDeath... Passed
5/8 DirectVulkan.Split.Ct.CtWireScenario.FramebufferDeath... ***Failed
6/8 DirectVulkan.Spawn.Ct.CtWireScenario.FramebufferDeath... Passed
7/8 DirectVulkan.Tcp.Ct.CtWireScenario.FramebufferDeath... Passed
75% tests passed, 2 tests failed out of 8
CtWireScenario.cpp:264: Failure
Expected: (afterCounters.deaths) > (deathsBefore), actual: 0 vs 0
Restored and rebuilt: 8/8 green again. This is precisely the shape package L's temporary probe
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 totrue, so the case also covers a backend WITHOUT the arm on the day one exists. - Client half — the new
CtWireScenario.TheServerPublishesTheResidentSubDataCapabilityFromIts OwnTable, registered through thectCaselist so tier 2 replays it: green onDirectGLES.{Split,Spawn,Tcp}(integration-{split,spawn,tcp}) andDirectVulkan.{Split,Spawn,Tcp}(integration-magma-all-*), 6/6. - Negative half —
CapsMirrorTest.APlaceholderMirrorConsumesNothingkeeps 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.
Slice 4 — the vertex-layout split and vertex-format-conversion (§3.2)
What landed. BuildWireVertexInput answered eight conditions with one false, and the
caller answered that with a P7-marked MagmaWireFatal — a session kill where the monolith arm
masks the attribute out and declines the draw. Split by what the reason is ABOUT:
| reason | before | after |
|---|---|---|
buffer-window |
false → P7-marked Fatal |
false → named Fatal Magma:vertex-layout-buffer-window (no P7 marker: it is a permanent protocol error, not an unmigrated shape) |
attribute-shape, native-fp64-format, format-map, native-format-feature, converted-format-feature, element-size, offset-overflow |
false → Fatal |
MGLOG_E_ONCE + unsupportedAttribMask |= 1<<loc + continue, then the monolith arm's own pre-flight in WireDraw.inc declines the draw when a masked bit is one the program reads |
vertex-format-conversion |
P7-marked Fatal | MGLOG_E_ONCE + return false (decline, rule I (a)) |
unsupportedAttribMask now enters the wire layout hash, for the reason the monolith entry's
hash already carries it: two VAOs can otherwise produce identical bindings/attributes/divisors
and differ only in which enabled attribute was masked, and a blind hash would serve one the
other's pipeline. It was safe to omit only while the mask was always zero.
Count correction. CONTRACT-P7 §3.2 says "the other six". There are SEVEN non-buffer-window
return sites in the tree: offset-overflow post-dates the audit's count and is an arithmetic
guard rather than a shape one. It is masked with the rest — an attribute whose base offsets
cannot be added is one Vulkan cannot fetch, the monolith arm never computes that sum at all,
and masking is strictly safer than a Fatal for a number no application can reach on purpose.
Family-word question for the integrator. buffer-window now goes through MagmaWireFatal,
whose family is UnmigratedVerb. The honest word for "the record's own numbers are impossible"
is ProtocolCorruption. MG_Pipe::MGPipeFatalFamily carries only UnmigratedVerb and
RoleViolation and its own comment says a third "costs a row here and an arm in the adapter -
which is the point, because that arm is where the choice gets argued". Not taken here: it is a
cross-package edit (MG_Pipe/PipeSessionFail.h + the MG_Remote adapter) and §3.3 records that
the three Magma funnels keep UnmigratedVerb deliberately, because renaming invalidates the
refusal census kept since P5b. Flagged rather than decided.
Census. grep -rn "@P7" MobileGL/ --include=*.cpp --include=*.inc --include=*.h:
15 sites / 4 files → 13 sites / 3 files. Renderer/WireDraw.inc leaves the list entirely
(both of its marked refusals are retired). scripts/ci/fatal_census.py is unchanged at
79 abort sites over 20 files, 43 distinct family words, 3 refusal words, 0 unmarked — the
funnels were already converted in wave 0, so retiring a MagmaWireFatal call moves no
std::abort() count. (Note for anyone editing near this row: the census counts the marker
literally, so a COMMENT that spells it raises the number. The first cut of this slice pushed
15 → 16 that way.)
New scenario. Scenarios/MagmaVertexLayoutScenario.cpp, DirectVulkan only — Espryt hands
GL_FIXED to a driver that supports it, so the same case under the same name would assert a
different fact there. DataType::Fixed32 has no arm in ToVkVertexFormat at all, which is
what makes it the one unmappable shape reachable on host lavapipe; CONTRACT-P7 §2.5 records
that no other P7-marked refusal is (they need real hardware), so without this shape the
retirement would have had no host gate.
AnUnmappableVertexFormatDeclinesTheDrawInsteadOfEndingTheSession— the program READS theGL_FIXEDarray, so the draw declines and the framebuffer keeps the clear colour.AnUnreadUnmappableArrayDoesNotStopTheRestOfTheDraw— the array is enabled and unmappable but unread, so the draw lands. This is what stops the first case being satisfied by "decline every draw that mentions GL_FIXED".
Registered on DirectVulkan.{Split,Spawn,Tcp}.VertexLayout. AND picked up by the ambient
monolith DirectVulkan. discovery, so the A/B is direct: the same two assertions on the wire
arm and on the monolith arm.
Proof (green). 8/8 — monolith DirectVulkan. ×2, Split ×2, Spawn ×2, Tcp ×2.
RED-ONCE (executed). format-map put back on the protocol verdict
(return reject("format-map")):
70% tests passed, 3 tests failed out of 10
#4238 DirectVulkan.Split.VertexLayout...Unmappable... Subprocess aborted***Exception
MGPipe: Fatal{UnmigratedVerb, "Magma:vertex-layout-buffer-window"}
#4240 DirectVulkan.Spawn.VertexLayout...Unmappable... Subprocess aborted
#4242 DirectVulkan.Tcp.VertexLayout...Unmappable... Subprocess aborted
#3408 DirectVulkan.MagmaVertexLayoutScenario...Unmappable... Passed <- monolith control
The three split arms KILL THE PROCESS and the monolith arm stays green, which is exactly the divergence this slice closes. Restored: 10/10.
Lane arithmetic. The three new Split entries carry integration-magma-split, so that
gating lane grows 73 → 75 (and -spawn 52 → 54, -tcp 54 → 56). Growth only; G14's name
sets are grow-only by rule.
Slice 3 — OQ-8: one reflection archive serves the storage-block consumers (§5.3)
What landed.
LinkArtifactsgainsVector<StorageBlockReflection> storageBlocks({name, binding, dataSize}),#if MOBILEGL_BUILD_DISAGGREGATEDonly — see G1 below.ProgramLinkTask::SnapshotStorageBlockIndexSpace()fills it in phase A, right afterSnapshotGlslangReflection.- Its
VisitFieldsrow and its own three-field table join the codec;kProgramArtifactsCodecVersion2 → 3;ProgramArtifactsSchemaFingerprint()and thereforewireFingerprintmove with them, which is expected and is what makes a mixed-version pair refuse at the handshake rather than at the first program. MagmaProgramSource::EnsureStorageBlocksis deleted, withm_storageBlocks,m_storageBlocksReady, the localStorageBlockstruct and#include <spirv_reflect.h>.DirectVulkan.cpp's monolith consumer reads the archive too, under#ifsymmetry.
Why phase A and not phase B. The obvious home is where the SPIR-V exists, and there is
exactly one function that sees both finished halves — ProgramSpirvTask::RunBody's L1 cache
insert — but its writes reach only the TRANSLATION CACHE. Phase A's artifacts has already
been moved into the ProgramObject by then (ProgramObject.cpp's JoinPendingLink), and
phase B is structurally forbidden from touching it. A field filled there would therefore be
present on a cache HIT and absent on every cold link — the worst of both, and invisible until
a second identical program linked. So the fill is glslang-derived, in phase A, and the
equivalence below is what makes that safe.
The equivalence is measured, not asserted. The consumers speak the index space
SPIRV-Reflect produces, and the archive now produces it from glslang's reflection instead.
ProgramTest.TheArchivesStorageBlockOrderIsTheOneSpirvReflectProduces runs the OLD algorithm —
SPIRV-Reflect over the real modules, sorted by (normalised name, binding), deduplicated across
stages — over a multi-stage program with an arrayed SSBO and an atomic-counter block, and
compares element for element. Measured reference on this tree:
{ "Blocks", "VertexOnly", "gl_AtomicCounterBlock_0", "FragmentOnly" }
which exercises all three of the cases that could have diverged: the instance array collapses
to one entry (glslang reflects per element, SPIR-V carries one descriptor with a count), the
synthesized atomic-counter block IS in the backend's space although GL's own
GL_SHADER_STORAGE_BLOCK enumeration excludes it, and FragmentOnly lands last because the
cross-stage dedupe walks stages in linked order.
MEASUREMENT — spvReflectCreateShaderModule on the Magma wire draw path, per frame.
Same binary, same workload, same window (frames=1 window=1 draws=1 acc=18, identical bytes),
DirectVulkan × inproc, StorageBufferRegrowScenario + the pipeline storage-block case, with
a temporary MGLOG_I("OQ8_REFLECT_CALL") at the call site:
| reflect calls | frames | draws | |
|---|---|---|---|
BEFORE (HEAD's per-draw EnsureStorageBlocks) |
4 | 1 | 1 |
| AFTER (reads the archive) | 0 | 1 | 1 |
The AFTER number is 0 structurally, not incidentally: the call site is gone and
MagmaProgramSource.h no longer includes spirv_reflect.h at all, which is the gate a future
edit would have to break on purpose. (A MagmaProgramSource is a borrowed view constructed
once per wire draw, so its mutable memo was cold every time it was read — that is why the
per-draw cost existed at all.)
The monolith half: DONE, under #if symmetry — with one caveat the integrator should see.
The task allowed deferring this if it could not be done without moving pull .text. It can,
and it is: GetShaderStorageBlockIndex / GetShaderStorageBlockBinding /
ClearProgramResourceCaches and the block-binding setter each have a disaggregated arm that
reads the archive and a #else arm that is the previous code verbatim. In the disaggregated
build that retires g_programResourceCaches, ProgramResourceCache, StorageBlockResource,
BufferVariableResource, AddBufferVariablesRecursive, NormalizeDescriptorName,
GetProgramResourceCache and the rehash hazard — the open-addressed-map dangling reference
that was a reproducible segfault in ProgramPipelineScenario's two storage-block cases.
Two things are worth stating plainly:
- The pull build keeps all of it. The archive member is disaggregated-only because one
more
VectorinLinkArtifactschanges its size, its implicit destructor and its move constructor, and G1 pins the pull build's.textbyte for byte. So §5.3's "g_programResource Caches随之删" is achieved in the disaggregated build and NOT in the pull one. Retiring the pull half is a one-line change (drop the guard on the member) the day that pin is retired or the member is moved — a P13 / integrator call, not a wave-2 one. - Nothing outside
DirectVulkan.cppread any of the retired machinery. Measured:BufferVariableResource,bufferVariables,activeVariablesandStorageBlockResourcehave no reference anywhere else in the tree, anddataSizewas written and never read —GL_BUFFER_VARIABLEandGL_ACTIVE_VARIABLESare answered byMG_Impl/GLImpl/Program/ProgramInterface.cppout of glslang's own reflection, which is a different index space. So the two arms differ in exactly the two functions their callers use and in nothing else.
One genuine behaviour improvement rides along: the monolith GetShaderStorageBlockBinding now
asks GetShaderStorageBlockBindingOverride on every read, over an immutable list, where it
used to read a binding patched into a mutable cache. Same answer by a shorter route, and a
rebind can no longer be lost to a cache rebuild that raced a state-version bump.
Proof (green). ctest -L unit 2403/2403 (2402 before this slice; +1 is the new case).
DirectVulkan.*(ProgramPipeline|StorageBufferRegrow) 60/60 — the monolith DirectVulkan.
arm and the Split / Spawn / Tcp arms together, which is what says both consumers were
switched.
G1 after the archive change: .text 0xa52203 = 10822147 → 10822147 (+0), 27837 → 27837
defined symbols, 0 added / 0 removed / 0 resized / 0 renamed, and the file is byte-identical
on disk (19143824 bytes both sides). The #if guards did their job; an unguarded call site
caught it once mid-slice (the pull build failed to compile, which is the loud version).
RED-ONCE (executed). SnapshotStorageBlockIndexSpace made to publish nothing:
ProgramTest.cpp:525: Failure
Which is: {}
Which is: { "Blocks", "VertexOnly", "gl_AtomicCounterBlock_0", "FragmentOnly" }
...
58% tests passed, 25 tests failed out of 60
3091 DirectVulkan.ProgramPipelineScenario.ComputeAndGraphicsStagesShareOnePipeline (SEGFAULT)
3092 DirectVulkan.ProgramPipelineScenario.AStageProgramsStorageBlockBindingReaches... (Failed)
3282 DirectVulkan.StorageBufferRegrowScenario... (Failed)
4179 DirectVulkan.Split.Buffers.StorageBufferRegrowScenario... (Failed)
4198 DirectVulkan.Spawn.Buffers.StorageBufferRegrowScenario... (Failed)
4217 DirectVulkan.Tcp.Buffers.StorageBufferRegrowScenario... (Failed)
4469 DirectVulkan.Split.ProgramPipelineScenario... (Failed)
The monolith arm reds beside the three transports, which is the evidence that the monolith consumer really was switched and not merely edited. Restored: 61/61.