docs: record 30-02 manual validation handoff
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
OpenAI Codex
2026-08-29 21:27:45 +08:00
parent 2f1506747b
commit ba488f8702
3 changed files with 252 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
# 30-02 GPU Ping-Pong 与 Context Recreation 最小技术验证报告 v2
日期:2026-08-29
当前状态:`WAITING_MANUAL_VALIDATION`
## 1. 前置状态与本轮范围
- 30-01 已由用户完成 Android OpenGL 实机验收并正式记录为 `PASS`;PASS 回填提交为 `b34b0a3e22f91a0101b0bbd02190e3384f6ef2ae`。
- 历史 v1 报告的 `BLOCKED` 保留不改;它记录的是当时 30-01 正式状态尚未回填,不是 GPU/gfx 失败。
- 本轮只准备 Android OpenGL 的 30-02 validation-only Stage A;未实现正式粒子、真实 Field、Mask、Vulkan 或 iOS。
## 2. 静态源码确认
以下是 Codex 可从源码确认的实现事实,不等同于设备运行结论:
- `RenderParticleValidationLayer` 建立固定 `16x16` 的 State A/B 两套独立 `RenderTarget`,每帧从 read slot 采样并写入另一个 slot。
- Trail A/B 是两套与当前 viewport 同尺寸的真实 `RenderTarget`;Trail pass 读取 previous trail,执行 `0.965` fade 并加入移动 marker,随后由主 translucent pass 合成。
- 正常帧只把 State write 与 Trail write 注册到当帧 render-target list;read target 不作为当帧附件,从结构上避免同一 texture 同时采样和写入。
- 首帧或 Trail resize 后,新的 read target 先以空 group 清屏;注册顺序为 State 初始化/写入、Trail 初始化/写入,主 LayerGroup 随后合成最新 Trail。
- ping-pong slot 在主 pass 的 Drawable Tweaker 到达时提交;此时当帧离屏 State/Trail pass 已按 `RenderOrchestrator` 的 render-target 顺序完成编码。
- 每帧只更新小型 UBO;没有 CPU 回读或复制完整 State/Trail 数据。
- viewport 改变时只重建 Trail A/B,State A/B 保持固定尺寸和当前状态。
- layer 不可渲染时移除已注册 target 并重置 slot/frame;layer remove 会移除 target;context lost 会记录旧 generation 被丢弃,新的 RenderLayer/resource set 使用递增 generation。
- shader 只注册到当前 OpenGL backend;没有修改 Vulkan、Metal 或 iOS shader/backend。
- 没有修改 `RenderTarget`、`CommandEncoder` 或其他通用 gfx API;使用现有 `createRenderTarget`、render-target 顺序、pass end/present 与 texture sampling 链。
## 3. DrawableBuilder 生命周期
所有新 Drawable 都严格执行:配置 builder → `flush(context)` 完成当前 Drawable → `clearDrawables()` 转移已完成对象 → `LayerGroup::addDrawable()`。没有恢复 30-01 曾出现的“未 finalize 就 clear”错误,也没有通过空指针跳过 render pass。
## 4. 验证日志
日志关键字:`ParticleValidation30_02`。状态变化与第 1 帧/每 60 帧记录:
- resource `generation`;
- State/Trail A/B create、resize、destroy/context-lost;
- `frame`、State read/write、Trail read/write;
- `order=State,Trail,Composite`;
- `hazard=SAFE` / `distinct=true` / `cpuStateCopy=false`;
- viewport、zoom、bearing、pitch;
- Test App ACTIVE、STATIC、DISABLED 状态。
日志是待用户采集的验证接口;本报告未伪造任何设备日志。
## 5. 静态检查结果
- shader manifest JSON 解析:PASS。
- shader 源生成并确认 OpenGL generated header、BuiltIn、manifest 注册存在:PASS。
- C++ `clang-format --dry-run --Werror`:PASS。
- `git diff --check`:PASS。
- shader ID、UBO、attribute、texture、GL ShaderInfo 与 GL backend 注册符号一致性检索:PASS。
- Android XML:已人工检查结构;环境缺少 `xmllint`/Ruby XML parser,未宣称解析器 PASS。
- 编译、Gradle、Android 构建、安装、ADB、设备运行:均未执行,遵守用户“不编译”和指令书限制。
## 6. 用户手工编译与安装
当前 Makefile 的 x86_64 Test App OpenGL 命令为:
```bash
cd /mnt/sda1/MapLibreParticleLab
source /root/.maplibre-particle-env
cd platform/android
RENDERER=opengl BUILDTYPE=Debug JOBS=3 make android-x86-64
```
预期 APK:
```text
platform/android/MapLibreAndroidTestApp/build/outputs/apk/opengl/debug/MapLibreAndroidTestApp-opengl-debug.apk
```
如需命令行安装,必须由用户手工执行:
```bash
adb -s 192.168.1.146 install -r platform/android/MapLibreAndroidTestApp/build/outputs/apk/opengl/debug/MapLibreAndroidTestApp-opengl-debug.apk
```
## 7. 用户手工运行步骤
1. 打开 Test App,在主菜单顶部确认 APK 版本/Git revision,再进入 `Custom Layer` → `Particle Core Layer Validation`。
2. 过滤日志:`adb -s 192.168.1.146 logcat | grep -E 'ParticleValidation30_02|ParticleValidation'`。
3. T1 初始创建:确认 State A/B、Trail A/B create,generation 初值和 `frame=1` commit;地图与蓝/黄顺序参照正常。
4. T2 ACTIVE:保持至少 120 帧;确认 State 与 Trail 均 A/B 交替、frame 增长、轨迹 marker 移动/衰减且地图正常。
5. T3 STATIC:点击 STATIC 并静置,确认 validation 不再主动推进;随后 pan/zoom/rotate/pitch,确认地图事件可触发 redraw,停止操作后不再持续推进。
6. T4 resize:旋转屏幕或改变模拟器窗口尺寸;确认 `Trail A/B resize old=... new=... StatePreserved=true`,State A/B 没有重新 create,地图无异常。
7. T5 disable/enable:点击 DISABLE,确认 layer/resource destroy 且停止;点击 ENABLE,确认新 generation 建立、slot/frame 从初始状态恢复,无 crash。
8. T6 context/surface recreation:保持 App 进程存活,先 Home/Recent Apps 切出再返回;若没有出现 `context lost discard`,启用开发者选项“不保留活动”后离开并返回验证 Activity,或旋转触发 Activity/Surface 重建。仅界面重现不算成功,必须捕获旧 generation 的 `context lost discard`/destroy 和新 generation create。
9. T7 recreation 后继续 ACTIVE 至少 120 帧;确认新 generation 的 ping-pong 恢复、Trail 从清空重新积累、无旧 generation 后续 commit、地图正常、无 crash/stale framebuffer。
10. 保存完整关键日志和可见现象,回传给 Codex 进入阶段 B。
可选手工启动命令:
```bash
adb -s 192.168.1.146 shell am start -n org.maplibre.android.testapp/.activity.particle.ParticleCoreLayerValidationActivity
```
## 8. 当前证据边界与未验证项
- 用户手工编译结果:未提供。
- Android OpenGL 实际运行结果:未提供。
- State/Trail 实机持续交替:未验证。
- 当前 OpenGL driver 的 pass 同步/hazard:静态设计成立,尚无实机证据。
- resize、disable/enable:尚无实机证据。
- 真实 EGL context/surface recreation、旧资源失效与新 generation 恢复:尚无实机证据。
- crash、framebuffer corruption、地图回归:尚无实机证据。
因此当前不能判定 PASS,也未发现可据以判定 FAIL 的运行证据;正式状态只能是 `WAITING_MANUAL_VALIDATION`。
## 9. 30-03 状态
不允许进入 30-03。必须先由用户完成上述 Android OpenGL 手工验证,并在阶段 B 将 30-02 正式更新为 `PASS`;若无法确认真实 context recreation,则按指令书判定 `BLOCKED`。

