mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
10 KiB
10 KiB
P8-D:Magma wire 臂原生 indirect draw
2026-09-29,分支
p8/d(基线8d3e2317)。计划:PLAN-P8.md包 D;审计 §4、§6 风险 3。主机 lavapipe,只动 Magma(Espryt 作对照)。
1. 结论
- wire 臂的
glDraw*Indirect/glMultiDraw*Indirect[Count]改为从 wire store 自己的VkBuffer发vkCmdDraw[Indexed]Indirect[Count]:VulkanRenderer::DrawWireIndirectNative(MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.inc:627)。 DrawWireIndirect(MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp:381)先走原生;CPU 展开只留给设备发不出的 COUNT 形态(与 monolith 同条件,§3)。- create-indirect × Magma × split:indirect 引起的 host 等待 321 → 0,等待时间 8.07 s → 0.013 s(剩下 6 次是应用回读,§2);SSIM 各臂不变。
- 新增 barrier:着色器写过的 store 在下一次 indirect 读前补一个
INDIRECT_COMMAND_READbarrier,每次写一次(WireDraw.inc:693-705)。create-indirect 的 322 次 cull→draw 里 320 次没有glMemoryBarrier,旧 CPU 路径靠整 GPU 等待碰巧有序,原生路径靠这个 barrier。
2. 测量(主机,MOBILEGL_PIPE_STATS=1 MOBILEGL_PIPE_STATS_PERIOD=1,server 日志 windr[...] 求和)
两个 fixture 都只有 1 帧(frames=1),所以"每帧"= 每次重放。主机上同时有 7 个包在跑,毫秒只看量级。
| fixture | 臂 | draws | wind | wixp | wibar | whw | whwi | whwus | SSIM |
|---|---|---|---|---|---|---|---|---|---|
| create-indirect | monolith(前后相同) | 13172 | 0 | 0 | 0 | 0 | 0 | 0 | 0.999956667 |
| create-indirect | inproc,改前 | 13172 | 0 | 321 | 0 | 327 | 321 | 8065617 | 0.999956667 |
| create-indirect | inproc,改后 | 13172 | 321 | 0 | 321 | 6 | 0 | 12540 | 0.999956667 |
| create-indirect | spawn,改前 | 13202 | 0 | 322 | 0 | 328 | 322 | 8360015 | 0.999956667 |
| create-indirect | spawn,改后 | 13202 | 322 | 0 | 322 | 6 | 0 | 15769 | 0.999956667 |
| create-instancing | 三臂,前后相同 | 14158 / 14194 | 0 | 0 | 0 | 0 | 0 | 0 | 0.999952470 |
- 字段:
wind原生 indirect 调用、wixpCPU 展开、wibar补的 barrier、whw/whwus真正等了的WaitForWireBufferHostAccess次数 / 微秒(MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp:321)、whwi其中由 indirect 读引起的。定义在MobileGL/MG_Util/Metrics/PipeStats.h:280-285,打印在MobileGL/MG_Util/Metrics/PipeStats.cpp:697。 - 改后剩下的 6 次等待全部来自
ReadbackWireBuffer(VkBufferManager.cpp:574,临时探针取调用方地址 +addr2line,未提交):应用对 6 个 store 的回读,改前也是这 6 次(327 − 321)。 - 同一 trace(apitrace dump 到 target call):322 次
glMultiDrawElementsIndirect、2901 次glDispatchCompute、9 次glMemoryBarrier;前 320 次 draw 与其 cull dispatch 之间没有应用 barrier,只有最后 2 次前有GL_COMMAND_BARRIER_BIT。每次 draw 前都有 compute 写它的命令 store,所以wibar= draw 数。 - create-instancing 没有 indirect draw,也没有等待:它的 p99 差异不归 D。
3. monolith 何时在 CPU 上读 indirect 字(D 只在同样的场合保留 CPU 路径)
| monolith 场合 | 位置 | wire 臂(本包后) |
|---|---|---|
没绑 GL_DRAW_INDIRECT_BUFFER:命令在 client 内存 |
DirectVulkan.cpp:684、:825、:884 |
client 在兼容档拒绝(MobileGL/MG_Remote/Client/EmitTables.cpp:730 CLIENT_COMMANDS);store 为空则具名拒绝 IndirectBufferUnbound |
glMultiDrawArraysIndirectCount:总在 CPU 读 count 字 |
DirectVulkan.cpp:720-757 |
原生 vkCmdDrawIndirectCount(MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp:15942 加载,只在 disaggregated 构建) |
glMultiDrawElementsIndirectCount:无 VK_KHR_draw_indirect_count,或 maxdrawcount > 1 且无 multiDrawIndirect → CPU 循环 |
VulkanRenderer.cpp:13421-13444 |
同条件(WireDraw.inc:630-635)返回 false,走原 CPU 展开(DirectVulkan.cpp:390-413,计入 wixp) |
无 multiDrawIndirect 或 stride 不是 4 的倍数:逐条原生 indirect,仍由 GPU 读 |
VulkanRenderer.cpp:13510、:13615 |
同(WireDraw.inc:732-740);stride 非 4 倍数在 GL 是 INVALID_VALUE,wire 上具名拒绝 IndirectCommandRange |
| client 顶点数组 + arrays indirect:CPU 读命令定上传范围,draw 仍原生 | VulkanRenderer.cpp:13576 |
不可达:client 把 client 数组拷成自有 buffer |
GL_LINE_LOOP:indirect 不改写,退化为 LINE_STRIP |
MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp:27 |
同 |
| uint8 索引无扩展 / 非固定 restart 索引 / 非对齐偏移:整段索引在 CPU 改写,命令仍 GPU 读 | 两臂都在各自的索引上传里 | WireDraw.inc:398,索引视图是整个 EBO(WireDraw.inc:681,同 monolith) |
| baseInstance / gl_DrawID | 原生(monolith 不查 drawIndirectFirstInstance) |
原生;旧 CPU 展开的第 3 档把 gl_DrawID 全给 0(VulkanRenderer.cpp:13154),原生反而更对 |
4. 设计
| 项 | 内容 |
|---|---|
| 入口 | DrawWireIndirect(DirectVulkan.cpp:381)→ DrawWireIndirectNative;false 才走 CPU 展开 |
| store | AcquireWireSlice 取 indirect / count / 元素 store(打 lastUseSerial,之后的 glBufferSubData 会排在这次读之后);范围、4 字节对齐、count 字越界都是具名拒绝(MobileGL/MG_Backend/DirectVulkan/Renderer/WireDeclines.def:115-118) |
| draw 准备 | 复用 SetupDraw → SetupWireDraw,带 IndirectDrawBuffer;该 aspect 原来是 buffer-legacy-arm Fatal,现只对 monolith 入口仍 Fatal(WireDraw.inc:213,m_wireNativeIndirectDraw);顶点流转换不再按 first+count 截断(WireDraw.inc:326) |
| 发射 | 同 monolith 的三档:COUNT 原生、multiDrawIndirect 一次发、否则逐条;另按 maxDrawIndirectCount 分块(WireDraw.inc:719-740) |
| barrier | MarkWireBufferGpuWritten 置 indirectReadBarrierPending(VkBufferManager.cpp:611),TakeWireIndirectReadBarrier(VkBufferManager.cpp:624)取走;有则结束 render pass 并记 ALL_COMMANDS/MEMORY_WRITE → DRAW_INDIRECT/INDIRECT_COMMAND_READ。transfer 写已有 MEMORY_READ 后置 barrier(VkBufferManager.cpp:514、:1437),不置位。应用自己的 glMemoryBarrier(GL_COMMAND_BARRIER_BIT) 仍走 BuildMemoryBarrierForGlBarriers(VulkanRenderer.cpp:7873),与 monolith 一样 |
| G1 | 全部在 MOBILEGL_BUILD_DISAGGREGATED / MOBILEGL_PIPE_PUSH 下;pull 构建 added=0 removed=0 |
5. 门与 red-once
用例 WireIndirectDrawScenario(MobileGL/MG_IntegrationTest/Scenarios/WireIndirectDrawScenario.cpp:225、:274、:304、:343),读数经 MobileGL/MG_IntegrationTest/Harness/WireIndirectPeek.cpp:22(进程内 PipeStats,只在 inproc 臂看得到 server)。登记:MobileGL/MG_IntegrationTest/CMakeLists.txt:4831-4864——Magma 门控三臂 DirectVulkan.{Split,Spawn,Tcp}.Indirect.、DirectGLES 三臂(经宏,含 Magma 信息层)、两后端 monolith(环境发现)。
| 用例 | 断言 | 两后端 / 各臂 |
|---|---|---|
EveryIndirectFormIsIssuedNativelyFromItsStore |
6 种 indirect 形态像素;inproc Magma:wind=6、wixp=0、whw=0、wibar=0 | 两后端全臂 |
ComputeWrittenCommandsDrawWithoutAHostWait |
compute 写命令(初值毒化为别的条带)+ 应用 barrier,像素;inproc Magma:wind=2、wixp=0、whw=0、wibar=2 | 两后端全臂 |
ComputeWrittenCountWordIsTheOneTheGpuReads |
compute 写 count 字,像素;inproc Magma 计数 | Magma;Espryt 跳过(count 字读 server 影子,OQ15 / P8-C);Magma monolith 跳过 arrays-count 半(§6) |
ShaderWrittenCommandsWithoutAnApplicationBarrierAreOrdered |
Flywheel 形状(无应用 barrier);inproc Magma:每次写恰好 1 个 barrier(wibar=2 / 3 次 draw) | Magma split 三臂;monolith 与 Espryt 跳过 |
| red-once | 变异 | 载体 | 观察 |
|---|---|---|---|
| 1 | DrawWireIndirectNative 开头 return false(强制 CPU 路径) |
4 用例 × 3 个 inproc 登记(.Split.Indirect. 门控、.Split. 信息层、.Split.Full.) |
12 红;ComputeWrittenCommandsDrawWithoutAHostWait:nativeDraws 0、cpuExpansions 2、indirectWaits 2、hostWaits 2;spawn/tcp 只断言像素,保持绿(CPU 路径像素是对的) |
| 2 | 去掉 barrier(if (false && ...)) |
同上 | 3 个 compute 写用例 × 3 个 inproc 登记 = 9 红,全部是 delta.barriers 0 ≠ 2;像素在 llvmpipe 上不红(lavapipe 按序执行),spawn/tcp 绿 |
| 3 | 去掉 Magma monolith 的 arrays-count 跳过 | DirectVulkan.WireIndirectDrawScenario.ComputeWrittenCountWordIsTheOneTheGpuReads |
红:compute 写的 count=2,条带 1、2 未画(monolith 读到陈旧影子 count 0) |
| fixture 门 | 改前 = 旧 CPU 路径 | create-indirect × Magma × {inproc, spawn} | whwi 321 / 322 > 0(§2 改前行);改后 0 |
6. monolith 缺陷(记给 dev,ID-P8-3,本包不修)
| 缺陷 | 位置 | 证据 |
|---|---|---|
Magma monolith glMultiDrawArraysIndirectCount 从前端影子读 count 字,不 SyncGpuWrites:着色器写的 count 读成旧值 |
DirectVulkan.cpp:749-757 |
red-once 3(主机 lavapipe 红) |
Magma monolith 对"compute 写 → indirect 读"没有任何依赖:不补 barrier,render pass 的外部依赖也不含 COMPUTE_SHADER / DRAW_INDIRECT(MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp:1413-1419)。create-indirect 的 320 次 cull→draw 不发 glMemoryBarrier(GL 未定义),真 GPU 上 cull 结果与 indirect 读竞争 |
VulkanRenderer.cpp:7727-7787(dispatch 后无 barrier) |
推断:主机 lavapipe 按序执行,red 不出;可能与 ID-P7-4(红米上 create-indirect monolith 坏)有关,需设备验证 |
7. 找到但未做
DispatchComputeIndirect的 wire 臂同样经ReadWireBuffer在 CPU 读 group 数(DirectVulkan.cpp:938-947),GPU 写过则整 GPU 等待;create fixtures 里没有glDispatchComputeIndirect,本包未改(monolith 是原生vkCmdDispatchIndirect)。- 补的 barrier 只覆盖 indirect 读;同一批 cull 写的实例 SSBO 被顶点着色器读时,两臂都没有 compute→vertex 的依赖(应用没发 barrier 时),不在 D 范围。
- 同一 store 既作 indirect 又作本 draw 的 SSBO 时,SetupWireDraw 解析描述符会再次置位,下一次 indirect draw 会多一个 barrier(保守,不影响正确性)。