Files
MobileGL/docs/Disaggregated/OPEN-QUESTIONS.md
T

13 KiB
Raw Blame History

Open questions, full text (formerly the "Open questions" section of ROADMAP.md, moved here 2026-09-24)

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).