mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
10 KiB
10 KiB
P8-C:Espryt server 暂存影子与 GPU 写
2026-09-29,分支
p8/c(基线8d3e2317)。计划行:PLAN-P8.md包 C。
1. 结论
- 前提成立(red-once 见 §4):split 下 server 的 R-11 暂存影子只由上传 / adopt 填,server 上的 GPU 写不进影子;CPU 读者读到旧字节,或因无影子直接拒绝。原 7 例在基线上 Espryt split / spawn / tcp 与 inproc A/B 全红。
- 修在 server,不动 client、不动 monolith:写者给 server 孪生体打标(
serverGpuWritten,MobileGL/MG_Backend/DirectGLES/Managers.h:1059),读者消费字节前经SplitHostBytesForCpuRead(MobileGL/MG_Backend/DirectGLES/Managers.cpp:3595)从 server 自己的 GL buffer 整 store 回读。 - XFB 不打标:两条捕获路径本来就把字节拿在手里(给 client 的 writeback),直接写进影子(
StageServerWrittenRange,MobileGL/MG_Backend/DirectGLES/Managers.cpp:3651)。 - 懒刷新:只在读者真正消费字节时回读;原生 indirect 不从 CPU 取字段时不回读,Create / Flywheel 式每帧 compute 剔除 + 原生 indirect 不付回读(
MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp:8555)。 - 顺带关掉 P3a M-3 声明的偏差(fp64 顶点收窄读旧字节)在 split 臂上的一半;monolith 臂不动。
- monolith 两后端共 5 处缺陷,skip 按名、记给 dev(§6,ID-P8-3);IndirectCount 的 monolith 按 OQ15 不改。
2. 读者与写者
| 读者(split 臂) | 位置 | 做法 |
|---|---|---|
| primitive restart 重写 | MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp:9309 |
SplitHostBytesForCpuRead(整 store) |
| multi-draw rebase | MobileGL/MG_Backend/DirectGLES/MultiDraw.cpp:196 |
同上 |
*IndirectCount 的 count |
DirectGLES.cpp:9632、:9798 |
参数 buffer 刷新;命令 buffer 仅在无影子时取第一份 |
| indirect 命令字段 | DirectGLES.cpp:8549 |
CPU 路径全取;原生路径只在程序读 gl_BaseVertex、或无 mg_IndirectParams 视图而读 gl_BaseInstance 时取 |
| 无影子的 indirect 命令 buffer | DirectGLES.cpp:487 |
被 GPU 写过就取第一份(原来 decline,indirect_command_bytes) |
| XFB scatter 的捕获前字节 | DirectGLES.cpp:2037 |
刷新 |
| fp64 顶点收窄 | MobileGL/MG_Backend/DirectGLES/Managers.cpp:6696 |
刷新,并让 memo 失效(GPU 写不动 serial) |
| 写者 | 位置 | 效果 |
|---|---|---|
| SSBO 记录臂(draw 与 dispatch) | DirectGLES.cpp:780 |
标记窗口内每个绑定点(与 client 的 ShaderBufferEmit.h 同口径) |
| 原子计数记录臂 | DirectGLES.cpp:1017 |
标记 |
| 可写 buffer image | DirectGLES.cpp:4464 |
纹理描述符的 BufferForTexBuffer → 标记 |
| XFB 普通捕获 | DirectGLES.cpp:1874 |
映射出的字节写进影子;T0 store 只标记(:1829) |
| XFB scatter | DirectGLES.cpp:2104 |
合成结果写进影子 |
| 清标记 | Managers.cpp:2308(respecify)、刷新末尾 |
— |
- 刷新顺序同
Ops_H_Readback:FlushPendingRangesFrom先落排队上传 →DrainResidentWritesNow→ 整 storeglMapBufferRange(READ)→MGL_SERVER_STAGED_ADOPT(覆盖变整段,孤儿 store 也就有了第一份)→ serial 戳 + epoch bump。 - T0 store:影子就是 server 对 client AHB 的一致映射;有标记或有排队常驻写时
DrainResidentWritesNow+glFinish(DEBTS 的 P11 B2 行的 split 半)。 - 刷新与写影子只在复制型暂存 store(split)下生效(
CopiesIntoServerStorage):monolith 的 store 保留调用方指针,映射一解就悬空。 - 全部新代码在
MOBILEGL_BUILD_DISAGGREGATED下;ReadsBaseInstance(Managers.h:3124)同。
3. 场景
GpuWrittenDrawInputScenario(MobileGL/MG_IntegrationTest/Scenarios/GpuWrittenDrawInputScenario.cpp,split-only 源,登记 MobileGL/MG_IntegrationTest/CMakeLists.txt:3461)12 例:GPU 只写一次,暂存影子里是画不出正确结果的毒值。
| 例 | 写者 | 读者 |
|---|---|---|
| RestartIndicesAComputeShaderWroteSplitTheStrip | SSBO | restart 重写 |
| …IntoAnOrphanedStoreSplitTheStrip | SSBO,glBufferData(NULL) |
restart 重写(原来无影子 → 跳过 draw) |
| …SplitEveryMultiDrawStrip | SSBO | multi-draw rebase |
| ACountAComputeShaderWroteDrivesMultiDrawElementsIndirectCount | SSBO(命令 + count) | count |
| …DrivesMultiDrawArraysIndirectCount | SSBO | count |
| ABaseVertexAComputeShaderWroteReachesGlBaseVertex | SSBO | 原生 indirect 的 gl_BaseVertex |
| IndirectCommandsAComputeShaderWroteIntoAnOrphanedStoreDraw | SSBO,glBufferData(NULL) |
无影子命令 buffer |
| RestartIndicesATransformFeedbackCapturedSplitTheStrip | XFB | restart 重写 |
| RestartIndicesAScatteredTransformFeedbackCapturedSplitTheStrip | XFB + gl_SkipComponents1(scatter) |
restart 重写 |
| RestartIndicesImageStoresWroteSplitTheStrip | imageStore 到 buffer 纹理 |
restart 重写 |
| ACountAnAtomicCounterProducedDrivesMultiDrawElementsIndirectCount | 原子计数即参数 buffer | count |
| DoubleVerticesAComputeShaderWroteAreNarrowedFromTheWrittenBytes | SSBO,两轮 | fp64 收窄(第 2 轮查 memo) |
- 臂:
DirectGLES.{Split,Spawn,Tcp}.(门控);分裂构建里的DirectGLES./DirectVulkan.即 monolith 对照与 inproc A/B;Magma split 各臂经 tier 2(integration-magma-all-*,信息层)与DirectVulkan.*.Full.(integration-magma-full-split,门控)。 - 三臂同源登记,
spawn_lane_parity.py无需例外行。
4. red-once
| # | 变异 | 载体 | 观察到的红 |
|---|---|---|---|
| 前提 | 基线 8d3e2317 + 原 7 例 |
ctest -R GpuWrittenDrawInput;inproc A/B 同命令加 gate 的环境 |
Espryt split / spawn / tcp 21/21 红(像素:黑 = 旧毒值 / 跳过;baseVertex 例红色 = 几何对、gl_BaseVertex 旧);inproc A/B 7/7 红;Magma split 各臂全绿 |
| F1 | 去 SSBO 标记、原子标记、image 标记、XFB 两处写影子(一个构建) | `ctest -R '^DirectGLES.(Split | Spawn |
| F2 | 读者侧:restart / rebase / 两处 count 改回 SplitHostBytes,CurrentIndirectCommandBytes 恒返回原指针,去无影子取第一份,fp64 memo 去掉 !sourceGpuWritten(一个构建) |
同上 | 10 例 × 3 臂 = 30 红;两例 XFB 绿(它们的字节来自写影子,不经刷新,符合设计);fp64 例只红第 2 轮(memo) |
| M | 把 SkipBrokenMonolith 探针成恒 false |
`ctest -R '^Direct(GLES | Vulkan).GpuWrittenDrawInput'` |
还原一律 copy + cmp + touch。
5. 门
- 场景:134 条(含 monolith skip 13 条)全绿;inproc A/B 24/24;相邻套件(Restart / MultiDraw / DrawParameters / IndexedDrawFamily / ClientVertexArray / Xfb / TransformFeedback / AtomicCounter / BufferTexture / DoublePrecision / ResidentIndex)1304/1304。
- G1:
added=0 removed=0,pull.texta5ba13与基线相同。 - retrace(主机):Espryt
SPLIT|SPAWN78 条 +create-*12 条共 86 条,84 过;挂的 2 条是iris-iterationrp-in-world的宿主 llvmpipe JIT 崩溃(LLVM ERROR: Cannot select ... vcvtps2ph,已知项)。全量 236 条跑到 47 条(全过)时中止,全量留给集成。 - create 的 SSIM:indirect Espryt 0.999961275 / Magma 0.999956667,instancing Espryt 0.999957572 / Magma 0.999952470,monolith、SPLIT、SPAWN 三臂逐位一致;Espryt indirect 与 P9 W2 普查的值相同(
../p9/W2-REMINT.md§4)。 - 懒刷新实测:create-indirect / create-instancing 的 Espryt SPLIT、SPAWN 回读 0 次(临时把刷新日志提到 INFO,数 server 日志,未提交)。
- 全门数字见包报告(
~/w7/notes/p8/c-report.md)。
6. monolith 缺陷(给 dev,ID-P8-3)
| 后端 | 缺陷 | 位置 | 场景里的表现 |
|---|---|---|---|
| Espryt | *IndirectCount 读前端影子不调 SyncGpuWrites(OQ15) |
DirectGLES.cpp:9688-9689、:9852-9853 |
count 读 0,什么都不画 |
| Espryt | 原生 indirect 的 gl_BaseVertex 从前端影子取(单进程臂 ResolveIndirectCommandBytes 只 SyncPersistentMappedRange) |
DirectGLES.cpp:521、:8640 |
几何对、颜色红 |
| Espryt | fp64 收窄的 handle 臂在 monolith 下读前端影子(P3a M-3 声明的偏差,monolith 半) | Managers.cpp:6675 |
画在屏外 |
| Magma | MultiDrawArraysIndirectCount 的 CPU 臂不调 SyncGpuWrites |
MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp:749 |
count 读 0 |
| Magma | draw 录制中途调 SyncGpuWrites(restart 重写、GL_DOUBLE 顶点转换):提交并等待结束了当前帧命令缓冲,draw 接着往已结束的缓冲里录 |
MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp:4513、:4179 |
SIGSEGV(lavapipe 工作线程 / vkCmdBindVertexBuffers);校验层 VUID-vkCmdBindIndexBuffer-commandBuffer-recording |
- skip 只在
transportName == "monolith"的对应后端生效,理由串带出处。
7. 未做 / 已知边界
- T0 与常驻写那一半主机上无法 red(主机无 T0,split 下采纳档钉在 T2);代码按
Ops_H_Readback的持久映射臂写,设备验证留给有设备的包。 - 标记是过近似:窗口内每个绑定的 SSBO 每次 draw 都标记;留着 SSBO 绑定、又拿同一 buffer 做任意 restart 索引的 EBO 时,每 draw 回读一次。
- bit 13 关(
BindingPointsComeFromRecords()为 false)的 A/B 配置下 SSBO / 原子不打标。 Ops_H_Readback(client 发起的回读)没有顺带刷新 server 影子(可省一次回读,未做)。- 同一 draw 既写 X(SSBO)又拿 X 当 EBO:标记在
SyncNeccessaryBuffers里打,restart / rebase 读者在 draw 执行前就刷新并清标记,写发生在清之后,X 留成未标记。monolith 同形(MarkShaderStorageBuffersGpuWritten先标,SyncGpuWrites在 draw 前清),是对齐不是倒退。 - DEBTS 的 P11 B2 行(handle 臂读采纳 / T0 映射前不落常驻写):split 半由
SplitHostBytesForCpuRead的 T0 臂做了、主机不可验证;单进程半归 dev。 - Magma 的
VulkanRenderer.cpp:3686(最大索引扫描)与 §6 最后一行同模式,未验证。