# Open questions, full text (formerly the "Open questions" section of `ROADMAP.md`, moved here 2026-09-24) > [`ROADMAP.md`](ROADMAP.md) keeps only a one-line status per question; here is the full statement of each question, the source for the answered parts, and the criteria for the unanswered parts. Numbering is stable; cite as "open question N". Questions answered in P0 are no longer listed (spike A's domain, spike B's tiers, `posix_spawn` unavailable, the OOM probing idiom, retirement of `GetInteger64i_v`/`GetProgramiv`, D21 and the `RenderbufferObject` lifetime id, the dynamic accessor baseline). 1. **The real per-draw CPU cost of the client-side dirty walk.** Answered (P2–P4a): pushing did not net a reduction below the pull baseline; under Release the P2 boundary is +6–12%, P3a adds another +17–19 pt on rd12 where VAO switching is dense, P4a +3.4–5.7 pt on Espryt (`MEASUREMENTS.md` §3–§5). The user accepted on 2026-09-08, and performance has been recorded only since then. 2. **The incidence of texture re-mint pulls on real corpora.** Answered (P4a): negligible; the LRU is kept and the default stays 0 — 2 times in 780 statistics windows over 79 cases × two backends (`iris-photon` and `iris-derivative` one each, only in DirectGLES). But under P5b this path was a named Fatal, exactly the first block of these two traces plus `create-indirect` GLES (P9). **P9 W2 closed**: re-mint needs no pull — the server reads the GPU level, the store fills the pending box (Espryt), GPU→GPU copy (Magma); the census over 77 rows × {monolith, inproc} is still the same two times (both are render targets whose store has no bytes), and `OnTexturePullRequest` is deleted (see git history). 3. **Re-check of spike B's `untrusted_app` domain.** The tiers for both devices were measured in the `shell` domain; run `extmem_probe` once more from the application process (spike A's exec hook is already available). Do before P11. **2026-09-22 downgraded**: only meaningful for the same-machine arm's T0 / T1; the stream data plane does not need it. 4. **Wire granularity of render state.** Answered (P2): 16 boundaries / 15 chunks, 7 pipeline 396 B + 8 dynamic 772 B; CSO LRU 64 provisional, retuned in P13. 5. **Capabilities without storage.** Answered (P2): `FramebufferSrgb`, `DepthClamp`, `TextureCubeMapSeamless` all gained real storage, and `sizeof(RenderStateParameters)` is still 1168. 6. **The shape of the named UBO host payload (D-B8).** Magma repacks 331 KB of named UBO bytes per frame in the 26.3 world; Espryt 0. Either freeze the second variable-length tail shape, or take the alternative (Magma binds a resident `VkBuffer` range directly at the descriptor, a separate `dev` PR). P7. 7. **The cut seam in `MG_Util`.** The server needs SPIRV-Cross / ESSL translation cache / format handlers / POST probe; the client needs glslang and the reflection layer; whether `MG_Util` has a clean Transpile-vs-Reflect seam inside has not been audited. P7. 8. **Can one reflection archive serve three consumers.** The Espryt half is answered (P4a): yes, and no copy is needed. Magma's first consumer was answered by P5f fm: `MagmaProgramSource` takes everything from the wire `ProgramArchive`, no frontend object, no copy. The second (storage block table) today is **no** and has gotten worse: under split, `MagmaProgramSource::EnsureStorageBlocks` **re-runs SPIRV-Reflect every draw** (the monolith arm's `DirectVulkan.cpp:177-200` at least has a persistent cache); the answer is to add ordered `storageBlocks` to `LinkArtifacts` (the data is already in `blockReflection`), and both consumers read it instead. **Answered (P7 wave 2-C, `dce9953d..cf7ca59f`)**: `storageBlocks` enters the archive, both consumers read it, per-draw SPIRV-Reflect 4→0; the monolith half of `g_programResourceCaches` goes to P13 (G1). 9. **Can viewport-array replay fit into one `draw_vbo`**: **answered (2026-09-30, ID-P8-1)**: yes — the server replays it pass by pass within one draw, grouped by state (`DirectGLES.cpp:8421-8465`), and `ViewportArrayScenario` is green on each split arm. 10. **The `ResidentSubData` asymmetry.** Half answered (checked 2026-09-22): **Magma's implementation is complete** (`VkBufferManager.cpp:123` monolith table, `:148` wire table, `fff9d639f`). **Still unanswered: `kCapResidentSubData` is never published by the server** — `MG_Backend/Init.cpp:192-219` is the only production `SetCapabilityBits` call site in the whole tree, OR-ing only XFB, the three query bits, and the run-ahead bit; so `PipeFill.cpp:848` is always false under split, and **both backends under split fall back to the in-place memcpy at `BufferObject.cpp:711-716`**, making Magma's implementation dead code in split. **Answered (P7 wave 2-C, ID-P7-20)**: `InitServerRoleCommon` publishes `kCapResidentSubData` from the server's own wire resource table, and the mirror accepts it on receipt; the white-box check "what is emitted is opcode 49" is unreachable before the P11 adoption tier lands (§5.4 debt). 11. **The `SEG_STAGE` limit.** Content side answered (2026-09-20): **chunking is implemented** — the buffer range walk (`1e7c372e`) and the texture full-width slab (`9469d48e`) split into multiple `resource_subdata` records per `MGPipeStageChunkBytes()` (default 1/4 of 32 MiB = 8 MiB), so a single 128 MiB upload no longer needs an explicit `MOBILEGL_IPC_STAGE_MB=256`; when a single piece still exceeds the arena or that record type is not wired into chunking, the `Fatal{RingOverrun, "SEG_STAGE"}` fallback remains. **Still unanswered (narrowed 2026-09-22)**: in a6 §6's measured results, the only things not walked are the **program archive** (the only unbounded blob) and the **`draw_vbo` `NumDraws*12` range tail**; the host index span has no producer (struck out); `set_global_constants` has a walk. Chunking both is the same work shared by P6.5 sl and P8, and the stream send window is sized against them. The occupancy distribution of MC in-world / Create is still unmeasured (gate 8 has given bytes/frame and records/frame for the two loads; the per-blob distribution was taken only on the integration lane). **P8-0 measured result (2026-09-30, ID-P8-5)**: largest program archive 354,846 B, largest `draw_vbo` record 1,936 B, both far below arena/4; **no chunking, closed**. 12. **A second opinion on the server side for split-only rendering bugs after P13.** The verify build + recorder cover only what is pushed, not the backend's interpretation of it. 13. **Whether baked internal shaders can express uniform locations and UBO layouts without a live `ProgramObject`.** Color blit answered (P5f fv: `WireColorBlit.{vert,frag}` uses push constants, and `WireColorBlitSpirv.h` is checked into the tree); narrowed to the depth mip program (today compiled out under split, whose split path is a named Fatal) and pass-through TCS (`ProgramFactory.cpp:4060-4262`, the third uninventoried internal shader generator). **Answered (P7 wave 2-B / B2)**: depth mip baking (A)(D) (`WireDepthMipmap.*`, push constant, `MOBILEGL_BAKED_INTERNAL_SHADERS` freshness gate table-driven) and (B′) (the disaggregated build's monolith arm switches to the baked module) landed; the third baked shader set, for multisample resolve, comes with B2; pass-through TCS is still uninventoried. 14. **Which pull-mode-tuned cache hit rates the push model changes.** Survivor capacity is retuned in P13. One clue: on device, 26.3 Espryt's `sve` ≈ draw count (the sampler-view set is re-sent once per draw), while desktop is only ~0.07/draw; on the P3b/P4b optimization list. 15. **Whether monolith's `*IndirectCount` not calling `SyncGpuWrites()` is a latent gap.** A separate `dev` issue; the split must not fix it opportunistically. **Answered (2026-09-30): yes** — P8-C / P8-D host red-once confirms (Espryt `*IndirectCount`, Magma `glMultiDrawArraysIndirectCount` read 0); the split half is fixed (ID-P8-9), and the monolith half is fixed in the P8 second wave's dev package (ID-P8-3). 16. **Actual memory footprint of the index host mirror.** The total element-array buffer in the MC / Sodium / Iris corpora is unmeasured; if it significantly exceeds 64 MiB, the frequency and cost of the fallback path must be measured. P8. 17. **The `create-indirect` fixture's failure on Adreno 830** predates this branch (reproduces with the baseline APK, `dev@81b17c0b`); it is not caused by this branch. The `scudo::reportMapError` crash with `rd12` + Magma on both Adreno 830s is also on the `dev` side. Both are excluded from device A/B and stay in the desktop corpus; P8 must assert `roundtrips-per-frame == 0` on `create-indirect`, so the `dev` fix is on someone else's critical path. **The create-indirect part is answered (2026-09-30)**: the original fixture was captured on a desktop driver with `GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT` = 16, and Flywheel laid out the scatter list for 16; Adreno 830 on both backends and Mali's Vulkan require 64, so all 1,270 range bindings were rejected with `GL_INVALID_VALUE`. The original fixture is retired and replaced by the derived `minecraft-1.21.1-neoforge-create-indirect-in-world-align1024` (**not the same trace**: `tools/trace_replay/fixture_tools/realign_buffer_ranges.cpp --align 1024` moves these bindings to 1024-aligned slots, inserting 3,812 calls, target calls 504631 → 508443; host SSIM matches the original fixture to 9 digits). Redmi passes on both backends for monolith / spawn; on Mali-G1-Ultra Magma passes (the split arm separately fixed the T0 POST, see the POST section of `CONTRACT-P11.md`), while Espryt is still 0.848: that machine's GLES driver vertex-stage SSBO limit is 0, so Flywheel indirect's vertex program cannot link (a missing capability, not a split defect). 18. **Load balancing between the two split sides.** A new question unlocked by P5e: under VD32 (~3550 draws/frame) the apply thread is **11-12 ms**, the client **15.7 ms**, and **the client is the bottleneck, pinned at ~94%**. Moving work from the client to apply would directly lower the bottleneck — meaningless under lockstep (the two sides add serially); **run-ahead is what makes it a lever**. Options: the emitter on the encode side, the tracker walk, dirty-page tracking of the persistent map (`change_protection` was 1.3% in the profile). Must re-profile first: with the waits gone, the denominators of all the tail items have changed. Queued after P5e; unowned; on 2026-09-22 suggested to attach to the optimization phase after the roadmap completes, with the 2×2 matrix lane of P6.5 re-profiling along the way. 19. **Link bandwidth budget and compression.** In the TCP endgame, `SEG_STAGE` bytes/frame is against Wi-Fi / USB (on the order of 30–100 MB/s), not memory; the ~1 MB/frame budget in `ARCHITECTURE.md` §14 at 60 fps is 60 MB/s, already at the Wi-Fi limit. Whether to LZ4-style compress the stage payload and whether to deduplicate texture uploads by content hash on the server side must wait for gate 8's distribution. Must be answered before the first line of the P6.5 contract is written. 20. **The stall of a large single upload.** A 128 MiB single upload at 50 MB/s is 2.5 s; content-side chunking solves "does it fit", not "how long must we wait". On the cross-machine arm, either accept and record it, or make uploads asynchronous (think it through together with P9's semantics). Do not pretend it is solved. 21. **The form of pairing / authentication.** **Ruled (ID-P7-3, 2026-09-22): a token is enough, no TLS.** The threat model is "a remote code path anyone can connect to", not eavesdropping: token comparison in constant time, ≥16 bytes, no token means loopback only, the data plane bound to the authenticated control connection by `Welcome.dataNonce`, authentication before fork. TLS is kept as a P12 open item. 22. **Client platform scope.** The default is Linux + Android first (existing CI can verify), the Windows client in the second batch (`ShmSegmentWin32` exists; the `pipe:` named refusal is retired, TCP replaces it), and macOS is still not split. This decides whether `ARCHITECTURE.md` §15.3 "Windows machines are not a correctness gate" needs changing. 23. **Detection of half-open connections.** A Wi-Fi disconnect does not produce a hang-up event; dl's latch comes from the descriptor hang-up, while TCP needs keepalive / heartbeat timeout and **reporting as a hang-up by the transport layer** — this does not contradict "never take it from an apply timeout"; write it down to avoid re-arguing. P6.5 ct. 24. **Redefinition of the E1 control (ID-122).** **Closed (2026-09-29 debt review)**: `10bfd29f` replaced E1's pixel race with the negative control `scripts/ci/wait_boundary_negative_control.py` that observes protocol waits. On 2026-10-18 that negative control retired: the probe could see the missing wait, so the resident unit case `RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait` proves it instead (the same `ApplierReset`, no wait after publishing, must read parked=0).