# 30-02 GPU Ping-Pong 与 Context Recreation 最小技术验证报告 v3 日期:2026-08-30 当前状态:`WAITING_MANUAL_VALIDATION` ## 1. 本轮实机发现 用户在 30-02 v2 对应 Android OpenGL APK 上完成 T3 检查并明确报告: - STATIC 静止时,粒子/Trail 正常停止; - STATIC 下拖动地图时,MapLibre redraw 会使 State/Trail 继续推进; - 停止拖动后,State/Trail 再次停止。 因此原实现的 T3 判定为 `FAIL`。这是明确的设备运行事实;本报告不补写用户未提供的 frame 数值或日志数值。 ## 2. 原因 原实现只在 `needsRepaint()` 中用 ACTIVE/STATIC 控制验证层是否主动请求下一帧,但 `RenderParticleValidationLayer::update()` 没有按 ACTIVE/STATIC 区分 GPU 工作。 地图 pan/zoom/bearing/pitch 会合法触发 MapLibre redraw;每次 redraw 都会再次执行原 `update()`,继而: 1. 注册 State write RenderTarget; 2. 注册 Trail write RenderTarget; 3. 设置 `pending=true`; 4. composite Tweaker 调用 `commit()`; 5. State/Trail slot 与 `committedFrame` 前进。 所以 `needsRepaint=false` 只能停止 validation 自己制造的连续帧,不能保证在其他合法地图帧中冻结 GPU simulation。 ## 3. 最小修复 仅修改: - `src/mbgl/renderer/layers/render_particle_validation_layer.cpp` - `src/mbgl/renderer/layers/render_particle_validation_layer.hpp`(说明性注释) 修复内容: - `update()` 在已有资源上先撤销上一帧注册的 RenderTarget,然后读取当前 evaluated ACTIVE 状态。 - STATIC 分支不注册任何 State/Trail RenderTarget,不创建 State/Trail update Drawable,并清除任何 `pending` swap。 - STATIC 只为主 LayerGroup 建立 `FrozenComposite` Drawable,采样最后一次已经 commit 的 `trailRead` texture。 - `FrozenComposite` Tweaker可以在当前 MapLibre translucent pass 更新 composite UBO/texture 并读取当前 viewport、zoom、bearing、pitch,但在调用 `commit()` 前明确返回。 - STATIC 的 shader frame 值使用当前 `committedFrame`,不预先计算为下一 simulation frame。 - STATIC 下即使 viewport 改变也不重建、清空或推进 Trail;冻结 texture 由主 pass 重新显示。恢复 ACTIVE 后,既有 ACTIVE resize 路径才重建 Trail。 - 如果 layer 从未有过 ACTIVE/已 commit Trail 就直接处于 STATIC,不创建或清屏离屏资源,也不显示未初始化 texture。 - ACTIVE 路径的 State A/B、Trail A/B、pass 顺序、hazard 约束和 slot commit 保持不变。 ## 4. 静态控制流确认 以下事实已经从修复后源码确认: - `AddRenderTargetRequest` 只存在于 STATIC 提前返回之后的 ACTIVE 路径。 - State/Trail `buildQuad()` 只存在于 ACTIVE 路径。 - `resources->pending = true` 只存在于 ACTIVE 路径。 - STATIC 进入时强制 `resources->pending = false`。 - `FrozenComposite` 在 Tweaker 内先返回,不可能落入 `resources->commit()`。 - STATIC composite 绑定 `trailTargets[trailRead]`,read/write slot 参数相同且没有 target attachment。 - MapLibre redraw 没有被阻止;主 LayerGroup composite 仍会在当前地图 render pass 中执行。 ## 5. 静态检查 - C++ `clang-format --dry-run --Werror`:PASS。 - `git diff --check`:PASS。 - ACTIVE/STATIC、RenderTarget 注册、pending、commit 与 `committedFrame` 控制流检索:PASS。 - 编译、Android、Gradle、ADB、安装与运行:未执行,遵守用户要求。 静态检查只能证明代码路径,不替代 Android OpenGL 重测。 ## 6. T3 重新验证 使用包含修复提交 `364b8f381e27eee36c4895084b2f0578a023fa61` 的新 APK: 1. 进入 `Particle Core Layer Validation`,保持 ACTIVE,确认 State/Trail 正常交替并记下最近一次 `commit frame=N`、State read slot 与 Trail read slot。 2. 点击 STATIC,确认日志出现:`STATIC frozen composite ... frame=N ... offscreenPasses=0 swap=false`。 3. STATIC 静止至少 3 秒,确认粒子/Trail 不推进。 4. STATIC 下连续执行 pan、zoom、bearing、pitch;地图必须正常移动和 redraw,冻结 Trail 可以随主 pass 重新显示。 5. 操作期间确认画面中的 marker/fade 不推进;不得出现新的 State pass、Trail pass、slot swap 或 simulation `commit frame` 增长。 6. 停止操作后仍保持冻结。 7. 点击 ACTIVE,确认 GPU ping-pong 从冻结的 State/Trail slot 继续,而不是在 STATIC 操作期间跳过 frame/index;继续运行到下一次节流日志,确认 frame 恢复增长。 8. 再执行一次 ACTIVE → STATIC → 地图手势 → ACTIVE,排除只在首次切换正确。 用户手工日志命令: ```bash adb -s 192.168.1.146 logcat | grep -E 'ParticleValidation30_02|ParticleValidation' ``` ## 7. 仍需完成的 30-02 验证 本轮只修复 T3。以下仍沿用 v2 的待验证状态: - 修复后 T3 Android OpenGL 重测; - T4 resize; - T5 disable/enable; - T6/T7 真实 context/surface recreation 与恢复; - 无 stale resource/framebuffer corruption/crash; - 最终完整 PASS 证据。 当前最终状态仍为 `WAITING_MANUAL_VALIDATION`,不得写 PASS,也不允许进入 30-03。