View File

@@ -0,0 +1,126 @@
# 30-02 GPU Ping-Pong 与 Context Recreation 验证 Codex 完成报告 v2
日期:2026-08-29
执行结论:`WAITING_MANUAL_VALIDATION`
## 1. 前置状态与 Git 基线
- 30-01 正式状态:`PASS`。
- 30-01 PASS 回填 commit:`b34b0a3e22f91a0101b0bbd02190e3384f6ef2ae`。
- 本任务开始分支:`particle/main`。
- 本任务开始 HEAD:`b34b0a3e22f91a0101b0bbd02190e3384f6ef2ae`。
- 开始时本地与 `origin/particle/main` 同步,工作区干净。
- 历史阻塞 commit `25072cff1b3a538af361b572cd7e79cd4a471b07` 与 30-02 v1 BLOCKED 报告均完整保留。
## 2. 前置材料读取
已读取 `AGENTS.md`、`PARTICLE_BASELINE.md`、`docs/00_项目总则/`、`docs/00_项目规范/`、当前 STEP-000、30-02 指令书、架构调查/设计、30-01 最新 PASS 技术报告与完成报告、30-02 v1 历史阻塞报告及 `粒子开发记录.md`。按指令书未创建或修改 Step。
## 3. 实际修改文件
Core validation 与 shader:
- `src/mbgl/renderer/layers/render_particle_validation_layer.cpp/.hpp`
- `shaders/particle_gpu_validation.vertex.glsl`
- `shaders/particle_gpu_validation.fragment.glsl`
- `shaders/manifest.json`
- `include/mbgl/shaders/gl/particle_gpu_validation.hpp`
- `include/mbgl/shaders/particle_gpu_validation_ubo.hpp`
- `include/mbgl/shaders/gl/shader_info.hpp`
- `include/mbgl/shaders/shader_defines.hpp`
- `include/mbgl/shaders/shader_manifest.hpp`
- `include/mbgl/shaders/shader_source.hpp`
- `src/mbgl/shaders/gl/shader_info.cpp`
- `src/mbgl/shaders/shader_source.cpp`
- `src/mbgl/gl/renderer_backend.cpp`
Android Test App:
- `platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/ParticleCoreLayerValidationActivity.kt`
- `platform/android/MapLibreAndroidTestApp/src/main/res/layout/activity_particle_core_layer_validation.xml`
- `platform/android/MapLibreAndroidTestApp/src/main/res/values/strings.xml`
文档:本 v2 完成报告、对应 v2 技术报告,并追加 `粒子开发记录.md`。
## 4. State A/B 实现与创建链
`ParticleGpuValidationResources` 持有两套固定 `16x16`、RGBA unsigned-byte `RenderTarget`。创建链为 `gfx::Context::createRenderTarget` → 现有 `RenderTarget`/`OffscreenTexture` → 独立 texture;每个 target 各有一个 validation-only `LayerGroup`。State shader 从 read slot 采样并将确定性演进结果写到相反 slot,没有 CPU full-state copy。
## 5. Trail A/B 与 composite
Trail A/B 是两套 viewport 尺寸的真实 `RenderTarget`。Trail pass 采样 previous trail,执行 fade 加移动 marker,写 next trail。主 RenderLayer 的 translucent Drawable 随后采样本帧 next trail并以预乘 alpha 合成到 MapLibre 主 pass;没有 MapView 外部 Overlay。
## 6. ping-pong swap、pass 顺序与 hazard
- 正常帧只注册 State write 和 Trail write;对应 read target 不进入当帧 render-target list。
- 初次创建/resize 时,新的 read target 以空 LayerGroup 先清屏。
- `RenderOrchestrator` 按添加顺序访问 RenderTarget;本层添加顺序保证 State 在 Trail 前,主 LayerGroup composite 在所有 offscreen target 后。
- Drawable Tweaker 在主 translucent pass 到达时提交 A/B slot 交换,因此下一帧才把本帧输出作为 read。
- Tweaker 同时比较 source/target texture identity,异常时输出 `HAZARD`;正常节流日志输出 `distinct=true`。
- 该结论目前是源码/调度顺序确认;Android OpenGL driver 的实际同步语义仍待用户实机日志与画面确认。
## 7. Android OpenGL backend 事实
新增 shader 只加入 OpenGL `RendererBackend::initShaders`、GL `ShaderInfo` 与生成的 GL source。没有添加 Vulkan/Metal/WebGPU shader,也没有把 raw OpenGL 调用写入 Core validation 逻辑。
## 8. resize、disable/enable、context lifecycle 与 generation
- resize:仅销毁并重建 Trail A/B;State A/B 不重建,日志标记 `StatePreserved=true`。
- disable:renderability 关闭时移除 target、清空验证 Drawable、重置 frame/slot;当前 Test App 的 DISABLE 通过移除 validation layer 强制完整 resource destroy。
- enable:Test App 重新加入 validation layer,新 resource generation 从初始 slot/frame 创建。
- context/surface:Android OpenGL 现有 surface lifecycle 会通知 renderer context lost;本 RenderLayer 的 `markContextDestroyed()` 记录旧 generation discard,RenderLayer 重建时进程内 atomic generation 递增。
- generation 机制只服务验证日志,不是正式粒子资源管理 API。
## 9. Test App 入口
继续复用 `Custom Layer` → `Particle Core Layer Validation`,保留 30-01 ACTIVE/STATIC,并增加 DISABLE/ENABLE。主菜单既有 APK version/renderer/build type/Git revision 标识保持不变。没有创建第二套 Test App 或产品 UI。
## 10. 手工编译与运行
手工编译命令:
```bash
cd /mnt/sda1/MapLibreParticleLab
source /root/.maplibre-particle-env
cd platform/android
RENDERER=opengl BUILDTYPE=Debug JOBS=3 make android-x86-64
```
APK 预期路径:`platform/android/MapLibreAndroidTestApp/build/outputs/apk/opengl/debug/MapLibreAndroidTestApp-opengl-debug.apk`。
完整 T1–T7 手工运行步骤、带 `adb -s 192.168.1.146` 的安装/启动/logcat 示例见技术报告 v2 第 6–7 节。
## 11. 检查与未执行项
- shader manifest JSON:PASS。
- shader 生成/注册链静态一致性:PASS。
- C++ clang-format:PASS。
- `git diff --check`:PASS。
- Android XML:人工结构检查;环境无 XML parser,未宣称工具 PASS。
- Android/Gradle/ADB/APK 构建安装:未执行。
- 任何 C++/Android 编译:未执行,遵守用户“不要编译”。
- 用户实机日志、画面、resize、context recreation:尚未提供。
## 12. 修改边界
- 通用 gfx API:未修改;现有 API 足以表达本验证。
- 正式生产 Particle/Field/Mask:未实现。
- 依赖、SDK、NDK、Gradle、MapLibre baseline:未修改。
- Vulkan/iOS/Metal、30-03:未开始。
- Shader/OpenGL backend 的修改仅是注册 validation-only shader,不是通用 renderer 重构。
## 13. 当前判定与 30-03
Stage A 代码已准备并推送,但所有关键 GPU/context 门槛仍缺用户实际运行证据,因此状态必须为 `WAITING_MANUAL_VALIDATION`,不是 PASS/FAIL。30-03 不允许开始;若用户环境最终无法用日志确认真实 context recreation,则 30-02 应判定 `BLOCKED`。
## 14. Git 收尾
- 主要变更 commit:`2f1506747b1dad8e89e526058a2951e9734ba99b`。
- commit message:`test: add particle gpu ping-pong validation`。
- 主要变更 push:成功。
- 公开远端:`origin`(`ssh://git@192.168.200.230:2222/tei/MapLibreParticleLab.git`)。
- 远端分支:`particle/main`。
- 主要变更远端可见 HEAD:`2f1506747b1dad8e89e526058a2951e9734ba99b`。
- 本文档与开发记录的收尾 commit 及最终远端 HEAD:按指令书的自引用处理规则,在最终回复中记录。
- 未提交的无关本地改动:无。

View File

@@ -80,3 +80,17 @@
- 本次只回填验收状态:未重新修改 30-01 代码、未重新编译、未重新运行 Android、未执行 ADB。
- 30-01 正式完成,允许重新开始 30-02。
- 30-02 v1 的 `BLOCKED` 历史记录继续保留;它反映当时 30-01 正式 PASS 尚未回填,不代表 GPU/gfx 架构验证失败。
## 2026-08-29 — 30-02 GPU Ping-Pong 与 Context Recreation(阶段 A)
- 30-01 正式状态为 `PASS`,此前 30-02 v1 的历史 `BLOCKED` 前置原因已解除;开始重新执行 30-02 指令书。
- validation-only State A/B 已使用两套固定尺寸独立 GPU RenderTarget 实现;每帧 read/write 反向选择,无 CPU 完整状态复制。
- Trail A/B 已使用两套 viewport 尺寸真实 RenderTarget 实现 previous fade/marker → next,并把 next 合成到 MapLibre 主 translucent pass。
- 静态确认只注册当帧 write target,read target 不作为当帧附件;RenderTarget 顺序为 State → Trail,主 pass 后续 composite,slot 在 composite Tweaker 到达时提交。
- resize 只重建 Trail A/B 并保留 State A/B;disable/remove 会停止并释放或重置资源;context lost 记录旧 generation discard,新资源使用递增 generation。
- 复用 `Particle Core Layer Validation`,保留 ACTIVE/STATIC 并增加 DISABLE/ENABLE;没有修改通用 gfx API、正式粒子、Mask、Vulkan 或 iOS。
- 静态格式、JSON、shader 注册链与 diff 检查通过;遵守用户要求未编译,未运行 Gradle/Android/ADB。
- 当前状态为 `WAITING_MANUAL_VALIDATION`:State/Trail 实际 GPU 交替、OpenGL 同步、resize、disable/enable 和真实 context recreation 均等待用户手工运行证据。
- 不允许进入 30-03;若无法确认真实 context recreation,最终应按指令书标记 `BLOCKED`。
- 技术报告:`docs/60_测试/30_02_GPU_PingPong与ContextRecreation最小技术验证报告_v2.md`。
- Codex 完成报告:`docs/报告/30_02_GPU_PingPong与ContextRecreation验证_Codex完成报告_v2.md`。