Compare commits
83 Commits
maplibre-p
...
53a844e818
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53a844e818 | ||
|
|
a4c6047d70 | ||
|
|
078ba40b76 | ||
|
|
6b48f905d2 | ||
|
|
7c5ed27b0b | ||
|
|
01863569fb | ||
|
|
64cf8f4435 | ||
|
|
d29e84c1a9 | ||
|
|
2780550b62 | ||
|
|
524aaf4af5 | ||
|
|
c4b481fd2c | ||
|
|
8a5214d92d | ||
|
|
e8b451fe70 | ||
|
|
95f0a45d6d | ||
|
|
baf9374318 | ||
|
|
1e7215e2a0 | ||
|
|
e9f84a8ec6 | ||
|
|
2e119961d9 | ||
|
|
750c14f527 | ||
|
|
4712e1752a | ||
|
|
5af91fe7a1 | ||
|
|
cd71640a84 | ||
|
|
1fdc5df625 | ||
|
|
2fca4a71a7 | ||
|
|
f081180bed | ||
|
|
159b0ad5f1 | ||
|
|
f3a44c7d5f | ||
|
|
b1239ac3b1 | ||
|
|
f49ffa68a1 | ||
|
|
5bb1a0e635 | ||
|
|
bd1cfe95b7 | ||
|
|
72d9369384 | ||
|
|
8c006ac83d | ||
|
|
0aab2bdc83 | ||
|
|
d0ced72351 | ||
|
|
be5b9080ed | ||
|
|
237a3e6608 | ||
|
|
0d13552bed | ||
|
|
91c2491b0e | ||
|
|
e67e4961b5 | ||
|
|
720d309364 | ||
|
|
b5c1c4879a | ||
|
|
82e8a18de7 | ||
|
|
4bc9d0ca56 | ||
|
|
ad5f92c48e | ||
|
|
05878d6623 | ||
|
|
45c4dca849 | ||
|
|
ee0b89e711 | ||
|
|
ddf20dec9f | ||
|
|
9d1f66c169 | ||
|
|
016fd1fff6 | ||
|
|
8fbb892a12 | ||
|
|
7eaff6e123 | ||
|
|
b001fd63c2 | ||
|
|
64715e4ce8 | ||
|
|
0dd475c22f | ||
|
|
66690b460e | ||
|
|
b3ec8edc87 | ||
|
|
8a5d88008c | ||
|
|
4908244d2d | ||
|
|
b89c8c8cb4 | ||
|
|
a9eaec6b65 | ||
|
|
b29e564944 | ||
|
|
57b42676a1 | ||
|
|
b17b9b0b1c | ||
|
|
e70b795098 | ||
|
|
f8f84da6a1 | ||
|
|
a2642bbedf | ||
|
|
4286c16a53 | ||
|
|
297b912620 | ||
|
|
746b316de0 | ||
|
|
4e3d27e1f3 | ||
|
|
98e1d2d977 | ||
|
|
bcec6ce5e0 | ||
|
|
07a17b4c70 | ||
|
|
a32e4e1d5b | ||
|
|
8384c8bd24 | ||
|
|
9ad2fe5d58 | ||
|
|
1d1a7915e0 | ||
|
|
e7d109246a | ||
|
|
c58d370e71 | ||
|
|
8b9ab30322 | ||
|
|
f47469d2ab |
63
AGENTS.md
63
AGENTS.md
@@ -89,3 +89,66 @@ Codex 的核心角色是“实现者”,不作为其自身修改结果的唯
|
||||
每一阶段必须有 `docs/50_Step记录/STEP-xxx_*.md`。完成报告固定写入 `docs/报告/STEP-xxx_<主题>_Codex完成报告_vN.md`(仅在任务要求 Codex 完成报告时)。
|
||||
|
||||
修改结束必须按当前构建规范验证、更新 Step、追加 `粒子开发记录.md`,并明确所有未完成/阻塞。Git 提交必须 scoped,禁止把既有无关 dirty/untracked 一并提交。
|
||||
## 8. Codex 写代码,RDC 执行编译 / 测试
|
||||
|
||||
本项目固定采用以下闭环,除非用户在当前任务中明确另行授权:
|
||||
|
||||
1. **Codex 负责实现与修复。**
|
||||
- 读取当前任务、设计、源码与错误信息;
|
||||
- 修改代码、测试代码、构建脚本和任务要求的文档;
|
||||
- 分析 RDC 返回的编译、链接、测试、codegen、打包失败;
|
||||
- 根据真实错误继续修复代码。
|
||||
|
||||
2. **RDC / 普通 Terminal 负责实际编译与验证。**
|
||||
- CMake / Ninja / Bazel 编译;
|
||||
- C++ / OpenGL / Metal 定向测试;
|
||||
- codegen、链接、打包、artifact 检查;
|
||||
- 其他耗时构建或机械性验证。
|
||||
Codex 不应自行启动、等待或轮询这些长时间命令。
|
||||
|
||||
3. **固定闭环:**
|
||||
`Codex 写代码 → Codex 停止并给出精确验证命令 → RDC 执行 → RDC 返回结果 → 若失败,Codex 分析并修复 → RDC 再验证`。
|
||||
|
||||
4. **失败日志只回传必要信息。**
|
||||
- RDC 保留完整 stdout/stderr;
|
||||
- 成功时只返回 exit code、耗时和必要摘要;
|
||||
- 失败时优先返回首个真实 error、相关 target、前后必要上下文和日志路径;
|
||||
- 不把完整大型 build log 反复送入 Codex。
|
||||
|
||||
5. **Codex 禁止行为。**
|
||||
- 未经用户明确授权,不得自行执行长时间编译、链接、codegen、打包或大型测试;
|
||||
- 不得创建 background terminal 等待 build;
|
||||
- 不得高频轮询构建状态;
|
||||
- 不得为了等待编译而自行切换模型或启动第二个 Codex 实例;
|
||||
- 不得把“编译未完成”当作理由持续占用高能力模型。
|
||||
|
||||
6. **RDC 发现问题后的处理。**
|
||||
- RDC 可以直接处理范围明确、风险低、原因清楚的小问题,包括:
|
||||
- 构建命令、CMake/Bazel 配置、链接配置、平台条件、测试条件等小型工程问题;
|
||||
- 单文件或少量文件内、无需重新设计架构的小型源码修正;
|
||||
- 明确的编译错误、声明/定义不一致、include/宏条件、简单 ABI/平台兼容问题;
|
||||
- 不改变既定业务语义和总体架构的局部修复。
|
||||
- RDC 修完后应立即重新编译/测试验证,不必为了这类小问题启动 Codex。
|
||||
- 以下情况才交给 Codex:
|
||||
- 大规模逻辑修改;
|
||||
- 跨模块或跨层重构;
|
||||
- renderer / runtime / API 设计需要重新判断;
|
||||
- 需要新增较完整功能或大量代码;
|
||||
- 用户明确指定“这是 Codex 代码任务”。
|
||||
- 一旦 RDC 判断修复范围开始扩大、需要重新设计或无法通过少量明确修改解决,应停止继续扩改,把错误证据和现状交给 Codex。
|
||||
|
||||
7. **用户可见性。**
|
||||
- 本项目未经用户明确授权,不得使用后台/headless Codex 执行任务;
|
||||
- Codex 默认运行在用户可见的 Terminal/TUI 中;
|
||||
- 当前 Mac 上如需启动或恢复 Codex,必须优先使用现有 GNU Screen 会话 `7445.codex`;
|
||||
- 不得另起隐藏的 Codex 进程或新的不可见会话,除非用户明确授权;
|
||||
- 用户应能够通过该 screen 会话直接看到 Codex 的输入、输出和当前状态。
|
||||
|
||||
8. **任务书冲突处理。**
|
||||
- 若旧任务书写有“Codex 必须执行编译/测试”,但用户未在当前任务明确重新授权,则以本节为准:
|
||||
Codex 只准备代码与验证命令,RDC 负责实际执行。
|
||||
- 只有用户在当前任务中明确要求“由 Codex 执行某项编译/测试”时,Codex 才可执行该项。
|
||||
|
||||
核心原则:
|
||||
|
||||
> Codex 负责写和改;RDC 负责跑和验;RDC 报错,再交回 Codex 修。
|
||||
|
||||
@@ -180,6 +180,12 @@ list(APPEND SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/uniform_buffer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/vertex_attribute.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_target.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/particle_fast_path.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/retained_underlay_cache.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/retained_underlay_cache.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/retained_underlay_runtime_state.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_segment_plan.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_segment_json.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/change_request.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layer_group.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/tile_layer_group.cpp
|
||||
@@ -305,6 +311,8 @@ list(APPEND INCLUDE_FILES
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_observer.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_state.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/renderer/retained_underlay.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/renderer/render_segment.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/program_parameters.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/shader_source.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/storage/database_file_source.hpp
|
||||
|
||||
@@ -279,6 +279,8 @@ MLN_CORE_SOURCE = [
|
||||
"src/mbgl/renderer/paint_parameters.hpp",
|
||||
"src/mbgl/renderer/paint_property_binder.hpp",
|
||||
"src/mbgl/renderer/paint_property_statistics.hpp",
|
||||
"src/mbgl/renderer/particle_viewport_reseed.hpp",
|
||||
"src/mbgl/renderer/particle_fast_path.hpp",
|
||||
"src/mbgl/renderer/pattern_atlas.cpp",
|
||||
"src/mbgl/renderer/pattern_atlas.hpp",
|
||||
"src/mbgl/renderer/possibly_evaluated_property_value.hpp",
|
||||
@@ -290,6 +292,9 @@ MLN_CORE_SOURCE = [
|
||||
"src/mbgl/renderer/render_light.hpp",
|
||||
"src/mbgl/renderer/render_orchestrator.cpp",
|
||||
"src/mbgl/renderer/render_orchestrator.hpp",
|
||||
"src/mbgl/renderer/continuous_layer_range.hpp",
|
||||
"src/mbgl/renderer/render_segment_plan.hpp",
|
||||
"src/mbgl/renderer/render_segment_json.hpp",
|
||||
"src/mbgl/renderer/render_pass.hpp",
|
||||
"src/mbgl/renderer/render_source.cpp",
|
||||
"src/mbgl/renderer/render_source.hpp",
|
||||
@@ -303,6 +308,9 @@ MLN_CORE_SOURCE = [
|
||||
"src/mbgl/renderer/renderer_impl.cpp",
|
||||
"src/mbgl/renderer/renderer_impl.hpp",
|
||||
"src/mbgl/renderer/renderer_state.cpp",
|
||||
"src/mbgl/renderer/retained_underlay_cache.cpp",
|
||||
"src/mbgl/renderer/retained_underlay_cache.hpp",
|
||||
"src/mbgl/renderer/retained_underlay_runtime_state.hpp",
|
||||
"src/mbgl/renderer/sources/render_custom_geometry_source.cpp",
|
||||
"src/mbgl/renderer/sources/render_custom_geometry_source.hpp",
|
||||
"src/mbgl/renderer/sources/render_geojson_source.cpp",
|
||||
@@ -659,8 +667,10 @@ MLN_CORE_HEADERS = [
|
||||
"include/mbgl/particle/field/field_profile_contract_json.hpp",
|
||||
"include/mbgl/particle/field/field_projection_materialization.hpp",
|
||||
"include/mbgl/particle/field/field_runtime.hpp",
|
||||
"include/mbgl/particle/field/particle_gpu_math.hpp",
|
||||
"include/mbgl/particle/field/field_binding.hpp",
|
||||
"include/mbgl/particle/renderer_debug_counters.hpp",
|
||||
"include/mbgl/renderer/layers/weather_color_texture_lifecycle.hpp",
|
||||
"include/mbgl/particle/ingress/qbin_v2_decoder.hpp",
|
||||
"include/mbgl/particle/ingress/qbin_v3_decoder.hpp",
|
||||
"include/mbgl/particle/ingress/encoded_field_transport.hpp",
|
||||
@@ -745,6 +755,8 @@ MLN_CORE_HEADERS = [
|
||||
"include/mbgl/renderer/renderer_frontend.hpp",
|
||||
"include/mbgl/renderer/renderer_observer.hpp",
|
||||
"include/mbgl/renderer/renderer_state.hpp",
|
||||
"include/mbgl/renderer/retained_underlay.hpp",
|
||||
"include/mbgl/renderer/render_segment.hpp",
|
||||
"include/mbgl/shaders/program_parameters.hpp",
|
||||
"include/mbgl/storage/database_file_source.hpp",
|
||||
"include/mbgl/storage/file_source.hpp",
|
||||
@@ -1139,6 +1151,7 @@ MLN_DRAWABLES_MTL_SOURCE = [
|
||||
"src/mbgl/shaders/mtl/line.cpp",
|
||||
"src/mbgl/shaders/mtl/location_indicator.cpp",
|
||||
"src/mbgl/shaders/mtl/particle_gpu_validation.cpp",
|
||||
"src/mbgl/shaders/mtl/retained_underlay_composite.cpp",
|
||||
"src/mbgl/shaders/mtl/raster.cpp",
|
||||
"src/mbgl/shaders/mtl/symbol.cpp",
|
||||
"src/mbgl/shaders/mtl/widevector.cpp",
|
||||
@@ -1183,6 +1196,7 @@ MLN_DRAWABLES_MTL_HEADERS = [
|
||||
"include/mbgl/shaders/mtl/line.hpp",
|
||||
"include/mbgl/shaders/mtl/location_indicator.hpp",
|
||||
"include/mbgl/shaders/mtl/particle_gpu_validation.hpp",
|
||||
"include/mbgl/shaders/mtl/retained_underlay_composite.hpp",
|
||||
"include/mbgl/shaders/mtl/raster.hpp",
|
||||
"include/mbgl/shaders/mtl/shader_group.hpp",
|
||||
"include/mbgl/shaders/mtl/shader_program.hpp",
|
||||
|
||||
@@ -48,6 +48,7 @@ list(APPEND
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/line.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/location_indicator.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/particle_gpu_validation.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/retained_underlay_composite.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/raster.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/shader_group.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/shader_program.hpp
|
||||
@@ -92,6 +93,7 @@ list(APPEND
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/line.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/location_indicator.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/particle_gpu_validation.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/retained_underlay_composite.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/raster.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/symbol.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/widevector.cpp
|
||||
|
||||
@@ -37,6 +37,7 @@ list(APPEND
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/fill_extrusion_pattern_instanced.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/heatmap.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/heatmap_texture.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/retained_underlay_composite.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/hillshade_prepare.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/hillshade.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/color_relief.hpp
|
||||
|
||||
68
docs/00_项目规范/07_Android粒子Validation_APK构建规范_v1.md
Normal file
68
docs/00_项目规范/07_Android粒子Validation_APK构建规范_v1.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Android 粒子 Validation APK 构建规范 v1
|
||||
|
||||
## 目的
|
||||
|
||||
固定 `MapLibreAndroidTestApp` 粒子验证 APK 的制作流程,避免每次重新调查 Gradle 参数、Java/Android SDK 环境、native strip 与 APK 体积异常。
|
||||
|
||||
权威入口:
|
||||
|
||||
```bash
|
||||
./tools/particle_android_validation/build_validation_apk.sh
|
||||
```
|
||||
|
||||
除排错外,不再手工拼接 Validation APK 的 Gradle 命令。
|
||||
|
||||
## 固定构建契约
|
||||
|
||||
```text
|
||||
App: MapLibreAndroidTestApp
|
||||
Renderer: OpenGL
|
||||
BuildType: Debug
|
||||
ABI: x86_64
|
||||
Native debug symbols: strip
|
||||
Gradle console: plain
|
||||
```
|
||||
|
||||
核心参数固定为:
|
||||
|
||||
```text
|
||||
:MapLibreAndroidTestApp:assembleOpenglDebug
|
||||
-Pmaplibre.abis=x86_64
|
||||
-Pmaplibre.validation.stripNativeDebugSymbols=true
|
||||
--console=plain
|
||||
```
|
||||
|
||||
## 必须执行的防错逻辑
|
||||
|
||||
1. 构建前删除目标 APK 文件,但不做全量 Gradle clean。
|
||||
2. Java 必须为 17;Android SDK 必须可解析。
|
||||
3. 构建失败时不得复用旧 APK。
|
||||
4. 构建成功后必须校验 APK ZIP 完整性。
|
||||
5. APK 内 native ABI 必须只有 `x86_64`。
|
||||
6. 必须包含 `lib/x86_64/libmaplibre.so`。
|
||||
7. 默认 `libmaplibre.so` 不得超过 80 MiB。
|
||||
8. 默认 APK 不得超过 100 MiB。
|
||||
9. 必须记录 SHA-256、commit、branch、dirty 状态和完整 Gradle 日志。
|
||||
|
||||
STEP-090/091 的正常 APK 参考体积约 57 MB。数百 MB 通常表示未 strip native debug symbols;约 95 MB 且 ZIP 逻辑内容仍约 60 MB,通常表示同一路径增量打包残留旧 ZIP 尾部数据。
|
||||
|
||||
## 标准产物目录
|
||||
|
||||
```text
|
||||
artifacts/maplibre-particle/android-validation/
|
||||
├── MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk
|
||||
├── build-info.txt
|
||||
└── build.log
|
||||
```
|
||||
|
||||
该目录为本地验证产物目录,不进入 Git。
|
||||
|
||||
## 边界
|
||||
|
||||
本 Pipeline 只负责构建和 APK 自验,不负责 ADB 安装、Activity 启动、设备交互或 STEP 业务场景验收。
|
||||
|
||||
Android 上机验证仍由后续独立步骤执行。构建成功不等于 STEP 场景已通过。
|
||||
|
||||
## 变更规则
|
||||
|
||||
若未来测试 App、ABI、Renderer、strip 策略或产物门槛发生正式变化,应先修改 Pipeline 脚本与本规范,再使用新流程;不得只在聊天或临时命令中形成新的事实基线。
|
||||
104
docs/20_调查/Particle_V13_严格代码审查报告_v1.md
Normal file
104
docs/20_调查/Particle_V13_严格代码审查报告_v1.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Particle V13 严格代码审查报告
|
||||
|
||||
日期:2026-09-25(Asia/Tokyo)
|
||||
审查性质:Mac 仓库只读代码审查、历史任务/报告交叉核对、定向原生测试复跑。未修改业务源码、未执行设备 GPU runtime 或 profiler。
|
||||
|
||||
## 1. 审查基线和证据分级
|
||||
|
||||
- Mac:`/Users/tei/dev/MapLibreParticleLab`;分支:`particle/main`。
|
||||
- HEAD 与 `origin/particle/main` 一致:`ee0b89e711bf01335b3aa71d36def785cd97fec3`;工作树 clean。
|
||||
- V13 功能提交:`016fd1fff6a33cf795c10d4f5c5413d308383677`。发布版本:`13.4.1-particle.13`。
|
||||
- Integration 主线:`39ad8415fbdb76c1822460bb09f3a3f35fe141e0`。
|
||||
- 参照:`docs/40_任务/STEP-092...`、`STEP-093...`、`STEP-094...` 任务;对应 `docs/报告/` 与 `docs/50_Step记录/`;STEP-088/089/090 性能历史报告。
|
||||
- 本次独立复跑:Render Segment/Retained/Fast Path 43/43 PASS;Field/QBIN/Viewport Reseed 69/69 PASS;V13 RN package TypeScript PASS。
|
||||
- 当前 RN Test Host `npm run typecheck` **失败(exit 2)**:TS2307 无法解析 `@navsea/maplibre-particle-rn`,伴随 TS7006。Host package.json 仍指向 `.10`。此前 Codex 使用临时工作树依赖通过,不等于当前默认安装可重复验证。
|
||||
- STEP-094 报告记录 OpenGL、Metal 构建、Codegen、Android Validation APK 通过;**Android/iOS V13 runtime、GPU frame/visual、功耗与长时间运行仍 NOT RUN**。此报告不将历史 Android 性能测量冒充 V13 性能。
|
||||
|
||||
## 2. 审查结论
|
||||
|
||||
没有在已复跑的纯数据结构/协议测试中发现失败;但 V13 在高频渲染、Metal 多段重建、GPU 资源分配和真实运行态验收上存在明确风险。不能由 43 项纯 Native 测试和编译 PASS 推断任意 R/C 交错的 GPU 输出、跨平台性能已经验收。
|
||||
|
||||
### F01 — P1:完整 Diagnostics 深复制进入每帧热路径(源码确定)
|
||||
|
||||
证据:`src/mbgl/renderer/renderer_impl.cpp:206-208,500-505`;`src/mbgl/renderer/retained_underlay_runtime_state.hpp:243-254`。
|
||||
|
||||
`tryRenderParticleFastFrame()` 和 `render()` 调用 `renderSegmentSnapshot()`;该函数在互斥锁内复制完整 `RenderSegmentDiagnostics`(含 `styleLayers` 与每个 Segment 的重复 `layers`/字符串),再补几个状态字段。一次 fast frame 可能复制两次。与 pull-based 诊断的设计意图不一致。
|
||||
|
||||
修复:增加只读的轻量 hot-path 状态(enabled/configRevision/topologyRevision 等);完整快照只在外部 pull 时构造;快照构建/复制次数做计数,稳定 30/60 FPS 应接近零。
|
||||
|
||||
### F02 — P1:Metal Retained 构建逐段 GPU 同步等待(源码确定,运行影响待 profile)
|
||||
|
||||
证据:`renderer_impl.cpp:790-824`;`retained_underlay_cache.cpp:316-322`;`src/mbgl/mtl/command_encoder.cpp:29-31`;`src/mbgl/mtl/offscreen_texture.cpp:86-91`。
|
||||
|
||||
`finishBuild()->present(*offscreenTexture)->swap()` 内部 `commit()` 后 `waitUntilCompleted()`。一帧多个 Retained 段失效时,可能产生多次串行 CPU/GPU 同步。使用 Metal System Trace / GPU Capture 测量 `waitUntilCompleted` 时间和 command-buffer 数量,再设计保持依赖顺序的异步提交/跨 pass 协调;不可仅删除 wait 而不确保纹理可读。
|
||||
|
||||
### F03 — P1:缓存内容失效与 GPU 资源销毁耦合(源码确定)
|
||||
|
||||
证据:`retained_underlay_cache.cpp:203-209,241-255,364-374`。
|
||||
|
||||
`revisionChanged` 使 `invalidate()` 与 `releaseResources()` 一起发生,即使 viewport 未变、GPU context 有效,重建时仍需重新创建纹理和 composite group。普通 camera、source/tile、content revision 在多 Retained 段下会放大分配/销毁和相关同步。
|
||||
|
||||
修复:分离 `contentDirty` 与 `resourceInvalid`,同尺寸同 context 保留 allocation、重新 clear/build;context loss、尺寸不兼容、显式关闭/释放时才销毁。用 peak GPU bytes、textureCreate、resourceRelease、每帧 build/reuse 证明收益与正确性。
|
||||
|
||||
### F04 — P1:多缓存没有 GPU 内存预算(源码确定,OOM 影响待量化)
|
||||
|
||||
证据:`renderer_impl.cpp:506-524`;`retained_underlay_cache.cpp:241-251`;`mtl/offscreen_texture.cpp:20-55`;`gl/offscreen_texture.cpp:18-35`。
|
||||
|
||||
每个 Retained Segment 均拥有完整 viewport 的 RGBA + depth/stencil 离屏资源;总成本约随 retainedCount×width×height 增长。相邻 R-R 也各自独立,符合契约,但高像素 iPad 与多段场景缺乏实际 bytes/peak/budget 观测。先增加预算与失败时的安全 full-render fallback,切勿通过强制合并相邻 R-R 破坏 V13 契约。
|
||||
|
||||
### F05 — P1:V13 Test Host 默认安装不可复现(已复现失败)
|
||||
|
||||
证据:`integration/react-native/MapLibreParticleRNTestHost/package.json` 指向 `.10`;当前 `npm run typecheck` exit 2;报告也明确临时本地替换后已恢复旧依赖。
|
||||
|
||||
修复:将 Test Host 依赖和 lockfile 与已发布 `.13` 精确对应,重新安装并验证 V13 typecheck/codegen/iOS & Android bridge/真实运行;测试日志注明源包及 Native 精确版本。不得将临时链接环境结果写成默认环境 PASS。
|
||||
|
||||
### F06 — P1:多 Continuous 的 Particle Fast Path 固定 fallback(明确设计限制)
|
||||
|
||||
证据:`renderer_impl.cpp:45-57,198-287` 和 `particle_fast_path.hpp:110-140`,STEP-094 设计与报告。
|
||||
|
||||
N 个 Continuous 中只支持恰好一个的 Fast Path;R-C-R-C-R 场景总走 Full CPU。STEP-090 历史 A/B 场景 RenderThread 约为 fast ON 21.7% / OFF 97.0%,只是历史参考,不能直接视作 V13 数据。先采集 V13 多 C 的 CPU/GPU 帧时间与频率,再设计可组合的 multi-C fast path;绝不可为性能偷偷移动 Style Layer 或合并相邻段。
|
||||
|
||||
### F07 — P2:范围访问反复计算线性 ordinal(源码确定)
|
||||
|
||||
证据:`src/mbgl/renderer/render_orchestrator.hpp:156-180`,`std::distance(multimap.begin(), lower_bound(...))`。每个 Segment 的 opaque/translucent、build、upload/tweaker 路径可重复触发定位。
|
||||
|
||||
修复:在 LayerGroup topology 变化时预计算有序索引/ordinal;高频 render 只用稳定区间和 ordinal,避免多段场景重复 O(N) 前缀遍历。
|
||||
|
||||
### F08 — P2:多段失效粒度仍过宽(明确设计限制)
|
||||
|
||||
证据:`renderer_impl.cpp:565-646`;`render_orchestrator.cpp:156-158,1005-1018`。Layer Impl fingerprint 可局部失效;source/tile/image/glyph/placement 与聚合 `retainedSupported` 仍可能使全部 R fallback/build。应先用 `sourceId` 与 Segment 关联建立有证据的保守依赖关系,无法归属继续全局失效,不得牺牲安全。
|
||||
|
||||
### F09 — P2:Diagnostics 资源指标可能发生归属偏差(源码路径风险)
|
||||
|
||||
证据:`retained_underlay_runtime_state.hpp:173-240` 更新新 topology/entry;`renderer_impl.cpp:506-524` 随后 `clear()` 旧 caches;旧对象析构在 `retained_underlay_cache.cpp:364-374` 用旧 segmentIndex 报 release。不同 topology 的相同 index 可能指向新段,旧资源的释放计数可能归于新段或被 Continuous cache=null 丢弃。建议使用 cache generation/unique ID 归属,或在更换 diagnostics topology 前记录旧 generation 的 release;聚合 totals 与 per-generation 分离。
|
||||
|
||||
### F10 — P2:发布与协调状态资料漂移(核对事实)
|
||||
|
||||
Integration 的 `status/`、`compatibility/MATRIX.md` 仍描述 `.5` / iOS XCFramework 等旧状态,Mac 粒子仓库已发布 `.13`。应由粒子负责人维护正式兼容矩阵,区分“Particle 已发布”和“NavSea 已接入/验证”,不得仅根据旧矩阵断言 V13 不存在或已在 NavSea 生效。
|
||||
|
||||
## 3. 正确性与验证缺口
|
||||
|
||||
以下不是已证实的渲染 bug,而是必须在设备上验证的风险:
|
||||
|
||||
- V13 segmented pass 改变了 opaque/translucent、offscreen composite 的执行拆分;纯 resolver GTest 不覆盖 GPU blending/depth/stencil 与 Metal/OpenGL 的最终像素。
|
||||
- 必须覆盖 R-C-R、C-R-R、R-R-C、R-C-R-C-R、相邻 R-R 独立 cache、全 R、全 C,特别是透明航标/半透明颜色场与动态粒子叠加。
|
||||
- hidden boundary、invalid/recovery、Style layer insert/remove/reorder、Resize/Rotate/Pan/Zoom/Pitch、Context loss、Cache ON/OFF、Source/Tile mutation、切换 Plan 后的下一帧,均需要截图/帧采样和资源 counter 交叉验收。
|
||||
- STEP-093/094 报告已明确运行态 NOT RUN,因此不以原生编译 PASS 代替此项。
|
||||
|
||||
## 4. 推荐执行顺序(分批,降低反复安装成本)
|
||||
|
||||
A. **无需设备的修复**:先处理 F01 快照深复制、F05 Test Host 精确版本与复现、F07 ordinal、F09 诊断归属;增加纯 C++ 定向回归。每阶段只做对应范围,不把源文件无关改动混入。
|
||||
|
||||
B. **Metal 资源修复**:合并 F02/F03/F04,统一处理 GPU 对象分配与同步、资源预算;OpenGL/Metal 静态编译和 Native regression 先行;再一次集中到 iPhone/iPad 采集 GPU Frame Capture。
|
||||
|
||||
C. **正式运行态验收**:集中执行 topology、alpha/depth、invalid/recovery、30/60 FPS、稳定/变更、内存峰值、30 分钟以上长稳态;记录相同设备、相同 Style、相同视口的 V12/V13 或 V13 ON/OFF 成对指标,不外推历史数字。
|
||||
|
||||
D. **后续性能架构**:在基础正确性通过后,再研究多 Continuous Fast Path 和 source-to-segment 精确依赖。保持 Style Z-order 唯一顺序源、不合并同策略相邻段、不引入业务频率分类。
|
||||
|
||||
## 5. 最终判定边界
|
||||
|
||||
- 纯 Native tests:43/43 + 69/69 PASS;RN V13 package TS PASS。
|
||||
- Test Host 默认环境:FAIL(缺失模块 / `.10` 引用)。
|
||||
- 源码性能问题:F01/F02/F03/F07 的触发调用链得到静态确认;真实节省量仍需设备测量。
|
||||
- V13 Android/iOS GPU runtime、完整画面、内存和长时间性能:本次 NOT RUN;不声称生产级全量 PASS。
|
||||
- Mac Git:HEAD 与 origin 一致,工作树 clean;未修改粒子源码。
|
||||
285
docs/20_调查/STEP-095_V13独立严格代码与性能调查_v1.md
Normal file
285
docs/20_调查/STEP-095_V13独立严格代码与性能调查_v1.md
Normal file
@@ -0,0 +1,285 @@
|
||||
# STEP-095 V13 独立严格代码与性能调查 v1
|
||||
|
||||
日期:2026-09-25(Asia/Tokyo)。性质:独立静态审查;只写指定中文文档,不修复源码,不运行构建、测试、codegen、安装或设备操作。
|
||||
|
||||
## 1. 执行摘要与结论边界
|
||||
|
||||
V13 的通用分段解析、Style 顺序及保守 Fast Path 门控有源码依据,但不能据此批准 GPU 正确性或性能。本轮先检查 V12→V13 diff、真实调用链和生命周期,再阅读旧报告逐项挑战。新增优先问题为:离屏目标切换未重置 CPU stencil 掩码记录;Metal depth/stencil 清除契约未完整下传;LayerGroup 抛错后 retained build 仍可能标成功;V4 PLAY 缺少生产时钟推进调用。前三项的实际像素、故障注入和设备行为仍需验证。
|
||||
|
||||
旧 F01/F02/F03/F04/F07 的成本路径成立,但没有本轮 profiler 数据证明热点排序、OOM 或收益。F06 多 Continuous 退出静态 CPU Fast Path、F08 保守全局失效属于显式设计限制。F09 可给出计数跨 topology 错归的具体链,但不证明 stale texture 被复用。F05 的 Host `.10`/native `.13` 错配成立,历史 TS2307 不能仅归因于版本号;F10 只证明协调文档滞后。
|
||||
|
||||
新增成本还包括 V4 全网格物化、UI 线程解码、普通粒子 Metal 离屏等待及 drawable 重建。未找到足以定 P0 的证据;不宣称不存在其他缺陷。调查内容完成,Git 交付结果见阶段记录的收尾条目。**V13 产品运行态不判 PASS:本轮所有构建、测试、设备、性能验证均 NOT RUN。** RDC 仍须独立复核和验证。
|
||||
|
||||
## 2. 冻结基线、环境与范围
|
||||
|
||||
| 项目 | 核实结果 |
|
||||
| --- | --- |
|
||||
| 机器/目录 | `Mac-mini-tei.lan`,Darwin x86_64;`/Users/tei/dev/MapLibreParticleLab` |
|
||||
| 分支/起始 HEAD | `particle/main`;`05878d662308b9a92c20c433c3cd2943d74ae718` |
|
||||
| 起始状态 | 工作树 clean;子模块未见偏离记录提交的标记 |
|
||||
| 远端 | cached origin 与实际 `git ls-remote` 都是上述 HEAD;origin 为 `https://git.eyaku.jp/tei/MapLibreParticleLab/` |
|
||||
| 冻结源码 | `ee0b89e711bf01335b3aa71d36def785cd97fec3` |
|
||||
| V13 功能/旧报告归档 | `016fd1fff6a33cf795c10d4f5c5413d308383677` / `45c4dca84928a57a2cf0226737c34cabd7fbfbfb` |
|
||||
| V12 比较点 | tag 解引用提交 `7eaff6e12360e26591dceb5e011129eeab7ba4c7` |
|
||||
| V13 tag | `.13` tag 对象 `bccbceddec70f7a2b6362bce653f9c577aa2db1d`,解引用为冻结源码,远端一致 |
|
||||
| manifest sourceCommit | `ddf20dec9fe9cd05b77274827b6825829500dd5a`;至冻结提交为发布元数据收尾,生产源码一致 |
|
||||
| 模型记录 | 用户在本次同一会话恢复指令中明确指定 `gpt-6-astra high`,与任务要求一致;这是当前会话的用户指定记录。未获得可独立核验的底层启动参数,不声称已独立验证实际模型/effort |
|
||||
|
||||
冻结源码至起始 HEAD 仅新增旧审查报告及 STEP-095 任务书;下文源码行号按冻结源码,与本轮起始工作树对应。V12→V13 整体 diff 为 45 文件、2393 行增加/203 行删除(含文档),主要新增 segment/parser/resolver、runtime diagnostics、renderer/orchestrator 调度与平台桥接,并非粒子数值 shader 的整体重写。
|
||||
|
||||
已读取 AGENTS、PARTICLE_BASELINE、项目规范、项目总则 README 所列当前有效规则及基线 v2,交叉阅读 STEP-088~094 的任务/设计/阶段/报告相关内容。总则仍含早期 `.3` 和 Linux 主线描述,本任务以 Mac `.13` 冻结点为准,不重写历史文档。官方 Core 基线 `18545ca10d296704b2cba675b182ff2c8da24588`,Android 官方 13.4.1、iOS 官方 6.28.0。
|
||||
|
||||
### 2.1 发布坐标(仅核对仓库记录)
|
||||
|
||||
来源:`release/maplibre-particle/release-manifest.json` 与 `release/maplibre-particle/ios/release-manifest.json`。
|
||||
|
||||
| 产物 | 坐标 | manifest 大小/SHA-256 |
|
||||
| --- | --- | --- |
|
||||
| RN | `@navsea/maplibre-particle-rn@13.4.1-particle.13`,MLRN 11.3.6 | 25,966 bytes;`3a726db06cb967fe56de77ca4a72feed98cd642a2d1b73fd51b1531d15ef7539` |
|
||||
| Android OpenGL | `cc.navsea.maplibre:android-sdk-particle-opengl:13.4.1-particle.13` | 169,786,873 bytes;`ddf1deced00cc963d1d625c73c68c449232602113a1490349e69100473dda82b` |
|
||||
| iOS Metal | `6.28.0-particle.13`,MapLibre XCFramework/SPM exactVersion | 8,418,198 bytes;`8d47d9f6d44479aff51299c213887bd9be204da320da4fd4fff15dd064930556` |
|
||||
|
||||
manifest 的 `PUBLISHED_PUBLIC_READBACK_PASS` 是 HISTORY。本轮没有下载、重算产物散列、重建或核实设备实际链接版本;文档提交不是新 V13 发布。
|
||||
|
||||
### 2.2 调用链和未覆盖范围
|
||||
|
||||
已沿 segment JSON/plan → runtime → orchestrator/renderer → cache → PaintParameters/GL/Metal target,及 canonical/QBIN/profile → Store/Clock/projection → RasterArraySource immutable Impl → WeatherColor/Particle RenderLayer → Field/State/Trail GPU 追踪;检查 Android Java/Kotlin/JNI、iOS ObjC++、RN typed API/JSON Spec、Host package/lock/plugin及 Integration 只读状态。阅读 resolver/cache/diagnostics/field/decoder/reseed 测试源码,不等同执行测试。
|
||||
|
||||
未逐行形式化验证全部官方 renderer/driver、Vulkan、业务 provider/HTTP/Route/ETA或所有并发交错;未使用设备、capture、sanitizer、fuzzer。此报告不是全仓无缺陷证明。
|
||||
|
||||
## 3. Finding 总表
|
||||
|
||||
D=源码可复核缺陷路径;C=源码确定成本;R=后果需运行验证;E=环境/文档偏差。置信度均指源码链而非事故发生概率。每项详细位置、修复与验收见后文。
|
||||
|
||||
| 编号 | 严重度/类别/置信度 | 触发与影响 | 旧报告关系 |
|
||||
| --- | --- | --- | --- |
|
||||
| N01 | P1/D+R/高 | 相同 tiles 跨 target,CPU stencil 映射复用,后段可能被裁掉 | 新增 |
|
||||
| N02 | P1/D+R/高 | Metal retained depth/stencil clear 未完整转换,模拟器 attachment 缺口 | 新增 |
|
||||
| N03 | P1/D+R/高 | group 绘制抛错被 catch,仍标 cache 成功,可能持续复用残图 | 新增 |
|
||||
| N04 | P1/D/高 | V4 只 PLAY 不继续 set time,生产时钟无推进调用 | 新增 |
|
||||
| N05 | P2/D/高 | JSON NUL/重复 strategy 被接受,严格解析失真 | 新增 |
|
||||
| N06 | P2/D+R/高 | 结构合法但数值非法的场先入库,物化失败后可用投影被置空 | 新增 |
|
||||
| N07 | P2/D/高 | 同 view 重复 attach,listener 和持有引用递增 | 新增 |
|
||||
| C01 | P1/C/高 | full diagnostics 锁内深复制进入 fast/render | F01 |
|
||||
| C02 | P1/C+R/高 | Metal retained build 与普通 State/Trail target 等待 | F02,新增扩大范围 |
|
||||
| C03 | P1/C/高 | 内容 dirty 释放同尺寸可复用 GPU allocation | F03 |
|
||||
| C04 | P1/C+R/高 | 多 R 全视口资源无总预算;Store 也缺 fieldKey 总预算 | F04,新增场预算 |
|
||||
| C05 | P2/C/高 | ordinal 线性前缀遍历,fast update 仍扫全部 items | F07,新增扫描 |
|
||||
| C06 | P1/C+R/高 | 同时间/mix-only 整网格物化;提交链可占用 UI | 新增 |
|
||||
| C07 | P2/C/高 | tick 重建 drawable/attributes/tweaker,窗口切换复制场 | 新增 |
|
||||
| O01 | P2/D/高 | 新 topology 先发布,旧 cache 析构向新 index 计数 | F09 |
|
||||
| E01 | P1/E/高 | Host RN `.10` 与 Android 插件 `.13` 错配 | F05 |
|
||||
| E02 | P3/E/高 | Integration 仍旧状态,实际接入 UNKNOWN | F10,降级 |
|
||||
|
||||
## 4. 新增正确性和生命周期发现
|
||||
|
||||
### N01:CPU stencil 记录跨 framebuffer 复用
|
||||
|
||||
证据:`src/mbgl/renderer/renderer_impl.cpp:798-847,938-945`;`src/mbgl/renderer/retained_underlay_cache.cpp:304-319`;`src/mbgl/renderer/paint_parameters.cpp:185-201,365-374`;`src/mbgl/gl/layer_group_gl.cpp:67-98`;`src/mbgl/mtl/tile_layer_group.cpp:118-119`。
|
||||
|
||||
最短链:同一个 PaintParameters → 第一个 R 清 stencil、写 mask 并记录 tileClippingMaskIDs → 第二个 R 新 target/清 stencil → tileIDsCovered 为真而跳过写 mask → draw 仍 Equal 非零 ID。CPU 记录属于前一 target,后一 GL target 实际为零。main buffer 清除后也未清 CPU 记录;updateStencilBufferAvailability 不是映射重置。
|
||||
|
||||
触发用例:同 vector source、同 tile 集合的两个 fill 分置相邻 R-R,首次都 build;或 R build 后同 tiles 的 C 绘入主目标。tile 集合变化可触发清记录,所以不是所有画面必坏。V13 任意 R-R 扩大暴露面,V12 多 target 也应回归,不能断言只由 V13 引入。
|
||||
|
||||
最小修复:target/pass 切换时明确失效 CPU mask/编号,或按 target generation 隔离,并保证 GPU clear 一致。验收:GL/Metal cache OFF 对照,同 source R-R/R-C-R 的首帧、rebuild、reuse 像素和 stencil capture。缺口源码已确认,实际像素 NEEDS_RUNTIME。
|
||||
|
||||
### N02:Metal depth/stencil 描述未完整下传
|
||||
|
||||
证据:`retained_underlay_cache.cpp:304-310` 请求 clearDepth=1、clearStencil=0;`src/mbgl/mtl/render_pass.cpp:13-38` 只转换 clearColor;`src/mbgl/mtl/offscreen_texture.cpp:32-83,139-148` 绑定附件但该路径未设置 depth/stencil clear/load/store。模拟器不创建 stencilTexture,也未将 combined depth/stencil texture 绑定 stencilAttachment;格式选择见 `src/mbgl/mtl/texture2d.cpp:99-104`。
|
||||
|
||||
触发:Metal retained tile/depth 绘制,尤其首次 target、simulator。源码确认契约缺口,但未测驱动默认内容/validation 行为,不宣称所有真机黑屏。isRenderable 仅验证 color,depth/stencil create 在 bind,早期就绪判断不能证明所有附件就绪。
|
||||
|
||||
最小修复:完整翻译 descriptor、正确绑定 combined attachment,并验证所有附件后允许 build;保持官方接入薄。验收:Metal API Validation、真机/模拟器 capture 核对 attachment/load/store/clear,故障注入验证安全 fallback。NEEDS_RUNTIME。
|
||||
|
||||
### N03:绘制异常未阻止 cache 发布为成功
|
||||
|
||||
证据:`src/mbgl/renderer/render_orchestrator.hpp:156-178` 的 visitor catch 只通知 observer;`renderer_impl.cpp:798-824` 随后无错误结果判断,finishBuild/markBuildSucceeded。
|
||||
|
||||
触发:R 的一个 layerGroup.render 抛错,observer 正常返回,其他 group 继续,残缺图像可变 valid 并在稳定帧 reuse。反例:资源准备阶段已有失败回退(`retained_underlay_cache.cpp:240-255`、`renderer_impl.cpp:662-677`),问题不等于所有资源失败都没有 fallback。
|
||||
|
||||
最小修复:build 汇总明确成功状态,失败 target 不发布/复用,可恢复错误进入完整渲染或约定安全失败。验收:第二个 group 故障注入,确认 successful build/reuse 不增长、无 stale composite、恢复和资源释放正确。NOT RUN。
|
||||
|
||||
### N04:V4 PLAY 没有生产时钟推进
|
||||
|
||||
证据:`src/mbgl/style/sources/raster_array_source.cpp:453-470`;`src/mbgl/particle/field/field_runtime.cpp:196-220`;`src/mbgl/renderer/layers/render_particle_validation_layer.cpp:1174-1189,1276-1278`。生产树 src/include/platform/packages 中 advanceForecastClock 仅见声明和定义,无调用。Android `platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/WeatherRuntime.java:225-242`、iOS `platform/ios/src/MLNParticleFieldRuntime.mm:147-165` 的 wake 只 repaint。
|
||||
|
||||
最短链:设一次 requested time → PLAY 改 clock state → repaint/particle cadence → renderer 读取不变的 immutable projection.mix。粒子运动不能证明预报时间在播放。Host 每次显式 set time 可绕过,但不代表 PLAY 本身有效。
|
||||
|
||||
最小修复:在既有 map/source 所属线程接入唯一单调时钟,明确速度、pause/resume、后台和销毁语义,不建立第二套时间真相。验收:仅设一次时间,PLAY 的 .5/1/2 倍 mix、PAUSE、跨窗口和恢复;`test/particle/field_runtime.test.cpp:272-310` 手动 advance 单测不覆盖生产接线。本轮 NOT RUN。
|
||||
|
||||
### N05:Segment JSON NUL 截断与重复键
|
||||
|
||||
证据:`src/mbgl/renderer/render_segment_json.hpp:16-49`。Parse 用 C 字符串,strategy/startLayerId 用 GetString 未带 GetStringLength;`"RETAINED\u0000x"` 截成 `RETAINED`,`"boundary\u0000x"` 可解析到另一真实 ID。segment 两个重复 strategy 满足 MemberCount<=2,白名单不检唯一性,读取首值。
|
||||
|
||||
触发可由 primitive JSON/native 入口构造,TS typed API 不替代校验。普通未知 strategy/schema、missing/reversed 仍会 invalid/fallback。最小修复:长度感知解析/构造,拒绝 NUL/重复键,保持 invalid 清旧 plan 语义。验收:上述 payload、raw NUL 后缀、正常 unicode、未知字段、invalid→valid及两桥接错误归一化。NOT RUN。
|
||||
|
||||
### N06:数值非法的场在入库后才被拒绝物化
|
||||
|
||||
证据:`src/mbgl/particle/ingress/qbin_v2_decoder.cpp:41-73` 校验结构、CRC、scale/offset但不扫描 Float32 样本;`src/mbgl/particle/field/field_profile_validator.cpp:77-166` 匹配契约;`field_projection_materialization.cpp:38-67,97-135` 才检查解码数值;`raster_array_source.cpp:422-440,496-522` 先 submit,再物化,失败 next 对应投影为空。
|
||||
|
||||
触发:已有绑定字段,同时间更高 revision、CRC 正确、valid=1、样本 NaN/Inf 的 QBV2。结构/契约可接受,替换旧帧后物化失败,但 submit 返回 AcceptedInserted/AcceptedReplacedRevision。有限 scale 乘法溢出也应纳入验证。此处不声称 NaN 上传 GPU——物化拒绝正是反例;缺陷是 accepted/可渲染语义及旧状态保留脱节。
|
||||
|
||||
最小修复:明确 ingress 数值约束,入库前校验或事务式验证,保留合法无效 cell/通用未绑定 plane 语义。验收:好旧帧→坏高 revision,检查返回值、Store/投影保持、noData/validity、极值及恢复。NOT RUN。
|
||||
|
||||
### N07:Android attach listener 积累
|
||||
|
||||
证据:`packages/maplibre-particle-rn/android/src/main/java/org/navsea/maplibre/particle/weather/WeatherRuntimeBridgeModule.kt:30-63`。每 attach 新增捕获 concrete map 的 listener;显式 detach 只 dispose runtime,view detach 回调也不移除 listener。
|
||||
|
||||
同一个存活 view 重复 attach/detach,listener/引用及回调为 O(k)。未证明 view 回收后永久泄漏或 UAF;每次重新解析 view、没有全局 reactTag registry 是正面约束。最小修复:view/map 单一 listener、attach 幂等、明确 remove 生命周期。验收:同 view 100 次、换 view、卸载/后台恢复,listener/dispose/存活对象回基线。NOT RUN。
|
||||
|
||||
## 5. 性能、资源与观测
|
||||
|
||||
### C01:完整 diagnostics 深复制进入热路径
|
||||
|
||||
证据:`renderer_impl.cpp:198-208,500-505` → `retained_underlay_runtime_state.hpp:243-254` 锁内复制 styleLayers、segments.layers、字符串。成功 fast frame 可两次,不是所有帧固定两次。成本 O(L+S),锁竞争程度待测。修复:一致的轻量热快照与完整外部 pull 分开,不能无同步拆字段。验收:稳定 30/60 FPS 的完整快照次数应随外部 pull,测 alloc/frame、锁等待、CPU;无收益百分比。
|
||||
|
||||
### C02:Metal 等待不限于 retained rebuild
|
||||
|
||||
证据:`retained_underlay_cache.cpp:316-319` → `src/mbgl/mtl/command_encoder.cpp:29-31` → `mtl/offscreen_texture.cpp:86-91` commit/waitUntilCompleted,失效 R 逐段同步;稳定 reuse 没有 retained build wait。
|
||||
|
||||
新增链:`src/mbgl/renderer/render_target.cpp:71-119` 普通 target 也 present;`render_particle_validation_layer.cpp:1990-2111` tick 注册 State/Trail 写目标,初始化可能更多。因此 retained 稳定也可能有多个普通粒子 wait,属继承成本。修复前先 capture buffers、wait 栈及依赖,再设计有序异步提交,不能直接删 wait。验收:State A/B 无别名,Trail/composite 顺序与轨迹一致,CPU wait/GPU frame time 成对比较。NEEDS_RUNTIME。
|
||||
|
||||
### C03:内容失效与 allocation 销毁耦合
|
||||
|
||||
证据:`retained_underlay_cache.cpp:161-237` revisionChanged 同时 invalidate/release;`:240-255,365-374` 重建/释放 texture/compositor。触发为内容/相机等 revision 改变,不是稳定帧无条件分配。修复:contentDirty 与尺寸/context/格式的 resourceInvalid 分开,同尺寸有效 context 保留 allocation。验收:mutation、camera、resize、context loss 分开统计 build/create/release/peak;先保证 N01/N02 clear 正确。耗时收益待测。
|
||||
|
||||
### C04:多段 GPU 与多字段 CPU 无总预算
|
||||
|
||||
证据:`renderer_impl.cpp:506-524`;`retained_underlay_cache.cpp:240-255`;`src/mbgl/gl/offscreen_texture.cpp:18-35`、`mtl/offscreen_texture.cpp:20-55`。每 R 全视口 color/depth/stencil;RGBA8 color 理论下界 R×W×H×4 bytes,另有附件、alignment、主 framebuffer、State/Trail及延迟释放,不能把逻辑 release 当物理显存回收。
|
||||
|
||||
新增:`src/mbgl/particle/field/field_runtime.cpp:223-230` 对新 fieldKey 建 series;`include/mbgl/particle/field/field_runtime.hpp:115` 每 series 默认容量并非总 key/总 bytes 上限。QBIN 单文件64 MiB/解码32 MiB 上限也不约束累计持有量。
|
||||
|
||||
修复:map 预算、分配前溢出安全估算、超限明确 full-render/拒绝策略,不合并相邻 R、不淘汰仍绑定窗口。验收:R×viewport×字段数阶梯、allocation failure/context loss、30分钟以上 peak/回收。OOM/泄漏未证实,NEEDS_RUNTIME。
|
||||
|
||||
### C05:ordinal 前缀遍历和全层扫描
|
||||
|
||||
证据:`render_orchestrator.hpp:156-178` 的 multimap std::distance 是线性,S 次范围定位可能 O(S×G);`render_orchestrator.cpp:1202-1226` fast updateParticleLayers 仍遍历完整 items,非粒子只跳过 update;`:973-975` 按 retained 稳定性还可能扫 source fading。
|
||||
|
||||
修复:topology 改变时缓存 global ordinal/粒子列表,保持 generation 和原始全局深度编号。验收:固定粒子层扩大 L/G/S,测 CPU/调用次数并回归 reorder/delete。实际热点占比待测。
|
||||
|
||||
### C06:同时间/mix-only 仍全网格物化,UI 成本遗漏
|
||||
|
||||
证据:`raster_array_source.cpp:453-455,496-522` 在 sameProjection 前分别物化 color/particle;`field_runtime.cpp:196-200` 相同非零时间也返回 true;`field_projection_materialization.cpp:69-75,97-211` 分配/遍历 validity、T0/T1 scalar/vector。Wind 双绑定可重复同向量物化。mix 更新产生新 Impl/onSourceChanged,`render_orchestrator.cpp:361-365` 全局 SourceMutation 可进一步失效所有 R。
|
||||
|
||||
桥接:Android withMap UI 调度经 `platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp:1501-1517` 同步到 Source;iOS `packages/maplibre-particle-rn/ios/WeatherRuntimeBridge.mm:105-120,235-240` 的 withMap 与 `platform/ios/src/MLNParticleFieldRuntime.mm:222-252` 也同步提交 Core。预读取文件不等于 CRC/解码/物化已经离开 UI。成本随格点/plane 增长,实际卡顿/ANR未测。
|
||||
|
||||
修复:相同时间早退,按端点 identity/binding 共享不可变物化、mix 单独更新;若 worker 解码,owner-thread 发布必须带 map/source generation,禁止 worker 直接改 Source。局部失效必须先有完整依赖。验收:同时间、mix-only、跨窗口、双绑定、最大网格的 CPU/alloc/UI阻塞/upload/rebuild计数。NEEDS_RUNTIME。
|
||||
|
||||
### C07:tick 对象重建及端点复制
|
||||
|
||||
证据:`render_particle_validation_layer.cpp:826-904` clearDrawables 后新 attributes/builder/tweaker;`:1668-1669,1990-2111` 更新 target 注册及 State/Trail/composite;`:657` createOrUpdate UBO 不证明跨新 drawable 复用;`:1494-1499,1538-1563` 场端点切换复制数组。
|
||||
|
||||
反例:State/Trail textures 和共享顶点不是每 tick 全部重建,field 复制主要在窗口边界,不能称 CPU 每帧模拟全部粒子或无条件上传场。修复:稳定 drawable 保留,仅更新 uniforms/texture,端点共享不可变存储。验收:tick/bootstrap/reseed/resize/端点轮换分别测对象、UBO、upload和峰值。NOT RUN。
|
||||
|
||||
### O01:旧 cache release 记入新 topology
|
||||
|
||||
证据:`retained_underlay_runtime_state.hpp:173-240` 先发布新 entries;`renderer_impl.cpp:500-524` 再 clear 旧 cache;`retained_underlay_cache.cpp:120-122,365-374` 析构按旧 segmentIndex 计数;runtime `:256-272` 寻找当前 entry。
|
||||
|
||||
旧 index 0 的 R 换成新 R,旧 release 可记入新 R;新 index 为 C/越界时 per-segment 计数丢失。不证明 GPU 未释放或旧图复用。另 runtime `:198` root fallback 只表示 resolution invalid,不能覆盖 cache OFF/unsupported/resource fallback;消费方不可读成 GPU 当前 retained 成功。
|
||||
|
||||
修复:计数带 cache identity/generation,聚合历史与当前段分开,明确两类 fallback。验收:真 cache R-R→C-R、相同 index 换边界、disable/restore析构计数,纯手动 counter 测试不足。NOT RUN。
|
||||
|
||||
## 6. 环境发现和旧 F01~F10 复核
|
||||
|
||||
E01 定位:`integration/react-native/MapLibreParticleRNTestHost/package.json:12`、`package-lock.json:12,1765-1766` 为 `.10`,`plugins/withParticleNativeAndroid.js:9` 注入 `.13`,当前 package/native 为 `.13`。默认干净 Host 不能证明 V13 全链。旧 typecheck exit2/TS2307/TS7006 为 HISTORY;缺模块和旧 API 是不同原因,本轮未复跑。修复方向:后续单独对齐 package/lock/native resolved version,干净安装、typecheck/codegen及设备调用;不能把临时工作树链接 PASS 当默认包 PASS。本轮不改依赖。
|
||||
|
||||
E02 定位:只读 `/Users/tei/dev/navsea-particle-integration` HEAD `39ad8415fbdb76c1822460bb09f3a3f35fe141e0`,`compatibility/MATRIX.md` 与 `status/` 仍含 Sept18 `.5`、iOS 等待、RN NOT_STARTED。它与本仓 `.13` 是不同时点,真实 NavSea 接入 UNKNOWN。降 P3 文档项,建议负责人以接入 commit/包/设备证据更新,不改 Core 协议;本轮不改 Integration。
|
||||
|
||||
| 旧项/原结论 | 当前意见 | 修正和依据 | 补证 |
|
||||
| --- | --- | --- | --- |
|
||||
| F01 P1 深复制 | CONFIRMED | C01,成功 fast 可两次而非每帧必两次 | alloc/锁/CPU NOT RUN |
|
||||
| F02 P1 retained wait | CONFIRMED | C02,限定 build,补普通粒子 wait | 收益 NEEDS_RUNTIME |
|
||||
| F03 P1 dirty 释放 | CONFIRMED | C03,稳定 reuse 不重建 | 内容/resize/context分测 |
|
||||
| F04 P1 无预算 | CONFIRMED | C04,无预算不等于已 OOM | peak/压力 NEEDS_RUNTIME |
|
||||
| F05 P1 Host 不可复现 | QUALIFIED | E01,错配确定,旧 TS2307非本轮且不可仅归版本 | 干净安装 NOT RUN |
|
||||
| F06 P1 multi-C full CPU | QUALIFIED | 显式设计限制,建议 P2;不等于关闭 GPU retained reuse | 相同Style A/B NOT RUN |
|
||||
| F07 P2 ordinal 线性 | CONFIRMED | C05,优化定位不能改全局编号 | S/G基准 NOT RUN |
|
||||
| F08 P2 全局失效宽 | QUALIFIED | 保守设计,未证明 stale;未知依赖不能裁窄 | source/tile/glyph依赖 |
|
||||
| F09 P2 错归风险 | CONFIRMED | O01具体先换entries后旧析构链;不证明stale texture | 真cache生命周期 NOT RUN |
|
||||
| F10 P2 文档漂移 | QUALIFIED | E02降P3;不推定实际接入 | 负责人跨项目核对 |
|
||||
|
||||
没有为凑数将整个旧项标 DISPROVED;被否定的是“多C关闭所有GPU reuse”“计数错归证明旧纹理复用”“.10必然造成TS2307”“单测证明像素正确”等外推。旧报告未明确作这些断言之处,也不反向指控它作过。
|
||||
|
||||
## 7. 反例、边界和设计限制
|
||||
|
||||
1. `render_segment_plan.hpp:32-157` 采用完整 ordered IDs(含 hidden),边界存在且递增,全R/全C合法,相邻同策略不合并,V12经同模型映射;首段可省 startLayerId 是契约。普通 invalid 清 resolution、恢复重解析有依据。
|
||||
2. reorder 不是“仅added/removed所以遗漏”:`src/mbgl/renderer/style_diff.cpp:9-42` 序列差异体现移动,`render_orchestrator.cpp:318-329` 重建 orderedLayers,未报告无可达链的 reorder bug。
|
||||
3. `renderer.cpp:37-59` 在 full createRenderTree 前清 prepared tree,fast gate 检查 style/source 等输入;不能只见缓存引用便断言 UAF。context/destroy 极端交错仍需验证。
|
||||
4. `renderer_impl.cpp:798-918` 保留 opaque reverse/translucent forward 与 global ordinal,compositor premultiplied alpha/depth disabled 有源码依据。但不能推导所有分段像素等价,N01/N02仍为 gate;不凭空宣称所有 alpha 顺序错误。
|
||||
5. Cache OFF、不支持内容、资源准备失败已有普通路径;多 C 只退出 CPU fast,R可reuse。3D/debug/legacy/custom gate保守,不建议绕过求性能。
|
||||
6. QBIN有文件/plane/cell/decoded上限、CRC/bounds/长度检查,Clock有速度/整数溢出检查;FieldSeries按时间/revision处理重复/旧版本并shared持有窗口。未找到可证实的任意写/越界链;N06是数值发布语义。旧canonical adapter已有有限性检查,不能一概否定。
|
||||
7. State A/B、Trail、T0/T1和方向/速度各有路径;`render_particle_validation_layer.cpp:1671-1810` resize重建Trail、按zoom-out gesture决定reseed并保留其他状态,不代表orientation/zoom-out设备通过。nearest/linear采样及双端点validity需数值视觉基准,本轮未断言错误插值。
|
||||
8. 共享海陆mask尚无验收证据:`field_projection_materialization.cpp:77-92` snapshot displayDomain为GLOBAL;`render_particle_validation_layer.cpp:278-293,1314-1324` 有synthetic coastal mask路径。不能据此证明Wave/Tide/Current共用正式Land/Ocean Mask;陆地覆盖显示不等于mask。列为架构/接入证据缺口,不擅改协议。
|
||||
9. 桥接保持map-scoped,typed object经内部JSON,没有引入业务协议;普通invalid plan会关闭旧计划。符号接线不等于ABI/codegen或销毁竞态已测。
|
||||
|
||||
## 8. V12/V13 架构与成本比较
|
||||
|
||||
L=层数、G=LayerGroup数、S=段数、R=retained数、W/H=物理视口、P=格点。仅源码模型,不是测量。
|
||||
|
||||
| 维度 | V12 | V13 | 判断 |
|
||||
| --- | --- | --- | --- |
|
||||
| topology | 固定range,最多两个R | 任意独立R/C,相邻R不合并 | 扩大管理/资源上界,不自动提高FPS |
|
||||
| GPU复用 | 固定retained部分 | 每R独立 | Layer fingerprint局部化,全局依赖仍保守 |
|
||||
| CPU fast | 合格单连续区间 | 恰好一个C且gate满足 | 多C full CPU不等于GPU cache关闭 |
|
||||
| diagnostics | 旧统计结构 | 完整Style和重复段层列表 | C01 O(L+S)复制 |
|
||||
| 范围定位 | 固定数量 | S段前缀可能O(S×G) | 实际占比待测 |
|
||||
| retained资源 | 至多两组 | O(R×W×H) | 物理峰值与OOM未测 |
|
||||
| Metal等待 | 固定targets | 随失效R增加 | 普通State/Trail是继承成本 |
|
||||
| 场/粒子更新 | 已有物化/drawable | 多数继承 | C06/C07不全归因V13 |
|
||||
|
||||
STEP-090 fast ON 21.7%/OFF97.0% RenderThread仅历史特定实验,不外推V13。历史一次退出及随后短时未复现,既不证明泄漏也不证明长稳。必须同设备、Style、源数据、视口、刷新率/热状态配对,先像素正确再比CPU/GPU。
|
||||
|
||||
## 9. 测试证据与 RDC 验证矩阵
|
||||
|
||||
| 项目 | 历史 | 本轮 |
|
||||
| --- | --- | --- |
|
||||
| STEP-094 GL构建、8 suites/43 tests | 报告PASS | HISTORY/NOT RUN;纯状态解析不能验证GPU |
|
||||
| Field/QBIN/viewport69 tests、package TS | 旧审查PASS | HISTORY/NOT RUN |
|
||||
| Metal-only iPhoneOS编译 | STEP-094 PASS | HISTORY/NOT RUN;不验证附件/像素 |
|
||||
| Android APK、RN codegen | STEP-094 PASS,codegen用底层入口 | HISTORY/NOT RUN |
|
||||
| Host工作树替换TS及旧默认失败 | 两种不同依赖环境 | HISTORY/NOT RUN,可同时成立 |
|
||||
| 本轮静态/文档检查 | 不适用 | 已执行;Git结果见阶段收尾 |
|
||||
|
||||
以下所有运行项本轮均未执行。
|
||||
|
||||
| Gate | Android GL | iOS Metal真机/模拟器 | Vulkan/其他 | 证据要求 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| R/C/R-R/R-C-R/C-R-R/R-R-C/R-C-R-C-R | NOT RUN | NOT RUN | NOT RUN | cache OFF/ON截图、像素差、pass/ordinal |
|
||||
| 同source stencil、半透明fill/line/symbol | NOT RUN | NOT RUN | NOT RUN | 首帧/rebuild/reuse、depth/stencil capture |
|
||||
| hidden/reorder/delete/restore/invalid | NOT RUN | NOT RUN | NOT RUN | 下一帧无stale、revision与plan |
|
||||
| OFF/资源失败/draw失败/context loss | NOT RUN | NOT RUN | NOT RUN | 失败不可reuse,资源配对与恢复 |
|
||||
| PLAY/T0T1/坏数值/乱序/旧revision | NOT RUN | NOT RUN | NOT RUN | time/mix、返回码、旧窗口保持策略 |
|
||||
| pan/zoom-out/pitch/resize/orientation | NOT RUN | NOT RUN | NOT RUN | state/trail/reseed及无额外field提交 |
|
||||
| 30/60FPS,单C/多C、mutation | NOT RUN | NOT RUN | NOT RUN | CPU/GPU p50/p95/p99、miss/wait/alloc |
|
||||
| 预算/峰值/30分钟以上长稳 | NOT RUN | NOT RUN | NOT RUN | R×视口×字段阶梯、bytes/后台/卸载 |
|
||||
| attach/detach/双map/销毁中完成 | NOT RUN | NOT RUN | NOT RUN | owner thread、generation、listener |
|
||||
| 干净包版本/ABI/codegen | NOT RUN | NOT RUN | 不适用 | resolved包/散列/生成接口/实际调用 |
|
||||
|
||||
RDC先记录HEAD/manifest/tag/构建配置;以下是既有精确命令,本轮未执行。新增缺陷尚未补测试,旧过滤集PASS不能关闭本报告。
|
||||
|
||||
```sh
|
||||
cmake --build build-step088-opengl --target mbgl-test-runner -j3
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='RenderSegmentPlan.*:RenderSegmentDiagnostics.*:ContinuousLayerRange.*:RetainedDynamicBoundary.*:RetainedLayerGroupRange.*:RetainedUnderlayCache.*:RetainedUnderlayRevision.*:ParticleFastPath.*'
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='ParticleFieldRuntime.*'
|
||||
cmake --build build-ios --target ios-sdk-static -- -quiet
|
||||
```
|
||||
|
||||
Metal命令前确认构建目录MLN_WITH_METAL=ON、MLN_WITH_OPENGL=OFF;缺目录由RDC按规范配置,不复用未知缓存。RN先解决真实resolved `.13`前提,再按STEP-094/083现行入口typecheck/codegen。失败回传首个真实error、target、上下文和日志路径,成功仅exit/耗时/摘要。
|
||||
|
||||
## 10. 优先级与建议修复批次
|
||||
|
||||
只建议,不创建修复任务、不改源码。
|
||||
|
||||
| 批次 | 范围 | Gate/安全约束 |
|
||||
| --- | --- | --- |
|
||||
| A 正确性 | N01/N02/N03 target/Metal/失败发布,N04时钟另小提交 | 最小复现、故障注入、两平台OFF基线、PLAY端到端;不借机全面重构 |
|
||||
| B 无设备可先回归 | N05/N06/N07/O01/E01 | 纯测试/桥接由RDC执行;版本可复现,不代替A的GPU gate |
|
||||
| C CPU安全优化 | C01轻快照、C05ordinal/粒子列表、C06相同时间早退/端点复用 | 同步一致、Style唯一顺序、调用/分配次数证明 |
|
||||
| D GPU资源/同步 | C02/C03/C04/C07 | 先profile,再预算和allocation/content分离;明确buffer依赖,不能直接删wait |
|
||||
| E 较大架构 | multi-C fast、source依赖、异步大场、正式共享mask | 单独设计,未知依赖继续全局失效,不引入provider/HTTP业务调度 |
|
||||
|
||||
## 11. Git 与交付
|
||||
|
||||
仅四个允许路径:本文、`docs/50_Step记录/STEP-095_V13独立严格代码与性能审查_v1.md`、`docs/报告/STEP-095_V13独立严格代码与性能审查_Codex完成报告_v1.md`、根`粒子开发记录.md`末尾追加。创建前同主题输出不存在,使用v1,不覆盖旧报告。
|
||||
|
||||
起始HEAD `05878d662308b9a92c20c433c3cd2943d74ae718`。检查路径/引用/`git diff --check`后仅scoped staged/commit/push origin particle/main。实际SHA/message、push/remote/status及最终收尾提交自引用说明见阶段记录第4节;推送未完成前不写Git PASS。冻结源码至最终文档提交的差异只含既有两份文档及本轮四份文档,不含源码/shader/API/依赖/build/manifest/tag/产物。
|
||||
231
docs/20_调查/STEP-096_V13隔离盲审调查_v1.md
Normal file
231
docs/20_调查/STEP-096_V13隔离盲审调查_v1.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# STEP-096 V13 隔离盲审调查 v1
|
||||
|
||||
本报告仅使用 `/Users/tei/dev/MapLibreParticleV13Blind096` 内的当前规范、任务书与源码。发现由本轮独立阅读、追踪调用链及反例检查形成;未读取外部仓库、其他轮次审查、历史报告、会话日志、网页或既有审查结论。未构建、未执行测试、未运行设备、未修改源码。
|
||||
|
||||
## 1. 输入、结论边界与证据等级
|
||||
|
||||
- 合同:[STEP-096 任务书](../40_任务/STEP-096_V13隔离盲审_Codex任务_v1.md)。已读取根 AGENTS、PARTICLE_BASELINE、项目规范目录、总则 README 指定的当前有效文件。
|
||||
- 唯一目录:`/Users/tei/dev/MapLibreParticleV13Blind096`。目标导出 SHA:`ee0b89e711bf01335b3aa71d36def785cd97fec3`,来源为任务书声明;本轮没有 Git 对象可供独立核验,未运行 Git。branch、当前 HEAD、Git clean/remote 状态均为 NOT AVAILABLE。
|
||||
- [release manifest](../../release/maplibre-particle/release-manifest.json#L4) 声明 particleRevision=13、sourceCommit=`ddf20dec9fe9cd05b77274827b6825829500dd5a`,RN/Android 为 `13.4.1-particle.13`,iOS 为 `6.28.0-particle.13`。manifest 的 sourceCommit 是产物来源声明,不能替代本次快照导出 SHA,也不能据此断言导出错误。
|
||||
- 当前基线总则仍包含 `.3` 发布坐标;本轮以快照源码及 `.13` manifest 描述实现。此为文档口径差异,未据此推断线上产物错误。manifest 的发布成功字样不是本轮公网回读证据。
|
||||
- 当前工作机器观察值为 `Mac-mini-tei.lan`;工作日期为 2026-09-25。实际 TUI 启动命令、screen 归属、模型配置及 reasoning 设置的独立启动记录均 NOT AVAILABLE;不访问目录外补证,不声称核实了 RDC 启动要求。
|
||||
|
||||
“确定缺陷”指限定触发条件下源码控制流或数值关系已闭合,不表示已复现用户事故。“源码确定成本”指可证明的调用、遍历、分配或等待,不表示 profiler 排序。“设计限制”指当前能力或上界不足。所有 GPU 画面、耗时、卡顿、OOM、崩溃、性能收益均 NEEDS_RUNTIME;所有构建和测试均 NOT RUN。未发现可由本轮证据成立的 P0。
|
||||
|
||||
## 2. 独立架构地图
|
||||
|
||||
| 路径 | 实际责任和所有权 | 审查定位 |
|
||||
|---|---|---|
|
||||
| RN RendererRuntime → 平台 bridge → Map/Renderer → RenderOrchestrator → segment plan | Host 提交策略配置;Style 层顺序仍是绘制顺序来源,策略在该顺序上划分区间 | [RN 入口](../../packages/maplibre-particle-rn/src/RendererRuntime.ts#L22)、[分段计划](../../src/mbgl/renderer/render_segment_plan.hpp)、[Renderer](../../src/mbgl/renderer/renderer_impl.cpp#L583) |
|
||||
| Renderer → RetainedUnderlayCache → gfx RenderPass/OffscreenTexture → GL/Metal | Renderer 持有各 retained 段缓存;缓存拥有离屏纹理/合成资源;同帧 PaintParameters 穿过多个目标 | [缓存生命周期](../../src/mbgl/renderer/retained_underlay_cache.cpp#L201)、[渲染调度](../../src/mbgl/renderer/renderer_impl.cpp#L798) |
|
||||
| RN FieldRuntime → Android/iOS bridge → WeatherRuntime/MLNParticleFieldRuntime → RasterArraySource | bridge 定位地图并派发;Source 持有 encoded store、绑定、forecast clock;发布不可变 Impl/投影供 RenderLayer 读取 | [RN API](../../packages/maplibre-particle-rn/src/FieldRuntime.ts)、[Source 提交](../../src/mbgl/style/sources/raster_array_source.cpp#L422) |
|
||||
| encoded ingress → QBIN V2/V3 → profile validator → EncodedFieldStore/FieldSeries → binding/window | magic 分派格式,校验结构和 profile;每个 fieldKey 保存时间序列;时间窗选取后物化数组 | [V3 解码](../../src/mbgl/particle/ingress/qbin_v3_decoder.cpp)、[profile](../../src/mbgl/particle/field/field_profile_validator.cpp)、[series](../../src/mbgl/particle/field/field_runtime.cpp#L144) |
|
||||
| projection → WeatherColorLayer / RasterParticleLayer → GL/Metal shader | 色场与粒子分层;颜色消费标量或向量幅值;粒子持有场纹理、State A/B、Trail A/B 和 drawable/tweaker | [物化](../../src/mbgl/particle/field/field_projection_materialization.cpp#L201)、[粒子更新](../../src/mbgl/renderer/layers/render_particle_validation_layer.cpp#L1489)、[颜色更新](../../src/mbgl/renderer/layers/render_weather_color_layer.cpp#L205) |
|
||||
|
||||
正常路径存在实际保护:Style diff 处理增删/重排;无效分段与不支持场景可 fallback;稳定 retained 帧可复用;解码有结构、尺寸、CRC 等检查;同时间身份冲突和陈旧修订会拒绝;超出时间覆盖范围不会凭空外推。Source 使用共享不可变帧/投影,使读者可持有旧快照;粒子 tweaker 使用 weak_ptr,不能仅因资源与 drawable 互相引用的表象断言泄漏。
|
||||
|
||||
固定边界的实现情况:正式 Host 提交路径未要求 Native 承担 provider/manifest 下载;旧 fixture/local FileSource 路径应单独理解。RN 是控制入口,绘制仍在 Native Core。粒子模块有独立 field/ingress/render 层,但 Renderer/RenderOrchestrator 中也存在较多集成逻辑;没有上游对照输入,无法量化“薄接入”变更比例。Wave/Tide/Current 的共享地理掩码要求见 B09。GL 与 Metal 不能由共享 C++ 自动推出行为一致;Vulkan 完整粒子链未在本轮闭合。
|
||||
|
||||
## 3. 独立发现总表
|
||||
|
||||
编号按本轮源码证实顺序,排除的候选不占编号;优先级是建议处理顺序,不是实测事故等级。
|
||||
|
||||
| 编号 | 优先级 | 类型 | 置信度 | 发现及作用边界 |
|
||||
|---|---|---|---|---|
|
||||
| B01 | P1 | 确定缺陷 | 高 | 切换离屏/主目标未重置 tile stencil 的 CPU 覆盖记录 |
|
||||
| B02 | P1 | 确定缺陷 | 高 | Metal RenderPass 没有落实 gfx 的 clearDepth/clearStencil 请求 |
|
||||
| B03 | P2 | 源码确定成本 | 高 | Metal 每次离屏 present 显式等待 GPU 完成 |
|
||||
| B04 | P2 | 源码确定成本 | 高 | retained 内容失效同时释放同尺寸 GPU 资源 |
|
||||
| B05 | P2 | 源码确定成本 | 高 | 时间更新先全量物化场,再判断投影是否相同 |
|
||||
| B06 | P1 | 确定缺陷 | 高 | encoded PLAY 无生产路径推进 forecast clock |
|
||||
| B07 | P2 | 确定缺陷 | 高 | 8-bit 对称场编码不能表示零,产生有符号偏差 |
|
||||
| B08 | P2 | 确定缺陷 | 高 | Mercator 东向 U 位移缺少纬度修正因子 |
|
||||
| B09 | P2 | 设计限制 | 高 | 当前掩码为合成 fixture,未形成共享地理 Ocean Mask |
|
||||
| B10 | P2 | 源码确定成本 | 高 | 平台 UI 派发链同步解码并物化 encoded 数据 |
|
||||
| B11 | P2 | 设计限制 | 高 | encoded store 仅限制每序列帧数,无全局字节/key 上界 |
|
||||
| B12 | P2 | 确定缺陷 | 高 | retained 段绘制抛异常后仍可能标记缓存构建成功 |
|
||||
| B13 | P1 | 确定缺陷 | 高 | 粒子场更新门槛漏掉合法时间窗内的 V/有效性变化 |
|
||||
|
||||
### B01 — stencil 覆盖记录没有随渲染目标切换
|
||||
|
||||
**触发与调用链。** 多个 retained 段,或 retained 构建后主目标绘制,复用相同瓦片集合且需要 stencil 裁剪。[Renderer buildRetainedRange](../../src/mbgl/renderer/renderer_impl.cpp#L798) 共用 PaintParameters;[beginBuild](../../src/mbgl/renderer/retained_underlay_cache.cpp#L304) 创建清 stencil=0 的离屏 pass,GL 只更新 stencil 可用性;随后[主 pass](../../src/mbgl/renderer/renderer_impl.cpp#L839) 也清 stencil=0。[updateStencilBufferAvailability](../../src/mbgl/renderer/paint_parameters.cpp#L100) 不清覆盖表;[tileIDsCovered](../../src/mbgl/renderer/paint_parameters.cpp#L147) 和[早退](../../src/mbgl/renderer/paint_parameters.cpp#L185) 只看 tile ID,不看 attachment。下一目标仍会用[旧的 Equal stencil ID](../../src/mbgl/renderer/paint_parameters.cpp#L365)。[GL LayerGroup](../../src/mbgl/gl/layer_group_gl.cpp#L67) 正常调用掩码生成,也无法绕过这个早退。
|
||||
|
||||
**所有权、正常/错误/销毁。** 覆盖表属于同帧 PaintParameters,stencil 内容属于每个目标附件。下一帧重建 PaintParameters 不能修复同帧跨目标;[显式 clearStencil](../../src/mbgl/renderer/paint_parameters.cpp#L156) 会重置记录,但上述目标切换没有调用。资源创建失败有 fallback,与本问题不同;正常创建成功即可触发。在 GL 已清为 0 的新目标上,旧正 ID 与实际内容不一致的路径已证实。Metal 的清屏另受 B02 影响。
|
||||
|
||||
**反例与边界。** 同一 attachment 内复用覆盖记录是正确优化;瓦片集合改变可能重绘掩码;无裁剪图层、没有跨目标构建或全部退回主绘制的帧不满足条件。不能据此称所有 retained 帧都缺图,实际画面 NEEDS_RUNTIME。
|
||||
|
||||
**安全修复/最小验证。** 在目标/attachment 世代切换时失效掩码元数据及 ID 分配状态,保留同目标复用;不可在已绘制内容中途随意清 stencil。构造 R/C/R 和 R/R 共用瓦片的 fill/line 层,逐目标检查掩码写入及截图,与关闭 retained 对照;含稳定复用、相机变化、主目标后续 live 层。
|
||||
|
||||
### B02 — Metal 没有传递 depth/stencil 清屏语义
|
||||
|
||||
**触发与证据。** retained beginBuild 明确请求 depth=1、stencil=0;[Metal offscreen bind](../../src/mbgl/mtl/offscreen_texture.cpp#L62) 新建 descriptor 并挂接 color/depth/stencil 纹理。[Metal RenderPass 构造](../../src/mbgl/mtl/render_pass.cpp#L13) 只处理 clearColor,没有读取 clearDepth 或 clearStencil;[GL 对应构造](../../src/mbgl/gl/render_pass.cpp#L14) 把这些请求交给 clear。调用链为 Renderer → cache.beginBuild → gfx descriptor → Metal RenderPass → MTL descriptor,丢失发生在最后的转换。
|
||||
|
||||
**所有权与反例。** 附件属于 offscreen texture,pass/command encoder 管理本次命令。创建和销毁附件均不等于每次按请求清屏。主平台 descriptor 可自行配置清屏,不能证明新建离屏 descriptor 已正确配置;无 depth/stencil 的 pass 不受这两个字段影响。未使用外部 SDK 默认值推断确定黑屏,也未把未显式设置 StoreAction 直接定性为丢帧。模拟器 stencil attachment 分支另需设备验证。
|
||||
|
||||
**影响/修复/验证。** 确定的是 gfx 请求未落实,裁剪/深度表现 NEEDS_RUNTIME。统一转换各附件的 clear/load/store 语义,分别覆盖有无 clear 值、有无附件及模拟器组合格式。用非默认 depth/stencil clear 值做离屏后续比较测试,并核对 GPU capture;不得以 color 正常替代 depth/stencil 验证。
|
||||
|
||||
### B03 — Metal 离屏 present 中存在同步等待
|
||||
|
||||
**触发与链。** [RenderTarget::render](../../src/mbgl/renderer/render_target.cpp#L71) 在每个 target 绘制结束后 present;[Metal command encoder](../../src/mbgl/mtl/command_encoder.cpp#L29) 调 resource.swap;[OffscreenTexture::swap](../../src/mbgl/mtl/offscreen_texture.cpp#L86) commit 后执行 waitUntilCompleted,再重置命令缓冲。粒子稳态在[目标注册](../../src/mbgl/renderer/layers/render_particle_validation_layer.cpp#L2097) 中至少包含 StateWrite 和 TrailWrite,初始化还有额外读目标;retained 构建也有 present。
|
||||
|
||||
**所有权、边界和反例。** 等待保护命令/资源完成,是确定同步点,不是已测出的长时间阻塞。仅实际执行这些目标的帧计入;停绘、未激活层不计,retained 稳定复用不计新的 cache build 等待,GL 不经过此 Metal 函数。完成/销毁安全不能通过简单删 wait 保证。
|
||||
|
||||
**修复/最小验证。** 研究同队列命令依赖与完成回调持有资源,合并或延后提交,保留读写顺序及销毁保护。Metal capture 记录单粒子层每帧 command buffer、wait 次数和 CPU 等待时长,再测多粒子层、首帧、retained 重建及移除层;性能收益 NEEDS_RUNTIME。
|
||||
|
||||
### B04 — 内容失效与资源释放绑定
|
||||
|
||||
**触发与链。** 相机矩阵/内容 revision 改变而 viewport 尺寸不变。[Renderer revision 更新](../../src/mbgl/renderer/renderer_impl.cpp#L583) → [cache evaluate](../../src/mbgl/renderer/retained_underlay_cache.cpp#L201) → releaseResources → [prepareResources](../../src/mbgl/renderer/retained_underlay_cache.cpp#L244) 重新创建离屏资源。[releaseResources](../../src/mbgl/renderer/retained_underlay_cache.cpp#L365) 清掉纹理、合成组及资源键。
|
||||
|
||||
**正常/错误/销毁与反例。** 缓存对象持有资源,失效已实际释放所有权;稳定 valid 分支会 Reuse,不能称每帧都分配。viewport、格式、context 改变或最终销毁确需释放;资源创建异常已有 fallback。本 finding 针对同尺寸、同 context 的内容失效成本,不主张旧图继续有效。
|
||||
|
||||
**修复/验证。** 分开“内容有效性”和“附件可复用性”,保留匹配资源键的附件并重绘。固定 viewport 平移/缩放,对比 offscreen 创建次数、峰值显存、帧时间;再覆盖 resize/context 重建,防止错误跨 context 复用。成本路径已证实,分配耗时与收益 NEEDS_RUNTIME。
|
||||
|
||||
### B05 — 时间更新重复物化整个场
|
||||
|
||||
**触发与链。** Host 高频 setEncodedForecastTime,包括相同非零时间。[setRequestedForecastTime](../../src/mbgl/style/sources/raster_array_source.cpp#L453) → refreshV4RendererState;[clock setter](../../src/mbgl/particle/field/field_runtime.cpp#L196) 不因同值返回 false;[refresh](../../src/mbgl/style/sources/raster_array_source.cpp#L496) 先物化两类投影,再用 sameProjection 比较。[validity](../../src/mbgl/particle/field/field_projection_materialization.cpp#L69) 展开 bitset,[vectorSnapshot](../../src/mbgl/particle/field/field_projection_materialization.cpp#L105) 遍历向量;颜色 UV 与粒子各有自己的 T0/T1 物化。[sameProjection](../../src/mbgl/style/sources/raster_array_source.cpp#L65) 还会比较实际数组。
|
||||
|
||||
**所有权和反例。** 新数组在临时共享快照中,比较相同后可释放;发布新 Impl 时旧读者继续持有旧快照。无投影、无绑定、无法解出窗口会少做或跳过相关分支。相同内容可避免发布或 GPU 重传,故不等于每次都上传纹理;每个动画帧也不一定调用该时间 setter。完整性校验是必要成本,问题是同内容重复重建。
|
||||
|
||||
**成本/修复/验证。** N 个格点、两个时间面、颜色 UV 与粒子同时激活时,四份 vector+valid 基础数组约为 36N 字节的新逻辑存储,另有比较/校验遍历和旧快照重叠。缓存以完整不可变内容身份(含两分量、validity、grid、解释规则)为键,将 mix/time 与数组物化分离。统计同值时间、仅 mix 变化、跨窗、修订替换时的分配与上传次数;不得牺牲 B13 所需的完整身份。
|
||||
|
||||
### B06 — encoded PLAY 没有接入 forecast clock 推进
|
||||
|
||||
**触发与链。** 提交两帧、绑定、设置时间后,经公开 API 切 PLAY 并等待 repaint。[Source 播放设置及 advanceForecastClock](../../src/mbgl/style/sources/raster_array_source.cpp#L459) 中,播放设置只改 clock 状态,推进由独立方法完成。对 src/include/platform/RN 生产路径检索 advanceForecastClock 仅有[声明](../../include/mbgl/style/sources/raster_array_source.hpp#L240)和定义,没有调用方。Android [repaint timer](../../platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/WeatherRuntime.java#L225) 与 iOS [timer](../../platform/ios/src/MLNParticleFieldRuntime.mm#L147) 只触发重绘;粒子[读取 v4 mix/cadence](../../src/mbgl/renderer/layers/render_particle_validation_layer.cpp#L1262) 不推进该 clock。
|
||||
|
||||
**正常/错误/销毁与反例。** clock 由 Source 所有;平台 timer 的生命周期清理不提供时间增量。[advance 实现](../../src/mbgl/particle/field/field_runtime.cpp#L209) 本身存在且处理溢出;[测试](../../test/source/raster_array_v4_host_api.test.cpp#L297) 直接手动调用,所以不能证明公开 PLAY 可自行推进。Host 持续设置 forecast time、C++ 调用方自行 advance、旧 sourceTimeline 路径均是反例;粒子在固定场中移动也不代表预报时刻在播放。
|
||||
|
||||
**修复/验证。** 为 encoded clock 指定唯一推进所有者和单调时钟来源,处理暂停/恢复、rate、地图移除、颜色单层及多层,避免每层重复推进。仅调用一次 setTime 和 PLAY,观察 requestedForecastTime/mix 随 elapsed 改变;暂停保持、恢复不跳跃、不同 rate 成比例,窗口末端行为明确。公开路径缺口可静态确定,运行表现 NEEDS_RUNTIME。
|
||||
|
||||
### B07 — 零值在 8-bit 场纹理中变成负值
|
||||
|
||||
**触发与证据。** 有效向量为零或接近抵消。[粒子编码](../../src/mbgl/renderer/layers/render_particle_validation_layer.cpp#L174) 用 uint8 转换 `(x / scale * 0.5 + 0.5) * 255`,scale 至少为 1;[颜色编码](../../src/mbgl/renderer/layers/render_weather_color_layer.cpp#L45) 同类处理;[GL 解码](../../shaders/particle_gpu_validation.fragment.glsl#L76) 还原为 `(channel * 2 - 1) * scale`,Metal [解码](../../include/mbgl/shaders/mtl/particle_gpu_validation.hpp#L48) 同理。x=0 被截为 127,解码为 `-scale/255`,两个零分量都带偏差,最小 scale=1 时向量长度也高于 1e-5 的近零阈值。
|
||||
|
||||
**边界和反例。** 场快照的 CPU float 正确不代表上传表示正确;无效点被 valid 分支拒绝,不计入这个有效零场问题。不同数值/缩放下误差相对大小不同;方向归一化可放大近零偏差。理论截断误差小于 `2*scale/255`,这是假定合法编码区间的算术界,不是 GPU 实测。重用或销毁纹理不会改变表示误差。
|
||||
|
||||
**修复/验证。** 选取可精确表达零的带符号编码或 float/half 表示,并评估纹理兼容性;仅改四舍五入仍无法让奇数 255 的对称映射精确表示零。以有效零场、微小速度、正负对消、纯东/北场做 GPU 解码读回和粒子位移检查,GL/Metal 同测。实际漂移像素/速度 NEEDS_RUNTIME。
|
||||
|
||||
### B08 — Mercator 东向位移少一个 sec(latitude)
|
||||
|
||||
**触发与链。** formal profile 使用 eastward/northward、m/s 的 U/V 语义,见[profile 校验](../../src/mbgl/particle/field/field_profile_validator.cpp#L25)。上传缩放在 shader 中还原后,[GL formal 位移](../../shaders/particle_gpu_validation.fragment.glsl#L169) 取 `dx=U*dt/C`、`dy=-V*dt/(C*cos(latitude))`;[Metal](../../include/mbgl/shaders/mtl/particle_gpu_validation.hpp#L133) 同样处理。这里 C 为地球周长,输入链未见对 U 预乘 sec(latitude)。
|
||||
|
||||
**推导、反例和边界。** Mercator x=λ/(2π)+0.5,而局部东向速度满足 dλ/dt=U/(R*cosφ),故 dx 应为 U*dt/(C*cosφ)。赤道、U=0 时不受此项影响;60° 纬度当前东向投影位移为应有值的一半。统一 visual time scale 不修复东西/南北相对畸变。此为固定输入语义下的数学缺陷,实际视觉速度、极区处理 NEEDS_RUNTIME;与资源销毁和错误 fallback 无关。
|
||||
|
||||
**修复/验证。** 在一致纬度与极区夹紧策略下对两个分量使用正确度量转换,保留既定视觉时间倍率。0°/45°/60° 的等物理速度东西向和南北向场,比较 CPU 解析预测与单步 GPU 位移;含符号、反向、极区边界与跨日期变更线。
|
||||
|
||||
### B09 — Ocean Mask 尚未形成正式共享地理输入
|
||||
|
||||
**触发与证据。** Wave/Tide/Current 需要 OCEAN_ONLY,或启用当前 mask 模式。[makeCoastalMaskFixture](../../src/mbgl/renderer/layers/render_particle_validation_layer.cpp#L281) 用格点坐标表达式生成合成海岸,formal 资源初始化[仍安装该 fixture](../../src/mbgl/renderer/layers/render_particle_validation_layer.cpp#L1314)。物化[统一写 GLOBAL](../../src/mbgl/particle/field/field_projection_materialization.cpp#L77)。[顶点 shader](../../shaders/particle_gpu_validation.vertex.glsl#L56) 的 v_world 源自 quad 坐标,粒子合成[据此采样 mask](../../shaders/particle_gpu_validation.fragment.glsl#L262);[颜色分支](../../shaders/particle_gpu_validation.fragment.glsl#L113) 在该处理前返回。
|
||||
|
||||
**所有权、正常路径与反例。** mask 纹理由粒子资源拥有,随资源重建/销毁;缺少正式地理 mask 数据入口和两类层共享采样语义。默认关闭 mask 的 GLOBAL wind 不因此错误,Host 数据 nodata 也可能已有陆地缺测,但这不等价于独立共享 Land/Ocean Mask。不能把本限制扩展为所有风场不可用。
|
||||
|
||||
**修复/验证。** 由 Host 决定业务域策略,Core 接收明确地理坐标、网格和版本的共享掩码,两层统一采样;不把 provider/下载引入 Native。加载真实小型固定海岸 mask,平移、缩放、旋转后地理边界应固定;分别验证 Wind GLOBAL 与 Wave/Current OCEAN_ONLY,以及 nodata 叠加、删除与替换。当前设计缺口已证实,设备画面 NEEDS_RUNTIME。
|
||||
|
||||
### B10 — encoded 提交在 UI 派发链同步完成重工作
|
||||
|
||||
**触发与链。** RN 提交较大 QBIN。[Android bridge](../../packages/maplibre-particle-rn/android/src/main/java/org/navsea/maplibre/particle/weather/WeatherRuntimeBridgeModule.kt#L310) 经 runOnUiQueueThread 定位并调用 runtime;[Java submit](../../platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/WeatherRuntime.java#L97) → [JNI](../../platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp#L1501) 拷贝 bytes 并同步调用 Source。iOS [withMapForTag](../../packages/maplibre-particle-rn/ios/WeatherRuntimeBridge.mm#L105) 使用 UI block → [runtime submit](../../platform/ios/src/MLNParticleFieldRuntime.mm#L222) 拷贝并解码。V3 [CRC](../../src/mbgl/particle/ingress/qbin_v3_decoder.cpp#L66) 对字节做位循环,后续解码/校验/投影也在同步调用链内。
|
||||
|
||||
**边界、错误与反例。** Android 本地文件读取在 UI 派发之前,不能笼统写成全部文件 IO 都在 UI;小文件和 Duplicate 后的工作量不同,格式失败会提前返回。解码有大小上限,但允许的输入仍可较大。临时 bytes、decoded frame、Source 持久状态的所有权清楚;问题是所在线程及连续占用,并非已证明泄漏。未观测 ANR 或卡顿。
|
||||
|
||||
**修复/验证。** worker 只做不可变 bytes 解码/校验,带 map/style/source 世代令牌返回所有者线程提交;限制队列并处理取消、地图销毁和过时结果,不能把整个可变 Source 移到 worker。在中小/近上限输入、CRC 失败、连发及页面退出下记录 UI 区间耗时、峰值内存和错误回调。性能影响 NEEDS_RUNTIME。
|
||||
|
||||
### B11 — 每序列八帧不是全局内存预算
|
||||
|
||||
**触发与链。** 单 Source 连续提交不同有效 fieldKey。[EncodedFieldStore 成员](../../include/mbgl/particle/field/field_runtime.hpp#L151) 是 map 和 perSeriesCapacity=8;[submit](../../src/mbgl/particle/field/field_runtime.cpp#L223) 可为新 key 创建 series;[FieldSeries](../../src/mbgl/particle/field/field_runtime.cpp#L168) 只检查本序列容量。Source [提交](../../src/mbgl/style/sources/raster_array_source.cpp#L422) 不要求该 key 已绑定。V3 [输入上限](../../src/mbgl/particle/ingress/qbin_v3_decoder.cpp#L19) 限单帧 encoded/decoded bytes,不限 store 总量。
|
||||
|
||||
**所有权和反例。** store 持有共享帧,直到显式 clear/clearAll 或 Source 销毁;渲染读者可能延长旧快照寿命。单帧 decoded 上限 32 MiB 与八帧组合形成每序列最高 256 MiB 的逻辑上界,但不表示每个 profile 都能达到该值。key 数无总上界,不能据此保证 Source 总内存。Host 固定少量 key 并主动清理时可控;本轮没有 OOM 证据。
|
||||
|
||||
**修复/验证。** 在提交前计入全局 key/字节预算,包含共享去重与临时峰值;明确容量拒绝语义,保护当前绑定窗口,避免静默淘汰。循环提交多个合法 key,验证达到预算后稳定拒绝且现有窗口可用;clear 后可恢复,地图销毁后所有者释放,读者完成后最终释放。设计上界不足已证实,实际内存峰值 NEEDS_RUNTIME。
|
||||
|
||||
### B12 — 构建过程错误没有阻止 retained 缓存成功提交
|
||||
|
||||
**触发与链。** retained 范围内某 LayerGroup 在 render 时抛出被捕获的异常,observer 记录错误后正常返回。[RenderOrchestrator 遍历](../../src/mbgl/renderer/render_orchestrator.hpp#L156) 捕获异常并 onRenderError,不把失败状态返回调用方。[buildRetainedRange](../../src/mbgl/renderer/renderer_impl.cpp#L798) 在 opaque/translucent 遍历后继续 finishBuild、markBuildSucceeded;[成功标记](../../src/mbgl/renderer/retained_underlay_cache.cpp#L328) 使其可在后续稳定帧 Reuse。
|
||||
|
||||
**所有权、错误/销毁与反例。** 缓存持有可能只完成部分绘制的纹理,直到下次失效或销毁。资源准备阶段[有 catch/fallback](../../src/mbgl/renderer/retained_underlay_cache.cpp#L240),不属于本路径;无异常、observer 再抛出异常、随后 revision 立刻改变都不满足持续复用条件。此处只讨论同步可捕获 render 异常,不将异步 GPU 错误视作同一证据。
|
||||
|
||||
**修复/验证。** 让范围绘制返回成败或设置本次构建错误标记,失败不提交 valid 缓存,安全 fallback/下帧重试;保留 observer 通知,不引入每帧 GPU 等待作为错误检测。用一次性抛异常的假 LayerGroup 和正常返回的 observer,检查 BuildFailed、禁止 Reuse、下一帧成功恢复,再由 GL/Metal 场景验证画面。
|
||||
|
||||
### B13 — 粒子更新身份没有覆盖第二分量和有效性
|
||||
|
||||
**触发与可构造输入。** 同 fieldKey 的三个不同 validTime 帧使用相同 fieldID/generation/dataVersion,U 内容相同、V 依次变化(或 validity 变化),网格和 schema 兼容。此输入在[FieldSeries 新时间插入](../../src/mbgl/particle/field/field_runtime.cpp#L146) 合法:newerRevision 检查针对同一 timestamp,未要求不同时间递增 generation。窗口从 T0/T1 切到 T1/T2。
|
||||
|
||||
**完整调用链。** [materializeParticleProjection](../../src/mbgl/particle/field/field_projection_materialization.cpp#L201) 仅以 t0/t1FirstPlane 身份传给 baseSnapshot;[baseSnapshot](../../src/mbgl/particle/field/field_projection_materialization.cpp#L77) 用 fieldID+该身份组成 fieldID,保留 producer generation/dataVersion。[Source sameProjection](../../src/mbgl/style/sources/raster_array_source.cpp#L65) 比较实际数组,可正确发布新的 V/validity。但[粒子 sourceChanged](../../src/mbgl/renderer/layers/render_particle_validation_layer.cpp#L1489) 只调用[fieldIdentityChanged](../../include/mbgl/particle/field/field_data_adapter.hpp#L111),其只比 generation/fieldID/dataVersion。两个槽身份都未变时,不进入内部 sameFieldGpuContent/纹理更新分支,继续使用旧场纹理。
|
||||
|
||||
**所有权、反例和边界。** Source 新投影存在、Renderer 仍持有旧 resources.field/nextField 及纹理,直到其他身份变化、重建或销毁。每时次主动递增 generation、U 也变化、初次资源创建都能触发更新;纯 mix 变化且窗口内容确实不变应保留纹理。此 finding 不指向颜色层:颜色投影的实际内容比较是反例,已排除“颜色仅看 U 而漏掉 V”的候选。
|
||||
|
||||
**修复/最小验证。** 粒子更新门槛使用包含 U/V、validity、grid 与解释语义的完整不可变内容身份;窗口时间状态单独更新,保持相同内容可复用。采用上述三帧并让 V/validity 可见地不同,seek 跨窗,检查 CPU 选窗、GPU 上传/复用判定及读回;增加不同时间同 generation、仅 V 改变、仅有效性改变、完全相同内容四组。错误门槛可静态证明,旧场画面 NEEDS_RUNTIME。
|
||||
|
||||
## 4. 反例检查及未成案候选
|
||||
|
||||
- **颜色投影身份:排除。** Source sameProjection → sameSnapshot → sameFieldGpuContent 会比较两分量/validity 实际数组;不能仅因第一 plane 参与 fieldID 就断言颜色不更新。B13 另有粒子特定的前置门槛证据。
|
||||
- **粒子资源强引用环:排除。** [ValidationDrawableTweaker](../../src/mbgl/renderer/layers/render_particle_validation_layer.cpp#L770) 持 weak_ptr;[layerRemoved](../../src/mbgl/renderer/layers/render_particle_validation_layer.cpp#L1140) 移除目标。本轮未形成泄漏闭环。
|
||||
- **关闭缓存后辅助段状态残留:排除简单结论。** [synchronizeRetainedUnderlayState](../../src/mbgl/renderer/renderer_impl.cpp#L142) 覆盖段缓存同步;不能仅查主缓存 setter。
|
||||
- **纯重排未被处理:排除。** [Style diff](../../src/mbgl/renderer/style_diff.cpp#L19) 与[orchestrator 更新](../../src/mbgl/renderer/render_orchestrator.cpp#L318) 处理移除/添加并重建有序层;分段不是独立 Z-order 数据源。
|
||||
- **任意交错不受支持:排除笼统结论。** 单 continuous 段的 fast path 限制不等于普通 segmented path 不支持多段;[普通绘制](../../src/mbgl/renderer/renderer_impl.cpp#L938) 仍按计划遍历。透明混合、深度与遮挡整体等价性尚需运行验证。
|
||||
- **异常输入必然越界:未成案。** QBIN 有 MaxCells、字节预算、偏移/结构/CRC 检查,FieldSeries 有冲突/陈旧/兼容性拒绝。未完成形式化证明或 fuzz,不能声称完全安全,也未凭大输入符号制造越界 finding。
|
||||
|
||||
## 5. 性能与资源模型(静态模型,非 benchmark)
|
||||
|
||||
设视口为 W×H,retained 段数 K,格点数 N,活跃粒子层数 L,每层有效粒子数 P,encoded 字节数 E,fieldKey 数 F。
|
||||
|
||||
| 路径 | 静态成本模型 | 稳定路径/限制 |
|
||||
|---|---|---|
|
||||
| retained | 每段至少一张 RGBA8 颜色纹理约 4WH 字节,另加实际 depth/stencil 附件、对齐及 drawable;总量随 K 线性增长 | 稳定内容复用;Build 时绘制该段并合成;B04 增加同尺寸分配抖动 |
|
||||
| 粒子 | State A/B 随状态纹理面积增长,Trail A/B 随视口面积增长;积分和粒子绘制约随 P 增长,全屏衰减/合成随 WH 增长 | 初始化/重播种还有额外工作;不可只凭 particleCount 推断全部成本 |
|
||||
| Metal 提交 | 实际执行的粒子层稳态至少两次离屏同步点/层,加当帧 retained Build 数量及其他 target | 这是显式 wait 次数下界模型,不是每次都发生长等待;需 capture 核对实际启用分支 |
|
||||
| 时间 setter | 激活投影物化和数组相等比较为 O(N),颜色 UV+粒子约 36N 字节基础新数组 | 单纯 draw 并不必然调用 setter;不同 scalar/vector 绑定成本不同 |
|
||||
| QBIN | CRC 和解码大体随 E/样本数增长;提交链还会临时复制并可能物化 | 上限约束单帧;失败可以提前结束,不可套用最坏值到所有输入 |
|
||||
| encoded store | 每 key 最多八帧,但总量随 F 增长;另有 materialized snapshot、上传图像、在途旧读者 | 需要显式 clear;现有容量拒绝保护单序列,未构成全局预算 |
|
||||
|
||||
未取得 profiler、锁竞争、allocator、GPU capture 或设备温度数据。不提供热点排名、FPS、毫秒、节省比例或确定 OOM 阈值。查到同步等待和 UI 同步调用,不等于证明死锁;本轮未形成有闭环证据的锁死 finding。
|
||||
|
||||
## 6. 测试源码覆盖与缺口
|
||||
|
||||
阅读测试源码只表示理解其断言,以下全部 NOT RUN。
|
||||
|
||||
| 已阅读的代表性测试 | 能说明的设计意图 | 不能替代的验证 |
|
||||
|---|---|---|
|
||||
| [retained 状态测试](../../test/renderer/retained_underlay_cache.test.cpp#L116)、[失败测试](../../test/renderer/retained_underlay_cache.test.cpp#L479)、[segment 测试](../../test/renderer/retained_underlay_cache.test.cpp#L657) | Build/Reuse/Fallback、范围与交错计划 | 真实 attachment stencil、Metal 清屏、异常经 orchestrator 传递、透明/深度像素等价 |
|
||||
| [clock 测试](../../test/particle/field_runtime.test.cpp#L272)、[Host API clock](../../test/source/raster_array_v4_host_api.test.cpp#L297) | 手动 advance 的时间算法 | 平台 PLAY 是否接入生产推进者 |
|
||||
| [materialization 测试](../../test/source/raster_array_v4_host_api.test.cpp#L392)、[颜色幅值](../../test/source/raster_array_v4_host_api.test.cpp#L433) | 快照不变性、mix、重建计算 | GPU 8-bit 数值误差、B13 跨窗同 producer 身份、数组分配次数 |
|
||||
| [binding 测试](../../test/particle/field_binding.test.cpp#L84)、[V3 解码测试](../../test/particle/qbin_v3_decoder.test.cpp#L346) | 内容身份/选面、格式失败和边界 | 全 renderer 资源切换、UI 延迟、全局 key 预算及竞态销毁 |
|
||||
|
||||
未覆盖或未闭合:Vulkan/WebGPU 完整粒子实现、所有 Style 图层组合、真实设备 Metal simulator/device 差异、GPU 厂商驱动行为、平台业务调用频率、RN 安装/codegen/ABI、所有 error string 和 promise 回调分支、跨线程竞争注入、完整 fuzz、产物字节与 manifest 的对应关系。当前审查是有边界的静态追踪,不是逐行穷尽全仓或安全证明。
|
||||
|
||||
## 7. 后续可执行验证矩阵(交给 RDC,全部 NOT RUN)
|
||||
|
||||
本轮禁止执行以下构建/测试。RDC 在独立获准的验证阶段准备测试 runner 与设备;新增回归用例/修复属于后续代码任务,不能把下表当作本轮已提供可运行补丁。
|
||||
|
||||
| 项目 | 准备与执行动作 | 最小通过条件 |
|
||||
|---|---|---|
|
||||
| B01 | GL、Metal 各加载同瓦片 fill/line,策略 R/C/R、R/R、C/R/C;首帧/重建/复用逐帧对照关闭缓存 | attachment 切换均生成对应掩码,像素裁剪等价 |
|
||||
| B02 | 离屏附件预置非默认值,创建带 depth/stencil clear 的 pass,再以比较绘制检查;device/simulator 各一次 | clear 值与请求一致,无依赖未定义旧内容 |
|
||||
| B03/B04 | 1/2 粒子层、1/3 retained 段;固定视口平移 300 帧,记录 capture/分配/CPU 等待 | 给出次数、时长分布;资源复用变化有证据且无画面回归 |
|
||||
| B05 | 两时间面,固定时间重复 100 次、mix 小步变化 100 次,再跨窗和修订 | 输出物化/分配/上传次数;相同内容复用且变化可靠可见 |
|
||||
| B06 | 仅颜色、仅粒子、两者共存;setTime 一次后 PLAY、PAUSE、不同 rate | forecast time/mix 正确推进,每地图只推进一次 |
|
||||
| B07 | 有效零场、微值、正负对消、东西南北常量场,GPU 单步读回 | 零保持零,近零阈值一致,误差有明确预算 |
|
||||
| B08 | 0°/45°/60°,相同 m/s、相同 dt,比较解析 Mercator 位移 | U/V 投影度量一致,符号正确,极区有限值 |
|
||||
| B09 | 同一固定地理 mask 同时供颜色/粒子,平移缩放旋转,切 GLOBAL/OCEAN_ONLY | 两层海陆边界一致且固定在地理位置 |
|
||||
| B10 | 合法中小/近上限 QBIN、坏 CRC、连发、加载中移除地图 | UI 耗时可量化;回调一次;无过时提交/释放后访问 |
|
||||
| B11 | 多 key 连续提交直至预算,clear 后再提交 | 可预测拒绝、峰值有界、当前窗口不破坏、最终释放 |
|
||||
| B12 | 假 LayerGroup 首次抛异常,observer 记录后返回,下一帧正常 | 首次不得缓存成功;下一帧重试并恢复 |
|
||||
| B13 | 同 generation/dataVersion/U 的三时次,分别仅改 V、validity,再 seek 跨窗 | 新窗口 GPU 内容正确;完全相同内容仍可复用 |
|
||||
| 兼容 | 增删/重排/隐藏边界层,透明层交错、3D/custom fallback、resize/context 重建 | Style 顺序一致,fallback 可恢复,无残留目标 |
|
||||
|
||||
已有 C++ 定向测试的执行入口需使用平台生成的实际 runner;[test/CMakeLists](../../test/CMakeLists.txt#L1) 的 mbgl-test 是库,[macOS 配置](../../platform/macos/macos.cmake#L84) 定义 mbgl-test-runner。RDC 已构建并确认路径后可执行下列命令(本轮未执行;路径必须由 RDC 填写):
|
||||
|
||||
```sh
|
||||
RDC_TEST_RUNNER=/absolute/path/to/mbgl-test-runner
|
||||
"$RDC_TEST_RUNNER" --gtest_filter='Retained*:ContinuousLayerRange.*:RenderSegment*:ParticleFastPath.*:ParticleField*:ParticleQbin*:ParticleEncoded*:Source.RasterArrayV4*'
|
||||
```
|
||||
|
||||
这只运行现有匹配测试,不覆盖全部上述新增场景。RDC 应先确认 runner 的过滤输出确实命中测试,保存命令、exit code、测试数量和日志路径;设备场景记录设备/后端/版本、输入 fixture、截图或 capture。实际构建目录、签名、设备 ID、安装产物及历史日志均 NOT AVAILABLE,本轮不臆造构建命令或成功状态。
|
||||
|
||||
## 8. 分批建议与停止边界
|
||||
|
||||
1. **先补正确性闭环:** B01/B02 的目标附件状态、B06 的唯一时钟推进者、B13 的完整内容身份;加入最小回归后再做跨后端运行验证。B12 同批修复失败提交语义,避免错误缓存长期复用。
|
||||
2. **再统一数值与域语义:** B07/B08 的数值表示/投影度量,以及 B09 的共享地理掩码;用解析输入和小型固定 fixture 定量验收。
|
||||
3. **最后按测量做性能与预算:** B03/B04/B05/B10/B11,先记录同步、分配、线程耗时和峰值,再分别改命令提交、资源复用、物化缓存、工作线程和容量策略;避免多项同时修改而无法归因。
|
||||
|
||||
本轮仅交付三份中文盲审文档,不修改实现或测试。完成后停止,由 RDC 冻结三份原稿 SHA-256 和时间,再独立移交、追加开发记录并做正式仓库 scoped 提交及远端核验。本会话不参与解除盲态比较,不根据外部材料回写发现。
|
||||
|
||||
配套:[Step 记录](../50_Step记录/STEP-096_V13隔离盲审_v1.md) · [完成报告](../报告/STEP-096_V13隔离盲审_Codex完成报告_v1.md)
|
||||
95
docs/30_设计/NavSea_PBF自动识别与轻量混淆跨平台方案设计_v1.md
Normal file
95
docs/30_设计/NavSea_PBF自动识别与轻量混淆跨平台方案设计_v1.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# NavSea PBF 自动识别与轻量混淆跨平台方案设计 v1
|
||||
|
||||
日期:2026-09-27
|
||||
状态:**规划 / DESIGN ONLY;未实现、未测试、未发布。**
|
||||
适用范围:NavSea 自有矢量海图 PBF,MapLibre Native Android / iOS 与 MapLibre GL JS Web。
|
||||
归属:MapLibre 资源加载适配层;**不是粒子、Weather Field 或 QBIN 格式变更**。
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
NavSea 计划对自己分发的 PBF 进行低成本字节混淆,避免直接下载后被普通 PBF/MBTiles 工具读取;客户端收到瓦片后恢复标准 PBF,继续使用原有 MapLibre 解析和渲染链。Web 版本也要消费同一份服务器资源。
|
||||
本方案明确只是增加直接复用数据的门槛,不承诺数字版权保护、防逆向或防止明文提取。客户端可被调试,解码后的 PBF 可在内存或缓存环节被截获;公开 Web 尤其如此。
|
||||
|
||||
**核心规则:检测到 NavSea 专用 Magic Header → 按版本解码;未检测到 → 维持 MapLibre 原有标准资源流程。** 不依据文件后缀决定是否解码,不修改标准 PBF 解析器,不建立第二套地图引擎。
|
||||
|
||||
## 2. 范围与不变项
|
||||
|
||||
- 只处理自有矢量瓦片资源;普通 PBF、第三方瓦片、Style JSON、Sprite、Glyph、图片以及 Weather/QBIN 不受影响。
|
||||
- 原有 URL 路径、Source/Layer、Style Z-order、粒子绘制、GPU、地图交互与正常 HTTP 重试行为不变。
|
||||
- 服务器可同时提供普通 PBF 与封装 PBF;三端遵守同一二进制契约,各自实现资源加载适配。
|
||||
- 只在有效响应体上进行格式识别;404、500、304、取消、空响应等继续走既有错误/缓存语义,不把错误页当 PBF 解码。
|
||||
- 本文是未来工作的设计草案;不指定当前源码已有某个具体 Hook,进入实施前须按当前 fork 做只读调查。
|
||||
|
||||
## 3. 封装结构草案
|
||||
|
||||
建议文件名仍为 `.pbf`,但编码后内容已不是标准 PBF,必须先解封装。以下 **16 字节头仅为待验证候选格式**,实施前冻结字节序、长度和兼容矩阵。
|
||||
|
||||
| 偏移 | 长度 | 字段 | 候选语义 |
|
||||
| --- | ---: | --- | --- |
|
||||
| 0 | 8 | Magic | ASCII `NSVTILE\0`,固定 8 字节 |
|
||||
| 8 | 1 | Version | `1` |
|
||||
| 9 | 1 | Codec | `1 = XOR-v1`;其他值拒绝 |
|
||||
| 10 | 1 | Flags | bit0 表示内部 GZIP,其余保留位须校验 |
|
||||
| 11 | 1 | HeaderLength | v1 固定 16,给后续扩展留入口 |
|
||||
| 12 | 4 | CRC32 | 解封装且内部解压后,标准 PBF 明文的 CRC32;约定字节序 |
|
||||
| 16… | 可变 | Payload | 经 XOR 混淆的原始 PBF 或内部 GZIP 字节 |
|
||||
|
||||
CRC32 只检测意外损坏或密钥不匹配的常见现象,**不是抗恶意篡改认证**。Magic 是类型标识,不是秘密,也不是加密强度来源。头部存在但格式、版本、长度、Flags、Codec 或校验不合法时,必须拒绝,不能退回普通 PBF。
|
||||
|
||||
## 4. 编码、识别、解码
|
||||
|
||||
服务器预处理一次:标准 PBF → 计算明文 CRC32 → 可选内部 GZIP → XOR → 添加 Header → 存储/CDN。对于已经是 GZIP 存储的原始瓦片,应先核对实际字节语义,再决定保留内部压缩还是重新封装,避免重复压缩。
|
||||
客户端:收到已按 HTTP 传输层规则处理的响应体 → 检查 Magic → 读取并验证头部 → XOR 还原 Payload → 按 Flags 内部解压 → 校验长度/CRC32 → 把标准 PBF 交给现有解析器。普通资源无 Magic 时完全返回既有处理链,不新增一次盲目 GZIP 解压。
|
||||
|
||||
**HTTP `Content-Encoding` 与封装内部 GZIP 必须分开。** XOR 后的压缩 Payload 不得冒充 HTTP gzip,否则网络库可能在 XOR 前尝试解压。封装可用 `Content-Type: application/octet-stream`,自定义 `X-NavSea-Tile-Encoding` 仅作辅助诊断;格式识别以响应体 Header 为准。HTTP 层若另行压缩整个封装,须确认客户端收到的是解开传输编码后的封装字节。
|
||||
|
||||
已声明为“必须受保护”的自有 Source 可另设严格策略:缺少 NavSea Header 也报错(fail-closed),防止服务端误发明文;普通/混合 Source 则执行“无头=原路径”。Source 保护策略与字节自动识别是不同层面,不得混为一谈。
|
||||
|
||||
## 5. 三个平台的接入边界
|
||||
|
||||
| 平台 | 未来接入方向 | 重要约束 |
|
||||
| --- | --- | --- |
|
||||
| Android | 在当前 fork 的 MapLibre HTTP 资源响应交给解析器前解封装 | 核对 OkHttp/Native 缓存与 Response 回调实际位置,避免双重解压或额外整份复制 |
|
||||
| iOS | 在当前 MapLibre Native 的对应资源响应路径解封装 | 以实际 iOS 网络资源实现为准,不假设 Android Java 修改会覆盖 iOS |
|
||||
| Web | MapLibre GL JS `addProtocol()` 负责 fetch、校验、解封装并返回标准 PBF `ArrayBuffer` | 自有瓦片 URL 经自定义 scheme 或 `transformRequest` 导向注册的协议;后者本身不能修改响应体 |
|
||||
|
||||
Web 必须核对 GL JS 版本及协议回调契约、Style/TileJSON 的 URL 重写、Worker/主线程负载、CORS 和取消信号;服务端需暴露必要响应头。第三方请求不接入解码器。三端共享格式测试向量,不共享平台相关 HTTP 实现。
|
||||
|
||||
## 6. 缓存、错误与日志
|
||||
|
||||
- CDN/HTTP 缓存以**封装字节**为对象;ETag、Content-Length、缓存键、版本及更新失效策略要与实际封装内容一致。
|
||||
- HTTP 304 没有新响应体;由现有缓存/重验证机制恢复对应实体,不能把空体交给解码器。按当前 Native/Web 路径调查 MapLibre 自有缓存究竟保存封装还是明文,不作默认安全保证。
|
||||
- 若业务要求离线缓存也不可直接导出标准 PBF,需单独设计落盘边界;仅 HTTP 层 XOR 不保证 MapLibre 离线库/内存缓存保存密文。
|
||||
- 解码前限制输入大小,解压后限制最大明文大小与压缩比,避免异常瓦片造成内存放大。数据损坏、版本不支持、密钥不匹配归类为独立 Codec 错误。
|
||||
- 将前述 HTTP URL 诊断与 Codec 诊断分开:`INVALID_URL`、`HTTP_ERROR`、`NETWORK_ERROR`、`UNSUPPORTED_TILE_CODEC`、`TILE_DECODE_FAILED`;只记录必要的 URL(敏感查询参数脱敏)、状态码、版本与长度,不输出密钥或明文 PBF;正常 200 不逐张刷日志,可限频。
|
||||
- 404/500、取消、超时、304、第三方瓦片和正常 200 行为均不因本功能改变。
|
||||
|
||||
## 7. 性能与安全预期
|
||||
|
||||
XOR 是 O(n) 的逐字节操作,优先服务器离线预编码、客户端原地处理,避免 React/JS Bridge 往返和多次完整 Buffer 拷贝。封装额外增加 Header 字节;“XOR 不改变 Payload 长度”不等于整体零额外字节。压缩应在 XOR 前进行,否则压缩收益可能明显恶化。
|
||||
Web 先评估主线程处理时长与地图交互,再决定是否使用 Worker;Android/iOS 关注实际缓存策略、内存峰值与线程阻塞。性能影响只能经真实 PBF 样本、冷/热缓存及拖动缩放实测后定论。
|
||||
|
||||
固定或循环 XOR 密钥可以被分析,已知格式和客户端代码会进一步降低逆向门槛;把算法放到 C++/WASM 或换成 AES 也无法阻止客户端截获已解码的明文。本方案定位为**轻量混淆**。若未来目标提高到防批量下载,应另行规划 HTTPS、授权、短期访问凭据、配额/限速、异常下载监测与必要的数据分发范围;不要把 XOR 描述成加密认证。
|
||||
|
||||
## 8. 未来实施阶段(本次均不执行)
|
||||
|
||||
| 阶段 | 工作 | 产出/闸门 |
|
||||
| --- | --- | --- |
|
||||
| A 只读调查 | 当前 fork Android/iOS HTTP、资源与磁盘缓存;GL JS 版本;服务器 PBF/GZIP、CDN、TileJSON/Style 路径 | 确认真实接入点、压缩语义和数据所有权 |
|
||||
| B 协议冻结 | 冻结 Header 字节序/CRC 覆盖、Codec、Flags、版本策略、普通/受保护 Source、Golden vectors | 三端可交叉解码同一 fixture |
|
||||
| C 服务端 | 离线编码工具、标准/封装样本、响应头及缓存策略 | 普通与封装资源同时可分发 |
|
||||
| D Android/iOS | 各自最小资源适配,保持解析器与业务接口不变 | 各平台原生样本验证;构建/实机由用户手工执行 |
|
||||
| E Web | 注册自定义协议、URL 路由、解码与取消/CORS | 自有/第三方混合地图正常 |
|
||||
| F 综合验收 | 性能、离线缓存、故障、日志、安全边界与回退 | 三端统一验收通过后再决定发布 |
|
||||
|
||||
验收矩阵至少覆盖:原始 PBF、标准 HTTP gzip、封装 RAW、封装内部 gzip、无头普通资源、受保护 Source 缺头、短头/错误 Magic/版本/Flags、错误 CRC/密钥、空瓦片、404/500/304、网络失败/取消、第三方瓦片、冷/热/离线缓存、跨平台 Golden Fixture、Web CORS 与长时间拖动缩放;不得以单张瓦片可显示作为最终通过。
|
||||
|
||||
## 9. 待实施前决策
|
||||
|
||||
1. 是否采用 XOR-v1 作为正式首版,还是只做试验;客户端密钥静态分发的风险是否接受。
|
||||
2. Magic/Header 的精确二进制布局、CRC 字节序、最大明文长度、Codec ID 和压缩 Flags 是否按本草案冻结。
|
||||
3. 哪些 NavSea Source 必须受保护;普通/混合资源的无头旁路范围。
|
||||
4. 离线缓存是否必须保存封装字节,以及 MapLibre 内建缓存如何满足该要求。
|
||||
5. Web URL 路由和跨域部署方案;第三方瓦片是否保持完全独立。
|
||||
|
||||
**本次结论:仅保留规划文档。没有新增 Codec、改动服务器资源、修改 MapLibre HTTP、引入密钥、触发构建/测试或宣布任何平台支持完成。**
|
||||
141
docs/30_设计/STEP-094_通用RenderSegment模型与Style分类Diagnostics设计_v1.md
Normal file
141
docs/30_设计/STEP-094_通用RenderSegment模型与Style分类Diagnostics设计_v1.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# STEP-094 通用 Render Segment 模型与 Style 分类 Diagnostics 设计 v1
|
||||
|
||||
## 1. 目标与边界
|
||||
|
||||
Renderer 只接收 Style 顺序和一组连续 Segment 定义。每个 Segment 的执行策略只有 `RETAINED` 或 `CONTINUOUS`;不引入 LOW/MEDIUM/HIGH,不识别 AIS、Route、Weather 等业务语义,不改变 Style Z-order,也不增加 Anchor/Marker Layer。
|
||||
|
||||
V12 的 Bottom Retained / Continuous / Top Retained 是本模型的兼容特例。新实现只有一套 resolved topology;旧 `setContinuousLayerRange()` 先转换为通用 Segment Plan,再进入同一 Renderer 路径。
|
||||
|
||||
## 2. 公共数据模型
|
||||
|
||||
Core 公共类型位于 `include/mbgl/renderer/render_segment.hpp`:
|
||||
|
||||
```text
|
||||
RenderSegmentPlan
|
||||
schemaVersion = 1
|
||||
segments[1..N]
|
||||
strategy = RETAINED | CONTINUOUS
|
||||
startLayerId = 第 1 段省略;后续段必填
|
||||
```
|
||||
|
||||
RN 公共类型位于 `packages/maplibre-particle-rn/src/types.ts`,`RendererRuntime.setRenderSegmentPlan(reactTag, plan)` 接收 typed object。TurboModule、Android/iOS package bridge 与 platform API 内部使用 schema-versioned JSON,JS 调用方不接触 raw JSON。
|
||||
|
||||
## 3. Config / Resolve 生命周期
|
||||
|
||||
1. map-scoped runtime state 保存 active config、`configSource` 和单调递增 `configRevision`。
|
||||
2. `setRenderSegmentPlan()` 将 active source 切到 `explicit-render-segment-plan`。
|
||||
3. `setContinuousLayerRange()` 将 active source 切到 `continuous-range-compat`,不会再并存第二套 Renderer topology。
|
||||
4. 没有显式配置时,既有 `startsRetainedDynamicRange()` 仅通过 `unconfigured/legacy` adapter 生成兼容 plan。
|
||||
5. RenderOrchestrator 只在 config、Style ready、Layer add/remove/change 时重新解析 boundary ID 和 diagnostics metadata;Particle 高频 Fast Path 不扫描/复制 Style 字符串,也不 stringify JSON。
|
||||
6. `topologyRevision` 只在 resolved strategy/begin/end 签名变化时递增。Visibility 变化更新 `visible`,不改变 topology revision。
|
||||
|
||||
显式 plan 合法条件:definitions 非空;第 0 段无 boundary;第 1..N-1 段 boundary 存在、非空且 index 严格递增;每段非空;完整覆盖 `[0, layerCount)`。失败时 segments 为空、`valid=false`、释放旧 cache 并走 full renderer;ordered `styleLayers` 仍可读取。
|
||||
|
||||
## 4. V12 Compatibility Mapping
|
||||
|
||||
旧闭区间 `[startLayerId,endLayerId]` 解析为:
|
||||
|
||||
```text
|
||||
startIndex > 0 -> RETAINED [0,startIndex)
|
||||
CONTINUOUS [startIndex,endIndex+1)
|
||||
endIndex+1 < styleLayerCount -> RETAINED [endIndex+1,layerCount)
|
||||
```
|
||||
|
||||
因此自然退化为 R-C-R、R-C、C-R 或 C。`configSource=continuous-range-compat`。旧 `getRetainedUnderlayDiagnostics()` 继续保留摘要;多段真实事实以新 API 为准。
|
||||
|
||||
## 5. Renderer 执行与 Cache Ownership
|
||||
|
||||
Resolved Segment 保留 Style 全局 `layerIndex` 和 LayerGroup 原 ordinal。Retained 构建继续使用 opaque reverse / translucent forward;Continuous 直接绘制同一全局 index 范围。`parameters.currentLayer` 和 `depthRangeSize` 不按 Segment 重新编号。
|
||||
|
||||
Renderer 按 resolved topology 动态创建 `retainedSegmentCaches[segmentIndex]`:
|
||||
|
||||
- RETAINED:独立 `RetainedUnderlayCache`、key、revision、texture、compositor 和 counters;
|
||||
- CONTINUOUS:对应 slot 为空,不创建 retained texture/FBO/compositor;
|
||||
- topology 改变:旧 cache 在 Renderer 线程释放,不能迁移或 composite stale resource;
|
||||
- GPU resource 仍由 `prepareResources()` lazy 创建,稳定 viewport 复用。
|
||||
|
||||
主 buffer 按 Segment 顺序执行:Retained composite 或 Continuous direct render。相邻同策略不会合并。
|
||||
|
||||
## 6. Invalidation
|
||||
|
||||
Layer immutable Impl identity 与全局 Style index 组成 retained Segment content fingerprint。Layer property/visibility 在某 retained Segment 内变化时,只推进该 Segment 的 content revision;其他 retained Segment 可继续 reuse。
|
||||
|
||||
以下仍保守 invalidate 全部 retained Segment:camera/viewport/pixel ratio、source/tile、image/sprite/glyph、placement/transition、context reset、debug/3D、style reload、resource/compositor failure。原因是当前 Core 没有足够稳定的通用 source-to-render-segment dependency graph;本 Step 不扩建该图。
|
||||
|
||||
## 7. Particle Fast Path
|
||||
|
||||
恰好一个 Continuous Segment、其内容满足既有 Particle-only 支持条件、所有实际 Retained cache 有效时,沿用 V12 Fast Path。缺少某个不存在的 retained cache 不构成 miss。
|
||||
|
||||
多个 Continuous Segment 暂不做高级 Fast Path:gate 以 `unsupported-continuous-layer` fail closed,进入正常 full CPU frame;Segment topology、cache 与 Z-order 不变。
|
||||
|
||||
## 8. Android / iOS / RN API
|
||||
|
||||
```text
|
||||
RN RendererRuntime
|
||||
setRenderSegmentPlan(reactTag, typedPlan)
|
||||
getRenderSegmentDiagnostics(reactTag)
|
||||
↓ schemaVersion=1 JSON transport
|
||||
WeatherRuntimeBridge(既有唯一 bridge)
|
||||
↓
|
||||
Android MapLibreMap / iOS MLNMapView
|
||||
↓
|
||||
mbgl::Map typed RenderSegmentPlan / RenderSegmentDiagnostics
|
||||
↓
|
||||
RetainedUnderlayRuntimeState + RenderOrchestrator + RendererImpl
|
||||
```
|
||||
|
||||
Android platform 内部 API 为 `setRenderSegmentPlan(String)` / `getRenderSegmentDiagnostics()`;iOS 为 `setRenderSegmentPlanJSON:` / `renderSegmentDiagnosticsJSON`。Core API 保持 typed。JSON/schema/strategy 非法时返回 REJECTED,同时把 active topology 置 invalid,避免继续复用旧 cache。
|
||||
|
||||
## 9. Diagnostics Schema
|
||||
|
||||
Snapshot 为 pull-based。顶层包含 enabled/configured/valid/configSource/configRevision/topologyRevision/counts/coverage/fallback/reason/Fast Path counters;`styleLayers[]` 始终按 Style 顺序;`segments[]` 包含 begin/end、实际 Layer 成员。Retained 的 `cache` 为 object;Continuous 的 `cache=null`,不会伪装为 disabled retained cache。
|
||||
|
||||
完整示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"enabled": true,
|
||||
"configured": true,
|
||||
"valid": true,
|
||||
"configSource": "explicit-render-segment-plan",
|
||||
"configRevision": 7,
|
||||
"topologyRevision": 4,
|
||||
"styleLayerCount": 6,
|
||||
"segmentCount": 4,
|
||||
"retainedSegmentCount": 3,
|
||||
"continuousSegmentCount": 1,
|
||||
"coverageComplete": true,
|
||||
"fallback": false,
|
||||
"lastReason": "none",
|
||||
"fastPathEligible": true,
|
||||
"fastPathActive": true,
|
||||
"fastPathFallbackReason": "none",
|
||||
"fastPathHit": 120,
|
||||
"fastPathMiss": 3,
|
||||
"fullCpuFrame": 4,
|
||||
"styleLayers": [
|
||||
{"layerIndex":0,"layerId":"background","layerType":"background","visible":true,"sourceId":null,"sourceLayer":null},
|
||||
{"layerIndex":1,"layerId":"water","layerType":"fill","visible":true,"sourceId":"chart","sourceLayer":"water"},
|
||||
{"layerIndex":2,"layerId":"rn-weather-color","layerType":"weather-color","visible":true,"sourceId":"rn-weather-fields","sourceLayer":null},
|
||||
{"layerIndex":3,"layerId":"rn-weather-particles","layerType":"raster-particle","visible":true,"sourceId":"rn-weather-fields","sourceLayer":null},
|
||||
{"layerIndex":4,"layerId":"land-area","layerType":"fill","visible":true,"sourceId":"chart","sourceLayer":"land"},
|
||||
{"layerIndex":5,"layerId":"ais-layer","layerType":"symbol","visible":true,"sourceId":"ais","sourceLayer":null}
|
||||
],
|
||||
"segments": [
|
||||
{"segmentIndex":0,"strategy":"RETAINED","configuredStartLayerId":null,"beginIndex":0,"endExclusive":2,"resolvedStartLayerId":"background","resolvedEndLayerId":"water","layerCount":2,"layers":[{"layerIndex":0,"layerId":"background","layerType":"background","visible":true,"sourceId":null,"sourceLayer":null},{"layerIndex":1,"layerId":"water","layerType":"fill","visible":true,"sourceId":"chart","sourceLayer":"water"}],"cache":{"cacheState":"reuse","contentRevision":19,"build":1,"reuse":120,"invalidation":0,"lastInvalidationReason":"none","textureCreate":1,"compositeCreate":1,"resourceRelease":0}},
|
||||
{"segmentIndex":1,"strategy":"CONTINUOUS","configuredStartLayerId":"rn-weather-color","beginIndex":2,"endExclusive":4,"resolvedStartLayerId":"rn-weather-color","resolvedEndLayerId":"rn-weather-particles","layerCount":2,"layers":[{"layerIndex":2,"layerId":"rn-weather-color","layerType":"weather-color","visible":true,"sourceId":"rn-weather-fields","sourceLayer":null},{"layerIndex":3,"layerId":"rn-weather-particles","layerType":"raster-particle","visible":true,"sourceId":"rn-weather-fields","sourceLayer":null}],"cache":null},
|
||||
{"segmentIndex":2,"strategy":"RETAINED","configuredStartLayerId":"land-area","beginIndex":4,"endExclusive":5,"resolvedStartLayerId":"land-area","resolvedEndLayerId":"land-area","layerCount":1,"layers":[{"layerIndex":4,"layerId":"land-area","layerType":"fill","visible":true,"sourceId":"chart","sourceLayer":"land"}],"cache":{"cacheState":"ready","contentRevision":22,"build":2,"reuse":80,"invalidation":1,"lastInvalidationReason":"layer-mutation","textureCreate":2,"compositeCreate":2,"resourceRelease":1}},
|
||||
{"segmentIndex":3,"strategy":"RETAINED","configuredStartLayerId":"ais-layer","beginIndex":5,"endExclusive":6,"resolvedStartLayerId":"ais-layer","resolvedEndLayerId":"ais-layer","layerCount":1,"layers":[{"layerIndex":5,"layerId":"ais-layer","layerType":"symbol","visible":true,"sourceId":"ais","sourceLayer":null}],"cache":{"cacheState":"reuse","contentRevision":11,"build":1,"reuse":40,"invalidation":0,"lastInvalidationReason":"none","textureCreate":1,"compositeCreate":1,"resourceRelease":0}}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
有效 snapshot 保证 Segment layers 拼接等于 styleLayers,Layer 不重号、不遗漏、不重复。RN Test Host 的按钮只在用户操作时配置/拉取并显示一次完整 JSON,不轮询、不逐帧打印。
|
||||
|
||||
## 10. 当前限制
|
||||
|
||||
- 多 Continuous 只保证正确 full renderer,不提供高级 Particle Fast Path。
|
||||
- Source/Tile mutation 尚无通用 dependency graph,仍全 retained invalidation。
|
||||
- Legacy diagnostics 只是兼容摘要,不能表达任意 N Segment。
|
||||
- OpenGL/Metal 编译、Native tests、Android codegen/Gradle 与设备运行由 RDC 独立验证后才能形成 PASS 事实。
|
||||
76
docs/30_设计/STEP-097_V13三轮问题去重与修复矩阵_v1.md
Normal file
76
docs/30_设计/STEP-097_V13三轮问题去重与修复矩阵_v1.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# STEP-097 V13 三轮问题去重与修复矩阵 v1
|
||||
|
||||
基线:Mac-mini-tei.lan;`particle/main`;起始 `720d309364628f85049cf9969d71c71e8dfacce9`,与查询时 `origin/particle/main` 一致;工作区干净。冻结 `.13` tag 指向 `ee0b89e71`。该 HEAD 相对任务书的 `b5c1c4879` 只新增 STEP-097 任务文档。Integration 本地 C-001/RFC-001 已只读;其远端是否最新尚未由 RDC 证实。所有构建、测试和运行验证:**NOT RUN**。
|
||||
|
||||
## 公共不变量
|
||||
|
||||
`fieldId/generation/validTime`、东向 U/北向 V、T0/T1/MIX、旧 T1→新 T0 复用、Style 全局 Z 顺序、每个 R 独立缓存、WeatherColor 与 Particle 分离、Host 拥有天气获取。无权改变 C-001 的公开生命周期、错误或数据所有权语义。
|
||||
|
||||
以下“入口”均为待执行回归,不能当作运行结果。源码状态随阶段更新;`WAITING_RDC_BUILD_AND_RUNTIME` 表示仅完成定位,尚未实现。
|
||||
|
||||
| # / 原编号 | 当前源码定位与最短触发 | 反例/安全约束 | 拟修文件与回归入口 | 状态 / commit / 平台待测 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 01 N01/B01 | `paint_parameters.cpp:185` tile 集合相同跨 R-R target 复用 CPU mask | 同一未清 stencil target 可复用 | PaintParameters、Renderer、RenderTarget 已在新目标重置;同 tiles R-R/R-C-R 像素入口尚缺 | WAITING_RDC_BUILD_AND_RUNTIME / `e67e4961b` / GL、Metal |
|
||||
| 02 N02/B02 | `mtl/render_pass.cpp:21` 仅 color clear;`offscreen_texture.cpp:67` 附件创建 | 无 depth/stencil 的 target 不可盲设附件 | Metal descriptor/combined 附件已修改;专用附件故障测试源码尚缺 | WAITING_RDC_BUILD_AND_RUNTIME / `e67e4961b` / Metal |
|
||||
| 03 N03/B12 | `render_orchestrator.hpp:156` catch;`renderer_impl.cpp:824` 仍 mark success | 失败后 full fallback、下帧重试 | visitor 错误标志与 fallback 已接入;异常注入测试源码尚缺 | WAITING_RDC_BUILD_AND_RUNTIME / `e67e4961b` / GL、Metal |
|
||||
| 04 N04/B06 | `Map::Impl::onUpdate()` 在 Continuous update 驱动每个 RasterArraySource;Source 使用 monotonic tick,并用 `forecastTickInProgress` 阻止同步 SourceObserver 重入二次推进 | 显式 setTime/PLAY/PAUSE/rate 由一个 Core clock owner 推进;平台 timer 只唤醒 | `map_impl.cpp`、RasterArraySource、Android/iOS WeatherRuntime;`RasterArrayV4ProductionClockTicksOnceForBothBindingsAndResetsOnCommands`、`RasterArrayV4ClockSourceNotificationReentryDoesNotAdvanceTwice` | CODE_IMPLEMENTED_STATIC_CHECKED / A2 源码提交 `f49ffa68a` / OpenGL 与 Metal host A2 定向 3/3 PASS;FZG1 V4 Golden PLAY/PAUSE/速率/clear/reload PASS;无 iOS runtime(用户豁免) |
|
||||
| 05 B13 | `sameFieldGpuContent()` 保持 revision/version 无关的 payload 相等;renderer GPU 槽位更新使用严格 `sameFieldGpuEndpoint()`;Source mix-only cache 使用 projection identity、binding、immutable frame 和 plane/validity storage pointer identity | mix-only 复用同一 T0/T1 snapshot;frame/revision、grid、validity、U/V、encoding 或 binding 改变则重物化并失效;invalid/clear fail-closed | `field_data_adapter.hpp`、projection materialization、RasterArraySource 与 renderer;Source cache 测试 + `ParticleGpuField.*` | CODE_IMPLEMENTED_STATIC_CHECKED / A2 源码提交 `f49ffa68a` / broad OpenGL 与 Metal host 各 121/121 PASS,A2 定向各 3/3 PASS;FZG1 stable mix 时 upload/reset 计数稳定;clear/reload 后合法增加 |
|
||||
| 06 B07 | 新增内部 signed RGBA8 编码,byte 128 明确表示零;GL/Metal shader 以 high precision 还原 byte | 不改公开 QBIN/FieldProfile encoding;量化误差与 sub-half-quantum 微速边界明确测试 | `particle_gpu_math.hpp`、GL/Metal shader 源及生成头、`particle_gpu_math.test.cpp` | CODE_IMPLEMENTED_STATIC_CHECKED / A2 源码提交 `f49ffa68a` / broad OpenGL 与 Metal host 各 121/121 PASS,含 signed zero、取消、NaN/invalid 与微速边界;两次 shader generator 幂等;无 iOS runtime |
|
||||
| 07 B08 | 统一 helper 与 GL/Metal 粒子位移路径都为 Mercator 东向 U、北向 V 应用相同 cos(latitude) metric,并夹紧极区分母 | 保持 U/V canonical m/s、V 北向为正、既有视觉时间倍率 | `particle_gpu_math.hpp`、renderer 与 GL/Metal shader 源及生成头;0°/45°/60° 四方向和极区测试 | CODE_IMPLEMENTED_STATIC_CHECKED / A2 源码提交 `f49ffa68a` / broad OpenGL 与 Metal host 各 121/121 PASS,含数学定向测试;Android FZG1 场景闭环通过;无 iOS runtime |
|
||||
| 08 N05 | 长度感知 JSON 解析,拒绝 NUL/重复键/非法 schema,invalid fail-closed | 清旧计划、合法恢复 | `render_segment_json.hpp`、双平台 bridge 与 GTest | CODE_IMPLEMENTED / B Core 定向 6/6、扩展 139/139 PASS;待 B Git,iOS runtime 未测 |
|
||||
| 09 N06 | FieldProfileValidator 在 Store commit 前检验有效数值及 scale/offset | validity=0/noData 合法;坏高 revision 不覆盖好帧 | FieldProfileValidator/Source GTest,修正旧 16-bit fixture | CODE_IMPLEMENTED / B Core 定向 6/6、扩展 139/139 PASS;待 B Git |
|
||||
| 10 N07 | View owner、generation guard、map destroy observer 源码已写 | 旧 map 回调不得影响新世代,监听须释放 | Kotlin/Java JUnit,真实 MapLibreMap.onDestroy | Android Native JUnit 2/2、Java 编译与 owner 隔离 JVM JUnit 4/4 PASS;BLOCKED_NATIVE_AAR_COMPATIBILITY:正式 `.13` 缺新 Map 方法,默认 RN consumer 不可配对编译,B 不判 PASS |
|
||||
| 11 F01/C01 | `renderer_impl.cpp:505` full diagnostics 快照 | 外部 pull schema 不变 | runtime/renderer;快照计数 | WAITING_RDC_BUILD_AND_RUNTIME / 待提交 / Core |
|
||||
| 12 F02/C02/B03 | Metal offscreen `swap()` 同步等待 | 不删不明依赖 wait | Metal;GPU capture | DEFERRED_DESIGN / 无 / Metal |
|
||||
| 13 F03/C03/B04 | cache revision 变化释放 texture | target clear 必须先正确 | cache;同尺寸重绘/resize | WAITING_RDC_BUILD_AND_RUNTIME / 待提交 / GL、Metal |
|
||||
| 14 F04/C04 | R 数 × viewport 独立附件内存 | 保留独立 R 与失败 fallback | cache;资源阶梯、失败注入 | DEFERRED_DESIGN / 无 / GL、Metal |
|
||||
| 15 C04/B11 | encoded Store 无总 key/byte 预算 | 不静默驱逐绑定窗口 | Store;容量/旧读者 | DEFERRED_DESIGN / 无 / Core |
|
||||
| 16 F07/C05 | `render_orchestrator.hpp:156` 每段前缀 distance | 全局 ordinal 与 Z 顺序不变 | orchestrator;重排/正反遍历 | WAITING_RDC_BUILD_AND_RUNTIME / 待提交 / Core |
|
||||
| 17 C06/B05/B10 | A2 已处理相同 immutable projection 的 mix-only 重物化;其余 diagnostics pull、ordinal 与 UI owner 世代发布仍属后续范围 | A2 mix-only 不再复制/比较整张浮点快照;不据此宣称 C 阶段整体完成 | A2 部分:RasterArraySource cache 定向测试及 FZG1;C 部分仍按原任务另行实施 | A2 mix-only 子项已验证 / 当前工作树 `5bb1a0e` / OpenGL、Metal host 定向 3/3 PASS;C 其余项目未开始 |
|
||||
| 18 C07 | drawable/tweaker tick 重建、端点复制 | 不保留 stale GPU 引用 | Particle renderer;tick/resize | DEFERRED_DESIGN / 无 / GL、Metal |
|
||||
| 19 F09/O01 | `runtime_state.hpp:230` 新 topology;旧 cache 析构按 index 计数 | 总量与当前 topology 分离 | runtime/cache 按 revision 过滤;新增 `OldCacheReleaseCannotChargeReplacementAtSameIndex` GTest | CODE_IMPLEMENTED_STATIC_CHECKED / `e67e4961b` / 2026-09-26 Core GTest 1/1 PASS,诊断组 5/5 PASS;GPU 资源验收仍待测 |
|
||||
| 20 F05/E01 | Test Host package/lock 已对齐正式 `.13`,SHA-256/SRI 核对一致 | RN/Native 必须精确同版,不改 `.13` 产物 | package/lock、release manifest、默认 RN 编译 | STATIC_VERSION_LOCK_PASS;B10 新 Native method 不在正式 `.13`,默认消费者仍阻塞 |
|
||||
| 21 F06 | `renderer_impl.cpp` multi-C 走 full CPU | 保留安全 fallback | 后续 A/B 设计 | DEFERRED_DESIGN / 无 / GL、Metal |
|
||||
| 22 F08 | source/tile 等全局失效 | 无依赖图前不缩窄 | 后续依赖设计 | DEFERRED_DESIGN / 无 / Core |
|
||||
| 23 F10/E02 | Integration status/compatibility 仍写 `.5` | 发布≠NavSea 支持 | 负责人协调证据请求 | DEFERRED_DESIGN / 无 / 跨项目 |
|
||||
| 24 STEP-095 §7/B09 | synthetic mask 非共享地理 mask | 不新建公开 mask API | C-001/RFC/ADR 协调 | DEFERRED_DESIGN / 无 / 跨项目 |
|
||||
|
||||
## 操作次数模型(静态,非性能实测)
|
||||
|
||||
- A1:跨 target 清除 CPU mask 为每次目标切换一次 O(1) 状态清理;当前同 tiles 可能零次重绘但产生错误像素。Metal clear 每个 pass 最多各一次附件 load 设置。异常后额外一次 full fallback,避免错误缓存跨帧复用。
|
||||
- A2:PLAY 由唯一 Map/Source clock 每次 update 推进一次;V4 mix-only 的投影重物化计数为 0(50 次 Source tick 定向测试),decoded endpoint 变化时 color/particle projection 各物化一次。FZG1 日志在单次 stable mix-only epoch 内 upload/reset 计数不变;clear/reload 会合法增加计数。以上是源码测试和该次设备记录,不构成普遍 GPU 性能结论。
|
||||
- B:JSON 严格校验 O(输入字节长度),有效数值验证 O(有效 cell × plane 数),View/map 每世代各至多一活跃监听;源码已实施,但正式 .13 Native ABI 不含新方法。无 GPU 性能实测。
|
||||
- C:预期 full diagnostics 深复制从每 render 次降到每外部 pull 次;ordinal 前缀从每段 O(G) 降到 topology 更新 O(G);同时间物化从 O(cell) 降为 O(1)。尚未实施。
|
||||
|
||||
## 2026-09-26 第 19 项实测更新
|
||||
|
||||
- RDC 使用 `0d13552be` 重编译并执行 GTest,第 19 项 1/1 PASS、RenderSegmentDiagnostics 全组 5/5 PASS;详见 `docs/报告/STEP-097_A1_第19项GTest运行验证报告_v1.md`。其他问题状态不因此变化,A1 仍为 PARTIAL。
|
||||
|
||||
## 2026-09-26 第 19 项 FZG1 设备辅助证据
|
||||
|
||||
- 基于相同生产源码的 APK 在 FZG1-2 通过覆盖安装和设备 SHA-256 校验。两轮拓扑循环、重排、缓存 OFF/ON、缺失边界回退与恢复的 20 张截图及 UI 诊断已保存;有效场景的 B/T 粗粒度 textureCreate-resourceRelease 均为 0 或 1。此证据只支持 Android 运行态冒烟,不替代此前旧 revision 归属的 GTest 1/1 PASS,也不能证明 GPU 无泄漏。详见 `docs/报告/STEP-097_A1_第19项FZG1运行验证报告_v1.md`。A1 仍因 01/02/03 未闭合保持 PARTIAL。
|
||||
|
||||
## 2026-09-26 A1 专用入口补充(待 RDC 执行)
|
||||
|
||||
- 01:Android Validation Activity 的 STEP-093 菜单新增 STEP-097 same-source stencil 子菜单;`Step097StencilFixture` 用一个本地 GeoJSON source 与四个 fill layer 生成 R-R、R-C-R、C-R-R,菜单可重载与读 segment 诊断,既有 CACHE ON/OFF 可切换。RDC 仍需首帧/重建/reuse 的实际像素截图与 cache 计数;此处仅是可操作 fixture,未实测。
|
||||
- 02:`mtl_retained_attachments.test.cpp` 检查实际离屏资源的 color/depth/stencil 绑定、simulator/x86 combined attachment、无 depth/stencil 时跳过 clear;生产 `RenderPass` 共用的 clear 辅助函数可检查 load/clear/store。设备资源创建失败与 encoder 失败的运行注入仍待 RDC 验证。
|
||||
- 03:`render_orchestrator_error.test.cpp` 注入正/反向范围 visitor 异常,核对 observer/failed 标志,并用 cache 状态机核对失败后继续 Build、成功重试后 Reuse。真实 GPU 绘制故障与完整 renderer fallback 仍待 RDC 注入验证。
|
||||
- 01/02/03 均保持 `WAITING_RDC_BUILD_AND_RUNTIME`;源码入口准备不等于编译或运行 PASS。19 的既有实测状态不变。
|
||||
|
||||
## 2026-09-26 A1 Android/Core 实测收口
|
||||
|
||||
- 01:same-source tiled fixture RR/RCR/CRR 两轮、rebuild/cache OFF/ON 共 18 点,屏幕中心与区域外采样两组像素均 18/18 一致;状态 valid=true/fallback=false,Android FZG1 实测通过。源码/测试提交 `d0ced7235`,编译修正 `8c006ac83`。
|
||||
- 02:Metal host runner 已编译,GTest 两个测试均因没有 Metal device SKIP;用户决定不执行 iOS 运行测试,记 `USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`,不得称 Metal 实测 PASS。
|
||||
- 03:`RenderOrchestratorError.*` 原生测试 2/2 PASS;真实 Android GPU fault injection 不在本次证据中。
|
||||
- 19:前述 Core 1/1 PASS、诊断组 5/5 PASS、FZG1 辅助测试均保留。A1 在 Android/Core 验收及 iOS 用户豁免口径下进入 A2;详细事实见 `docs/报告/STEP-097_A1_Android专项验收报告_v1.md`。
|
||||
|
||||
## 2026-09-26 A2 RDC 最终验证
|
||||
|
||||
- 04–07 已在同一 dirty 源码工作树完成实现与 RDC 独立验证。旧 `sameFieldGpuContent()` 测试断言修复后原 58 项 58/58 PASS;更新后的 broad filter OpenGL 与 Metal host 各 121/121 PASS,三项 A2 Source 定向测试两平台各 3/3 PASS。
|
||||
- 标准 Android Validation APK 为 OpenGL Debug/x86_64,61,753,803 bytes,SHA-256 `27bf4a6cc191bd86c4d1c10398478d81c2999edd61fe7242e40e5188516b0ca5`;安装到 FZG1-2 后设备 APK hash 相同。V4 Golden Wind test-only T0/T1 场景 17 张 PNG、24 个步骤、runner exit 0、ANR=0;PLAY 30 秒 UI 可响应,PAUSE 稳定,clear/reload 后再次 PLAY/PAUSE。
|
||||
- 日志按资源窗口分别记录:clear/reload 前 mix-only epoch `fieldUploadCount=6/resetCount=2` 稳定;explicit clear/reload 后计数合法增加,达到 `fieldUploadCount=10/resetCount=4`,此后 replay 稳定。不可把 clear/reload 期间的上传与 reset 写成零。
|
||||
- build source diff SHA-256 `18927a5f30a88f5c1c3a00e0017473dc33b04151c227e4baf52250dc4ad2e6a2` 与忽略目录中的 APK source snapshot 完全一致。未运行 iOS device runtime,按用户豁免记 `USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`;不得称 Metal host GTest 为 iOS PASS。A2 Android/Core 完成,B/C 未开始,整个 STEP-097 仍 PARTIAL。
|
||||
|
||||
## 2026-09-26 B 独立验证及阻塞
|
||||
|
||||
- RDC Core 定向 4/4、修正旧 16-bit fixture 后 broad 134/134 PASS;正式 .13 Android SDK 缺 B10 新 map destroy observer,默认 RN consumer 无法与该新源码配对。B PARTIAL,C 不启动;详见 B 独立报告。
|
||||
76
docs/30_设计/STEP-098_QBIN_V4单帧与八帧滚动缓存设计_v1.md
Normal file
76
docs/30_设计/STEP-098_QBIN_V4单帧与八帧滚动缓存设计_v1.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# STEP-098 QBIN V4 单帧与八帧滚动缓存设计
|
||||
|
||||
日期:2026-09-26
|
||||
状态:**已按接受的设计契约实现源码;Native 构建、定向测试与平台运行验收待 RDC 执行**。
|
||||
|
||||
## 1. 权威契约
|
||||
|
||||
- RFC-004:`/Users/tei/dev/navsea-particle-integration/rfc/RFC-004_QBIN_V4单帧首显与八帧滚动缓存.md`
|
||||
- ADR-001:`/Users/tei/dev/navsea-particle-integration/adr/ADR-001_QBIN_V4单帧与滚动缓存语义.md`
|
||||
- Contract C-002:`/Users/tei/dev/navsea-particle-integration/contracts/C-002_QBIN_V4单帧与滚动缓存.md`
|
||||
- COORD-005:`/Users/tei/dev/navsea-particle-integration/coordination/active/COORD-005/决议.md`
|
||||
- 读取时 Integration `main` / `origin/main` 均为 `1dd576d3304e267e53ed64d31f9711d2350ce10a`。四份文件为 ACCEPTED / DESIGN ONLY 或 IMPLEMENTATION_PENDING,不代表实现、设备或 V15 发布通过。
|
||||
- 原任务 v1 的旧 RFC-003 闸门和 `docs/报告/STEP-098_公共契约阻塞报告_v1.md` 属于此前状态。用户已确认旧 RFC-003/COORD-004 B 作废删除;本次不恢复、不改写任务书或旧报告。
|
||||
|
||||
八帧时间示例按 C-002 为 **13:00–20:00**。提交 21:00 后删除最早端 13:00。等距离时固定删除原最早端。
|
||||
|
||||
## 2. Core 存储与身份
|
||||
|
||||
`EncodedFieldStore` 按 `fieldKey` 保存独立 `FieldSeries`,默认每个序列容量 8。每个序列按 `validTimeMs` 严格升序存储;插入新帧使用 lower-bound 定位,不假定时间间隔。重复时间继续走 Duplicate、revision、stale、identity conflict 现有判定,不触发容量淘汰。
|
||||
|
||||
为同时支持新预报批次单帧首显和防止跨批次混插,分开处理两类兼容性:
|
||||
|
||||
- **同 key 共存兼容性**:grid、validity plane schema、所有 data plane 的数量与 schema 相同。不同 `fieldID`、QBIN `referenceTimeMs` 或解析 profile 的批次可以在序列中共存;否则有效的新日/新批次单帧可能无法进入序列。
|
||||
- **时间插值兼容性**:除共存 schema 相同外,`fieldID`、`referenceTimeMs`、`profileID` 和 `profileVersion` 必须全部相同。resolve 只在相邻时间端点满足这些条件时返回双帧;不同批次之间的时间返回 unavailable。Ingress 会把已验证的 profile 身份保留到解码帧,避免校验后丢失身份。
|
||||
|
||||
满额插入先复制现有有序序列,在候选序列上执行淘汰和新增端点 schema 校验。候选有效后才 swap 到正式序列;grid/plane schema 不兼容时返回既有 `RejectedIncompatible` 路径,不淘汰现有帧。对容量 8 的正常有效插入返回既有 `AcceptedInserted`;`RejectedCapacity` 枚举与 RN 字符串继续保留兼容,不用于正常满额滚动。
|
||||
|
||||
对提交时间 `n` 和原序列最早/最晚端点 `e/l`,淘汰较远端:`|n-e| >= |l-n|` 时淘汰原最早端,否则淘汰原最晚端。距离计算避免无符号时间相减下溢。该规则不读取 forecast clock,所以仍符合 RN 先 submit、后 setTime 的顺序。
|
||||
|
||||
## 3. 单帧、双帧与投影
|
||||
|
||||
- 精确匹配某个 `validTimeMs` 时 resolver 返回同一个不可变 frame 作为 T0/T1,`mix=0`。颜色和有合法 U/V 或方向分量的粒子可从该帧物化;标量帧仍不能通过向量绑定校验。
|
||||
- 非精确时间只在合法的相邻双帧 bracket 内插值,mix 按两端实际时间比例计算。落在窗口外、跨 batch/profile 边界或只有一帧但时间不匹配时返回 unavailable;不回退为最近帧。
|
||||
- 后续帧到达只更新实际变化的投影。新帧位于当前 bracket 外时保留 Source 的不可变物化 projection 与样本时刻;当前端点被淘汰后重新 resolve,原投影不可用则清除。
|
||||
- 单帧 materialization 只创建一个 `ParticleFieldSnapshot` 并让 T0/T1 共用它。Particle 和 WeatherColor 的首次资源安装也共享相同 endpoint 的 GPU texture/image/scale;GL 与 Metal 共用这段 renderer 代码及语义。设备资源实际行为仍须各平台独立验证。
|
||||
- 淘汰仅释放 Store 对已移除 frame 的引用。已发布 projection 或 GPU 更新仍持有共享 snapshot/texture 引用时,按 shared ownership 生命周期释放。
|
||||
|
||||
## 4. 内存观测范围
|
||||
|
||||
新增 `DecodedFieldFrame::decodedByteSize()`、`FieldSeries::decodedByteSize()` 与 `EncodedFieldStore::decodedByteSize()`,返回 validity bitset 加所有非 validity 原始 plane storage 的字节总和;共享 validity plane storage 不重复计数。该计数**不包含** profile/容器元数据、物化 Float32 snapshot、图像、GL/Metal texture、粒子 state 或 trail。
|
||||
|
||||
公式为单帧 `ceil(cellCount / 8) + Σ(plane payload bytes)`;序列与 Store 分别对帧及 key 求和。Native 定向测试代码覆盖滚动时每 key 保持 8 帧及此原始字节计数回落/清空,但本轮没有运行测试,也没有 CPU/GPU 峰值、32 帧总量或长期泄漏实测。8 帧不构成全局内存保证;不新增固定 MiB 上限或公开容量错误码。
|
||||
|
||||
## 5. API、职责与平台
|
||||
|
||||
RN TypeScript、Android Kotlin/JNI、iOS Objective-C++/Facade 保持 `submitEncodedField(...)` 与 `setEncodedForecastTime(...)` 两步调用及原有方法名、参数和结果名称;Codex 静态核对现有传递路径,本轮没有改 bridge。submit 不修改目标时钟,setTime 不下载数据。两调用仍非原子,不承诺无闪烁。
|
||||
|
||||
`DecodedFieldFrame` 增加内部解析 profile 身份字段及 decoded-byte 计数方法,改变该 C++ 数据结构的二进制布局;不改变 RN/Android/iOS bridge 的公开方法签名或结果枚举。Core 与两个平台必须使用同一源码重编;不得把既有 `.13` AAR 或旧发布物当作新 ABI 消费证据。
|
||||
|
||||
NavSea、Server、下载/cache/manifest、日期 UI、QBIN 格式、Rice 解码、V3、Style Z-order、provider、公开 RN API 均未改。
|
||||
|
||||
## 6. 定向测试映射
|
||||
|
||||
以下是本轮新增或扩展的测试代码覆盖,不代表已执行:
|
||||
|
||||
| 项目 | 测试入口 | 本轮代码覆盖 |
|
||||
| --- | --- | --- |
|
||||
| T01 | `ParticleFieldBinding.SingleFrameProjectsAtItsExactTimeAndUpgradesToInterpolation`;`Source.RasterArrayV4SingleExactFrameMaterializesAndSecondFrameEnablesInterpolation` | 单帧精确颜色/向量 projection、T0=T1、mix=0、第二帧中点升级;标量-only 无向量绑定 |
|
||||
| T02 | 同上 | 第二帧到达后返回双帧与 mix=0.5 |
|
||||
| T03 | `ParticleFieldRuntime.UnorderedSubmissionsAreSortedAndReferenceCyclesStaySeparate` | 乱序排序;wind/wave 分 key;跨 reference/profile 只可单帧解析 |
|
||||
| T04 | `ParticleFieldRuntime.FullWindowRollsBothDirectionsAndBreaksTiesAtEarliest`;`Source.RasterArrayV4RollingWindowPreservesAnUnchangedActivePair` | 满 8 后向未来提交、淘汰最早端并保持活动 pair |
|
||||
| T05 | `ParticleFieldRuntime.FullWindowRollsBothDirectionsAndBreaksTiesAtEarliest`;Source rolling 测试 | 满额向过去提交并淘汰最晚端 |
|
||||
| T06 | `ParticleFieldRuntime.FullWindowRollsBothDirectionsAndBreaksTiesAtEarliest` | 中部等距插入固定淘汰最早端、有序且 8 帧 |
|
||||
| T07 | `Source.RasterArrayV4StoresNewReferenceAndProfileAsSingleFrameWithoutCrossBatchMix` | 新 reference/profile 批次精确首显、补帧后同批插值、回到早批次单帧 |
|
||||
| T08 | `ParticleFieldRuntime.RevisionIdentityAndIndependentCadence`;既有 Source stale/revision cases | Duplicate、identity conflict、revision;既有 stale 路径保留。满额下 revision 不占槽由同一 Store 分支处理 |
|
||||
| T09 | `Source.RasterArrayV4HostApiRejectsKeepPublicProjectionStable`、`...RejectsNumericOverflowBeforeReplacingAcceptedRevision`、`ParticleFieldRuntime.RejectedFullWindowInsertDoesNotEvictAndKeysRemainIndependent` | 解码/profile/数值和 grid schema 拒绝路径、有效帧/投影/clock 零副作用。数据帧入 Store 前仍由现有 ingress 完成解码与 profile 校验 |
|
||||
| T10 | `ParticleFieldBinding.MixChangesSampleStateButNotContentIdentityOrStorage`、单帧升级 Source 测试 | 合法 bracket 的中间比例;精确端点以单帧语义表示 |
|
||||
| T11 | 既有 binding `WindowUnavailable` assertions;新单帧测试 | 非覆盖时间 unavailable,不用最近帧顶替 |
|
||||
| T12 | `Source.RasterArrayV4RollingWindowPreservesAnUnchangedActivePair` | 外围补帧/双向滚动不改变当前物化 projection 与 requested time |
|
||||
| T13 | `Source.RasterArrayV4EvictedActiveFrameClearsItsMaterializedProjection` | 当前单帧端点淘汰后 fail-closed 清除物化 projection |
|
||||
| T14 | `ParticleFieldRuntime.RejectedFullWindowInsertDoesNotEvictAndKeysRemainIndependent` | 两个 key 各自 8 帧,独立计数与字节 |
|
||||
| T15 | 单帧 binding/materialization 测试;GL/Metal renderer 资源安装源码 | T0/T1 共用物化 snapshot;renderer 首装共用 endpoint texture/image。GPU/context-loss 运行验证未覆盖 |
|
||||
| T16 | `ParticleFieldRuntime.RepeatedBidirectionalRollingAndClearStayBounded` | 120 次双向提交、每 key 8 帧边界、clear/clearAll 和 decoded bytes 归零 |
|
||||
| T17 | 现有 `FieldRuntime.ts`、`NativeWeatherRuntimeBridge.ts`、Android/iOS bridge 源码静态核对 | 旧 API、类型、方法名和两步语义未变;RN 自动化/双平台 consumer build 未运行 |
|
||||
|
||||
所有测试代码的执行状态均为 **NOT RUN**。完整执行状态与 RDC 命令见 STEP-098 完成报告。
|
||||
113
docs/40_任务/STEP-082_XCFramework_Bazel_Codegen阻塞收尾_Codex任务_v1.md
Normal file
113
docs/40_任务/STEP-082_XCFramework_Bazel_Codegen阻塞收尾_Codex任务_v1.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# STEP-082 XCFramework Bazel Codegen 阻塞收尾 Codex 任务 v1
|
||||
|
||||
## 1. 当前基线
|
||||
|
||||
- 项目:`/Users/tei/dev/MapLibreParticleLab`
|
||||
- 分支:`particle/main`
|
||||
- 起始 HEAD:`dd9687f5ed6bb9be4b58b73f2efd39f54faef7d6`
|
||||
- `origin/particle/main` 应与起始 HEAD 一致。
|
||||
- STEP-082 当前状态:`IMPLEMENTED / WAITING_MAC_XCFRAMEWORK_VALIDATION`。
|
||||
- 本任务仍属于 STEP-082 收尾,不创建 STEP-083,不进入 RN/JS/NavSea。
|
||||
|
||||
开始前必须读取:
|
||||
- `docs/报告/STEP-082_iOS_Native_FieldRuntime_Facade与XCFramework发行基线_Codex完成报告_v1.md`
|
||||
- `docs/50_Step记录/STEP-082_iOS_Native_FieldRuntime_Facade与XCFramework发行基线_v1.md`
|
||||
- `bazel/core.bzl`
|
||||
- `BUILD.bazel`
|
||||
- `scripts/generate-style-code.mjs`
|
||||
- `CMakeLists.txt`
|
||||
|
||||
## 2. 已知阻塞与调查结论
|
||||
|
||||
当前 `bazel build //platform/ios:MapLibre.dynamic` 在 `//:generated_style_code` 阶段失败:
|
||||
`src/mbgl/style/layers/particle_validation_layer.cpp` 被声明为 generator 输出,但 `scripts/generate-style-code.mjs` 不生成它。
|
||||
|
||||
当前只读核对显示:
|
||||
- `particle_validation_layer.cpp` 是仓库中已存在的手写源文件;
|
||||
- CMake 在 `CMakeLists.txt` 中把它作为普通源文件直接编译;
|
||||
- `bazel/core.bzl` 目前把它放在 `MLN_GENERATED_STYLE_SOURCE`;
|
||||
- style generator 只遍历 style spec 中的正式 layer type 并生成对应标准 style layer 文件。
|
||||
|
||||
这说明高概率是 Bazel 文件分类错误,而不是 generator 缺功能。Codex 必须先自行核对真实代码和 Git 历史后再修改。## 3. 本次唯一目标
|
||||
|
||||
用最小改动修复 Bazel 对 `particle_validation_layer.cpp` 的源文件分类,使:
|
||||
1. 它不再作为 `generated_style_code` 的声明输出;
|
||||
2. 它仍然作为 Core 普通源文件参与 Bazel 构建;
|
||||
3. 不改变现有 CMake 编译行为;
|
||||
4. 不改变 Particle Validation Layer、RasterParticle、WeatherColor、QBIN、Metal 或 Runtime 语义。
|
||||
|
||||
优先方案是:若调查确认该文件确属手写源文件,则从 `MLN_GENERATED_STYLE_SOURCE` 移出并纳入正确的普通 Core source 集合。不得为了通过构建而让 generator 复制、伪造或空生成该文件。
|
||||
|
||||
## 4. 必做清单
|
||||
|
||||
- [ ] 核对起始 branch / HEAD / remote / dirty。
|
||||
- [ ] 用 Git 历史确认 `particle_validation_layer.cpp` 的来源和预期性质。
|
||||
- [ ] 最小修复 Bazel source classification。
|
||||
- [ ] `bazel build //:generated_style_code` 通过。
|
||||
- [ ] `bazel build //platform/ios:MapLibre.dynamic` 通过。
|
||||
- [ ] 验证生成的 `MapLibre.dynamic.xcframework` 中包含 public `MLNParticleFieldRuntime.h`。
|
||||
- [ ] 验证 umbrella/public header 链可见 `MLNParticleFieldRuntime`。
|
||||
- [ ] 验证 binary 中存在该 facade 的 Objective-C public symbol/class。
|
||||
- [ ] 验证 `MLNMapView_Private.h` 没有被导出为 public header。
|
||||
- [ ] 若正式 XCFramework 可压缩,则生成临时 zip,记录真实文件大小与 SHA-256;大二进制默认不提交 Git。
|
||||
- [ ] 更新 `release/maplibre-particle/ios/release-manifest.json` 中可由本次真实构建确认的字段;URL 尚未发布时保持明确占位,不伪造远端地址。
|
||||
- [ ] `git diff --check` PASS。
|
||||
- [ ] 更新 STEP-082 Step 记录和 `粒子开发记录.md`。
|
||||
- [ ] 生成 `docs/报告/STEP-082_iOS_Native_FieldRuntime_Facade与XCFramework发行基线_Codex完成报告_v2.md`。
|
||||
- [ ] scoped commit、push 到 `origin/particle/main`,记录 Final HEAD。
|
||||
|
||||
## 5. 严格禁止
|
||||
|
||||
不得:
|
||||
- 修改 `scripts/generate-style-code.mjs` 去伪造 particle validation 输出,除非调查证明它本应由 style spec 生成;若出现这种相反证据,先在报告中说明,不要扩大实现。
|
||||
- 修改 Particle renderer、Metal shader、QBIN decoder、FieldProfile、BindingProjection、粒子 physics。
|
||||
- 修改 `packages/maplibre-particle-rn/`、`integration/react-native/` 或 NavSea。
|
||||
- 重做 STEP-081 真机 smoke。
|
||||
- 开始 STEP-083。
|
||||
- 为通过构建删除或停用 Particle Validation Layer。
|
||||
- 使用 `git add -A`、`git add .`、`git reset --hard`、`git clean`。## 6. 用户已有 dirty,必须原样保护
|
||||
|
||||
以下路径不得修改、暂存、提交:
|
||||
- `docs/doxygen/doxygen-awesome-css`
|
||||
- `platform/ios/app/MBXAppDelegate.m`
|
||||
- `platform/windows/vendor/vcpkg`
|
||||
|
||||
另外,现有未跟踪任务书:
|
||||
- `docs/40_任务/STEP-082_iOS_Native_FieldRuntime_Facade与XCFramework发行基线_Codex任务_v1.md`
|
||||
不得为了清理工作树而删除或改写。
|
||||
|
||||
本任务书自身也不要求提交;提交时只显式 stage 本次实现与正式记录文件。
|
||||
|
||||
## 7. 构建与验证要求
|
||||
|
||||
macOS 上使用仓库当前真实工具链。编译输出遵循全局 `$build-output-throttle`。
|
||||
|
||||
至少执行:
|
||||
- `bazel build //:generated_style_code`
|
||||
- `bazel build //platform/ios:MapLibre.dynamic`
|
||||
|
||||
若第二条成功,必须直接检查实际 XCFramework,不允许只根据 Bazel exit code 判定:
|
||||
- public Headers;
|
||||
- umbrella import;
|
||||
- Objective-C class/symbol;
|
||||
- private header 排除;
|
||||
- artifact 路径、大小;
|
||||
- zip SHA-256(若生成 zip)。
|
||||
|
||||
如果修复第一个 blocker 后暴露新的、与本次 Bazel/iOS packaging 直接相关的小型问题,可以继续最小修复并验证;如果新问题涉及 renderer/Core 语义或较大架构改动,停止并报告,不得扩大范围。
|
||||
|
||||
## 8. 关闭判定
|
||||
|
||||
只有以下全部成立,STEP-082 才可改为 `CLOSED / PASS`:
|
||||
- Bazel generated style target PASS;
|
||||
- `MapLibre.dynamic` XCFramework build PASS;
|
||||
- `MLNParticleFieldRuntime.h` 确认存在于 public Headers;
|
||||
- public ObjC facade symbol/class 确认存在;
|
||||
- `MLNMapView_Private.h` 未 public export;
|
||||
- artifact size 与真实 SHA-256 已记录;
|
||||
- `git diff --check` PASS;
|
||||
- v2 完成报告、Step 记录、粒子开发记录更新;
|
||||
- scoped commit 已 push,local HEAD == `origin/particle/main`;
|
||||
- 用户三处 dirty 和旧任务书保持未触碰。
|
||||
|
||||
若任一门槛未满足,保持 `IMPLEMENTED / WAITING_MAC_XCFRAMEWORK_VALIDATION` 或明确 BLOCKED,不得写 PASS。
|
||||
@@ -0,0 +1,801 @@
|
||||
# STEP-082 iOS Native FieldRuntime Facade 与 XCFramework 发行基线 — Codex 任务 v1
|
||||
|
||||
状态:READY FOR IMPLEMENTATION
|
||||
项目:MapLibreParticleLab / 粒子系统
|
||||
目标分支:`particle/main`
|
||||
任务基线 HEAD:`3e03949d3411c830cfff4bf320cd350f5b6a7527`
|
||||
|
||||
---
|
||||
|
||||
## 0. 开始前必须确认
|
||||
|
||||
先完整阅读本任务文件,再开始修改。
|
||||
|
||||
开始时只读执行并记录:
|
||||
|
||||
```bash
|
||||
git branch --show-current
|
||||
git rev-parse HEAD
|
||||
git rev-parse origin/particle/main
|
||||
git status --short
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
```text
|
||||
branch = particle/main
|
||||
HEAD = origin/particle/main = 3e03949d3411c830cfff4bf320cd350f5b6a7527
|
||||
```
|
||||
|
||||
已知且必须保持不动的用户无关修改:
|
||||
|
||||
```text
|
||||
docs/doxygen/doxygen-awesome-css
|
||||
platform/ios/app/MBXAppDelegate.m
|
||||
platform/windows/vendor/vcpkg
|
||||
```
|
||||
|
||||
若还有新的未知 dirty:
|
||||
|
||||
- 不自动清理;
|
||||
- 不 reset;
|
||||
- 不 checkout 覆盖;
|
||||
- 不纳入本任务 commit;
|
||||
- 在完成报告中说明。
|
||||
|
||||
### 执行清单
|
||||
|
||||
开始实施前建立简短勾选清单,至少包含:
|
||||
|
||||
- [ ] iOS public Native FieldRuntime facade
|
||||
- [ ] MapView-owned 生命周期
|
||||
- [ ] V4 submit / binding / forecast / clear
|
||||
- [ ] fail-closed / style reload 安全
|
||||
- [ ] public header 无 Core 类型泄漏
|
||||
- [ ] XCFramework public header / symbol 验证
|
||||
- [ ] distribution-ready 元数据
|
||||
- [ ] 中文设计/Step/完成报告
|
||||
- [ ] git diff --check
|
||||
- [ ] scoped commit / push
|
||||
- [ ] 最终逐项对照
|
||||
|
||||
不得静默跳过;无法完成必须明确写明。
|
||||
|
||||
---
|
||||
|
||||
# 1. 本 Step 的定位
|
||||
|
||||
STEP-081 已正式 `CLOSED / PASS`。
|
||||
|
||||
本任务禁止重新做:
|
||||
|
||||
- Uniform East
|
||||
- Uniform North
|
||||
- Vortex
|
||||
- QBIN T0 / MID / T1
|
||||
- Clear / Clear 后重建
|
||||
- STEP081 readback
|
||||
- 固定绿线
|
||||
- Point fallback
|
||||
- STEP-081 Metal pipeline cache 根因复验
|
||||
|
||||
STEP-082 是新的产品化前置层:
|
||||
|
||||
```text
|
||||
RN / Consumer(后续 STEP-083)
|
||||
↓
|
||||
MapLibre iOS public ObjC API
|
||||
↓
|
||||
MLNParticleFieldRuntime
|
||||
↓
|
||||
MLNMapView private implementation seam
|
||||
↓
|
||||
mbgl::Style / RasterArraySource / QBIN V4
|
||||
↓
|
||||
WeatherColor / RasterParticle / Metal
|
||||
```
|
||||
|
||||
本 Step 只做到 **公开 Native facade + 正式 XCFramework 导出基线**。
|
||||
|
||||
不做 RN TurboModule。
|
||||
|
||||
---
|
||||
|
||||
# 2. 已确认的当前真实事实
|
||||
|
||||
实施前只需快速复核,不得重新做大范围架构调查。
|
||||
|
||||
## 2.1 当前 RN package 是 Android-only Native
|
||||
|
||||
当前:
|
||||
|
||||
```text
|
||||
packages/maplibre-particle-rn/package.json
|
||||
version = 13.4.1-particle.5
|
||||
```
|
||||
|
||||
`files` 没有 `ios`。
|
||||
|
||||
`react-native.config.js` 仅有 Android。
|
||||
|
||||
这是 STEP-083 的工作,不在本 Step 修改。
|
||||
|
||||
## 2.2 STEP-081 private seam 不能作为正式 SDK API
|
||||
|
||||
当前:
|
||||
|
||||
```text
|
||||
platform/ios/src/MLNMapView_Private.h
|
||||
```
|
||||
|
||||
暴露:
|
||||
|
||||
```objc
|
||||
- (mbgl::Map &)mbglMap;
|
||||
```
|
||||
|
||||
但该 header 属于:
|
||||
|
||||
```text
|
||||
MLN_IOS_PRIVATE_HEADERS
|
||||
```
|
||||
|
||||
不在 `MapLibre.dynamic.xcframework` 正式 public headers 中。
|
||||
|
||||
因此本任务必须新增一个正式 public facade,而不是把 `MLNMapView_Private.h` 暴露出去。
|
||||
|
||||
## 2.3 Particle/Weather 类型继续保持 Core-only
|
||||
|
||||
当前 Darwin LayerManager 已注册:
|
||||
|
||||
```text
|
||||
RasterParticleLayerFactory
|
||||
WeatherColorLayerFactory
|
||||
```
|
||||
|
||||
为 Core-only layer。
|
||||
|
||||
不要新建:
|
||||
|
||||
```text
|
||||
MLNRasterParticleLayer
|
||||
MLNWeatherColorLayer
|
||||
MLNRasterArraySource
|
||||
```
|
||||
|
||||
RN / Host 仍通过 Style JSON 创建:
|
||||
|
||||
```text
|
||||
raster-array
|
||||
weather-color
|
||||
raster-particle
|
||||
```
|
||||
|
||||
Native facade 只负责找到这些对象并提交/控制数据。
|
||||
|
||||
---
|
||||
|
||||
# 3. 目标架构
|
||||
|
||||
## 3.1 新增公开 iOS Runtime facade
|
||||
|
||||
推荐类型:
|
||||
|
||||
```text
|
||||
MLNParticleFieldRuntime
|
||||
MLNParticleFieldRuntimeConfiguration
|
||||
MLNMapView+ParticleFieldRuntime
|
||||
```
|
||||
|
||||
如果现有 ObjC 命名规范要求轻微调整,可以调整文件名,但必须在设计文档说明。
|
||||
|
||||
禁止使用:
|
||||
|
||||
```text
|
||||
STEP081*
|
||||
Smoke*
|
||||
Fixture*
|
||||
NavSea*
|
||||
```
|
||||
|
||||
作为公开 Native 类型名。
|
||||
|
||||
## 3.2 Public API 绝不能暴露 Core 类型
|
||||
|
||||
公开 header 只能依赖:
|
||||
|
||||
- Foundation
|
||||
- MapLibre public Objective-C 类型
|
||||
|
||||
禁止出现在 public header:
|
||||
|
||||
```text
|
||||
mbgl::Map
|
||||
mbgl::Style
|
||||
mbgl::style::RasterArraySource
|
||||
std::vector
|
||||
std::unique_ptr
|
||||
void * core handle
|
||||
raw Core pointer
|
||||
MLNMapView_Private.h
|
||||
```
|
||||
|
||||
QBIN payload 使用:
|
||||
|
||||
```objc
|
||||
NSData *
|
||||
```
|
||||
|
||||
而不是文件 URL。
|
||||
|
||||
“本地文件读取”属于后续 RN bridge 边界,不进入 Native SDK facade。
|
||||
|
||||
## 3.3 MapView-owned 生命周期
|
||||
|
||||
目标语义与 Android map-owned `WeatherRuntime` 一致:
|
||||
|
||||
```text
|
||||
MLNMapView
|
||||
owns
|
||||
MLNParticleFieldRuntime
|
||||
```
|
||||
|
||||
必须提供等价的:
|
||||
|
||||
- replace/create
|
||||
- current runtime
|
||||
- dispose
|
||||
|
||||
行为。
|
||||
|
||||
建议:
|
||||
|
||||
```objc
|
||||
- replaceParticleFieldRuntimeWithConfiguration:
|
||||
- particleFieldRuntime
|
||||
- disposeParticleFieldRuntime
|
||||
```
|
||||
|
||||
可使用 category + associated object,或符合当前 iOS SDK 结构的等价方式。
|
||||
|
||||
要求:
|
||||
|
||||
1. `MLNParticleFieldRuntime` 不得强引用 MapView 形成循环;
|
||||
2. MapView 释放后 runtime 不得访问悬空 Core;
|
||||
3. replace 必须先 dispose 旧 runtime;
|
||||
4. dispose 幂等;
|
||||
5. runtime 不保存 `RasterArraySource *` 等跨 style 生命周期 Core pointer;
|
||||
6. 每次命令通过当前 MapView/current style 重新查找目标 source/layer。
|
||||
|
||||
---
|
||||
|
||||
# 4. Configuration
|
||||
|
||||
Configuration 至少包含:
|
||||
|
||||
```text
|
||||
sourceId
|
||||
weatherColorLayerId
|
||||
particleLayerId
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- 初始化时字符串非空;
|
||||
- Runtime 不硬编码 RN package ID;
|
||||
- RN package 后续仍可使用当前固定值:
|
||||
- `rn-weather-fields`
|
||||
- `rn-weather-color`
|
||||
- `rn-weather-particles`
|
||||
- Native SDK 保持 Host-neutral。
|
||||
|
||||
---
|
||||
|
||||
# 5. attach / replace 时的对象验证
|
||||
|
||||
创建 runtime 时必须检查当前 style 中:
|
||||
|
||||
```text
|
||||
sourceId -> RasterArraySource
|
||||
weatherColorLayerId -> WeatherColor layer
|
||||
particleLayerId -> RasterParticle layer
|
||||
```
|
||||
|
||||
验证必须基于 Core 真实类型,而不是只检查字符串 ID。
|
||||
|
||||
失败时:
|
||||
|
||||
- 不创建“半可用” runtime;
|
||||
- 返回明确错误;
|
||||
- 不创建第二套 source/layer;
|
||||
- 不偷偷替换 style;
|
||||
- 不吞掉错误后返回成功。
|
||||
|
||||
Style 尚未 ready 时也必须明确失败。
|
||||
|
||||
后续 Host 会在 style ready 后重试/attach。
|
||||
|
||||
---
|
||||
|
||||
# 6. STEP-082 必须实现的 QBIN V4 API
|
||||
|
||||
本 Step 只冻结新的 V4 产品主链。
|
||||
|
||||
至少实现:
|
||||
|
||||
```text
|
||||
submitEncodedField
|
||||
setEncodedColorBinding
|
||||
setEncodedParticleBinding
|
||||
setEncodedForecastTime
|
||||
setEncodedForecastPlaybackState
|
||||
setEncodedForecastPlaybackSpeed
|
||||
clearEncodedField
|
||||
clearAllEncodedFields
|
||||
```
|
||||
|
||||
## 6.1 submitEncodedField
|
||||
|
||||
输入语义与当前 Android/Core 保持一致:
|
||||
|
||||
```text
|
||||
fieldKey
|
||||
fieldID
|
||||
generation
|
||||
dataVersion
|
||||
contractJson
|
||||
NSData * qbin
|
||||
```
|
||||
|
||||
必须继续复用现有:
|
||||
|
||||
```text
|
||||
parseResolvedFieldProfileContractJson(...)
|
||||
RasterArraySource::submitEncodedField(...)
|
||||
```
|
||||
|
||||
禁止在 ObjC facade 中重新实现 QBIN decoder。
|
||||
|
||||
必须继续遵守:
|
||||
|
||||
- `generation > 0`
|
||||
- `dataVersion >= 0`
|
||||
- ID UTF-8 边界与当前 V4 contract 一致
|
||||
- contract JSON 有明确大小上限
|
||||
- QBIN transport 有明确上限
|
||||
- 空 payload 拒绝
|
||||
- invalid contract fail-closed
|
||||
|
||||
建议继续保持当前 64 MiB payload 上限与 256 KiB contract 上限;若 Core 当前权威 guard 已定义同一值,应优先复用,不复制新的魔法数字。
|
||||
|
||||
## 6.2 结果映射
|
||||
|
||||
必须提供稳定 ObjC enum / typed result,能够无损表达当前 Core:
|
||||
|
||||
```text
|
||||
ACCEPTED_INSERTED
|
||||
ACCEPTED_REPLACED_REVISION
|
||||
DUPLICATE
|
||||
REJECTED_DECODE
|
||||
REJECTED_CONTRACT
|
||||
REJECTED_INVALID_FIELD_KEY
|
||||
REJECTED_IDENTITY_CONFLICT
|
||||
REJECTED_STALE_REVISION
|
||||
REJECTED_INCOMPATIBLE
|
||||
REJECTED_CAPACITY
|
||||
```
|
||||
|
||||
“source missing / runtime disposed / style not ready”属于 facade/lifecycle 错误,不得伪装成某个 Core submit result。
|
||||
|
||||
## 6.3 Binding
|
||||
|
||||
Color Binding 必须保持:
|
||||
|
||||
```text
|
||||
fieldKey
|
||||
semanticID
|
||||
groupID
|
||||
expectedValueClass
|
||||
expectedUnitID
|
||||
```
|
||||
|
||||
支持:
|
||||
|
||||
```text
|
||||
SCALAR
|
||||
VELOCITY_COMPONENT
|
||||
DIRECTION_ANGLE
|
||||
```
|
||||
|
||||
Particle Binding 必须保持:
|
||||
|
||||
```text
|
||||
VECTOR_COMPONENTS
|
||||
DIRECTION_ANGLE
|
||||
```
|
||||
|
||||
inactive selector 仍必须严格为:
|
||||
|
||||
```text
|
||||
semanticID = 0
|
||||
groupID = 0
|
||||
```
|
||||
|
||||
不得按“是否为空”猜模式。
|
||||
|
||||
## 6.4 Forecast
|
||||
|
||||
保留 Core-owned:
|
||||
|
||||
```text
|
||||
requested forecast time
|
||||
play / pause
|
||||
playback speed
|
||||
T0 / T1 / mix
|
||||
boundary reuse
|
||||
```
|
||||
|
||||
ObjC runtime 只转发命令。
|
||||
|
||||
禁止复制 Forecast Clock。
|
||||
|
||||
## 6.5 Clear
|
||||
|
||||
实现:
|
||||
|
||||
```text
|
||||
clearEncodedField(fieldKey)
|
||||
clearAllEncodedFields()
|
||||
```
|
||||
|
||||
Clear 后必须触发必要 rerender / resource release 路径,但不得增加 STEP081 临时 readback。
|
||||
|
||||
---
|
||||
|
||||
# 7. Renderer / Style 边界
|
||||
|
||||
本任务不得改变:
|
||||
|
||||
- QBIN V2 格式
|
||||
- FieldProfile contract
|
||||
- BindingProjection
|
||||
- V4 materialization
|
||||
- T0/T1 interpolation
|
||||
- Metal particle physics
|
||||
- 1 px current-endpoint Metal coverage
|
||||
- WeatherColor/RasterParticle shader 语义
|
||||
|
||||
如果 facade 接入暴露出 Core 缺陷:
|
||||
|
||||
- 先停止扩大修改;
|
||||
- 在完成报告中列出 blocker;
|
||||
- 不以“顺便重构 renderer”解决。
|
||||
|
||||
---
|
||||
|
||||
# 8. Public Header / XCFramework 接线
|
||||
|
||||
必须把新 public API 正式加入 iOS framework 导出链。
|
||||
|
||||
至少检查并按真实结构更新:
|
||||
|
||||
```text
|
||||
platform/ios/bazel/files.bzl
|
||||
platform/ios/src/Mapbox.h
|
||||
platform/ios/BUILD.bazel
|
||||
```
|
||||
|
||||
若实现文件属于现有 source list 自动包含机制,按真实情况处理,不得重复注册。
|
||||
|
||||
最终产出的:
|
||||
|
||||
```text
|
||||
MapLibre.dynamic.xcframework
|
||||
```
|
||||
|
||||
必须包含新 public header。
|
||||
|
||||
同时确认:
|
||||
|
||||
- `MLNMapView_Private.h` 仍然不是 public header;
|
||||
- 没有把任何 Core header 整包暴露给 Consumer;
|
||||
- 新 public header 可以单独被 Objective-C Consumer import;
|
||||
- public symbol 存在于 framework binary。
|
||||
|
||||
---
|
||||
|
||||
# 9. Distribution-ready 基线
|
||||
|
||||
当前 MLRN 11.3.6 iOS 通过 SwiftPM distribution 消费 MapLibre Native。
|
||||
|
||||
本 Step **不创建远端 Git 仓库,不擅自打 release tag,不发布公网 Swift Package**。
|
||||
|
||||
但必须在仓库中建立一个最小、可审计的 iOS Particle binary distribution 说明/模板,建议放在:
|
||||
|
||||
```text
|
||||
release/maplibre-particle/ios/
|
||||
```
|
||||
|
||||
至少包含:
|
||||
|
||||
```text
|
||||
README.md
|
||||
Package.swift.template
|
||||
release-manifest.json
|
||||
```
|
||||
|
||||
或与现有 release 目录规范等价的结构。
|
||||
|
||||
`Package.swift.template` 的目标是后续独立 distribution repo 可以直接使用:
|
||||
|
||||
```text
|
||||
binaryTarget(name: "MapLibre", url: "...MapLibre.dynamic.xcframework.zip", checksum: "...")
|
||||
```
|
||||
|
||||
注意:
|
||||
|
||||
- product/module 名必须保持 `MapLibre`,以满足 MLRN import;
|
||||
- 不允许再生成第二个不同 module 名并要求 MLRN 同时链接两份 MapLibre;
|
||||
- URL 可以先用明确占位符;
|
||||
- checksum 必须在真实 zip 生成后记录真实值;
|
||||
- 若当前 Step 尚不能生成正式 zip,则 manifest 明确标记 `NOT_BUILT`,禁止伪造 hash。
|
||||
|
||||
建议 external distribution version 独立采用语义版本,例如:
|
||||
|
||||
```text
|
||||
6.28.0-particle.1
|
||||
```
|
||||
|
||||
但不要直接修改 `platform/ios/VERSION=6.28.0`,除非通过构建系统审查确认不会破坏 upstream-compatible framework version。
|
||||
|
||||
本 Step 的核心是建立“MapLibre module 不变 + 自定义 binary distribution version 独立管理”的边界。
|
||||
|
||||
---
|
||||
|
||||
# 10. MLRN 11.3.6 后续兼容性记录
|
||||
|
||||
STEP-082 不写 RN module,但设计文档必须记录下一 Step 已确认的 iOS 视图链:
|
||||
|
||||
```text
|
||||
reactTag
|
||||
-> React View Registry
|
||||
-> Fabric ComponentView
|
||||
-> contentView
|
||||
-> MLNMapView
|
||||
-> public MLNParticleFieldRuntime
|
||||
```
|
||||
|
||||
同时记录:
|
||||
|
||||
- MLRN 11.3.6 是当前 peer dependency;
|
||||
- 后续 STEP-083 不建立第二套 Map registry;
|
||||
- 不依赖 MLRN private ObjC header 作为长期 API;
|
||||
- 后续通过 `$MLRN_SPM_SPEC` 让 MLRN 与 Particle Bridge 使用同一份自定义 `MapLibre` binary。
|
||||
|
||||
不要在本 Step 修改:
|
||||
|
||||
```text
|
||||
packages/maplibre-particle-rn/
|
||||
integration/react-native/
|
||||
NavSea
|
||||
```
|
||||
|
||||
除非仅为文档引用且任务确实需要;默认都不改。
|
||||
|
||||
---
|
||||
|
||||
# 11. 测试与验证
|
||||
|
||||
## 11.1 必做静态检查
|
||||
|
||||
必须执行:
|
||||
|
||||
```text
|
||||
git diff --check
|
||||
```
|
||||
|
||||
并检查:
|
||||
|
||||
1. public header 没有 `mbgl::`;
|
||||
2. public header 没有 STL 类型;
|
||||
3. `MLNMapView_Private.h` 未进入 public header list;
|
||||
4. Core-only layer/source wrapper 没有被新增;
|
||||
5. Android/RN 文件没有变化;
|
||||
6. STEP-081 临时诊断代码没有恢复。
|
||||
|
||||
## 11.2 Objective-C / Objective-C++ 编译
|
||||
|
||||
如果执行环境是 macOS:
|
||||
|
||||
- 使用当前仓库真实 iOS build 工具链;
|
||||
- 编译输出遵循全局 `$build-output-throttle`;
|
||||
- 构建包含新 facade 的 iPhoneOS arm64 或正式 XCFramework target;
|
||||
- 不需要重新做 STEP-081 真机视觉 smoke。
|
||||
|
||||
如果执行环境不是 macOS:
|
||||
|
||||
- 不得伪造 iOS build PASS;
|
||||
- 完成源码/构建图静态接线;
|
||||
- 完成报告标记 `WAITING_MAC_XCFRAMEWORK_VALIDATION`。
|
||||
|
||||
## 11.3 XCFramework 验证
|
||||
|
||||
如果成功生成 XCFramework,必须验证:
|
||||
|
||||
```text
|
||||
Headers 中存在新 public header
|
||||
Mapbox.h 可导入该 API
|
||||
framework binary 中存在对应 public ObjC symbol
|
||||
MLNMapView_Private.h 不在导出 Headers
|
||||
```
|
||||
|
||||
并记录 zip:
|
||||
|
||||
```text
|
||||
文件名
|
||||
size
|
||||
SHA-256
|
||||
```
|
||||
|
||||
可将临时大产物保留在 build/release 临时目录,但是否提交大二进制必须遵守仓库现有规则;默认不要把 XCFramework zip 直接提交进 Git。
|
||||
|
||||
## 11.4 本 Step 不做
|
||||
|
||||
禁止:
|
||||
|
||||
- Android Gradle
|
||||
- APK
|
||||
- ADB
|
||||
- Android 设备
|
||||
- STEP-081 iPad 视觉复测
|
||||
- RN iOS build
|
||||
- NavSea build
|
||||
- provider/network 测试
|
||||
|
||||
---
|
||||
|
||||
# 12. 文档输出
|
||||
|
||||
所有文档使用中文。
|
||||
|
||||
必须新增/更新:
|
||||
|
||||
```text
|
||||
docs/30_设计/STEP-082_iOS_Native_FieldRuntime_Facade与XCFramework发行设计_v1.md
|
||||
docs/50_Step记录/STEP-082_iOS_Native_FieldRuntime_Facade与XCFramework发行基线_v1.md
|
||||
粒子开发记录.md
|
||||
```
|
||||
|
||||
Codex 完成报告必须固定生成到:
|
||||
|
||||
```text
|
||||
docs/报告/STEP-082_iOS_Native_FieldRuntime_Facade与XCFramework发行基线_Codex完成报告_v1.md
|
||||
```
|
||||
|
||||
若同名报告已存在,递增 v2/v3,不覆盖旧报告。
|
||||
|
||||
完成报告至少写明:
|
||||
|
||||
- 起始 HEAD
|
||||
- 结束 HEAD
|
||||
- 修改文件
|
||||
- 新 public API
|
||||
- 生命周期模型
|
||||
- V4 API 覆盖
|
||||
- public/private header 检查
|
||||
- XCFramework build 结果
|
||||
- artifact/hash(若有)
|
||||
- 未完成/阻塞
|
||||
- Git commit hash
|
||||
- commit message
|
||||
- push result
|
||||
- remote branch
|
||||
- Final HEAD
|
||||
- 无关 dirty 未触碰确认
|
||||
|
||||
---
|
||||
|
||||
# 13. Git 边界
|
||||
|
||||
只提交本 STEP 相关修改。
|
||||
|
||||
禁止:
|
||||
|
||||
```text
|
||||
git add -A
|
||||
git add .
|
||||
git reset --hard
|
||||
git checkout -- <用户无关文件>
|
||||
git clean
|
||||
```
|
||||
|
||||
必须显式按文件/目录暂存。
|
||||
|
||||
提交前再次检查:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
git diff --check
|
||||
git diff --cached --stat
|
||||
git diff --cached
|
||||
```
|
||||
|
||||
建议实现提交 message:
|
||||
|
||||
```text
|
||||
feat: add ios particle field runtime facade
|
||||
```
|
||||
|
||||
若文档收尾需要第二个 scoped commit,可使用:
|
||||
|
||||
```text
|
||||
docs: record STEP-082 ios runtime distribution baseline
|
||||
```
|
||||
|
||||
提交后:
|
||||
|
||||
```bash
|
||||
git push origin particle/main
|
||||
```
|
||||
|
||||
若 push 失败,不得宣称成功。
|
||||
|
||||
---
|
||||
|
||||
# 14. STEP-082 关闭门槛
|
||||
|
||||
只有同时满足以下条件,才可判定本 Step `CLOSED / PASS`:
|
||||
|
||||
- [ ] public iOS FieldRuntime facade 已实现;
|
||||
- [ ] MapView-owned lifecycle 已实现;
|
||||
- [ ] V4 submit/binding/forecast/clear 已接通现有 Core;
|
||||
- [ ] public API 无 Core/STL 类型泄漏;
|
||||
- [ ] style reload / source missing fail-closed;
|
||||
- [ ] `MLNMapView_Private.h` 仍为 private;
|
||||
- [ ] XCFramework public header 接线完成;
|
||||
- [ ] macOS 环境下 XCFramework/arm64 compile 已通过;
|
||||
- [ ] 若产出 zip,SHA-256 已记录;
|
||||
- [ ] distribution-ready 模板/manifest 已建立;
|
||||
- [ ] 没有修改 RN/Android/NavSea;
|
||||
- [ ] 没有重新验证 STEP-081;
|
||||
- [ ] 3 处用户无关 dirty 未触碰;
|
||||
- [ ] `git diff --check` PASS;
|
||||
- [ ] 中文 Step 记录与完成报告已更新;
|
||||
- [ ] scoped commit 已完成;
|
||||
- [ ] push `origin/particle/main` 成功;
|
||||
- [ ] Final HEAD 已记录。
|
||||
|
||||
如果 macOS/XCFramework 构建条件不可用:
|
||||
|
||||
```text
|
||||
IMPLEMENTED / WAITING_MAC_XCFRAMEWORK_VALIDATION
|
||||
```
|
||||
|
||||
不得写 `CLOSED / PASS`。
|
||||
|
||||
---
|
||||
|
||||
# 15. 下一 Step 边界
|
||||
|
||||
STEP-082 通过后,下一任务才是:
|
||||
|
||||
```text
|
||||
STEP-083
|
||||
iOS RN TurboModule + Fabric reactTag/MLNMapView Bridge + Custom MapLibre SwiftPM Consumer
|
||||
```
|
||||
|
||||
STEP-083 预计负责:
|
||||
|
||||
```text
|
||||
packages/maplibre-particle-rn/ios
|
||||
Podspec / codegen
|
||||
React View Registry
|
||||
contentView -> MLNMapView
|
||||
本地 QBIN file:// -> NSData
|
||||
调用 STEP-082 public facade
|
||||
$MLRN_SPM_SPEC 指向自定义 MapLibre binary distribution
|
||||
RN Test Host iOS
|
||||
V4 T0 / MID / T1 / Clear
|
||||
```
|
||||
|
||||
本 STEP 不提前进入该范围。
|
||||
@@ -0,0 +1,609 @@
|
||||
# STEP-083 iOS RN 统一 FieldRuntime / TurboModule / Fabric 与 XCFramework 消费 Codex 任务 v1
|
||||
|
||||
## 0. 任务定位
|
||||
|
||||
本任务是 STEP-082 之后的一个完整实现批次,不再拆成多个很小的 Step。
|
||||
|
||||
目标是让当前已经在 Android 使用的同一套 React Native / JS FieldRuntime 公共 API,在 iOS 上获得真实 native 实现,并通过 React Native Fabric 的 reactTag 找到当前 MLRN MapView,最终调用 STEP-082 已冻结并验证的 iOS Native FieldRuntime / MapLibre Core。
|
||||
|
||||
完成后应形成:
|
||||
|
||||
JS/TS FieldRuntime
|
||||
→ 同一 NativeWeatherRuntimeBridge TurboModule ABI
|
||||
→ iOS ObjC++ TurboModule
|
||||
→ 当前 Fabric / MLRN MapView
|
||||
→ public MLNMapView / MLNParticleFieldRuntime
|
||||
→ Shared Core / Renderer / Metal
|
||||
|
||||
不得建立 iOS 第二套 JS API、第二套 Map registry、第二套 QBIN/Field contract。
|
||||
|
||||
---
|
||||
|
||||
## 1. 正式跨项目依据
|
||||
|
||||
开始前必须以以下正式约束为准,不以聊天记忆替代:
|
||||
|
||||
- Integration repo HEAD:`39ad8415fbdb76c1822460bb09f3a3f35fe141e0`
|
||||
- Contract:`C-001`
|
||||
- `contracts/PARTICLE_NAVSEA_BOUNDARY.md`
|
||||
- `rfc/RFC-001_RN公共API与接入基线.md`
|
||||
- `coordination/TEAM_MODEL.md`
|
||||
- `WORKFLOW.md`
|
||||
|
||||
RFC-001 已接受的关键事实:
|
||||
|
||||
1. 当前公共 RN 包为 `@navsea/maplibre-particle-rn@13.4.1-particle.5`。
|
||||
2. Map / Camera 继续由 `@maplibre/maplibre-react-native@11.3.6` 提供,Particle 包不得转导出或复制 MapView。
|
||||
3. Particle 是纯 Field Renderer 控制面,不负责 provider、HTTP、缓存、Route/ETA 或 NavSea 业务。
|
||||
4. Android 当前已有 RN bridge + Native + QBIN V4;iOS RN 在本任务前未实现。
|
||||
5. Android / iOS 必须共享同一 Shared Core 与同一公共 RN 语义;不得平台分叉。
|
||||
|
||||
Integration 的 `PARTICLE_IOS_STATUS.md` / compatibility 在本任务创建时仍保留 STEP-082 验证前的旧状态,但 Particle 仓库实际 STEP-082 已于当前 HEAD 正式 CLOSED / PASS。不得因为 Integration 状态文件滞后而重新打开 STEP-082。
|
||||
|
||||
---
|
||||
|
||||
## 2. 起始 Git 基线
|
||||
|
||||
仓库:
|
||||
|
||||
`/Users/tei/dev/MapLibreParticleLab`
|
||||
|
||||
分支:
|
||||
|
||||
`particle/main`
|
||||
|
||||
起始 HEAD / origin:
|
||||
|
||||
`44af5e68b8569d90c26e37c2a5ae5493a5b485eb`
|
||||
|
||||
STEP-082 关键提交:
|
||||
|
||||
- implementation blocker close:
|
||||
`136ed726cf749c24b3673c046581a38ef48be390`
|
||||
- STEP-082 final closeout:
|
||||
`44af5e68b8569d90c26e37c2a5ae5493a5b485eb`
|
||||
|
||||
STEP-082 已验证发行产物:
|
||||
|
||||
- `bazel-bin/platform/ios/MapLibre.dynamic.xcframework.zip`
|
||||
- distributionVersion:`6.28.0-particle.1`
|
||||
- module:`MapLibre`
|
||||
- size:`24639200` bytes
|
||||
- SHA-256:
|
||||
`64faa174c5c0dc0a2da3e289f0beaf2dedf7298665e3345f39836729b668e239`
|
||||
- device:arm64
|
||||
- simulator:arm64 + x86_64
|
||||
- public `MLNParticleFieldRuntime.h`:两个 slice 均存在
|
||||
- `Mapbox.h`:两个 slice 均导入该 header
|
||||
- `MLNMapView_Private.h`:public export count = 0
|
||||
- ObjC class symbols:device/simulator 均存在
|
||||
|
||||
必须先阅读:
|
||||
|
||||
- `docs/50_Step记录/STEP-082_iOS_Native_FieldRuntime_Facade与XCFramework发行基线_v1.md`
|
||||
- `docs/报告/STEP-082_iOS_Native_FieldRuntime_Facade与XCFramework发行基线_Codex完成报告_v2.md`
|
||||
- `release/maplibre-particle/ios/release-manifest.json`
|
||||
- `platform/ios/src/MLNParticleFieldRuntime.h`
|
||||
- `platform/ios/src/MLNParticleFieldRuntime.mm`
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前 RN 公共 API 是冻结基线
|
||||
|
||||
以下文件是本任务必须兼容的公共面:
|
||||
|
||||
- `packages/maplibre-particle-rn/src/NativeWeatherRuntimeBridge.ts`
|
||||
- `packages/maplibre-particle-rn/src/FieldRuntime.ts`
|
||||
- `packages/maplibre-particle-rn/src/types.ts`
|
||||
- `packages/maplibre-particle-rn/src/EncodedFieldGuards.ts`
|
||||
|
||||
当前 Android reference implementation:
|
||||
|
||||
- `packages/maplibre-particle-rn/android/src/main/java/org/navsea/maplibre/particle/weather/WeatherRuntimeBridgeModule.kt`
|
||||
- `WeatherRuntimeBridgePackage.kt`
|
||||
- 当前 JNI / WeatherRuntime Android 实现与 guard/error 语义
|
||||
|
||||
### 3.1 硬性一致性要求
|
||||
|
||||
本任务原则上不得改变:
|
||||
|
||||
- TurboModule 名称:`WeatherRuntimeBridge`
|
||||
- `NativeWeatherRuntimeBridge.ts` 方法名
|
||||
- 参数顺序与 primitive ABI
|
||||
- TypeScript public `FieldRuntime` 方法名
|
||||
- `types.ts` public union / enum 字符串
|
||||
- V4 submit result 字符串
|
||||
- V3 submit result 字符串
|
||||
- `CommandResult = ACCEPTED | REJECTED`
|
||||
- JS 层 encoded guard 语义
|
||||
- V4 contract JSON field names
|
||||
- inactive selector `(0,0)` ABI
|
||||
- `reactTag` 作为 MapView 入口的调用方式
|
||||
|
||||
不得让 NavSea / Test Host 写:
|
||||
|
||||
`Platform.OS === "ios" ? iOS专用API : AndroidAPI`
|
||||
|
||||
也不得新增第二套 iOS JS module。
|
||||
|
||||
### 3.2 JS 文案允许修正
|
||||
|
||||
`FieldRuntime.ts` 当前 `nativeBridge()` 的错误文本仍写死 “requires the Android WeatherRuntimeBridge native module”。
|
||||
|
||||
iOS 模块完成后必须改成平台中性的错误文本,但不得改变 API 类型或成功路径。
|
||||
|
||||
---
|
||||
|
||||
## 4. 第一阶段:真实代码调查,禁止凭猜测实现
|
||||
|
||||
在写 iOS bridge 前必须完成一次短而完整的只读调查,并在 v1 完成报告中记录结果。
|
||||
|
||||
### 4.1 调查当前 MLRN 11.3.6
|
||||
|
||||
必须取得并检查 `@maplibre/maplibre-react-native@11.3.6` 的真实 iOS source / podspec / codegen 配置。
|
||||
|
||||
可通过当前 Test Host 安装依赖、npm pack 或其他不会改变产品版本锁的方式取得源码。
|
||||
|
||||
必须确认:
|
||||
|
||||
1. Fabric 下 MapView ComponentView 的实际 ObjC/ObjC++ 类名。
|
||||
2. `reactTag` 在 RN 0.85.2 / Fabric 中如何从当前 surface/UIManager 安全 resolve ComponentView。
|
||||
3. MLRN ComponentView 内实际持有/暴露的 content view 类型。
|
||||
4. 如何从该 content view 获得真正的 public `MLNMapView`。
|
||||
5. 哪些接口是 MLRN public/稳定 seam,哪些是私有实现。
|
||||
6. MLRN 11.3.6 的 MapLibre iOS binary dependency 方式。
|
||||
7. 当前版本是否支持 `$MLRN_SPM_SPEC` 或等价官方/仓库已有 override;必须以真实 podspec/source 为准,不得凭记忆发明变量。
|
||||
|
||||
### 4.2 调查 RN 0.85.2 TurboModule iOS codegen
|
||||
|
||||
必须检查当前 RN 0.85.2 codegen 生成的 iOS Spec / base class pattern,确认:
|
||||
|
||||
- ObjC++ module class声明/继承方式;
|
||||
- Promise resolve/reject ABI;
|
||||
- module provider / codegen registration;
|
||||
- 新架构 Fabric / TurboModule 的实际编译入口;
|
||||
- package podspec 需要包含哪些 generated files / dependency。
|
||||
|
||||
### 4.3 做一张 parity matrix
|
||||
|
||||
在完成报告中列出当前 `NativeWeatherRuntimeBridge.ts` 每一个方法:
|
||||
|
||||
- Android 当前实现
|
||||
- iOS 可映射到哪个 public Native/Core seam
|
||||
- 是否需要扩展 `MLNParticleFieldRuntime`
|
||||
- 最终 parity 状态
|
||||
|
||||
不得只实现 Wind V4 后声称“接口一致”。
|
||||
|
||||
---
|
||||
|
||||
## 5. 第二阶段:iOS TurboModule 与 Fabric reactTag → MLNMapView
|
||||
|
||||
### 5.1 新增 iOS package 实现
|
||||
|
||||
在 `packages/maplibre-particle-rn/` 中增加正式 iOS native implementation,并补齐 RN package iOS autolinking 所需文件。
|
||||
|
||||
预期包括但不限于:
|
||||
|
||||
- iOS ObjC++ TurboModule implementation
|
||||
- 必要的 package podspec / codegen 接线
|
||||
- `package.json files` 纳入 iOS / podspec
|
||||
- `react-native.config.js` 正确支持 iOS(若当前 RN autolinking 实际需要)
|
||||
- README / CHANGELOG 更新
|
||||
|
||||
实际文件名必须遵循当前 RN 0.85.2 codegen 生成基类和模块注册要求,不得照旧版 RN 模板硬套。
|
||||
|
||||
### 5.2 reactTag 规则
|
||||
|
||||
每一个需要 Map 的 native command 都必须:
|
||||
|
||||
1. 验证 reactTag 为正整数;
|
||||
2. 在 Main/UI thread 执行 view lookup;
|
||||
3. 从当前 Fabric surface/UIManager 重新 resolve reactTag;
|
||||
4. 确认它确实是当前 MLRN MapView ComponentView/content view;
|
||||
5. 获得当前真实 `MLNMapView`;
|
||||
6. 再调用 MapView-owned Particle runtime。
|
||||
|
||||
禁止:
|
||||
|
||||
- 缓存 reactTag → map 指针;
|
||||
- 建立全局/静态 Map registry;
|
||||
- 缓存跨 style / surface 生命周期的 `MLNMapView *`;
|
||||
- 通过 private Core 指针绕过 public MLN facade;
|
||||
- 用 Test App private seam 作为 RN 正式 API。
|
||||
|
||||
### 5.3 Map 生命周期
|
||||
|
||||
`attach` 必须对应 Android 现有语义:
|
||||
|
||||
- 当前 Map 存在;
|
||||
- style ready;
|
||||
- 配置要求的 `raster-array` source / `weather-color` / `raster-particle` 已存在并类型正确;
|
||||
- attach/re-attach 的 replace/dispose 语义稳定;
|
||||
- runtime 由 MapView 持有;
|
||||
- bridge 不持有第二份 runtime owner。
|
||||
|
||||
`detach`:
|
||||
|
||||
- 对当前 MapView 的 runtime 执行 dispose;
|
||||
- 不销毁 MapView;
|
||||
- 不缓存 stale runtime。
|
||||
|
||||
后续每条 command 都必须针对当前 MapView/current runtime执行,style reload 后按 Native facade fail-closed。
|
||||
|
||||
---
|
||||
|
||||
## 6. 第三阶段:Android ↔ iOS 公共行为完整对齐
|
||||
|
||||
### 6.1 V4 必须完整实现
|
||||
|
||||
至少包括:
|
||||
|
||||
- `submitEncodedField`
|
||||
- `setEncodedColorBinding`
|
||||
- `setEncodedParticleBinding`
|
||||
- `setEncodedForecastTime`
|
||||
- `setEncodedForecastPlaybackState`
|
||||
- `setEncodedForecastPlaybackSpeed`
|
||||
- `clearEncodedField`
|
||||
- `clearAllEncodedFields`
|
||||
|
||||
QBIN 必须从本地绝对 path / `file://` URI读取为 bytes,再交给 `MLNParticleFieldRuntime`;JS 不解码 QBIN。
|
||||
|
||||
文件 guard 必须与 Android 当前 contract 等价:
|
||||
|
||||
- local file only;
|
||||
- readable regular file;
|
||||
- 1..64 MiB;
|
||||
- read 时防文件长度变化;
|
||||
- contract JSON 上限与 encoded ID UTF-8 byte guard保持一致;
|
||||
- 不使用 Base64 / JS number[]。
|
||||
|
||||
### 6.2 V3 与当前公共控制面也必须审计并实现 parity
|
||||
|
||||
当前 RN public Spec 不只有 V4。
|
||||
|
||||
必须逐项处理:
|
||||
|
||||
- `submitWindField`
|
||||
- `submitField`
|
||||
- `clearWindFields`
|
||||
- `clearFields`
|
||||
- `getState`
|
||||
- `setFieldValueSource`
|
||||
- `setParticleAdvectionScale`
|
||||
- `setWeatherEnabled`
|
||||
- `setWeatherColorVisible`
|
||||
- `setParticleVisible`
|
||||
- `setTargetFps`
|
||||
- `setForecastTime`
|
||||
- `setPlaybackState`
|
||||
- `setPlaybackSpeed`
|
||||
- `setWeatherColorStops`
|
||||
- `setParticleColor`
|
||||
- `setTrailColor`
|
||||
- `setParticleCount`
|
||||
- `setParticleLineWidth`
|
||||
- `setParticleLength`
|
||||
- `setParticleOpacity`
|
||||
- `setTrailOpacity`
|
||||
- `setTrailFade`
|
||||
- `setWeatherColorOpacity`
|
||||
|
||||
原因:RFC-001 当前 NavSea 事实仍包含 Wind V4 + Wave/Rain/Tide V3;不能靠 iOS-specific JS branch规避。
|
||||
|
||||
### 6.3 如果 082 facade 覆盖不足
|
||||
|
||||
当前 `MLNParticleFieldRuntime` 主要冻结 V4 submit/binding/forecast/clear。
|
||||
|
||||
如 parity audit 证明 V3 / state / visual control 在 iOS 已有 Shared Core 能力但没有 public ObjC facade:
|
||||
|
||||
- 允许在本 STEP-083 内最小扩展 `MLNParticleFieldRuntime.h/.mm`;
|
||||
- 仅做现有 Core 能力的薄 public facade;
|
||||
- Android API/Shared Core contract不改变;
|
||||
- 不把 provider、NavSea、RN 类型写进 Core;
|
||||
- 仍保持每次 command current-style lookup 与 fail-closed;
|
||||
- public header 不泄漏 C++/STL/private Core 类型。
|
||||
|
||||
如某个现有 RN public method 在 iOS 根本没有对应 Shared Core 语义,且实现它必须新增/改变跨平台公共协议:
|
||||
|
||||
**停止扩大该部分并在报告中明确 BLOCKED。不得:**
|
||||
|
||||
- 返回假的 ACCEPTED;
|
||||
- 写 no-op stub;
|
||||
- iOS 永久返回 REJECTED 却仍宣称 parity;
|
||||
- 在 JS 用 Platform.OS 绕过;
|
||||
- 私自创建 iOS-only public protocol。
|
||||
|
||||
需要新的跨平台语义时必须回到 Integration RFC/Contract。
|
||||
|
||||
---
|
||||
|
||||
## 7. Error / Promise 语义对齐
|
||||
|
||||
以 Android `WeatherRuntimeBridgeModule.kt` 为 reference。
|
||||
|
||||
iOS 必须尽量保持相同 reject code 和触发条件:
|
||||
|
||||
- `INVALID_ARGUMENT`
|
||||
- `MAP_VIEW_NOT_FOUND`
|
||||
- `MAP_NOT_READY`
|
||||
- `STYLE_NOT_READY`
|
||||
- `RUNTIME_DISPOSED`
|
||||
- `SOURCE_MISSING`
|
||||
- `NATIVE_REJECTED`
|
||||
- 以及当前 attach / weather-object readiness 已有的稳定错误码
|
||||
|
||||
Core V4 submit rejection 仍作为 **resolved `EncodedFieldSubmitResult`** 返回,不得错误地全部转成 Promise reject。
|
||||
|
||||
Facade/lifecycle/file/ABI 错误使用 Promise reject。
|
||||
|
||||
同类输入在 Android 与 iOS 的成功/reject类别不得无理由分叉。
|
||||
|
||||
---
|
||||
|
||||
## 8. XCFramework / MapLibre 单实例消费
|
||||
|
||||
这是本任务的关键发行门槛之一。
|
||||
|
||||
### 8.1 只能有一个 MapLibre.framework
|
||||
|
||||
MLRN 与 Particle iOS bridge 必须消费 **同一个** STEP-082 custom MapLibre module/product。
|
||||
|
||||
禁止:
|
||||
|
||||
- MLRN 拉官方 MapLibre,而 Particle bridge再静态/动态链接另一份 custom MapLibre;
|
||||
- App 中出现两个 `MapLibre.framework`;
|
||||
- 两份 ObjC class runtime;
|
||||
- 私自 rename module 绕开 duplicate symbol。
|
||||
|
||||
### 8.2 使用真实 MLRN 11.3.6 支持的 override
|
||||
|
||||
先调查 MLRN 11.3.6 podspec / SPM consumer。
|
||||
|
||||
如果真实支持 `MLRN_SPM_SPEC`,按其实际格式接 STEP-082 custom distribution。
|
||||
|
||||
如果实际版本使用其他机制,按真实机制实现,并在报告记录。
|
||||
|
||||
不能凭聊天记忆写一个未被 MLRN 读取的环境变量。
|
||||
|
||||
### 8.3 本地验证允许使用 STEP-082 artifact
|
||||
|
||||
当前 artifact 尚未远端发布,manifest URL 为 `LOCAL_BUILD`。
|
||||
|
||||
本任务测试阶段允许用本地 `MapLibre.dynamic.xcframework.zip` / 解包后的 XCFramework 做可重复的 local consumer 验证。
|
||||
|
||||
不得伪造远端 URL。
|
||||
|
||||
远端正式发布可以后续单独处理;但本 STEP 必须证明 iOS RN/Test Host 能消费同一 custom MapLibre binary。
|
||||
|
||||
---
|
||||
|
||||
## 9. RN package 版本规则
|
||||
|
||||
本任务首先完成实现与验证,不为了“有 iOS 文件”立即发布新 npm。
|
||||
|
||||
当前 `13.4.1-particle.5` 与 Android Native `.5` 是 RFC-001 正式发布基线。
|
||||
|
||||
除非任务过程中发现已有正式 release 流程明确要求同步 bump,否则:
|
||||
|
||||
- 不擅自发布 `.6`;
|
||||
- 不让 RN package version 与 Android Native strict lock失配;
|
||||
- 可以在工作树/Test Host 用 source package验证 iOS。
|
||||
|
||||
最终若需要新跨平台发行版本,在 STEP-083 验证通过后单独记录 release decision。
|
||||
|
||||
---
|
||||
|
||||
## 10. Test Host:同一 JS 代码验证 Android/iOS
|
||||
|
||||
优先复用:
|
||||
|
||||
`integration/react-native/MapLibreParticleRNTestHost/`
|
||||
|
||||
当前它没有生成 iOS 工程。
|
||||
|
||||
本任务应让它具备 iOS validation 能力,但不要复制第二份业务 App。
|
||||
|
||||
### 10.1 JS 层不得另写 iOS 页面
|
||||
|
||||
同一个 `App.tsx` / `FieldRuntime` 调用路径用于 Android 与 iOS。
|
||||
|
||||
允许平台专属的 build/config/native files,不允许平台专属业务调用。
|
||||
|
||||
### 10.2 iOS 最小验收内容
|
||||
|
||||
至少准备并尽可能实际执行:
|
||||
|
||||
1. dependency install;
|
||||
2. RN codegen;
|
||||
3. TypeScript typecheck;
|
||||
4. iOS project/prebuild/pods 或实际当前工具链对应生成流程;
|
||||
5. simulator 或 device Debug build;
|
||||
6. module link/launch smoke;
|
||||
7. Map reactTag attach;
|
||||
8. V4 Wind Golden T0/T1 submit;
|
||||
9. color/particle binding;
|
||||
10. forecast time midpoint;
|
||||
11. PLAY/PAUSE/speed;
|
||||
12. clear selected / clear all;
|
||||
13. detach/re-attach;
|
||||
14. style/source not ready fail-closed;
|
||||
15. 验证没有 duplicate MapLibre framework/class。
|
||||
|
||||
如真实运行需要用户手工设备操作,Codex必须把代码、测试入口和明确验收步骤准备完整;不得把未运行项目写 PASS。
|
||||
|
||||
---
|
||||
|
||||
## 11. 允许修改范围
|
||||
|
||||
主要允许:
|
||||
|
||||
- `packages/maplibre-particle-rn/src/`(仅为平台中性错误文本/必要 codegen 修正;不得分叉 public API)
|
||||
- `packages/maplibre-particle-rn/ios/` 新增
|
||||
- `packages/maplibre-particle-rn/*.podspec` / package/autolinking配置
|
||||
- `packages/maplibre-particle-rn/package.json`(仅 package files/codegen/build必要项;默认不 bump version)
|
||||
- `packages/maplibre-particle-rn/react-native.config.js`
|
||||
- `packages/maplibre-particle-rn/README.md`
|
||||
- `packages/maplibre-particle-rn/CHANGELOG.md`
|
||||
- `integration/react-native/MapLibreParticleRNTestHost/` 的 iOS build/config/test入口
|
||||
- 如 parity 必需:`platform/ios/src/MLNParticleFieldRuntime.h/.mm` 的薄 facade 扩展
|
||||
- 必要 iOS Bazel/public header build graph,前提是不改变 Shared Core contract
|
||||
- STEP-083 设计/记录/报告文件
|
||||
|
||||
不得修改:
|
||||
|
||||
- NavSea 仓库
|
||||
- Android public RN API签名
|
||||
- Android Kotlin/JNI行为,除非只是无语义的测试/编译兼容且报告说明;默认不改
|
||||
- QBIN/FieldProfile/BindingProjection contract
|
||||
- Renderer/Metal particle physics
|
||||
- provider/network/cache/Route/ETA
|
||||
- STEP-081/082 已关闭事实
|
||||
- unrelated submodule / user dirty
|
||||
|
||||
---
|
||||
|
||||
## 12. 必须保护的现有 dirty / untracked
|
||||
|
||||
以下必须原样保护,不得暂存或提交:
|
||||
|
||||
- `docs/doxygen/doxygen-awesome-css`
|
||||
- `platform/ios/app/MBXAppDelegate.m`
|
||||
- `platform/windows/vendor/vcpkg`
|
||||
|
||||
现有未跟踪任务书也不得删除、改写、提交:
|
||||
|
||||
- `docs/40_任务/STEP-082_XCFramework_Bazel_Codegen阻塞收尾_Codex任务_v1.md`
|
||||
- `docs/40_任务/STEP-082_iOS_Native_FieldRuntime_Facade与XCFramework发行基线_Codex任务_v1.md`
|
||||
|
||||
本 STEP-083 任务书自身默认也不要求提交。
|
||||
|
||||
禁止:
|
||||
|
||||
- `git add -A`
|
||||
- `git add .`
|
||||
- `git reset --hard`
|
||||
- `git clean`
|
||||
|
||||
只允许 scoped stage。
|
||||
|
||||
---
|
||||
|
||||
## 13. 验证要求
|
||||
|
||||
### 13.1 静态/代码
|
||||
|
||||
至少:
|
||||
|
||||
- package TypeScript typecheck
|
||||
- RN Codegen 成功生成 iOS/Android Spec,且 Android ABI未意外变化
|
||||
- iOS ObjC++ compile / Xcode build能证明 module与 generated spec匹配
|
||||
- podspec lint/解析或等价 CocoaPods 集成检查
|
||||
- `git diff --check`
|
||||
- package tarball 内容检查:必须包含 iOS native files / podspec,不丢现有 Android/src
|
||||
|
||||
编译输出遵循全局 `$build-output-throttle`。
|
||||
|
||||
### 13.2 公共 API parity
|
||||
|
||||
完成报告必须逐项证明:
|
||||
|
||||
- public TS API无 iOS新增/删减
|
||||
- Native Spec无平台分叉
|
||||
- Android既有 API仍可 codegen
|
||||
- iOS module实现同一 Spec
|
||||
- 关键 error code/submit result映射一致
|
||||
- Test Host JS 不需要 Platform.OS分支
|
||||
|
||||
### 13.3 MapLibre binary identity
|
||||
|
||||
必须检查最终 iOS build graph:
|
||||
|
||||
- 只解析一个 MapLibre module/product;
|
||||
- 实际链接的是 STEP-082 custom binary;
|
||||
- `MLNParticleFieldRuntime` symbol可见;
|
||||
- 无 duplicate framework/class warning;
|
||||
- MLRN 与 Particle bridge看到同一 ObjC runtime class。
|
||||
|
||||
---
|
||||
|
||||
## 14. 执行策略
|
||||
|
||||
这是复杂任务,可以在一个 STEP 内分“调查 → 实现 → 构建/验证 → 收尾”四个阶段,但不要人为拆成 STEP-083A/B/C/D。
|
||||
|
||||
遇到小型编译/Pod/codegen/headers问题,Codex继续最小修复并验证。
|
||||
|
||||
只有遇到以下情况才停止:
|
||||
|
||||
1. 需要改变当前 RN public API;
|
||||
2. 需要改变 C-001 Shared Core contract;
|
||||
3. 需要新增跨平台 semantic/error/lifecycle协议;
|
||||
4. MLRN 11.3.6 无法在不双链接 MapLibre 的情况下消费 custom binary,需要新的架构决定。
|
||||
|
||||
停止时写明确 blocker,不得用 stub/Platform.OS/第二 registry绕过。
|
||||
|
||||
---
|
||||
|
||||
## 15. Git 与文档输出
|
||||
|
||||
实质进展后更新:
|
||||
|
||||
- `粒子开发记录.md`
|
||||
- 新建:
|
||||
`docs/50_Step记录/STEP-083_iOS_RN统一FieldRuntime_TurboModule_Fabric与XCFramework消费_v1.md`
|
||||
|
||||
Codex完成报告固定生成:
|
||||
|
||||
`docs/报告/STEP-083_iOS_RN统一FieldRuntime_TurboModule_Fabric与XCFramework消费_Codex完成报告_v1.md`
|
||||
|
||||
报告至少写:
|
||||
|
||||
- 调查结论
|
||||
- parity matrix
|
||||
- MLRN 11.3.6 Fabric lookup真实路径
|
||||
- RN 0.85.2 TurboModule registration真实路径
|
||||
- package/podspec改动
|
||||
- Native facade扩展(如有)
|
||||
- custom MapLibre single-binary消费方式
|
||||
- typecheck/codegen/build/test结果
|
||||
- 未实际运行项
|
||||
- user dirty保护情况
|
||||
- commit hash / message
|
||||
- push result
|
||||
- remote branch
|
||||
- Final HEAD
|
||||
|
||||
在合适检查点做 scoped commit;完成范围才提交,不要带入 unrelated dirty。
|
||||
|
||||
最终 push:
|
||||
|
||||
`origin/particle/main`
|
||||
|
||||
---
|
||||
|
||||
## 16. CLOSED / PASS 门槛
|
||||
|
||||
只有全部成立才能标记 STEP-083 `CLOSED / PASS`:
|
||||
|
||||
- iOS native module 实现现有同一 `NativeWeatherRuntimeBridge` Spec;
|
||||
- JS/TS public API 不分叉;
|
||||
- reactTag → 当前 Fabric/MLRN view → public MLNMapView 链真实成立;
|
||||
- bridge 不缓存 Map/Core pointer,不建立第二 registry;
|
||||
- V4 入口完整;
|
||||
- V3 + 当前公共控制面 parity audit 完成,所有方法真实实现或已由正式跨平台决定处理;
|
||||
- iOS package autolinking/codegen可用;
|
||||
- MLRN 与 bridge只消费一个 STEP-082 custom MapLibre;
|
||||
- iOS compile/link成功;
|
||||
- Test Host 至少完成真实 module/link/attach smoke;QBIN/Field功能未实际运行的项目不得伪报 PASS;
|
||||
- `git diff --check` PASS;
|
||||
- 完成报告与 Step记录更新;
|
||||
- scoped commit/push成功;
|
||||
- local HEAD == `origin/particle/main`;
|
||||
- 三处用户 dirty和历史任务书未触碰。
|
||||
|
||||
若代码完整但真实 iOS runtime/device验收尚未完成,应写:
|
||||
|
||||
`IMPLEMENTED / WAITING_IOS_VALIDATION`
|
||||
|
||||
不得提前写 CLOSED/PASS。
|
||||
357
docs/40_任务/STEP-088_静态底图缓存PoC与Renderer观测_Codex任务_v1.md
Normal file
357
docs/40_任务/STEP-088_静态底图缓存PoC与Renderer观测_Codex任务_v1.md
Normal file
@@ -0,0 +1,357 @@
|
||||
# STEP-088 静态底图缓存 PoC 与 Renderer 可观测性 Codex 任务 v1
|
||||
|
||||
## 1. 任务目标
|
||||
基于最新 origin/particle/main 实际代码,实现第一阶段 Retained Static Underlay PoC。
|
||||
|
||||
核心目标:
|
||||
- Camera 固定、Style/Tile 稳定、存在 WeatherColor/Particle 时,把动态层之前连续普通 LayerGroup 前缀 render 到 offscreen texture。
|
||||
- 后续 Particle Tick 继续走当前 onUpdate/createRenderTree,但不再重复 draw 静态前缀,而复用 cached underlay texture。
|
||||
- Particle simulation/render 继续按 cadence 正常。
|
||||
- 增加 Renderer 可观测性。
|
||||
- 本任务只执行 STEP-088,不得执行 STEP-089/090。
|
||||
|
||||
## 2. 执行基线
|
||||
直接在主仓库:
|
||||
/Users/tei/dev/MapLibreParticleLab
|
||||
|
||||
目标主线:
|
||||
origin/particle/main
|
||||
|
||||
执行前:
|
||||
1. git fetch origin particle/main。
|
||||
2. 确认本地 HEAD、origin/particle/main、git status。
|
||||
3. 不 reset/clean,不覆盖用户既有未提交修改。
|
||||
4. 如存在无关本地修改,只修改、stage、commit STEP-088 相关文件。
|
||||
5. 以当前实际代码为准,不得凭旧版本猜接口。
|
||||
|
||||
必须阅读:
|
||||
- docs/40_任务/粒子连续渲染静态底图缓存与动画快路径优化规划_v1.md
|
||||
- 本任务书
|
||||
- docs/20_调查/STEP-086_Particle连续渲染唤醒MapLibre整图成本调查_v1.md
|
||||
- docs/00_项目总则/
|
||||
- docs/00_项目规范/
|
||||
- 根目录 AGENTS.md(如存在)
|
||||
|
||||
## 3. 开工前只读确认
|
||||
必须确认并在完成报告记录:
|
||||
- RenderRasterParticleLayer::needsRepaint()
|
||||
- RenderWeatherColorLayer::needsRepaint()
|
||||
- Renderer::render()
|
||||
- RenderOrchestrator::createRenderTree()
|
||||
- RenderOrchestrator::hasTransitions()
|
||||
- renderer_impl.cpp 的 updateLayers/processChanges
|
||||
- LayerGroup tweaker/upload
|
||||
- visitRenderTargets
|
||||
- main clear
|
||||
- opaque/translucent pass
|
||||
- present
|
||||
- RenderTarget / OffscreenTexture
|
||||
- Heatmap/Hillshade offscreen + texture composite 样板
|
||||
|
||||
涉及文件至少:
|
||||
- src/mbgl/renderer/layers/render_particle_validation_layer.cpp
|
||||
- src/mbgl/renderer/layers/render_weather_color_layer.cpp
|
||||
- src/mbgl/renderer/renderer.cpp
|
||||
- src/mbgl/renderer/render_orchestrator.cpp/.hpp
|
||||
- src/mbgl/renderer/renderer_impl.cpp/.hpp
|
||||
- include/mbgl/renderer/render_target.hpp
|
||||
- src/mbgl/renderer/render_target.cpp
|
||||
- src/mbgl/renderer/layers/render_heatmap_layer.cpp
|
||||
- src/mbgl/renderer/layers/render_hillshade_layer.cpp
|
||||
|
||||
## 4. 修改一:retained dynamic boundary
|
||||
重点修改:
|
||||
- src/mbgl/renderer/render_layer.hpp
|
||||
- src/mbgl/renderer/layers/render_weather_color_layer.hpp
|
||||
- src/mbgl/renderer/layers/render_raster_particle_layer.hpp
|
||||
|
||||
增加仅 Renderer 内部使用的虚函数,语义类似:
|
||||
virtual bool startsRetainedDynamicRange() const { return false; }
|
||||
|
||||
WeatherColor 与 RasterParticle override=true。
|
||||
|
||||
规则:
|
||||
- 两者同时存在取最小实际 layerIndex。
|
||||
- only Particle 使用 Particle index。
|
||||
- 无 candidate 时完全走原 Renderer。
|
||||
- 禁止根据 React layer ID/name 字符串硬编码。
|
||||
- 禁止让 NavSea/Host 传 boundary。
|
||||
- 不新增 public API。
|
||||
|
||||
## 5. 修改二:RenderOrchestrator LayerGroup 分段访问
|
||||
重点修改:
|
||||
- src/mbgl/renderer/render_orchestrator.hpp
|
||||
- src/mbgl/renderer/render_orchestrator.cpp
|
||||
|
||||
必要时小改:
|
||||
- include/mbgl/renderer/layer_group.hpp
|
||||
- src/mbgl/renderer/layer_group.cpp
|
||||
|
||||
需要:
|
||||
- 查询 retained dynamic boundary。
|
||||
- 遍历 boundary 之前 groups。
|
||||
- 遍历 boundary 及之后 groups。
|
||||
- forward/reverse 都支持。
|
||||
- callback 能获得完整原始 LayerGroup 序列 ordinal。
|
||||
|
||||
可采用 before/from visitor 或更通用 indexed/range visitor。
|
||||
|
||||
关键:
|
||||
parameters.currentLayer、depthRangeSize、opaque/translucent ordering 必须与完整遍历等价。
|
||||
不得让 dynamic subset 从 0 重新编号导致 depth/transparency 变化。
|
||||
|
||||
## 6. 修改三:Renderer-owned RetainedUnderlayCache
|
||||
优先新增:
|
||||
- src/mbgl/renderer/retained_underlay_cache.hpp
|
||||
- src/mbgl/renderer/retained_underlay_cache.cpp
|
||||
|
||||
并在 CMakeLists.txt 真实 source list 注册。
|
||||
|
||||
至少维护:
|
||||
- offscreen renderable / texture
|
||||
- viewport size
|
||||
- boundary
|
||||
- valid
|
||||
- buildCount
|
||||
- reuseCount
|
||||
- fallbackCount
|
||||
- last invalidation reason
|
||||
|
||||
不要修改 Heatmap/Hillshade RenderTarget 原有语义。
|
||||
|
||||
STEP-088 至少保证:
|
||||
- resource 创建失败 -> full-render fallback
|
||||
- viewport resize -> invalid
|
||||
- boundary change -> invalid
|
||||
- has3D -> fallback
|
||||
- Debug Overdraw 等明显不兼容模式 -> fallback
|
||||
- context 不可用 -> 不复用
|
||||
- Camera fingerprint 连续相同、style loaded、2D、boundary stable 才允许 reuse
|
||||
|
||||
任何不能证明安全的状态都 full render。
|
||||
|
||||
## 7. 修改四:Renderer::Impl 分离 Static Underlay / Dynamic Range
|
||||
重点:
|
||||
- src/mbgl/renderer/renderer_impl.hpp
|
||||
- src/mbgl/renderer/renderer_impl.cpp
|
||||
|
||||
STEP-088 必须保留:
|
||||
- Renderer::render -> createRenderTree
|
||||
- source update/prepare
|
||||
- layer prepare
|
||||
- updateLayers
|
||||
- LayerGroup tweaker/upload
|
||||
|
||||
只修改真正 draw 阶段。
|
||||
|
||||
目标流程:
|
||||
1. existing independent RenderTargets。
|
||||
2. cache invalid 时,boundary 前 LayerGroups render 到 offscreen 并 mark valid。
|
||||
3. main clear。
|
||||
4. composite cached underlay texture。
|
||||
5. boundary 及之后 opaque/translucent 正常 render。
|
||||
6. legacy items 按安全规则。
|
||||
7. debug。
|
||||
8. present。
|
||||
|
||||
Underlay offscreen 优先透明 clear。
|
||||
main framebuffer 保持原 backgroundColor/depth/stencil 语义。
|
||||
必须确认 background layer optimized-as-color 路径。
|
||||
|
||||
## 8. Legacy RenderItems
|
||||
renderer_impl.cpp translucent pass 仍有 legacy layerRenderItems。
|
||||
|
||||
必须确认是否有 boundary 以下 legacy item。
|
||||
- 无法安全按 layerIndex 切分时不纳入 cache。
|
||||
- 可能破坏 ordering 时 fallback full render。
|
||||
- 不得静默改变顺序。
|
||||
|
||||
## 9. 修改五:cached texture compositor
|
||||
不要正式复用 LocationIndicatorTexturedShader。
|
||||
|
||||
建议新增:
|
||||
RetainedUnderlayCompositeShader
|
||||
|
||||
职责:
|
||||
- viewport/fullscreen quad
|
||||
- sample cached RGBA
|
||||
- 正确 premultiplied alpha
|
||||
- 不做 color ramp
|
||||
- 不做 map transform
|
||||
- 不做 Particle 业务
|
||||
|
||||
按当前真实 shader 注册规则检查/修改至少:
|
||||
- include/mbgl/shaders/shader_source.hpp
|
||||
- src/mbgl/shaders/shader_source.cpp
|
||||
- include/mbgl/shaders/shader_defines.hpp
|
||||
- include/mbgl/shaders/shader_manifest.hpp
|
||||
- include/mbgl/shaders/gl/shader_info.hpp
|
||||
- src/mbgl/shaders/gl/shader_info.cpp
|
||||
- src/mbgl/gl/renderer_backend.cpp
|
||||
- src/mbgl/mtl/renderer_backend.cpp
|
||||
- cmake/opengl.cmake
|
||||
- cmake/metal.cmake
|
||||
- 新增 GL/Metal compositor shader 文件
|
||||
|
||||
如当前 shader codegen 要求生成文件,按真实 codegen 走。
|
||||
Android OpenGL 与 iOS Metal 语义都要完整。
|
||||
|
||||
## 10. Renderer Debug Counters
|
||||
修改:
|
||||
- include/mbgl/particle/renderer_debug_counters.hpp
|
||||
|
||||
至少新增:
|
||||
- FullRendererFrame
|
||||
- RetainedUnderlayBuild
|
||||
- RetainedUnderlayReuse
|
||||
- RetainedUnderlayFallback
|
||||
- RetainedUnderlayInvalidation
|
||||
- RetainedUnderlayStaticGroupDraw
|
||||
- RetainedDynamicGroupDraw
|
||||
|
||||
要求:
|
||||
- NDEBUG 下 no-op
|
||||
- 不增加 Host API
|
||||
- 不每帧刷大量日志
|
||||
|
||||
允许有限首次日志:cache enabled/build/fallback reason。
|
||||
|
||||
## 11. Native 定向测试
|
||||
不新增或运行 Jest。
|
||||
|
||||
至少覆盖:
|
||||
Boundary:
|
||||
- 无 Weather/Particle -> disabled
|
||||
- only Particle
|
||||
- Weather + Particle 不同顺序
|
||||
|
||||
LayerGroup range:
|
||||
- before/from
|
||||
- forward/reverse
|
||||
- original ordinal
|
||||
|
||||
Cache:
|
||||
- first build
|
||||
- reuse
|
||||
- resize invalid
|
||||
- boundary change invalid
|
||||
- unsupported 3D fallback
|
||||
|
||||
Failure:
|
||||
- compositor/resource 创建失败 -> safe fallback/no crash
|
||||
|
||||
测试放当前 renderer test 真实结构,不新造孤立测试框架。
|
||||
|
||||
## 12. Mac 编译与验证
|
||||
|
||||
本任务采用仓库 AGENTS.md 的固定闭环:
|
||||
|
||||
`Codex 写代码 → RDC 执行编译/测试 → 失败则把错误证据交回 Codex 修复 → RDC 再验证`
|
||||
|
||||
Codex 本轮负责:
|
||||
- 完成 Renderer / OpenGL / Metal 相关源码与测试代码实现;
|
||||
- 给出 RDC 需要执行的精确命令;
|
||||
- 不自行启动、等待或轮询长时间编译/测试;
|
||||
- RDC 返回失败日志后,继续分析并修复代码。
|
||||
|
||||
由 RDC / 普通 Terminal 实际执行:
|
||||
- 编译受影响 Renderer C++ target;
|
||||
- 编译 OpenGL compositor shader 相关 target;
|
||||
- 编译 Metal compositor shader 相关 target;
|
||||
- 运行直接相关 C++ 定向测试;
|
||||
- git diff --check。
|
||||
|
||||
RDC 返回结果时:
|
||||
- 成功:返回 exit code、耗时、target 摘要;
|
||||
- 失败:返回首个真实 error、相关 target、必要上下文和完整日志路径;
|
||||
- 不把完整大型 build log 反复送入 Codex。
|
||||
|
||||
明确禁止 Codex 执行:
|
||||
- CMake / Ninja / Bazel 长时间编译;
|
||||
- OpenGL / Metal 长时间构建等待;
|
||||
- Android Gradle build;
|
||||
- AAR/APK build/install;
|
||||
- ADB;
|
||||
- Android App 启动/清数据;
|
||||
- Android 真机/虚拟机;
|
||||
- Jest。
|
||||
|
||||
Android 最终由用户手工验证。
|
||||
|
||||
## 13. PoC 验收证据
|
||||
理想 fixed Camera + Particle ~30 FPS:
|
||||
- RetainedUnderlayBuild ≈ 1
|
||||
- RetainedUnderlayReuse 持续增长
|
||||
- ParticleSimulationAdvance 持续增长
|
||||
- RetainedUnderlayStaticGroupDraw 建 cache 后不随 Particle FPS 增长
|
||||
- RetainedDynamicGroupDraw 持续增长
|
||||
|
||||
如 Mac 无法跑完整 Particle runtime:
|
||||
- 完成 Native 定向/静态验证
|
||||
- 给出用户最少 Android 手工验证项
|
||||
- 最终状态必须:IMPLEMENTED / WAITING FOR ANDROID VALIDATION
|
||||
- 不得写 CLOSED / PASS
|
||||
|
||||
## 14. 本任务明确不做
|
||||
- production 全部 tile/style/placement invalidation
|
||||
- Map::Impl::onUpdate fast path
|
||||
- 跳过 createRenderTree
|
||||
- 跳过 source->update
|
||||
- 跳过 TilePyramid::update
|
||||
- 跳过普通 prepare
|
||||
- 新 Host API
|
||||
- NavSea React 修改
|
||||
- AIS 专项优化
|
||||
- 通用任意 static/dynamic RenderGraph
|
||||
- STEP-089
|
||||
- STEP-090
|
||||
|
||||
## 15. 文档输出
|
||||
必须生成:
|
||||
- docs/50_Step记录/STEP-088_静态底图缓存PoC与Renderer观测_v1.md
|
||||
- docs/报告/STEP-088_静态底图缓存PoC与Renderer观测_Codex完成报告_v1.md
|
||||
|
||||
如同主题已有,递增版本,不覆盖旧报告。
|
||||
|
||||
Step 记录至少写:
|
||||
- 当前问题
|
||||
- 实现结构
|
||||
- boundary
|
||||
- underlay cache flow
|
||||
- PoC 限制
|
||||
- 用户 Android 手工验证项
|
||||
|
||||
完成报告必须写:
|
||||
- 起始 HEAD / 最终 HEAD
|
||||
- 修改文件
|
||||
- boundary 实现位置
|
||||
- LayerGroup 分段实现位置
|
||||
- cache/compositor 实现位置
|
||||
- debug counters
|
||||
- 测试/编译结果
|
||||
- Android 未执行项
|
||||
- fallback
|
||||
- 是否满足进入 STEP-089 条件
|
||||
- final commit hash
|
||||
- commit message
|
||||
- push result
|
||||
- remote branch
|
||||
- Final HEAD
|
||||
|
||||
## 16. Git 收尾
|
||||
完成后:
|
||||
1. 只 stage/commit STEP-088 相关文件。
|
||||
2. 不提交当前主仓库既有无关 dirty/untracked 内容。
|
||||
3. commit message 建议:feat: add retained underlay render cache poc
|
||||
4. push 到 origin/particle/main。
|
||||
5. push 前 fetch;远端前进时安全整合本任务。
|
||||
6. push 失败不得声称完成。
|
||||
7. 最终确认任务提交已在 origin/particle/main。
|
||||
|
||||
## 17. 最终响应
|
||||
直接逐项报告:
|
||||
- 已完成
|
||||
- 未完成
|
||||
- 无法验证
|
||||
|
||||
不要再输出实施计划。
|
||||
144
docs/40_任务/STEP-089_静态渲染Revision与Invalidation基础层_Codex任务_v1.md
Normal file
144
docs/40_任务/STEP-089_静态渲染Revision与Invalidation基础层_Codex任务_v1.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# STEP-089 静态渲染 Revision 与 Invalidation 基础层 Codex 任务 v1
|
||||
|
||||
## 目标
|
||||
|
||||
为 STEP-090 Static CPU Fast Path 建立可靠、可验证的静态状态失效判据。
|
||||
|
||||
STEP-089 不负责跳过 static source/tile/render-tree/prepare,也不负责降低 CPU;只建立“静态前缀当前是否仍可安全复用”的 revision / invalidation 基础,确保后续 fast path 不会复用过期静态状态。
|
||||
|
||||
当前基线:
|
||||
|
||||
- branch: `particle/main`
|
||||
- baseline HEAD: `07a17b4c701cd22c8e8b516ad84f4044f291f9fa`
|
||||
- STEP-088 retained underlay 已完成:
|
||||
- GPU static draw cache
|
||||
- map-scoped runtime Cache ON/OFF
|
||||
- Release 最小 diagnostics
|
||||
- Android/iOS/RN runtime API
|
||||
- Android NavSea 真实 PBF 性能场景
|
||||
- 实测证明主要剩余成本是 Particle continuous repaint 下 PBF/vector/static renderer pipeline 被重复 processing。
|
||||
- STEP-090 才负责 CPU fast path。
|
||||
|
||||
## 必做项
|
||||
|
||||
- [ ] 引入单调递增的 static/retained revision 机制,能表达“静态前缀相关输入已变化”。
|
||||
- [ ] retained cache build 时记录所依据的 revision。
|
||||
- [ ] reuse 前必须确认当前 revision 与 build revision 一致。
|
||||
- [ ] 下列变化必须使 revision 前进或明确 invalidate:
|
||||
- [ ] camera / transform
|
||||
- [ ] viewport / size / pixel ratio
|
||||
- [ ] style reload / style mutation
|
||||
- [ ] layer add/remove/reorder/property mutation
|
||||
- [ ] source add/remove/change
|
||||
- [ ] tile / drawable 内容更新
|
||||
- [ ] image / sprite / glyph 变化
|
||||
- [ ] placement / symbol fade 状态变化
|
||||
- [ ] render context / surface / backend resource reset
|
||||
- [ ] debug render options
|
||||
- [ ] terrain / 3D / custom layer 等当前不能安全证明静态的场景
|
||||
- [ ] symbol fade / placement transition 活跃时不得复用 stale static cache。
|
||||
- [ ] unsupported / uncertain 情况继续 fail closed 到 full renderer。
|
||||
- [ ] Cache OFF/ON runtime 行为保持不变。
|
||||
- [ ] Release diagnostics 保持低成本;允许新增只读 revision 字段,但禁止 per-frame Release 日志。
|
||||
- [ ] Debug 日志可增加 revision / invalidation reason,便于 RDC 验证。
|
||||
- [ ] 原有 LayerGroup ordinal、dynamic boundary、transparent underlay、legacy ordering 语义不得回退。
|
||||
- [ ] 不实现 STEP-090 的 static CPU processing skip。
|
||||
- [ ] 不新增 React 管理的 renderer cache。
|
||||
- [ ] 不把 provider/network/NavSea Store 逻辑引入 MapLibre Core。
|
||||
- [ ] 不使用 Jest。
|
||||
|
||||
## Revision 设计约束
|
||||
|
||||
优先采用单调 `uint64_t` revision / generation:
|
||||
|
||||
- revision 只增不减;
|
||||
- 不依赖 wall clock;
|
||||
- 不要求每种事件都维护独立缓存副本;
|
||||
- 可以保留 reason/category 作为诊断,但 correctness 依赖 revision,而不是字符串 reason;
|
||||
- revision wraparound 可按现实上不可达处理,但类型必须明确。
|
||||
|
||||
建议最少区分:
|
||||
|
||||
- 当前 static revision;
|
||||
- retained underlay build revision;
|
||||
|
||||
如现有 Renderer/RenderOrchestrator 的生命周期更适合其他命名,可调整,但不要把模型复杂化。
|
||||
|
||||
## Invalidation 范围原则
|
||||
|
||||
STEP-089 首选“安全偏保守”。
|
||||
|
||||
如果某类状态变化是否会影响 static prefix 无法可靠判断:
|
||||
|
||||
- 直接 bump revision / invalidate;
|
||||
- 不做复杂局部 dependency graph;
|
||||
- 不为了提高 hit rate 提前实现 STEP-090 级别的细粒度 source/layer dependency tracking。
|
||||
|
||||
目标是让 STEP-090 有一个可信的 fast-path gate,而不是在 089 追求最高 cache reuse。
|
||||
|
||||
## Placement / Symbol 特别规则
|
||||
|
||||
已有 Android 真机证据:
|
||||
|
||||
- Particle 放到全部 63 层上方时,symbol-heavy prefix 会出现持续 `static-placement` / `static-transition`。
|
||||
- BEFORE_SYMBOL_CLUSTER(fill/line 在下,symbol cluster 在上)是目前最稳定边界。
|
||||
|
||||
因此:
|
||||
|
||||
- placement/fade active 必须阻止旧 static state 被错误复用;
|
||||
- fade/placement 稳定后允许重新 build 并记录新 revision;
|
||||
- 不得简单忽略 symbol placement 来提高 TOP 场景 reuse。
|
||||
|
||||
## Runtime API / Diagnostics
|
||||
|
||||
保持现有:
|
||||
|
||||
- `setRetainedUnderlayEnabled`
|
||||
- `getRetainedUnderlayDiagnostics`
|
||||
|
||||
允许在 diagnostics 增加:
|
||||
|
||||
- `staticRevision`
|
||||
- `buildRevision`
|
||||
|
||||
如增加,Android/iOS/RN 三端需一致;如果为了控制 STEP-089 范围决定暂不公开,也至少在 Debug Native 日志中可观察。
|
||||
|
||||
不要新增第二套 runtime module / map registry。
|
||||
|
||||
## 测试要求
|
||||
|
||||
新增/扩展 Native directed tests,至少覆盖:
|
||||
|
||||
1. revision 单调递增;
|
||||
2. build revision 与 current revision 相等时允许 reuse;
|
||||
3. camera/viewport/style/source 变化后 revision 改变并禁止旧 cache reuse;
|
||||
4. placement/fade active 时不 reuse stale cache;
|
||||
5. Cache OFF -> invalidate;重新 ON -> 下一次重新 build;
|
||||
6. unsupported / resource failure 保持安全 fallback;
|
||||
7. 原有 RetainedDynamicBoundary / LayerGroupRange / RetainedUnderlayCache 测试继续通过。
|
||||
|
||||
不要求 Jest。
|
||||
|
||||
## Codex 执行规则
|
||||
|
||||
- 先完整阅读本任务、AGENTS.md、STEP-088 v3 Step/报告,以及 retained underlay 当前实现。
|
||||
- 不需要提交实施计划。
|
||||
- 直接实现。
|
||||
- 不运行 Gradle / Android build / ADB / Xcode。
|
||||
- 不运行大型 noisy build/test;编译和最终验证由 RDC 负责。
|
||||
- 可以做必要的静态代码检查,但不要替代 RDC 编译。
|
||||
- 不 commit/push,等 RDC 验证后统一收尾。
|
||||
- 不触碰用户既有无关 dirty/untracked。
|
||||
- 不使用 worktree。
|
||||
- 无法完成的项明确说明,不得静默跳过。
|
||||
|
||||
## 完成时必须报告
|
||||
|
||||
逐项按“已完成 / 未完成 / 无法验证”报告上述必做项,并列出:
|
||||
|
||||
- 修改文件;
|
||||
- revision 的实际所有者与生命周期;
|
||||
- 每类 invalidation 在哪里触发;
|
||||
- 新增测试名;
|
||||
- RDC 应执行的精确 OpenGL / Metal / Android Validation 命令;
|
||||
- 明确说明没有实现 STEP-090 fast path。
|
||||
@@ -0,0 +1,239 @@
|
||||
# STEP-090 Particle-only Static CPU Fast Path Codex 任务 v1
|
||||
|
||||
## 目标
|
||||
|
||||
基于 STEP-088 retained underlay GPU cache 与 STEP-089 static revision/invalidation gate,真正减少 Particle continuous repaint 下静态 PBF/vector 的 CPU processing。
|
||||
|
||||
当前已实测:
|
||||
|
||||
- PURE PBF idle:RenderThread ~0%
|
||||
- PURE PARTICLE 2048 / FPS30:RenderThread ~10.2%
|
||||
- production Style + 60 个 PBF layer hidden + Particle:RenderThread ~25.5%
|
||||
- production PBF + Particle:RenderThread ~96~100%
|
||||
- STEP-088 仅省 static GPU draw,RenderThread 仍 ~96%
|
||||
- STEP-089 已保证:
|
||||
- steady state staticRevision/buildRevision 稳定;
|
||||
- camera/style/tile 等真实变化会 revision bump;
|
||||
- transient drawable/topology churn 不误触发。
|
||||
|
||||
因此 STEP-090 的目标是:
|
||||
|
||||
> 当当前帧仅由 Particle animation 触发,且静态状态 revision/topology/camera/viewport/placement 等均稳定时,跳过不必要的静态 PBF/vector CPU pipeline,复用上一帧已准备好的 render state / RenderTree,并只执行 Particle 所需的动态 render。
|
||||
|
||||
## 基线
|
||||
|
||||
- branch: `particle/main`
|
||||
- baseline HEAD: `98e1d2d977ba9e007ba313c14d63bfa54ab9d23b`
|
||||
- STEP-089: CLOSED / PASS
|
||||
|
||||
## 核心约束
|
||||
|
||||
### 1. Fast path 必须在 createRenderTree 之前判定
|
||||
|
||||
当前:
|
||||
`Renderer::render(UpdateParameters)`
|
||||
→ `RenderOrchestrator::createRenderTree()`
|
||||
→ `renderTree->prepare()`
|
||||
→ `Renderer::Impl::render()`
|
||||
|
||||
如果 fast path 只放在 `Renderer::Impl::render()`,已经无法节省 createRenderTree/source update/prepare,不满足 STEP-090。
|
||||
|
||||
### 2. 仅允许 Particle-only frame
|
||||
|
||||
必须内部区分:
|
||||
|
||||
- Particle-only animation frame;
|
||||
- 其他任何会影响 map/render tree 的更新。
|
||||
|
||||
Fast path 只在能够证明“除了 Particle animation,没有其他更新需要处理”时使用。
|
||||
|
||||
不得新增 Host/RN 手工调用:
|
||||
- 不要 `particle.renderFastFrame()`
|
||||
- 不要 `cacheMapNow()`
|
||||
- 不要 `invalidateBasemap()`
|
||||
|
||||
这是 MapLibre Native 内部自动行为。
|
||||
|
||||
### 3. 安全 gate
|
||||
|
||||
至少要求:
|
||||
|
||||
- retained underlay runtime enabled;
|
||||
- retained cache valid;
|
||||
- retained buildRevision == current staticRevision;
|
||||
- camera / transform unchanged;
|
||||
- viewport / pixelRatio unchanged;
|
||||
- styleLoaded;
|
||||
- dynamic boundary unchanged;
|
||||
- topologyRevision unchanged;
|
||||
- no static transition;
|
||||
- no placement/fade transition affecting correctness;
|
||||
- no source/tile/image/glyph/style/layer mutation;
|
||||
- no context reset/debug/3D/custom unsupported;
|
||||
- no pending change request that would alter render tree;
|
||||
- no non-particle layer transition/repaint;
|
||||
- no fading tile;
|
||||
- no still image request;
|
||||
- no map mode / special render path that cannot safely reuse。
|
||||
|
||||
任何不确定情况必须 full path。
|
||||
|
||||
### 4. RenderTree reuse
|
||||
|
||||
优先实现一个最小、明确的 cached prepared RenderTree / render state 生命周期:
|
||||
|
||||
- full frame 成功创建并 prepare 后,可作为下一次 particle-only fast frame 的候选;
|
||||
- fast frame 不重新 createRenderTree;
|
||||
- fast frame 不重新调用 full `RenderTree::prepare()`;
|
||||
- 复用对象生命周期必须明确,不能悬挂引用;
|
||||
- full path 创建新 tree 后替换旧缓存;
|
||||
- revision/context/style reset 时失效。
|
||||
|
||||
若 RenderTree 当前设计无法安全长期持有,允许提取一个专用 immutable prepared frame state,但不要大规模重构。
|
||||
|
||||
### 5. 动态部分
|
||||
|
||||
Particle fast frame必须仍然:
|
||||
- 正常 advance simulation;
|
||||
- 正常 render Particle;
|
||||
- composite retained underlay;
|
||||
- 正常 present;
|
||||
- 保持 particle cadence/targetFps 语义。
|
||||
|
||||
对于 Particle 之外的动态 layer:
|
||||
- 如果存在其他 `needsRepaint()` / transition / placement / fading tile,则禁止 fast path,走 full path。
|
||||
- 不要在本步骤尝试支持 AIS/Ownship 等所有未来动态 layer;先确保 Particle-only fast frame 安全。
|
||||
|
||||
WeatherColor:
|
||||
- 若本身不需要 repaint 且输入未变,可与静态 prepared state 一起复用;
|
||||
- 若其状态变化,full path。
|
||||
|
||||
### 6. Source/PBF processing
|
||||
|
||||
Fast path 命中时,目标是明确不执行:
|
||||
- full `createRenderTree()`
|
||||
- static PBF/vector source update
|
||||
- tile pyramid normal per-frame update
|
||||
- static layer prepare
|
||||
- static placement orchestration
|
||||
- static layer group rebuild
|
||||
|
||||
允许仍执行 fast frame 所必须的最小 upload/tweaker/render,但要避免重新走静态 source/layer CPU pipeline。
|
||||
|
||||
### 7. Runtime ON/OFF
|
||||
|
||||
继续使用现有 `retainedUnderlayEnabled` 作为整体优化 gate:
|
||||
|
||||
- ON:允许 retained cache + CPU fast path;
|
||||
- OFF:完全回到原 full-render baseline,用于 A/B;
|
||||
- 不新增第二个用户开关,除非内部 debug-only 诊断确实必要。
|
||||
|
||||
这样同一个 Release 构建仍可通过 RN RendererRuntime 做完整 A/B。
|
||||
|
||||
### 8. 诊断
|
||||
|
||||
Debug 增加最小 counters/log:
|
||||
|
||||
- `ParticleFastFrameHit`
|
||||
- `ParticleFastFrameMiss`
|
||||
- `ParticleFullFrame`
|
||||
- miss reason(debug only)
|
||||
|
||||
建议周期日志继续并入 `ParticleRetained:` 或新增稳定前缀 `ParticleFastPath:`,字段至少:
|
||||
|
||||
```text
|
||||
hit=<n>
|
||||
miss=<n>
|
||||
full=<n>
|
||||
reason=<...>
|
||||
staticRevision=<n>
|
||||
buildRevision=<n>
|
||||
```
|
||||
|
||||
Release 不做 per-frame 日志。
|
||||
|
||||
如现有 diagnostics 适合,可在 Release pull snapshot 中增加累计 hit/miss,但不是强制;不要扩大 RN 高频接口。
|
||||
|
||||
### 9. 性能验收目标
|
||||
|
||||
不预设固定百分比门槛,但 Android NavSea PBF Test App 必须证明:
|
||||
|
||||
- AFTER_CLIP / BEFORE_SYMBOL_CLUSTER 固定 Camera;
|
||||
- Cache/FastPath ON 时 fast hit 持续增长;
|
||||
- full frame 不随 Particle frame 一比一增长;
|
||||
- Particle 仍连续运动;
|
||||
- static draw 继续不随 particle frame 增长;
|
||||
- RenderThread CPU 相比 STEP-089 / Cache OFF 有明显下降。
|
||||
|
||||
关键基线用于比较:
|
||||
|
||||
```text
|
||||
PURE PARTICLE ~10.2% RenderThread
|
||||
PBF hidden + Particle ~25.5%
|
||||
PBF visible + Particle ~96~100%
|
||||
```
|
||||
|
||||
目标方向是让 PBF visible + Particle 大幅向前两者靠近,而不是只减少几个百分点。
|
||||
|
||||
### 10. 回退验证
|
||||
|
||||
必须验证:
|
||||
|
||||
- pan / zoom / rotate → fast path 立即 miss/full;
|
||||
- tile load/change → full;
|
||||
- style/layer/source change → full;
|
||||
- cache OFF → full;
|
||||
- static state重新稳定后 → fast hit恢复;
|
||||
- TOP symbol-heavy / placement active → 保守 full/fallback;
|
||||
- resize/context reset → full;
|
||||
- no crash / stale map / ordering regression。
|
||||
|
||||
## 不做
|
||||
|
||||
- 不新增 Host API 触发 fast frame。
|
||||
- 不把 provider/network/vendor 逻辑放入 Native。
|
||||
- 不改 RN Weather Service 边界。
|
||||
- 不做 STEP-091 级别的多动态层通用调度。
|
||||
- 不做复杂 dependency graph。
|
||||
- 不做 Jest。
|
||||
- 不使用 worktree。
|
||||
|
||||
## Native 测试
|
||||
|
||||
新增 directed tests,至少覆盖:
|
||||
|
||||
1. particle-only + stable revision → fast candidate true;
|
||||
2. revision mismatch → false;
|
||||
3. camera/viewport mismatch → false;
|
||||
4. non-particle transition/repaint → false;
|
||||
5. placement/fade/fading tile → false;
|
||||
6. cache disabled → false;
|
||||
7. context/style/source/tile mutation后 → false;
|
||||
8. full frame重新稳定后 → fast candidate true;
|
||||
9. cached RenderTree 生命周期/替换不悬挂;
|
||||
10. 原 12 个 retained tests继续通过。
|
||||
|
||||
## Codex 执行规则
|
||||
|
||||
- 先完整读取 AGENTS.md、STEP-088 v3、STEP-089 task/Step/报告、当前 renderer.cpp / render_orchestrator / renderer_impl / RenderTree。
|
||||
- 不提交实施计划,直接调查并实现。
|
||||
- 不运行 Gradle/ADB/Xcode/大构建。
|
||||
- 不 commit/push。
|
||||
- 不触碰无关 dirty/untracked。
|
||||
- 不使用 worktree。
|
||||
- 不加 Jest。
|
||||
- 如果发现当前 RenderTree 不能安全缓存,先在当前架构内采用最小安全方案,不进行无边界重构。
|
||||
- 如果实现范围明显膨胀到新的架构层,停止并报告阻塞点,不强行完成。
|
||||
|
||||
## 完成时必须报告
|
||||
|
||||
逐项说明:
|
||||
- fast path gate 实际位于哪里;
|
||||
- 如何判断 particle-only;
|
||||
- 哪些 full-frame 工作被跳过;
|
||||
- cached RenderTree / prepared state 生命周期;
|
||||
- 所有 miss/fallback 条件;
|
||||
- 修改文件;
|
||||
- 新增 tests;
|
||||
- RDC 精确验证命令;
|
||||
- 明确没有新增 Host 手工 fast-frame API。
|
||||
@@ -0,0 +1,225 @@
|
||||
# STEP-091 Zoom Out 后粒子 Viewport 一次性重播种 Codex 任务 v1
|
||||
|
||||
## 目标
|
||||
|
||||
修复正式 RasterParticleLayer 在“大 Zoom → 小 Zoom(Zoom Out)”后,旧粒子短时间聚集在原视野小区域,必须等待粒子生命周期/运动逐渐扩散后才恢复正常密度的问题。
|
||||
|
||||
用户实机现象:
|
||||
- Zoom Out 完成后,粒子集中成一小团/小矩形;
|
||||
- 风场和粒子仍继续运动;
|
||||
- 等旧粒子跑远或寿命到期后,整个新视野才逐渐恢复正常铺开;
|
||||
- Zoom In 暂未观察到同类问题。
|
||||
|
||||
本任务必须以当前实际源码为准修复,不改 Field/T0/T1/Timeline 数据契约,不引入第二套粒子系统。
|
||||
|
||||
## 基线
|
||||
|
||||
- repo: MapLibreParticleLab
|
||||
- branch baseline: `origin/particle/main`
|
||||
- baseline HEAD: `297b9126200cea3da3bdef2faacedcd9845f6d08`
|
||||
- 实施工作树: `/Users/tei/dev/MapLibreParticleLab-step091`
|
||||
- 实施分支: `step091-zoom-out-reseed`
|
||||
- STEP-090 已 CLOSED / PASS,retained underlay + Particle-only CPU fast path 必须保持。
|
||||
## 已确认根因
|
||||
|
||||
重点读取:
|
||||
- `src/mbgl/renderer/layers/render_particle_validation_layer.cpp`
|
||||
- OpenGL / Metal particle shader
|
||||
- 当前 camera invalidation、respawnBounds、State/Trail seed 逻辑。
|
||||
|
||||
当前正式路径已经存在:
|
||||
- `TrailCameraStableDelay = 150ms`
|
||||
- camera 变化时 `trailCameraInvalidated=true`
|
||||
- camera stable 后重新启用 viewport density
|
||||
- `viewportRespawnDomain(...)`
|
||||
- `respawnBounds`
|
||||
- shader 的 `outsideRespawnBounds(position)`
|
||||
- `stateSeeded / trailSeeded`
|
||||
- initial seed 时由 shader 使用 `respawnSeed(...)` 在 `respawnBounds` 内播种。
|
||||
|
||||
问题是 Zoom Out 后新的 respawnBounds 变大,旧粒子仍位于扩大后的 bounds 内,因此不会命中 outsideRespawnBounds,只能等待 age/invalid/运动触发 respawn。
|
||||
|
||||
## 修复原则
|
||||
|
||||
Camera 稳定后,如果本次手势/连续 camera 变化的最终结果是明确 Zoom Out,则执行一次 viewport density reseed:
|
||||
- 下一次 State pass 把全部粒子重新播种到“新的、已经计算完成的 respawnBounds”;
|
||||
- 同时清理/重新 seed Trail,避免旧小视野 trail 与新粒子叠加;
|
||||
- 之后立即恢复正常连续 simulation。
|
||||
这不是完整 Simulation reset。禁止为了 Zoom Out:
|
||||
- 调用 `resetSequence("ZOOM_OUT")`;
|
||||
- 重建 State/Trail GPU resource;
|
||||
- 重新上传 Field texture;
|
||||
- reset Timeline / T0 / T1 / MIX;
|
||||
- style reload;
|
||||
- source reload;
|
||||
- Host/RN 手工 reset API。
|
||||
|
||||
优先复用现有 `stateSeeded=false` / `trailSeeded=false` 和 shader initial-seed 机制;若实际代码证明还需一个极小内部状态,可最小新增。
|
||||
|
||||
## Camera 手势状态
|
||||
|
||||
需要记录“一次 camera invalidation 开始前”的稳定 Zoom,不能每帧覆盖。
|
||||
|
||||
建议最小状态语义:
|
||||
- camera gesture / invalidation 开始时保存 `startZoom`;
|
||||
- camera 变化期间仍沿用现有 invalidated 行为;
|
||||
- 150ms stable 时比较最终 `endZoom`;
|
||||
- 仅当 `endZoom < startZoom - epsilon` 才请求 viewport reseed;
|
||||
- epsilon 仅防浮点/微小抖动,建议约 0.05,实际值可按现有 double/zoom 语义选择并写明理由;
|
||||
- Zoom In、纯 Pan、Rotate、Pitch 不触发 full reseed;
|
||||
- 新一轮 camera invalidation 必须重新建立 startZoom,不得复用上一轮。
|
||||
## 执行顺序要求
|
||||
|
||||
必须保证 reseed 使用新的 viewport domain:
|
||||
|
||||
1. camera 变化 → invalidated;
|
||||
2. camera 连续变化期间不触发 reseed;
|
||||
3. stable debounce 到期;
|
||||
4. 计算当前 `viewportRespawnDomain(transformState, viewport, field)`;
|
||||
5. 得到当前 `viewportDensityActive` 和新的 `respawnBounds`;
|
||||
6. 仅在本轮最终为 Zoom Out 且 viewport respawn domain 有效时执行一次:
|
||||
- `stateSeeded = false`
|
||||
- `trailSeeded = false`
|
||||
- 清除 pending 标记;
|
||||
7. 下一次 State/Trail pass 使用现有 initial seed 流程;
|
||||
8. 后续帧继续正常 simulation。
|
||||
|
||||
若 stable 时 respawn domain fallback/无效:
|
||||
- 不允许留下会在未来无关 Pan 时突然触发的 pending reseed;
|
||||
- 必须安全消费/取消本轮请求并记录原因;
|
||||
- 不得伪造 viewport domain。
|
||||
|
||||
## Trail
|
||||
|
||||
Zoom Out viewport reseed 必须同时让 Trail 重新 seed/清理。
|
||||
不得保留旧小视野 trail 作为新 viewport 的历史。
|
||||
不得改变正常连续播放时的 trail fade、T0/T1 overlap、simulationRebuild=false 语义。
|
||||
## Shader / UBO 边界
|
||||
|
||||
当前 GL/Metal shader 已有:
|
||||
- initial seed;
|
||||
- respawnSeed;
|
||||
- respawnBounds;
|
||||
- alpha/validity 防止 respawn jump 连线。
|
||||
|
||||
本任务默认不修改 shader、UBO layout 或公共 Layer API。
|
||||
只有实际源码证明现有 initial-seed 无法正确完成 viewport reseed 时,才允许最小修改,并在完成报告说明原因。
|
||||
|
||||
绝对禁止:
|
||||
- partial reseed ratio 新协议;
|
||||
- per-particle CPU readback;
|
||||
- JS/RN 粒子重播种;
|
||||
- 新增 Host camera callback 驱动粒子;
|
||||
- 将 provider/network/vendor 逻辑放入 Renderer。
|
||||
|
||||
## STEP-089 / STEP-090 保护
|
||||
|
||||
Camera 变化期间原本就应使 fast path miss/full;稳定后应恢复 fast hit。
|
||||
本次修复不得破坏:
|
||||
- retained underlay cache;
|
||||
- Particle-only static CPU fast path;
|
||||
- staticRevision/buildRevision 语义;
|
||||
- camera/style/tile mutation fail-closed;
|
||||
- normal continuous particle cadence;
|
||||
- WeatherColor 与 RasterParticle 分层;
|
||||
- Runtime Field Window/T0/T1/MIX。
|
||||
## 诊断日志
|
||||
|
||||
新增或扩展最小 Debug 日志,至少能确认一次 Zoom Out reseed:
|
||||
|
||||
建议稳定前缀:
|
||||
`ParticleDensity: event=VIEWPORT_RESEED reason=ZOOM_OUT ...`
|
||||
|
||||
字段至少包含:
|
||||
- startZoom
|
||||
- endZoom
|
||||
- respawnBounds
|
||||
- stateReseed=true
|
||||
- trailReseed=true
|
||||
- fieldResubmit=false
|
||||
- timelineReset=false
|
||||
- gpuResourceRebuild=false
|
||||
|
||||
fallback/无效 domain 时输出一次 skip/cancel 原因即可。
|
||||
禁止 per-frame spam。
|
||||
|
||||
## 定向测试
|
||||
|
||||
不得加入 Jest。
|
||||
|
||||
至少验证:
|
||||
1. Zoom Out 超过 epsilon → stable 后请求一次 reseed;
|
||||
2. Zoom In → 不请求;
|
||||
3. 纯 Pan → 不请求;
|
||||
4. Rotate/Pitch 且 Zoom 未下降 → 不请求;
|
||||
5. Zoom Out 后 fallback domain → 不留下延迟 pending;
|
||||
6. 一次 gesture 只 reseed 一次;
|
||||
7. 下一轮 gesture startZoom 正确刷新;
|
||||
8. 现有 respawn/State/Trail 生命周期测试继续通过(如已有可复用测试)。
|
||||
测试实现优先使用小型 C++ helper / directed native test;不要为了测试扩大公共 API。
|
||||
若当前测试结构无法直接覆盖 private renderer 状态,允许测试提取出的纯判定 helper,但不得引入复杂新模块。
|
||||
|
||||
## Mac 验证
|
||||
|
||||
Codex 需要:
|
||||
- 读取根目录 `AGENTS.md` 与相关 STEP-055、STEP-089、STEP-090 记录/报告;
|
||||
- 执行与修改直接相关的 Mac 原生精确编译/定向 C++ 测试;
|
||||
- noisy build 遵循全局 `$build-output-throttle`;
|
||||
- `git diff --check` PASS;
|
||||
- 不运行 Android Gradle/APK/ADB;
|
||||
- 不运行 Jest。
|
||||
|
||||
## Android Test App
|
||||
|
||||
本任务默认不新增 UI 控件。
|
||||
现有 Particle Core Layer Validation / 正式 RasterParticleLayer 场景应足以通过 ADB 手势验证。
|
||||
如果现有 Test App 无法可靠触发正式 viewport respawn path,才允许最小增加 debug-only validation 入口,禁止改产品 Host API。
|
||||
|
||||
## 实机验收预期(由 RDC 在 Codex 完成后执行)
|
||||
|
||||
- 编译 OpenGL Debug Test APK;
|
||||
- 安装到当前可用 Android 测试设备;
|
||||
- 启动 `ParticleCoreLayerValidationActivity`;
|
||||
- 使用正式 RasterParticleLayer 场景与较高粒子数;
|
||||
- 做明显 Zoom In 后再快速 Zoom Out;
|
||||
- 日志出现一次 VIEWPORT_RESEED;
|
||||
- Zoom Out 后不再等待 10 秒级生命周期才铺满新视野;
|
||||
- 无 crash / ANR / GL/EGL error;
|
||||
- camera stable 后 ParticleFastPath fast hit 恢复增长。
|
||||
## 文档与 Git
|
||||
|
||||
Codex 完成后必须:
|
||||
- 更新 `粒子开发记录.md`;
|
||||
- 生成 `docs/50_Step记录/STEP-091_ZoomOut_Viewport_Particle_Reseed_v1.md`;
|
||||
- 生成 `docs/报告/STEP-091_ZoomOut_Viewport_Particle_Reseed_Codex完成报告_v1.md`;
|
||||
- 完成报告逐项写“已完成 / 未完成 / 无法验证”,不要写实施计划;
|
||||
- 记录实际修改文件、实际测试命令与结果;
|
||||
- 记录是否改 shader/UBO/API;
|
||||
- 记录 STEP-090 fast path 是否保持。
|
||||
|
||||
提交本任务相关变更到当前工作分支,并 push:
|
||||
`git push origin HEAD:particle/main`
|
||||
|
||||
不得提交任何与本任务无关的改动。
|
||||
push 失败不得声称任务完成。
|
||||
|
||||
完成报告必须记录:
|
||||
- implementation commit hash
|
||||
- commit message
|
||||
- push result
|
||||
- remote branch
|
||||
- Final HEAD / origin/particle/main
|
||||
|
||||
## 完成标准
|
||||
|
||||
只有同时满足以下条件才可判定 Codex 阶段 PASS:
|
||||
- 根因按当前代码被修复;
|
||||
- Zoom Out stable 后只做一次 viewport State/Trail reseed;
|
||||
- 不做 full simulation reset / field resubmit / timeline reset / GPU resource rebuild;
|
||||
- Zoom In/Pan/Rotate/Pitch 不误触发;
|
||||
- 定向 C++ 测试与 Mac 精确编译通过;
|
||||
- git diff --check 通过;
|
||||
- 文档完成;
|
||||
- commit + push 到 `origin/particle/main` 成功。
|
||||
|
||||
Android APK 构建、安装与设备手势验收由后续 RDC 串行执行,不属于 Codex 自己的完成条件。
|
||||
@@ -0,0 +1,249 @@
|
||||
# STEP-092 Continuous Range 可退化分段 Retained Cache — Codex 任务 v1
|
||||
|
||||
## 0. 目标
|
||||
在当前 MapLibreParticleLab / Particle V11 Renderer 上实现一个 explicit Continuous Range。
|
||||
正式架构定义:
|
||||
App 用正常 Style 定义唯一 Z-order,并指定两个现有 Style Layer ID 作为闭区间 Continuous Range;
|
||||
Renderer 将两个 ID 解析为连续 layerIndex,根据 Continuous Range 在 Style 中的位置自然形成 0~2 个 Retained Range Cache;
|
||||
最终可以是 1、2 或 3 个 Render Range,绝不是固定“三层三明治”。
|
||||
|
||||
本任务只支持一个 Continuous Range,不做多个 Range 或任意 N Segment Planner。
|
||||
|
||||
## 1. 环境与起始确认
|
||||
工作目录:/Users/tei/dev/MapLibreParticleLab
|
||||
目标分支:particle/main
|
||||
历史参考 HEAD:b29e564944197072a0effaec36ca95c48fe0649b,但不得据此强制回退。
|
||||
修改前必须:
|
||||
1. git fetch origin
|
||||
2. 确认当前分支、HEAD、origin/particle/main
|
||||
3. 记录起始 HEAD
|
||||
4. 检查 git status --short
|
||||
5. 不覆盖、不删除、不 reset/clean、不提交无关 dirty/untracked
|
||||
6. 若远端更新,以当前真实 origin/particle/main 为基线
|
||||
## 2. 唯一 Z-order 事实来源
|
||||
Style Layer 顺序是唯一 Z-order 事实来源:
|
||||
App/RN 负责业务语义和 Style Layer 顺序;
|
||||
MapLibre Style 保存正式 Z-order;
|
||||
C++ Renderer 只消费最终连续 layerIndex。
|
||||
Renderer 禁止硬编码 NavSea 业务 Layer ID(AIS/Alarm/Wind/Wave 等)。
|
||||
禁止建立 BOTTOM/CONTINUOUS/TOP 第二套 metadata 分类表。
|
||||
禁止增加 AnchorLayer、ContinuousStartLayer、ContinuousEndLayer、RenderAnchorLayer、空白 Marker Layer。
|
||||
|
||||
## 3. Continuous Range API 语义
|
||||
App 直接指定两个已有正常 Style Layer:
|
||||
startLayerId
|
||||
endLayerId
|
||||
公开语义为闭区间 [startLayerId, endLayerId]。
|
||||
Native 按当前 Style 解析成 startIndex/endIndex。
|
||||
合法条件必须是 startIndex <= endIndex。
|
||||
必须正式支持 startLayerId == endLayerId,单 Layer Continuous 完全合法。
|
||||
App/RN 不传数字 index,RN 不计算 layerIndex。
|
||||
|
||||
内部建议转半开区间:
|
||||
continuousBegin = startIndex
|
||||
continuousEndExclusive = endIndex + 1
|
||||
Bottom=[0,continuousBegin)
|
||||
Continuous=[continuousBegin,continuousEndExclusive)
|
||||
Top=[continuousEndExclusive,layerCount)
|
||||
Range 存在判断统一为 begin < endExclusive。
|
||||
## 4. 四种合法结构与 Cache 数量
|
||||
必须支持:
|
||||
1. Bottom + Continuous + Top → 2 retained caches
|
||||
2. Bottom + Continuous → 1 Bottom cache
|
||||
3. Continuous + Top → 1 Top cache
|
||||
4. Continuous only → 0 retained caches
|
||||
|
||||
Continuous only 当 startIndex==0 且 endIndex==layerCount-1,是完全合法状态,不是 error/fallback。
|
||||
不存在的 Range:
|
||||
不创建 cache;
|
||||
不分配 texture;
|
||||
不分配 framebuffer/render target;
|
||||
不 build;
|
||||
不 composite;
|
||||
不参与 cache-valid gate。
|
||||
|
||||
当前 RetainedUnderlayCache 应合理泛化为可复用 retained range cache 能力。
|
||||
具体命名服从当前源码,但不得固定创建 bottomCache+topCache 占位资源。
|
||||
Bottom/Top GPU 资源必须 lazy 创建。
|
||||
|
||||
## 5. Final Render 顺序
|
||||
如果 Bottom 存在:build/reuse/composite Bottom
|
||||
然后 render Continuous
|
||||
如果 Top 存在:build/reuse/composite Top
|
||||
顺序只能来自 Style,自然退化为 3/2/1 段。
|
||||
## 6. global layerIndex / pass 语义
|
||||
分 Range 后仍必须保持整个 Style 的:
|
||||
原始 layerIndex;
|
||||
原始 ordinal;
|
||||
parameters.currentLayer;
|
||||
depthRangeSize。
|
||||
不得给 Bottom/Continuous/Top 各自从 0 重新编号。
|
||||
Opaque 继续 reverse traversal;
|
||||
Translucent 继续 forward traversal。
|
||||
|
||||
## 7. Visibility
|
||||
Range topology 来自 Style/orderedLayers,不来自当前 visible/renderable Layers。
|
||||
hidden start/end 仍必须参与 Range index 解析。
|
||||
Layer ON/OFF 是内容变化,不是 Range topology 变化。
|
||||
例如 start=10 hidden、11 visible、end=12 hidden,Range 仍是 10..12,不得缩成 11..11。
|
||||
|
||||
## 8. Style mutation
|
||||
必须支持正常 Style mutation。
|
||||
Continuous 内新增/删除普通成员正常工作。
|
||||
Bottom/Top 内增删使对应实际存在 cache invalid/rebuild。
|
||||
同一 Range 内 reorder 使 cache 内容 invalid/rebuild。
|
||||
Layer 跨 Range 移动:重新解析 start/end index → topology revision → invalidate affected caches → invalidate fast prepared state → rebuild。
|
||||
startLayerId/endLayerId 自身删除:Range 立即 invalid,清除 stale cache/index/prepared state并安全 fallback。
|
||||
Layer 后续重新出现且顺序合法时可恢复 explicit Range。
|
||||
## 9. Top Cache 正确性
|
||||
Top retained texture 必须是透明 RGBA,并正确使用 premultiplied alpha。
|
||||
目标支持 Particle 属于 Continuous、land-area 属于 Top Retained 时,Top texture 正确覆盖 Particle。
|
||||
不得新增 LandMask RT、Wave/Tide mask system 或 Native land-mask 业务判断。
|
||||
land-area 仍只是普通 Style Z-order。
|
||||
|
||||
## 10. Legacy compatibility
|
||||
当前 WeatherColor/RasterParticle 可能通过 startsRetainedDynamicRange() 参与 legacy boundary。
|
||||
规则:
|
||||
explicit Continuous Range 已配置且有效 → STEP-092 Range 模式;
|
||||
未配置 explicit Range → 保持当前 legacy retained-underlay 行为。
|
||||
一旦 explicit Range 生效,startLayerId/endLayerId 是唯一 topology/boundary 来源。
|
||||
禁止 explicit Range 与 startsRetainedDynamicRange() 混合生成第二套 boundary。
|
||||
|
||||
## 11. RN / Native API
|
||||
在现有 RendererRuntime 增加稳定入口,目标语义:
|
||||
setContinuousLayerRange(reactTag,startLayerId,endLayerId)
|
||||
要求:ID 非空;允许相同;map-scoped;thread-safe;monotonic config revision;
|
||||
Range 修改触发 renderer update/invalidation。
|
||||
Android/iOS bridge 不理解业务;C++ 按当前 Style 解析 index。
|
||||
保留 setRetainedUnderlayEnabled() 与 getRetainedUnderlayDiagnostics() 兼容。
|
||||
## 12. Invalid / fallback
|
||||
至少处理:
|
||||
start 不存在;
|
||||
end 不存在;
|
||||
startIndex > endIndex;
|
||||
Style 未 ready;
|
||||
未配置 explicit range。
|
||||
要求:不 crash;不猜 index;不复用 stale index/cache/prepared state;
|
||||
不根据 Particle/WeatherColor 类型偷偷修正 explicit Range;
|
||||
安全进入 legacy/full renderer;
|
||||
diagnostics 说明 fallback 原因。
|
||||
Continuous-only 不是 invalid/fallback。
|
||||
|
||||
## 13. Particle-only CPU Fast Path
|
||||
现有 Particle-only CPU fast path 不得退化。
|
||||
三段:Bottom reuse → Continuous render → Top reuse
|
||||
只有 Bottom:Bottom reuse → Continuous render
|
||||
只有 Top:Continuous render → Top reuse
|
||||
0 cache:Continuous render
|
||||
不存在的 cache 不能成为 Fast Path miss 原因。
|
||||
Bottom 不存在时不得要求 bottomCache.valid=true;Top 同理。
|
||||
Fast gate 只检查实际存在 retained ranges。
|
||||
Continuous Range 本身必须通用,不依赖 RasterParticleLayer/Weather Layer ID。
|
||||
若 Continuous 中存在当前 Fast Path 无法证明安全的未知 Layer:Fast Path fail closed,走正常 full CPU frame,Range Z-order/cache 仍正确。
|
||||
不要为此重写整个 RenderGraph。
|
||||
## 14. Invalidation
|
||||
继续 correctness-first、偏保守模型,至少覆盖:
|
||||
camera/transform/viewport/resize/pixel ratio/style reload;
|
||||
layer add/remove/reorder/property;
|
||||
source mutation/tile update/drawable update;
|
||||
image/sprite/glyph;
|
||||
placement/symbol fade/transition;
|
||||
context reset/debug options/unsupported 3D/legacy ordering/resource/compositor failure。
|
||||
无法判断只影响 Bottom 还是 Top 时,可同时 invalidate 所有实际存在 retained caches。
|
||||
本任务不做复杂 dependency graph。
|
||||
topology 变化还要 invalid Range index resolution、retained prepared state、Particle fast prepared state。
|
||||
|
||||
## 15. Symbol / Placement
|
||||
Top 可能包含 symbol/label/Navigation/AIS/Alarm。
|
||||
correctness 优先。
|
||||
placement/symbol fade/transition 无法证明 reuse 安全时,invalidate/full fallback,稳定后 rebuild。
|
||||
不重写 Placement 系统。
|
||||
|
||||
## 16. Diagnostics
|
||||
至少增加/暴露:
|
||||
rangeConfigured, rangeValid,
|
||||
startLayerId,endLayerId,startIndex,endIndex,
|
||||
bottomPresent,bottomBuild,bottomReuse,bottomInvalidation,
|
||||
topPresent,topBuild,topReuse,topInvalidation,
|
||||
fallback,lastReason。
|
||||
bottomPresent=false 且 topPresent=false 可以只是 Continuous-only,不能自动判 error。
|
||||
Release 保持 pull-based,不增加 per-frame 高频日志。
|
||||
## 17. GPU 生命周期与双后端
|
||||
实际 retained texture 数量必须严格等于实际存在 retained ranges:
|
||||
Continuous only=0;
|
||||
Bottom+Continuous=1;
|
||||
Continuous+Top=1;
|
||||
三段=2。
|
||||
稳定 Continuous frame 不得每帧重建 retained texture/framebuffer/compositor resources。
|
||||
|
||||
Mac 上完成当前工程允许的精确 Native 验证。
|
||||
OpenGL 重点:Bottom/Top target 创建复用、Top RGBA/premultiplied alpha、0/1/2 cache 无空资源、Continuous-only、Fast Path。
|
||||
Metal 重点:同等 Range/cache topology、透明 Top composite、0/1/2 cache 生命周期、explicit Range/fallback 与 OpenGL 一致。
|
||||
不得用 Linux Core/OpenGL 构建替代 Mac 基线。
|
||||
若 Android 实机/Gradle/ADB 项未执行,报告必须明确列为“未执行 Android 项”,不得伪造通过。
|
||||
|
||||
## 18. Native directed tests(不加入 Jest)
|
||||
至少覆盖:
|
||||
start==end;
|
||||
start<end;
|
||||
start>end invalid;
|
||||
start missing;
|
||||
end missing;
|
||||
hidden start/end;
|
||||
Style reorder;
|
||||
start/end 删除;
|
||||
四种 Range 结构;
|
||||
Bottom empty/Top empty/both empty;
|
||||
不存在 Bottom 时不因 bottom cache missing 而 Fast Path miss;
|
||||
不存在 Top 时不因 top cache missing 而 Fast Path miss。
|
||||
还应验证:
|
||||
explicit Range 有效时不混用 legacy boundary;
|
||||
explicit Range invalid 不复用 stale cache;
|
||||
single-layer Continuous 正常;
|
||||
hidden endpoint 不改变 topology;
|
||||
reorder 后重新解析 index;
|
||||
endpoint 删除 fail safe;
|
||||
0/1/2 retained cache resource 数量与 topology 一致;
|
||||
Opaque/Translucent traversal 保持 global layerIndex 语义。
|
||||
|
||||
## 19. 明确不做
|
||||
不做:任意 N Segment Planner、多个 Continuous Range、LOW/MEDIUM/HIGH 分类、
|
||||
自动分析 Layer 更新频率、自动调整 Z-order、Anchor/Marker Layer、新 Style Layer 类型、
|
||||
NavSea AIS 专项优化、Weather provider、Route ETA、Native land-mask 业务、
|
||||
Wave/Tide 业务判断、第二套 Z-order、NavSea Layer ID 硬编码、
|
||||
复杂 per-source dependency graph、PBF 重构/重生成、通用 RenderGraph、
|
||||
Placement 重写、Particle 专属 Continuous 机制、Jest。
|
||||
如发现必须突破边界才能继续,停止扩大范围并记录阻塞。
|
||||
|
||||
## 20. 必做文档
|
||||
生成:
|
||||
docs/50_Step记录/STEP-092_ContinuousRange可退化分段RetainedCache_v1.md
|
||||
docs/报告/STEP-092_ContinuousRange可退化分段RetainedCache_Codex完成报告_v1.md
|
||||
追加:粒子开发记录.md
|
||||
同主题已存在则递增版本,不覆盖旧文件;所有文档中文。
|
||||
完成报告必须包含:
|
||||
起始 HEAD、最终 HEAD、修改文件清单;
|
||||
C++ Renderer、RN API、Android API/bridge、iOS API/bridge;
|
||||
Range config 所有者、ID→layerIndex 解析位置机制;
|
||||
single-layer range、1/2/3 Render Range、0/1/2 retained cache;
|
||||
invalidation、legacy compatibility、Fast Path、diagnostics、GPU lifecycle;
|
||||
OpenGL、Metal、Native tests、未执行 Android 项、known limitations;
|
||||
commit SHA/message、push result、remote branch、origin/particle/main Final HEAD、最终 git status --short。
|
||||
|
||||
## 21. Git
|
||||
仅 scoped staging。
|
||||
禁止 git add -A、git add .、git reset --hard、git clean。
|
||||
不得触碰无关 dirty/untracked。
|
||||
结束前:git diff --check;核对变更;只 stage 本任务文件;scoped commit;push origin particle/main。
|
||||
push 后必须确认:
|
||||
git rev-parse HEAD
|
||||
git rev-parse origin/particle/main
|
||||
git status --short
|
||||
要求 HEAD == origin/particle/main。
|
||||
push 失败不得声明完成。
|
||||
|
||||
## 22. PASS Gate
|
||||
只有架构、四种 Range、0/1/2 cache、global layerIndex、Opaque/Translucent、Fast Path、
|
||||
diagnostics、RN/Android/iOS API、Native directed tests、OpenGL/Metal 规定验证、
|
||||
文档、scoped commit、push、远端 HEAD 一致全部满足,才可判 STEP-092 PASS。
|
||||
关键项无法完成必须明确 PARTIAL/BLOCKED 和原因,禁止伪造 PASS。
|
||||
652
docs/40_任务/STEP-093_ContinuousRange运行态综合验收_Codex任务_v1.md
Normal file
652
docs/40_任务/STEP-093_ContinuousRange运行态综合验收_Codex任务_v1.md
Normal file
@@ -0,0 +1,652 @@
|
||||
# STEP-093:Continuous Range 运行态综合验收
|
||||
|
||||
## 1. 任务目标
|
||||
|
||||
在 STEP-092 已完成并关闭的 Continuous Range / Retained Cache 架构基础上,进入真实 Renderer 运行态综合验收。
|
||||
|
||||
本任务不重新设计 STEP-092 架构,不引入新的 Z-order 体系,不重做 retained cache 结构。目标是通过可重复、可观察、可诊断的运行态场景,确认以下核心事实在真实渲染过程中成立:
|
||||
|
||||
1. Style Layer 顺序仍然是唯一 Z-order 真相。
|
||||
2. `[startLayerId, endLayerId]` 能稳定解析为一个 Continuous Range。
|
||||
3. Bottom / Continuous / Top 四种合法退化结构均能正确工作。
|
||||
4. 实际 retained cache 数量严格为 0 / 1 / 2,不存在的 range 不创建无意义 GPU 资源。
|
||||
5. Particle 连续播放时,稳定的普通 Style/PBF 内容能够命中 retained cache,而不是每帧重复完整绘制。
|
||||
6. Top range 能真实覆盖 Continuous 中的 Particle,验证最终画面顺序为:`Bottom → Continuous → Top`。
|
||||
7. Camera、viewport、style、layer/source 等变化发生时能够正确 invalidation、rebuild,并重新进入稳定 reuse。
|
||||
8. Fast Path 失败时必须安全退回 full CPU frame,但不得破坏 Continuous Range、retained cache 和 Z-order。
|
||||
9. Android/OpenGL 与 iOS/Metal 最终表现一致;若本阶段只能先完成 Android,则 iOS 可明确记录为后续待验收,不得虚报通过。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前基线
|
||||
|
||||
开始任务前必须先检查真实仓库,不得只依赖任务书中的 SHA。
|
||||
|
||||
预期起始基线:
|
||||
|
||||
- Repository: `MapLibreParticleLab`
|
||||
- Branch: `particle/main`
|
||||
- STEP-092 最终状态:`CLOSED / PASS(Mac Native 验证闭环)`
|
||||
- STEP-092 最终已知 HEAD:`8a5d88008c142e2a80e0ff602d228e0e67de2d83`
|
||||
- `HEAD == origin/particle/main`
|
||||
- 工作区应为 clean
|
||||
|
||||
STEP-092 已完成的关键内容包括:
|
||||
|
||||
- Continuous Range 显式配置;
|
||||
- Bottom / Continuous / Top 三段模型;
|
||||
- 四种合法退化拓扑;
|
||||
- 0 / 1 / 2 retained cache;
|
||||
- Bottom/Top retained cache;
|
||||
- Top 透明 RGBA + premultiplied alpha 合成;
|
||||
- Style mutation 后重新解析;
|
||||
- invalid explicit range 的安全 fallback;
|
||||
- Particle Fast Path 与 retained cache 的协同;
|
||||
- Android / iOS Bridge 接口;
|
||||
- OpenGL Native build;
|
||||
- iOS Metal-only native build;
|
||||
- 定向 Native tests 29/29 PASS。
|
||||
|
||||
本 STEP 不重新证明上述静态设计,而是证明它们在真实运行态画面与资源生命周期中成立。
|
||||
|
||||
---
|
||||
|
||||
## 3. 开始前必读
|
||||
|
||||
Codex 开始前必须读取:
|
||||
|
||||
1. 仓库根目录 `AGENTS.md`
|
||||
2. 当前粒子系统总规则 / 架构约束文档
|
||||
3. `docs/50_Step记录/STEP-092_ContinuousRange可退化分段RetainedCache_v2.md`
|
||||
4. `docs/报告/STEP-092_ContinuousRange可退化分段RetainedCache_Codex完成报告_v2.md`
|
||||
5. STEP-092 相关实现文件与 diagnostics 实现
|
||||
6. 当前 Android/iOS Particle Test App / validation 页面 / Host runtime 实现
|
||||
7. 当前可用的测试按钮、场景、日志和 diagnostics 展示入口
|
||||
|
||||
如实际仓库结构与本任务书描述不一致,以当前真实代码为准,并在报告中写明差异。
|
||||
|
||||
---
|
||||
|
||||
## 4. 本任务职责划分
|
||||
|
||||
### 4.1 Codex 负责
|
||||
|
||||
Codex 负责:
|
||||
|
||||
- 调查当前可用的运行态测试入口;
|
||||
- 为 STEP-093 补齐必要的 Test/Validation UI、场景切换、diagnostics 展示和最小测试辅助代码;
|
||||
- 只在确有必要时补充可观察性;
|
||||
- 修复在准备阶段发现的明确、局部、与 STEP-093 验收直接相关的问题;
|
||||
- 为 RDC / 用户准备清晰、可逐项执行的运行态验收入口;
|
||||
- 更新中文 STEP 记录、完成报告和开发记录;
|
||||
- 提交并 push 自己完成的代码与文档。
|
||||
|
||||
### 4.2 RDC / 用户负责
|
||||
|
||||
实际 Android / iOS 编译、安装、启动、真机或模拟器运行、视觉操作与最终画面确认由 RDC / 用户执行。
|
||||
|
||||
Codex 不得仅依据静态代码、日志或自己推测就把运行态测试标记为 PASS。
|
||||
|
||||
如果没有实际执行某个运行态场景,必须写为 `NOT RUN`、`WAITING_RUNTIME_VALIDATION` 或其他明确的未完成状态,不得写成 PASS。
|
||||
|
||||
---
|
||||
|
||||
## 5. 严格保护的 STEP-092 架构
|
||||
|
||||
本 STEP 禁止借“测试方便”修改以下既定架构:
|
||||
|
||||
1. Style 顺序是唯一 Z-order 真相。
|
||||
2. App/RN 只提供 `startLayerId`、`endLayerId`。
|
||||
3. Native 自行解析 index。
|
||||
4. Continuous Range 为闭区间 `[startLayerId, endLayerId]`。
|
||||
5. 内部:`continuousBegin = startIndex`,`continuousEndExclusive = endIndex + 1`。
|
||||
6. 三段:Bottom `[0, begin)`、Continuous `[begin, endExclusive)`、Top `[endExclusive, layerCount)`。
|
||||
7. `start == end` 合法。
|
||||
8. Continuous-only 合法,不是 fallback。
|
||||
9. 不允许加入 anchor / marker layer。
|
||||
10. 不允许增加第二套 BOTTOM / CONTINUOUS / TOP metadata 真相源。
|
||||
11. 不允许 Renderer 硬编码 NavSea/AIS/Alarm/Wind/Wave 等业务 Layer ID。
|
||||
12. Visibility 不改变 topology。
|
||||
13. explicit range 有效时不得再混用旧 `startsRetainedDynamicRange()` 作为 topology boundary。
|
||||
14. 不允许把普通 style layer 改造成新的特殊 layer type 来解决测试问题。
|
||||
15. 不做任意 N-range planner。
|
||||
16. 不做多个 Continuous Range。
|
||||
17. 不做频率分类系统。
|
||||
18. 不做 PBF 通用重构。
|
||||
19. 不做 RenderGraph 重构。
|
||||
20. 不加入 Jest。
|
||||
|
||||
---
|
||||
|
||||
# 6. STEP-093 必测场景
|
||||
|
||||
## 6.1 场景 A:Bottom + Continuous + Top
|
||||
|
||||
构造至少:Bottom 为普通 Style/PBF Layer,Continuous 为 Particle / Weather 动态 Layer,Top 为普通 Style/PBF Layer。
|
||||
|
||||
必须验证:
|
||||
|
||||
- explicit range valid;
|
||||
- Bottom present = true;
|
||||
- Top present = true;
|
||||
- retained cache 实际数量 = 2;
|
||||
- Bottom cache 可 build/reuse;
|
||||
- Top cache 可 build/reuse;
|
||||
- 最终画面顺序 `Bottom → Continuous → Top`;
|
||||
- Top 内容能够真实覆盖 Particle;
|
||||
- 无明显黑边、错误透明、旧 cache 或层序颠倒。
|
||||
|
||||
## 6.2 场景 B:Bottom + Continuous
|
||||
|
||||
Continuous 结束于当前 Style 最顶层。
|
||||
|
||||
必须验证:
|
||||
|
||||
- Bottom present = true;
|
||||
- Top present = false;
|
||||
- retained cache 实际数量 = 1;
|
||||
- 不得创建 Top texture/FBO/compositor;
|
||||
- 不存在的 Top 不得阻塞 Fast Path;
|
||||
- 画面顺序正确。
|
||||
|
||||
## 6.3 场景 C:Continuous + Top
|
||||
|
||||
Continuous 从当前 Style 最底层开始。
|
||||
|
||||
必须验证:
|
||||
|
||||
- Bottom present = false;
|
||||
- Top present = true;
|
||||
- retained cache 实际数量 = 1;
|
||||
- 不得创建 Bottom texture/FBO/compositor;
|
||||
- 不存在的 Bottom 不得阻塞 Fast Path;
|
||||
- Top 可正确覆盖 Particle。
|
||||
|
||||
## 6.4 场景 D:Continuous only
|
||||
|
||||
Continuous 覆盖全部 Style Layer。
|
||||
|
||||
必须验证:
|
||||
|
||||
- Bottom present = false;
|
||||
- Top present = false;
|
||||
- explicit range valid = true;
|
||||
- retained cache 实际数量 = 0;
|
||||
- 这是合法 Continuous-only,不得被识别为 fallback;
|
||||
- 不创建 retained texture/FBO/compositor;
|
||||
- Particle/continuous renderer 正常运行。
|
||||
|
||||
## 6.5 场景 E:Single-layer Continuous
|
||||
|
||||
设置 `startLayerId == endLayerId`。
|
||||
|
||||
必须验证:
|
||||
|
||||
- range valid;
|
||||
- startIndex == endIndex;
|
||||
- continuousEndExclusive == startIndex + 1;
|
||||
- 正常参与 Bottom/Top 切分;
|
||||
- 不得 fallback;
|
||||
- 不得被误认为空 range。
|
||||
|
||||
---
|
||||
|
||||
# 7. Z-order 核心视觉验收
|
||||
|
||||
这是 STEP-093 的最高优先级运行态验收。
|
||||
|
||||
必须构造一个肉眼可以清楚确认覆盖关系的场景:
|
||||
|
||||
1. Bottom 中放一个明显底图/填充层;
|
||||
2. Continuous 中运行 Particle;
|
||||
3. Top 中放一个能够明显覆盖 Particle 的普通 Style Layer。
|
||||
|
||||
优先使用已有普通 Style Layer;如果测试环境缺少合适图层,可添加纯测试用普通 Style Layer,但不得引入新的特殊 layer type 或业务硬编码。
|
||||
|
||||
建议 Top 验证至少覆盖:opaque fill、半透明 fill、line;如当前测试环境可行,再加入 symbol/text。
|
||||
|
||||
必须确认:
|
||||
|
||||
- Top 真正绘制在 Particle 上方;
|
||||
- Particle 不会穿透 opaque Top;
|
||||
- 半透明 Top 与 Particle 合成正确;
|
||||
- 无黑边;
|
||||
- 无颜色异常变暗;
|
||||
- 无错误双重 alpha;
|
||||
- 无 cache 旧内容残留。
|
||||
|
||||
---
|
||||
|
||||
# 8. Stable Frame / Cache Reuse 验收
|
||||
|
||||
在 Camera、viewport、Style、普通 PBF/source 内容都不变化,仅 Particle 持续动画时,必须观察:
|
||||
|
||||
- Bottom/Top retained cache build 后持续 reuse;
|
||||
- 不应每个 Particle frame 重建 retained cache;
|
||||
- 不应每 frame 重建 retained texture;
|
||||
- 不应每 frame 重建 FBO/offscreen texture;
|
||||
- 不应每 frame 重建 composite group;
|
||||
- Fast Path 在满足条件时能够持续命中;
|
||||
- diagnostics 能够区分 build / reuse / invalidation。
|
||||
|
||||
如果现有 diagnostics 无法证明资源生命周期,可增加最小 debug-only counter / reason 字段。禁止为了测试引入生产环境高频日志风暴。
|
||||
|
||||
---
|
||||
|
||||
# 9. Camera / Viewport invalidation
|
||||
|
||||
至少验证:Pan、Zoom、Rotate、Pitch、viewport resize/orientation 或等价尺寸变化,以及测试环境可实际触发时的 pixel ratio 变化。
|
||||
|
||||
每项检查:
|
||||
|
||||
- 旧 retained cache 被正确 invalidation;
|
||||
- 下一帧或正确时点 rebuild;
|
||||
- rebuild 后重新进入 reuse;
|
||||
- 不出现 stale cache;
|
||||
- 不出现错位;
|
||||
- 不出现黑屏;
|
||||
- 不出现 Particle 与地图不同步;
|
||||
- topology 不应无故改变。
|
||||
|
||||
---
|
||||
|
||||
# 10. Style Mutation / Topology 重新解析
|
||||
|
||||
必须提供可重复测试入口,至少覆盖:
|
||||
|
||||
### 10.1 Continuous 内部变更
|
||||
|
||||
添加 Layer、删除非 endpoint Layer、reorder Continuous 内部 Layer。
|
||||
|
||||
预期:重新解析必要状态;range 仍有效时保持正确 topology;cache 根据需要 invalidation;不产生第二套 Z-order。
|
||||
|
||||
### 10.2 Bottom 内变更
|
||||
|
||||
添加、删除、reorder,检查 Bottom cache invalidation / rebuild / reuse。
|
||||
|
||||
### 10.3 Top 内变更
|
||||
|
||||
添加、删除、reorder,检查 Top cache invalidation / rebuild / reuse。
|
||||
|
||||
### 10.4 Layer 跨边界移动
|
||||
|
||||
将某个普通 Layer 做有代表性的 Bottom → Continuous、Continuous → Top 或 Top → Bottom 跨 range 移动。
|
||||
|
||||
必须验证:
|
||||
|
||||
- ID → index 重新解析;
|
||||
- topology/cache/prepared state 正确 invalidation;
|
||||
- 新 Z-order 正确;
|
||||
- 不使用旧 index;
|
||||
- 不出现 stale cache。
|
||||
|
||||
---
|
||||
|
||||
# 11. Endpoint 删除与恢复
|
||||
|
||||
删除 start endpoint 时:explicit range 立即 invalid;不 crash;不猜测新 start;不继续使用 stale index/cache;进入既定安全 fallback;diagnostics 有明确 lastReason。
|
||||
|
||||
恢复 start endpoint 时:Native 能重新解析;explicit range 恢复 valid;正确重建需要的 cache;不需要重建整个 Map instance。
|
||||
|
||||
对 end endpoint 做同等验证。
|
||||
|
||||
---
|
||||
|
||||
# 12. Visibility 不改变 topology
|
||||
|
||||
对 start endpoint、end endpoint 或 Continuous 内普通 Layer 做 visible → hidden → visible。
|
||||
|
||||
必须验证:
|
||||
|
||||
- topology 仍以 Style 顺序解析;
|
||||
- startIndex/endIndex 不因 visibility 改变;
|
||||
- hidden endpoint 仍是 endpoint;
|
||||
- 不自动收缩 Continuous Range;
|
||||
- ON/OFF 只作为内容变化处理;
|
||||
- 如需 cache invalidation,应是内容 invalidation,不得变成新的 topology 规则。
|
||||
|
||||
---
|
||||
|
||||
# 13. Invalid Explicit Range
|
||||
|
||||
至少验证:
|
||||
|
||||
1. startLayerId 不存在;
|
||||
2. endLayerId 不存在;
|
||||
3. startIndex > endIndex;
|
||||
4. Style 尚未 ready 时配置;
|
||||
5. 空 ID / 未配置(按实际 API 合法性处理)。
|
||||
|
||||
预期:不 crash;不猜测;不复用 stale cache;不按 Layer 类型自动修正;进入明确安全 fallback;diagnostics 显示 invalid 原因;条件恢复后能够重新进入 explicit range。
|
||||
|
||||
---
|
||||
|
||||
# 14. Particle Fast Path
|
||||
|
||||
## 14.1 可命中场景
|
||||
|
||||
Continuous 内只保留当前 Fast Path 已支持的动态内容。
|
||||
|
||||
检查:Fast Path 可持续命中;Bottom/Top 的存在与否只影响实际存在的 retained cache;0 cache 的 Continuous-only 不得因为“没有 retained cache”而错误阻止 Fast Path。
|
||||
|
||||
## 14.2 Fail Closed 场景
|
||||
|
||||
在 Continuous 中加入当前 Fast Path 不支持的普通/未知 Continuous Layer。
|
||||
|
||||
必须验证:
|
||||
|
||||
- Fast Path 退回 full CPU frame;
|
||||
- 不 crash;
|
||||
- 不错误跳过该 Layer;
|
||||
- Continuous Range 本身保持 valid;
|
||||
- Bottom / Top retained cache Z-order 仍正确;
|
||||
- 不允许为了 Fast Path 强行把未知 Layer 移出 Continuous。
|
||||
|
||||
---
|
||||
|
||||
# 15. Top Alpha / Premultiplied Alpha 验收
|
||||
|
||||
必须重点看 Top retained cache 合成:transparent background、opaque fill、semi-transparent fill、line,以及可行时 symbol/text。
|
||||
|
||||
检查:透明区域正确露出 Particle;半透明区域正常叠加;opaque 区域完整覆盖;无黑色边缘;无 halo;无颜色被错误乘两次;无残影;OpenGL 与 Metal 设计保持一致。
|
||||
|
||||
---
|
||||
|
||||
# 16. 长时间运行与资源稳定性
|
||||
|
||||
在测试环境允许情况下进行持续运行测试,至少包括:Particle 连续播放、多次 Pan/Zoom/Rotate、多次 range 配置变化、endpoint 删除/恢复、visibility 切换、style mutation、再次稳定运行。
|
||||
|
||||
观察:
|
||||
|
||||
- 无持续 GPU resource 增长迹象;
|
||||
- retained texture/FBO 不因 stable frame 每帧新建;
|
||||
- 无明显内存持续增长;
|
||||
- 无黑屏;
|
||||
- 无闪屏;
|
||||
- 无 stale cache;
|
||||
- 无偶发层序颠倒;
|
||||
- 无 Particle 停止后恢复不了的问题。
|
||||
|
||||
如果当前工程缺少可靠 GPU memory 数字来源,不要求伪造数值;允许使用资源创建/释放 counter、diagnostics 和稳定运行现象作为当前阶段证据,并明确限制。
|
||||
|
||||
---
|
||||
|
||||
# 17. Diagnostics 要求
|
||||
|
||||
STEP-093 的测试入口必须能清楚观察至少以下信息:
|
||||
|
||||
### Range
|
||||
|
||||
- configured
|
||||
- valid
|
||||
- startLayerId
|
||||
- endLayerId
|
||||
- startIndex
|
||||
- endIndex
|
||||
- continuousBegin
|
||||
- continuousEndExclusive
|
||||
- fallback / lastReason
|
||||
|
||||
### Bottom
|
||||
|
||||
- present
|
||||
- cache state
|
||||
- build count
|
||||
- reuse count
|
||||
- invalidation count
|
||||
- last invalidation reason
|
||||
|
||||
### Top
|
||||
|
||||
- present
|
||||
- cache state
|
||||
- build count
|
||||
- reuse count
|
||||
- invalidation count
|
||||
- last invalidation reason
|
||||
|
||||
### Fast Path
|
||||
|
||||
- eligible / active
|
||||
- fallback reason
|
||||
- full CPU frame count 或等价诊断(若现有已有)
|
||||
- unknown continuous layer 导致 fail-closed 的可观察原因
|
||||
|
||||
如当前已有字段足够,不得重复造一套 diagnostics。
|
||||
|
||||
---
|
||||
|
||||
# 18. Test UI / Playground 要求
|
||||
|
||||
优先复用现有 Particle Test App / validation UI。
|
||||
|
||||
如果需要新增 STEP-093 面板,要求:
|
||||
|
||||
- 不影响生产 MapLibre API 语义;
|
||||
- 不引入 NavSea 业务逻辑;
|
||||
- 场景按钮命名明确;
|
||||
- 能一键切换四种 topology;
|
||||
- 能设置 single-layer range;
|
||||
- 能触发 invalid range;
|
||||
- 能隐藏/显示 endpoints;
|
||||
- 能删除/恢复 endpoint;
|
||||
- 能触发代表性的 reorder / cross-boundary move;
|
||||
- 能显示当前 diagnostics;
|
||||
- 能恢复默认测试场景。
|
||||
|
||||
测试 UI 本身不应成为复杂新框架。
|
||||
|
||||
---
|
||||
|
||||
# 19. Android 运行态验收
|
||||
|
||||
Android 作为本 STEP 首要完整运行态验收平台。
|
||||
|
||||
Codex 必须准备:
|
||||
|
||||
- 明确的 build/install/run 手工命令;
|
||||
- STEP-093 测试入口位置;
|
||||
- 每项场景的操作步骤;
|
||||
- 需要采集的日志/diagnostics;
|
||||
- PASS/FAIL 判定。
|
||||
|
||||
实际 Android Gradle / APK / ADB / 真机或模拟器操作由 RDC / 用户执行。
|
||||
|
||||
不得由 Codex 仅凭代码静态分析代替 Android 实机/模拟器结论。
|
||||
|
||||
---
|
||||
|
||||
# 20. iOS / Metal 运行态验收
|
||||
|
||||
Android 完整通过后,再进行 iOS / Metal 对等测试。
|
||||
|
||||
至少需要验证:四 topology、Single-layer Continuous、Top 覆盖 Particle、Stable cache reuse、Camera invalidation、endpoint missing/recovery、visibility、Fast Path fail-closed、Top alpha。
|
||||
|
||||
如果本 STEP 暂未执行 iOS runtime:
|
||||
|
||||
- 报告必须明确写 `WAITING_IOS_RUNTIME_VALIDATION`;
|
||||
- Android PASS 不得自动推导为 iOS PASS;
|
||||
- Metal native build PASS 也不能替代 Metal runtime rendering PASS。
|
||||
|
||||
---
|
||||
|
||||
# 21. 编译与测试原则
|
||||
|
||||
1. MapLibre Native 的 Android/iOS 开发与编译统一使用 Mac。
|
||||
2. 不安排 Linux Native 编译。
|
||||
3. noisy native build 遵循 Mac/Codex 全局构建输出节流规则。
|
||||
4. 不加入 Jest。
|
||||
5. 可运行已有 Native 精确测试;如代码变更影响 STEP-092 核心逻辑,应重跑相关现有 Native tests。
|
||||
6. 不为了“让测试绿”删除或弱化 STEP-092 已有覆盖。
|
||||
|
||||
---
|
||||
|
||||
# 22. 发现问题时的处理规则
|
||||
|
||||
### 22.1 小型、事实明确、局部问题
|
||||
|
||||
例如测试 UI 接线错误、diagnostics 漏展示、明确计数器错误、局部 invalidation reason 错误、编译错误、测试工具小缺陷,允许直接修复,并重新验收受影响项目。
|
||||
|
||||
### 22.2 架构级问题
|
||||
|
||||
如果发现需要修改 Continuous Range 数学定义、Style 顺序唯一 Z-order、0/1/2 retained cache 模型、renderer 大范围遍历结构、Fast Path 核心模型、第二套 layer metadata、新特殊 layer type、新 RenderGraph 或大规模 GPU cache redesign,必须停止当前修复扩张,先生成明确的“STEP-093 架构阻塞报告”,说明实际问题、复现方式、为什么 STEP-092 架构无法满足、建议方案和影响范围。
|
||||
|
||||
不得在运行态测试任务中擅自重构架构。
|
||||
|
||||
---
|
||||
|
||||
# 23. 验收矩阵
|
||||
|
||||
最终报告必须至少给出以下矩阵:
|
||||
|
||||
| 类别 | 场景 | Android | iOS/Metal | 关键证据 | 结论 |
|
||||
|---|---|---|---|---|---|
|
||||
| Topology | Bottom+Continuous+Top | | | | |
|
||||
| Topology | Bottom+Continuous | | | | |
|
||||
| Topology | Continuous+Top | | | | |
|
||||
| Topology | Continuous-only | | | | |
|
||||
| Range | Single-layer Continuous | | | | |
|
||||
| Z-order | Top covers Particle | | | | |
|
||||
| Cache | Stable reuse | | | | |
|
||||
| Invalidation | Pan/Zoom/Rotate/Pitch | | | | |
|
||||
| Mutation | Continuous internal mutation | | | | |
|
||||
| Mutation | Cross-range move | | | | |
|
||||
| Endpoint | start missing/recovery | | | | |
|
||||
| Endpoint | end missing/recovery | | | | |
|
||||
| Visibility | hidden endpoint topology | | | | |
|
||||
| Invalid | missing/reversed range | | | | |
|
||||
| Fast Path | eligible | | | | |
|
||||
| Fast Path | unknown layer fail-closed | | | | |
|
||||
| Alpha | Top premultiplied composite | | | | |
|
||||
| Stability | repeated operations / long run | | | | |
|
||||
|
||||
状态只允许使用清楚枚举,例如:PASS、FAIL、NOT RUN、BLOCKED、WAITING_ANDROID_RUNTIME_VALIDATION、WAITING_IOS_RUNTIME_VALIDATION。
|
||||
|
||||
---
|
||||
|
||||
# 24. STEP-093 完成条件
|
||||
|
||||
只有满足下面条件,才允许把 STEP-093 标记为 `CLOSED / PASS`:
|
||||
|
||||
1. Android 运行态核心矩阵实际执行并通过;
|
||||
2. 四种 topology 实际验证;
|
||||
3. Single-layer Continuous 实际验证;
|
||||
4. Top 覆盖 Particle 实际验证;
|
||||
5. 0/1/2 cache 与 diagnostics 一致;
|
||||
6. Stable reuse 实际验证;
|
||||
7. Camera invalidation → rebuild → reuse 实际验证;
|
||||
8. endpoint missing/recovery 实际验证;
|
||||
9. visibility 不改变 topology 实际验证;
|
||||
10. invalid range fallback 实际验证;
|
||||
11. Fast Path eligible + fail-closed 实际验证;
|
||||
12. Top alpha 无明显错误;
|
||||
13. 发现的阻塞性缺陷已解决并回归;
|
||||
14. 文档与 Git 收尾完整;
|
||||
15. `HEAD == origin/particle/main`;
|
||||
16. 工作区 clean。
|
||||
|
||||
如果 Android 已全部通过但 iOS 尚未运行,可记录为:`ANDROID CLOSED / PASS + IOS WAITING_RUNTIME_VALIDATION`。不得把未运行的平台写成 PASS。
|
||||
|
||||
---
|
||||
|
||||
# 25. 必做输出
|
||||
|
||||
本任务所有需要 ChatGPT/RDC 后续审查的文件必须由 Codex 在任务中实际生成,不得在结束后临时补要。
|
||||
|
||||
## 25.1 STEP 记录
|
||||
|
||||
生成:
|
||||
|
||||
`docs/50_Step记录/STEP-093_ContinuousRange运行态综合验收_v1.md`
|
||||
|
||||
如果同名主题已有版本,递增为 v2/v3……,不得覆盖旧版。
|
||||
|
||||
内容至少包括:起始 Git 基线、调查到的实际测试入口、新增/修改内容、Android/iOS 实际执行状态、验收矩阵、问题与修复、未执行项、当前状态、下一步。
|
||||
|
||||
## 25.2 Codex 完成报告
|
||||
|
||||
生成:
|
||||
|
||||
`docs/报告/STEP-093_ContinuousRange运行态综合验收_Codex完成报告_v1.md`
|
||||
|
||||
同主题已有版本时递增版本号。
|
||||
|
||||
报告至少包含:任务目标、修改文件、测试入口、diagnostics、build/test 实际结果、Android runtime 实际结果、iOS runtime 实际结果、PASS/FAIL/WAITING 状态、Git commit、push、Final HEAD、origin/particle/main、git status、未执行事项、风险与已知限制。
|
||||
|
||||
## 25.3 开发记录
|
||||
|
||||
追加:
|
||||
|
||||
`粒子开发记录.md`
|
||||
|
||||
只追加,不覆盖历史内容。
|
||||
|
||||
---
|
||||
|
||||
# 26. Git 规则
|
||||
|
||||
严格遵守项目 Git 规则:
|
||||
|
||||
- 只提交本任务相关文件;
|
||||
- 禁止 `git add -A`;
|
||||
- 禁止 `git add .`;
|
||||
- 禁止 `git reset --hard`;
|
||||
- 禁止 `git clean`;
|
||||
- 不触碰无关 dirty/untracked;
|
||||
- 功能代码与文档可按合理 scoped commit 拆分;
|
||||
- push 到 `origin/particle/main`;
|
||||
- push 后确认 `HEAD == origin/particle/main`;
|
||||
- 最终确认 `git status --short`;
|
||||
- push 失败不得标记任务完成。
|
||||
|
||||
---
|
||||
|
||||
# 27. Codex 执行终态规则
|
||||
|
||||
Codex 本轮结束时必须给出真实状态,而不是默认写 PASS。
|
||||
|
||||
建议状态:
|
||||
|
||||
- `IMPLEMENTED / WAITING_RUNTIME_VALIDATION`
|
||||
- `PARTIAL / WAITING_ANDROID_RUNTIME_VALIDATION`
|
||||
- `ANDROID PASS / WAITING_IOS_RUNTIME_VALIDATION`
|
||||
- `CLOSED / PASS`
|
||||
- `BLOCKED`
|
||||
- `FAILED`
|
||||
|
||||
如果只是完成测试工具与代码准备,而 RDC/用户尚未真正运行 Android,则只能标记等待运行态验收。
|
||||
|
||||
---
|
||||
|
||||
# 28. 明确不做
|
||||
|
||||
STEP-093 不做:
|
||||
|
||||
- 任意 N-range planner;
|
||||
- 多 Continuous Range;
|
||||
- frequency class 自动分类;
|
||||
- 自动 Z-order;
|
||||
- anchor/marker layer;
|
||||
- 第二套 BOTTOM/CONTINUOUS/TOP metadata;
|
||||
- NavSea AIS 专用优化;
|
||||
- Alarm 专用优化;
|
||||
- Weather provider;
|
||||
- Route ETA;
|
||||
- LandMask 业务系统;
|
||||
- Wave/Tide 业务系统;
|
||||
- PBF 通用重构;
|
||||
- RenderGraph 大改;
|
||||
- 新粒子算法;
|
||||
- 粒子视觉美化;
|
||||
- unrelated performance tuning;
|
||||
- Jest。
|
||||
|
||||
---
|
||||
|
||||
# 29. 最终核心判定
|
||||
|
||||
STEP-093 最终不是检查“按钮能不能点”,而是验证 STEP-092 的核心架构在真实渲染中确实成立:
|
||||
|
||||
> 普通地图内容可以被合理拆成 Bottom / Top retained cache,Particle 所在 Continuous Range 保持高频实时绘制;Style 顺序仍然决定最终 Z-order;不存在的 retained range 不产生资源;缓存只在需要时失效,并在稳定状态持续复用。
|
||||
|
||||
只要这个结论还没有通过真实运行态证据建立,STEP-093 就不能写成最终 `CLOSED / PASS`。
|
||||
599
docs/40_任务/STEP-094_通用RenderSegment分段渲染与Style分类诊断_Codex任务_v1.md
Normal file
599
docs/40_任务/STEP-094_通用RenderSegment分段渲染与Style分类诊断_Codex任务_v1.md
Normal file
@@ -0,0 +1,599 @@
|
||||
# STEP-094 通用 Render Segment 分段渲染与 Style 分类诊断 — Codex 任务 v1
|
||||
|
||||
## 0. 任务目标
|
||||
|
||||
从已冻结的 Particle V12 `Bottom Retained / Continuous / Top Retained` 单 Continuous Range 模型,演进到最终通用模型:
|
||||
|
||||
```text
|
||||
Style Z-order
|
||||
↓
|
||||
连续 layerIndex
|
||||
↓
|
||||
若干连续 Render Segment
|
||||
↓
|
||||
每个 Segment 的执行策略仅为:
|
||||
CONTINUOUS 或 RETAINED
|
||||
```
|
||||
|
||||
Style 顺序继续是唯一 Z-order 事实来源。Renderer 不理解 LOW / MEDIUM / HIGH,也不理解 AIS、Route、地图、Weather 等业务语义。
|
||||
|
||||
本 STEP 同时必须建立新的 pull-based Segment Diagnostics,能够取回“当前 Style → Segment → Layer”的真实分类快照,供后续人工判断 Segment 划分是否合理。
|
||||
|
||||
## 1. 当前冻结基线
|
||||
|
||||
工作目录:`/Users/tei/dev/MapLibreParticleLab`
|
||||
目标分支:`particle/main`
|
||||
|
||||
当前已知 V12 冻结基线:
|
||||
- HEAD:`7eaff6e12360e26591dceb5e011129eeab7ba4c7`
|
||||
- Commit:`release: finalize unified particle.12`
|
||||
- Tag:`maplibre-particle/rn/13.4.1-particle.12`
|
||||
- `HEAD == origin/particle/main`
|
||||
- 工作区 clean
|
||||
|
||||
开始前必须重新执行 fetch/status/HEAD 检查,以实际仓库为准。禁止 reset/clean,禁止回退、移动、覆盖 V12 Tag。STEP-094 是 V12 之后的新开发,不修改 V12 已发布事实。
|
||||
|
||||
必须先读取:
|
||||
- 根目录 `AGENTS.md`
|
||||
- `ARCHITECTURE.md`
|
||||
- STEP-092 / STEP-093 任务、Step 记录与完成报告
|
||||
- V12 当前 Continuous Range、Retained Cache、Fast Path、Android/iOS/RN bridge 实现
|
||||
- 当前 `RendererRuntime.ts`、`types.ts`、`NativeWeatherRuntimeBridge.ts`
|
||||
|
||||
## 2. 最终架构原则
|
||||
|
||||
1. Style Layer 顺序是唯一 Z-order 真相。
|
||||
2. 整个 Style 必须按连续 layerIndex 被完整划分为 1..N 个 Render Segment。
|
||||
3. 每个 Segment 只声明 `RETAINED` 或 `CONTINUOUS`。
|
||||
4. 允许多个 RETAINED、多个 CONTINUOUS,且可按 Style 顺序任意交错。
|
||||
5. 相邻同策略 Segment 不得自动合并。
|
||||
6. 每个 RETAINED Segment 独立拥有 cache / revision / invalidation / rebuild / GPU resource。
|
||||
7. CONTINUOUS Segment 不创建 retained texture/FBO/compositor。
|
||||
8. Renderer 不自动分析 Layer 更新频率,不自动调整 Z-order。
|
||||
9. App 通过 Style 顺序 + Segment boundary 表达分类。
|
||||
10. 不建立 LOW/MEDIUM/HIGH 第二套业务分类。
|
||||
11. 不硬编码 NavSea Layer ID。
|
||||
12. 不增加空白 Anchor / Marker Layer。
|
||||
|
||||
V12 三明治只是本模型的一个特例:`RETAINED → CONTINUOUS → RETAINED`。
|
||||
|
||||
## 3. 命名约束
|
||||
|
||||
MapLibre 现有代码已经大量使用 shader/bucket 的 `Segment` / `SegmentVector`。本任务禁止新增裸名称 `Segment` 造成语义冲突。
|
||||
|
||||
新模型命名必须明确属于 Renderer execution topology,例如:
|
||||
- `RenderSegmentStrategy`
|
||||
- `RenderSegmentDefinition`
|
||||
- `RenderSegmentPlan`
|
||||
- `ResolvedRenderSegment`
|
||||
- `RenderSegmentDiagnostics`
|
||||
- `RetainedSegmentCache`
|
||||
|
||||
具体文件名可按当前代码结构调整,但必须避免与 geometry/shader Segment 混淆。
|
||||
|
||||
## 4. Segment Plan 公共语义
|
||||
|
||||
推荐公共模型:
|
||||
|
||||
```text
|
||||
RenderSegmentDefinition
|
||||
strategy: RETAINED | CONTINUOUS
|
||||
startLayerId?: string
|
||||
```
|
||||
|
||||
规则:
|
||||
- definitions 至少 1 个。
|
||||
- 第 0 个 Segment 从 Style index 0 开始,`startLayerId` 必须省略/为空。
|
||||
- 第 1..N-1 个 Segment 必须提供非空 `startLayerId`。
|
||||
- startLayerId 必须引用当前 Style 中已经存在的普通 Layer。
|
||||
- Native 根据当前 Style 解析真实 layerIndex。
|
||||
- App/RN 不传数字 index。
|
||||
|
||||
例如:
|
||||
|
||||
```text
|
||||
[
|
||||
{ strategy: RETAINED },
|
||||
{ strategy: CONTINUOUS, startLayerId: "rn-weather-color" },
|
||||
{ strategy: RETAINED, startLayerId: "land-area" },
|
||||
{ strategy: RETAINED, startLayerId: "ais-layer" }
|
||||
]
|
||||
```
|
||||
|
||||
解析后:
|
||||
- Segment 0 = `[0, index(rn-weather-color))`
|
||||
- Segment 1 = `[index(rn-weather-color), index(land-area))`
|
||||
- Segment 2 = `[index(land-area), index(ais-layer))`
|
||||
- Segment 3 = `[index(ais-layer), layerCount)`
|
||||
|
||||
Segment 2/3 虽然都是 RETAINED,也必须保持两个独立 cache,不得合并。
|
||||
|
||||
这种定义保证整个 Style 完整覆盖;插入普通 Layer 后依据 Style 位置自然进入对应 Segment;最顶端新增 Layer自动属于最后一个 Segment。
|
||||
|
||||
## 5. Plan 合法性与 resolve
|
||||
|
||||
有效计划必须满足:
|
||||
- Style ready;
|
||||
- 所有 boundary Layer 存在;
|
||||
- boundary index 严格递增;
|
||||
- 无重复 boundary;
|
||||
- 每个 resolved Segment 非空;
|
||||
- 最终覆盖 `[0, layerCount)`;
|
||||
- 不重叠、不留 gap。
|
||||
|
||||
hidden / visibility=none 的 boundary Layer 仍参与 topology。Visibility 变化不得改变 Segment membership。
|
||||
|
||||
以下情况 plan invalid:
|
||||
- boundary missing;
|
||||
- boundary 顺序反转或重复;
|
||||
- Style 未 ready;
|
||||
- 空 definitions;
|
||||
- 非法 strategy;
|
||||
- JSON/schema 非法。
|
||||
|
||||
invalid 时不得 crash、不得猜 boundary、不得复用 stale cache。必须释放/隔离失效 topology 的 retained resource,安全走 full renderer,并在 diagnostics 给出明确 reason。Style 后续恢复合法时允许自动重新 resolve。
|
||||
|
||||
必须有 monotonic `configRevision`。Style add/remove/reorder 后重新 resolve;实际 begin/end 变化时推进 `topologyRevision`。Visibility 变化不得推进 topologyRevision。
|
||||
|
||||
## 6. Renderer 执行模型
|
||||
|
||||
Resolved Segment 按 Style 顺序执行:
|
||||
|
||||
```text
|
||||
for segment in resolvedSegments:
|
||||
RETAINED -> build/reuse cache -> composite
|
||||
CONTINUOUS -> direct render
|
||||
```
|
||||
|
||||
逻辑上按 Segment 顺序组合,但实际实现必须继续满足 MapLibre 原有 RenderPass 语义。
|
||||
|
||||
必须保持:
|
||||
- 全 Style 原始 global layerIndex;
|
||||
- 原始 ordinal;
|
||||
- `parameters.currentLayer`;
|
||||
- `depthRangeSize`;
|
||||
- Opaque reverse traversal;
|
||||
- Translucent forward traversal。
|
||||
|
||||
禁止每个 Segment 从 0 重新编号,禁止为了 Segment 模型重新排序 Style Layer。
|
||||
|
||||
## 7. RETAINED Segment 独立 Cache
|
||||
|
||||
每个 RETAINED Segment 必须拥有独立:
|
||||
- cache state;
|
||||
- content revision;
|
||||
- build/reuse/invalidation count;
|
||||
- last invalidation reason;
|
||||
- texture/compositor create count;
|
||||
- resource release count。
|
||||
|
||||
GPU resource 必须 lazy 创建。CONTINUOUS Segment 绝不能因为占位而创建 retained texture。
|
||||
|
||||
Plan/topology 改变后,不能证明安全复用的旧 cache 必须释放并重建;stale segment cache 绝不能错误 composite。本 STEP 不要求复杂 cache migration,correctness-first。
|
||||
|
||||
## 8. Invalidation 粒度
|
||||
|
||||
目标是 Segment 级 invalidation。
|
||||
|
||||
能够明确定位影响 Segment 时,只 invalidate 受影响 RETAINED Segment;其他 RETAINED Segment 保持 valid/reuse。
|
||||
|
||||
LayerMutation 若明确属于某 Segment,应只使该 Segment dirty。Source/Tile mutation 若能从现有 RenderLayer/Source 关系安全确定影响的多个 Segment,应 invalidate 对应集合。
|
||||
|
||||
若无法可靠确定影响范围,允许保守 invalidate 全部 RETAINED Segment。本 STEP 不建立复杂通用 dependency graph。
|
||||
|
||||
camera/transform/viewport/resize/pixel ratio、style reload、image/sprite/glyph、placement/symbol fade/transition、context reset、debug options、unsupported 3D、resource/compositor failure 等无法证明局部安全时可 invalidate 全部 retained。
|
||||
|
||||
完成报告必须区分“已实现的 segment-local invalidation”和“仍采用全 retained invalidation 的原因”。
|
||||
|
||||
## 9. Cache ON/OFF
|
||||
|
||||
现有 Cache ON/OFF 总开关继续有效。
|
||||
|
||||
Cache OFF:
|
||||
- Segment Plan 仍然 resolve;
|
||||
- Diagnostics 仍能看到 Style/Segment/Layer 分类;
|
||||
- RETAINED Segment 不错误复用 retained GPU cache;
|
||||
- Renderer 安全走非 retained 路径。
|
||||
|
||||
Cache ON:
|
||||
- 每个 RETAINED Segment 独立 build/reuse;
|
||||
- CONTINUOUS Segment 直接绘制。
|
||||
|
||||
关闭所有 CONTINUOUS Layer 的 visibility 不得删除 Segment topology。
|
||||
|
||||
## 10. V12 兼容入口
|
||||
|
||||
不得破坏现有:
|
||||
- `setRetainedUnderlayEnabled()`
|
||||
- `setContinuousLayerRange(startLayerId,endLayerId)`
|
||||
- `getRetainedUnderlayDiagnostics()`
|
||||
|
||||
新内部实现应尽量只有一套 Segment topology。
|
||||
|
||||
`setContinuousLayerRange(start,end)` 应作为 compatibility adapter,转换成等价 RenderSegmentPlan:
|
||||
- start 前存在 → RETAINED
|
||||
- start..end → CONTINUOUS
|
||||
- end 后存在 → RETAINED
|
||||
|
||||
自然退化成 V12 原有 1/2/3 Range。
|
||||
|
||||
旧 API 与新 Segment Plan 不得同时形成两套 Renderer topology。最后一次显式 topology 配置必须只有一个 active plan,并从 diagnostics 可见 `configSource`。
|
||||
|
||||
旧 `getRetainedUnderlayDiagnostics()` 保留兼容摘要;新的多 Segment 事实以新 Diagnostics API 为准。
|
||||
|
||||
## 11. 新 RN / Native API
|
||||
|
||||
新增稳定公共语义:
|
||||
|
||||
```text
|
||||
RendererRuntime.setRenderSegmentPlan(reactTag, plan)
|
||||
RendererRuntime.getRenderSegmentDiagnostics(reactTag)
|
||||
```
|
||||
|
||||
建议 TS:
|
||||
|
||||
```text
|
||||
RenderSegmentStrategy = "RETAINED" | "CONTINUOUS"
|
||||
|
||||
RenderSegmentDefinition = {
|
||||
strategy: RenderSegmentStrategy
|
||||
startLayerId?: string
|
||||
}
|
||||
|
||||
RenderSegmentPlan = {
|
||||
schemaVersion: 1
|
||||
segments: readonly RenderSegmentDefinition[]
|
||||
}
|
||||
```
|
||||
|
||||
RN public API 必须是 typed object。TurboModule ABI 可使用 JSON string 作为 plan/snapshot 内部传输,避免可扩展嵌套数组变成脆弱 parallel arrays。若采用 JSON,必须有 schemaVersion 与严格校验;public TS 不暴露 raw JSON。
|
||||
|
||||
必须沿当前真实链路扩展:
|
||||
`RendererRuntime → NativeWeatherRuntimeBridge/TurboModule → Android/iOS package bridge → MapLibre platform API → Core Renderer state`。
|
||||
|
||||
不得新建第二套独立 RN bridge。
|
||||
|
||||
## 12. Segment Diagnostics — 强制要求
|
||||
|
||||
这是 STEP-094 的强制验收项,不是附加日志。
|
||||
|
||||
新增 pull-based `getRenderSegmentDiagnostics()`,必须能够取回当前真实 Style 的:
|
||||
1. Style 全 Layer 顺序;
|
||||
2. 当前 resolved Segment 顺序;
|
||||
3. 每个 Segment 的 strategy;
|
||||
4. 每个 Segment 的 begin/end;
|
||||
5. 每个 Segment 内实际包含的 Layer;
|
||||
6. 每个 RETAINED Segment 的 cache 状态与统计。
|
||||
|
||||
Diagnostics 只能按需读取。禁止 per-frame 打印完整 Style/Segment/Layer,禁止每帧构造大 JSON/大字符串。
|
||||
|
||||
## 13. Diagnostics Snapshot 最低字段
|
||||
|
||||
顶层至少:
|
||||
|
||||
```text
|
||||
schemaVersion
|
||||
enabled
|
||||
configured
|
||||
valid
|
||||
configSource
|
||||
configRevision
|
||||
topologyRevision
|
||||
styleLayerCount
|
||||
segmentCount
|
||||
retainedSegmentCount
|
||||
continuousSegmentCount
|
||||
coverageComplete
|
||||
fallback
|
||||
lastReason
|
||||
fastPathEligible
|
||||
fastPathActive
|
||||
fastPathFallbackReason
|
||||
fastPathHit
|
||||
fastPathMiss
|
||||
fullCpuFrame
|
||||
```
|
||||
|
||||
`configSource` 至少区分:
|
||||
- `explicit-render-segment-plan`
|
||||
- `continuous-range-compat`
|
||||
- `unconfigured/legacy`(若仍存在)
|
||||
|
||||
必须提供完整 ordered `styleLayers[]`,用于 plan invalid 时仍可人工分析 Style。
|
||||
|
||||
## 14. Style Layer Diagnostics
|
||||
|
||||
每个 `styleLayers[]` / Segment 内 `layers[]` 至少返回:
|
||||
|
||||
```text
|
||||
layerIndex
|
||||
layerId
|
||||
layerType
|
||||
visible
|
||||
```
|
||||
|
||||
如当前 Style/Core 可低成本稳定取得,还应返回:
|
||||
- `sourceId`
|
||||
- `sourceLayer`
|
||||
|
||||
不适用时可为空/null,禁止伪造。Layer Diagnostics 只描述通用 Style 信息,禁止加入 AIS/Route/Wind 等业务判断。
|
||||
|
||||
## 15. 每个 Segment Diagnostics
|
||||
|
||||
每个 Segment 至少:
|
||||
|
||||
```text
|
||||
segmentIndex
|
||||
strategy
|
||||
configuredStartLayerId
|
||||
beginIndex
|
||||
endExclusive
|
||||
resolvedStartLayerId
|
||||
resolvedEndLayerId
|
||||
layerCount
|
||||
layers[]
|
||||
```
|
||||
|
||||
如果 `strategy=RETAINED`,还必须有:
|
||||
|
||||
```text
|
||||
cacheState
|
||||
contentRevision
|
||||
build
|
||||
reuse
|
||||
invalidation
|
||||
lastInvalidationReason
|
||||
textureCreate
|
||||
compositeCreate
|
||||
resourceRelease
|
||||
```
|
||||
|
||||
如果 `strategy=CONTINUOUS`,不得伪装成 disabled retained cache。retained-only 字段应为 null/not-applicable,或由 TS discriminated union 排除。
|
||||
|
||||
## 16. Diagnostics 一致性
|
||||
|
||||
当 plan valid:
|
||||
- `styleLayers.length == styleLayerCount`
|
||||
- Segment 按 segmentIndex/beginIndex 严格递增
|
||||
- 所有 `segment.layers[]` 拼接后必须与 `styleLayers[]` 完全一致
|
||||
- 每个 Style Layer 必须且只能属于一个 Segment
|
||||
- `segment.layerCount == segment.layers.length`
|
||||
- `coverageComplete=true`
|
||||
|
||||
当 plan invalid:
|
||||
- 仍尽可能返回当前 ordered styleLayers
|
||||
- segments 可为空或只返回已安全解析部分,但必须 `valid=false`
|
||||
- 禁止把猜测结果标成 valid
|
||||
|
||||
## 17. DEBUG 使用目标
|
||||
|
||||
必须能取回类似:
|
||||
|
||||
```text
|
||||
Segment 0 RETAINED [0..18)
|
||||
background
|
||||
water
|
||||
coastline
|
||||
|
||||
Segment 1 CONTINUOUS [18..20)
|
||||
rn-weather-color
|
||||
rn-weather-particles
|
||||
|
||||
Segment 2 RETAINED [20..27)
|
||||
land-area
|
||||
navigation-marks
|
||||
route
|
||||
|
||||
Segment 3 RETAINED [27..30)
|
||||
ais
|
||||
radar
|
||||
alarm
|
||||
```
|
||||
|
||||
此信息只用于人工分析 Style 分类是否合理。Renderer 不根据 Debug 结果自动重新分类。
|
||||
|
||||
设计文档必须给出至少一个完整 JSON/TS snapshot 示例。
|
||||
|
||||
## 18. Particle Fast Path
|
||||
|
||||
V12 已有 Particle Fast Path 不得无声退化。
|
||||
|
||||
至少保证:
|
||||
- 对“恰好一个 CONTINUOUS Segment + 其余为 RETAINED”的 V12 等价拓扑,且 Continuous 内容满足现有支持条件时,现有 Fast Path 能继续工作。
|
||||
- 不存在某个 retained cache 时不得成为 miss 原因。
|
||||
|
||||
对于多个 CONTINUOUS Segment,若当前 Fast Path 无法安全支持,允许 fail closed:
|
||||
- `fastPathActive=false`
|
||||
- 明确 fallback reason
|
||||
- 正常 full CPU frame
|
||||
- Segment topology / retained cache / Z-order 仍正确
|
||||
|
||||
禁止为了 STEP-094 一次性重写整个 RenderGraph 或实现所有 N-Continuous Fast Path 优化。
|
||||
|
||||
## 19. Style Mutation
|
||||
|
||||
必须验证:
|
||||
- Segment 内新增/删除/reorder Layer
|
||||
- boundary 前后插入 Layer
|
||||
- boundary Layer hidden
|
||||
- boundary Layer 删除/重新出现
|
||||
- boundary reorder 导致 plan invalid
|
||||
- plan 更新后恢复
|
||||
|
||||
普通 Layer 插入 Segment 内后应自然成为该 Segment 成员,不要求 App 给每个 Layer 单独注册频率 metadata。
|
||||
|
||||
相邻 RETAINED Segment 即使策略相同,也必须依靠显式 boundary 保持独立。
|
||||
|
||||
## 20. GPU 生命周期
|
||||
|
||||
实际 retained GPU resource 数量应与当前 resolved plan 中 RETAINED Segment 数量一致,允许 lazy 未创建状态,但不得为 CONTINUOUS Segment 分配 retained target。
|
||||
|
||||
Plan topology 变化后 stale resource 必须正确 release。稳定帧不得重复创建 retained texture/FBO/compositor。OpenGL 与 Metal 行为必须一致。
|
||||
|
||||
## 21. Native Directed Tests(不加入 Jest)
|
||||
|
||||
本任务明确不加入 Jest。
|
||||
|
||||
至少覆盖:
|
||||
1. 单 RETAINED Segment 覆盖全 Style
|
||||
2. 单 CONTINUOUS Segment 覆盖全 Style
|
||||
3. V12 等价 R-C-R
|
||||
4. C-R-R
|
||||
5. R-R-C
|
||||
6. R-C-R-C-R 多段交错
|
||||
7. 相邻 R-R 不自动 merge
|
||||
8. hidden boundary 不改变 topology
|
||||
9. boundary missing / duplicate / 顺序非法
|
||||
10. Style 内插入 Layer 后 membership 正确
|
||||
11. boundary 删除后 fail safe、恢复后重新 resolve
|
||||
12. retained resource 数量匹配 RETAINED Segment 数
|
||||
13. CONTINUOUS 无 retained resource
|
||||
14. Cache OFF 时 topology diagnostics 仍完整
|
||||
15. V12 `setContinuousLayerRange` 兼容映射
|
||||
16. global layerIndex / Opaque / Translucent 语义不变
|
||||
17. 多 CONTINUOUS Fast Path 安全 fallback
|
||||
|
||||
Diagnostics tests 额外验证:
|
||||
- styleLayers 顺序精确
|
||||
- segments 顺序精确
|
||||
- 每个 Segment 的 layers 成员精确
|
||||
- 拼接 segment.layers == styleLayers
|
||||
- layerIndex 不重编号
|
||||
- layerId/layerType/visible 正确
|
||||
- sourceId/sourceLayer(若实现)正确
|
||||
- invalid plan 仍能取得 ordered styleLayers
|
||||
- snapshot pull 不产生 per-frame 日志
|
||||
|
||||
## 22. 平台与 RN 验证
|
||||
|
||||
Core/C++:
|
||||
- 执行规定的 Mac 原生精确编译与 Native tests
|
||||
- OpenGL / Metal
|
||||
- 编译输出遵循全局 build-output-throttle
|
||||
|
||||
Android:
|
||||
- 完成源码、JNI/Kotlin/Java/RN bridge 修改
|
||||
- Android Gradle 编译由用户手工执行,不由 Codex 运行
|
||||
- 报告列明“未执行 Android 编译/实体机”直到用户验证
|
||||
|
||||
iOS:
|
||||
- 只面向 iPhone/iPad arm64 真机架构
|
||||
- 不要求 Simulator slice
|
||||
- 可执行当前工程已有的 Mac/iOS arm64 精确编译验证
|
||||
- 不伪造实体机运行 PASS
|
||||
|
||||
## 23. 最小 Debug/Validation 入口
|
||||
|
||||
在当前已有 Particle validation/Test Host 体系中补一个最小 pull 入口:
|
||||
- Fetch Segment Diagnostics
|
||||
- 展示或输出一次完整 snapshot
|
||||
- 能看到 Segment 列表和每个 Segment 的 Layer 列表
|
||||
|
||||
不得增加持续轮询,不得把大 snapshot 每帧写日志。若现有 UI 已有 diagnostics panel,优先复用。
|
||||
|
||||
## 24. 性能要求
|
||||
|
||||
Segment topology/style diagnostic 数据只在 config/style topology 或相关 debug metadata 变化时更新,或在显式 diagnostics pull 时做必要轻量组装。
|
||||
|
||||
禁止因为 Particle 30/60fps:
|
||||
- 每帧扫描/复制完整 Style Layer 字符串
|
||||
- 每帧 JSON stringify
|
||||
- 每帧创建 Segment diagnostics 大对象
|
||||
- 每帧 Logger 输出 topology
|
||||
|
||||
高频 render path 只消费预解析的轻量 segment ranges/cache state。
|
||||
|
||||
## 25. 明确不做
|
||||
|
||||
本 STEP 不做:
|
||||
- LOW/MEDIUM/HIGH 枚举或自动频率分类
|
||||
- Renderer 识别 NavSea 业务 Layer
|
||||
- 自动调整 Style Z-order
|
||||
- Anchor/Marker Layer
|
||||
- 复杂通用业务 dependency graph
|
||||
- Weather provider / Route ETA / AIS 专项 renderer
|
||||
- PBF 重生成
|
||||
- 通用 RenderGraph 重写
|
||||
- 自动合并同策略 Segment
|
||||
- 多 Continuous 的高级 Fast Path 优化
|
||||
- per-frame Segment debug log
|
||||
- Jest
|
||||
|
||||
如发现必须突破这些边界才能保证 correctness,停止扩大范围并在报告明确 BLOCKED/PARTIAL。
|
||||
|
||||
## 26. 必做文档
|
||||
|
||||
新增:
|
||||
- `docs/30_设计/STEP-094_通用RenderSegment模型与Style分类Diagnostics设计_v1.md`
|
||||
- `docs/50_Step记录/STEP-094_通用RenderSegment分段渲染与Style分类诊断_v1.md`
|
||||
- `docs/报告/STEP-094_通用RenderSegment分段渲染与Style分类诊断_Codex完成报告_v1.md`
|
||||
|
||||
追加:
|
||||
- `粒子开发记录.md`
|
||||
|
||||
同主题已有文件则递增版本,不覆盖历史。所有文档中文。
|
||||
|
||||
设计文档必须包含最终 Segment 数据模型、config/resolve 生命周期、V12 compatibility mapping、cache ownership、invalidation、Fast Path 边界、Android/iOS/RN API、Diagnostics schema、完整 Style→Segment→Layer snapshot 示例。
|
||||
|
||||
## 27. 完成报告必须记录
|
||||
|
||||
- 起始 HEAD / 最终 HEAD
|
||||
- 修改文件清单
|
||||
- RenderSegment config API
|
||||
- boundary Layer ID → layerIndex 解析
|
||||
- N Segment 遍历
|
||||
- retained cache ownership/resource lifecycle
|
||||
- segment-local 与 global invalidation
|
||||
- V12 compatibility / Fast Path
|
||||
- Diagnostics schema
|
||||
- styleLayers / segment.layers 验证
|
||||
- RN/Android/iOS bridge
|
||||
- OpenGL / Metal
|
||||
- Native test 数量与结果
|
||||
- 未执行 Android/实体机项目
|
||||
- known limitations
|
||||
- commit SHA/message
|
||||
- push result / remote branch
|
||||
- origin/particle/main Final HEAD
|
||||
- 最终 `git status --short`
|
||||
|
||||
## 28. Git 规则
|
||||
|
||||
仅 scoped staging。禁止 `git add -A`、`git add .`、`git reset --hard`、`git clean`,不得删除/覆盖无关 dirty/untracked。
|
||||
|
||||
结束前必须:
|
||||
1. `git diff --check`
|
||||
2. 核对 diff
|
||||
3. 只 stage 本任务文件
|
||||
4. scoped commit
|
||||
5. push `origin particle/main`
|
||||
6. fetch/确认远端
|
||||
|
||||
最终要求:
|
||||
```text
|
||||
HEAD == origin/particle/main
|
||||
git status --short 无本任务残留
|
||||
```
|
||||
|
||||
push 失败不得声称完成。
|
||||
|
||||
## 29. PASS Gate
|
||||
|
||||
只有以下全部成立才可判 STEP-094 PASS:
|
||||
- Style Z-order 仍唯一
|
||||
- 1..N 连续 Segment 完整覆盖
|
||||
- RETAINED/CONTINUOUS 可任意交错
|
||||
- 相邻同策略 Segment 不自动合并
|
||||
- 每个 RETAINED Segment 独立 cache/revision/invalidation/resource
|
||||
- V12 Continuous Range 兼容
|
||||
- V12 等价 Fast Path 不无声退化
|
||||
- 新 Diagnostics 能真实返回 Style→Segment→Layer
|
||||
- diagnostics pull-based、无 per-frame 大开销
|
||||
- Android/iOS/RN API 完整
|
||||
- Native directed tests 通过
|
||||
- 规定编译验证完成
|
||||
- 文档完成
|
||||
- scoped commit/push 完成
|
||||
- HEAD 与远端一致
|
||||
|
||||
关键项未完成必须明确 PARTIAL/BLOCKED,禁止伪造 PASS。
|
||||
109
docs/40_任务/STEP-095_V13独立严格代码与性能审查_Codex任务_v1.md
Normal file
109
docs/40_任务/STEP-095_V13独立严格代码与性能审查_Codex任务_v1.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# STEP-095 V13 独立严格代码与性能审查 — Codex 任务 v1
|
||||
|
||||
## 0. 工作性质与模型
|
||||
- 本任务是独立、严格、全链路、以源码为最终证据的只读代码/性能调查,不是修复任务,也不是批准已有报告。
|
||||
- 执行环境:Mac-mini-tei.lan,仓库 /Users/tei/dev/MapLibreParticleLab,分支 particle/main。
|
||||
- 运行模型:gpt-6-astra,reasoning effort=high;由外部启动命令实际指定,报告记录实际模型。
|
||||
- 最终交付物必须为中文,具体源码符号、文件路径、命令、证据可保留英文。
|
||||
- 不以“编译通过”“定向单元测试通过”“已有 Codex 报告 PASS”推断 GPU 运行态、安全性、性能或跨平台一致性已经通过。
|
||||
|
||||
## 1. 源码冻结基线
|
||||
- 被审查的 V13 代码提交:ee0b89e711bf01335b3aa71d36def785cd97fec3。
|
||||
- V13 核心功能提交:016fd1fff6a33cf795c10d4f5c5413d308383677。
|
||||
- V13 发布标识:RN 13.4.1-particle.13;iOS 6.28.0-particle.13;以实际 manifest/tag/API 核实,不假设已在 NavSea 接入。
|
||||
- 前置独立审查归档提交:45c4dca84928a57a2cf0226737c34cabd7fbfbfb。
|
||||
- 审查前读取 AGENTS.md、PARTICLE_BASELINE.md、docs/00_项目规范/、docs/00_项目总则/README.md 与其中标记的当前有效总则。
|
||||
- 同步检查 git branch、HEAD、origin/particle/main、working tree、release manifest 和相关 tag;记录所有偏差。
|
||||
- 本任务自身新增文档提交不改变上述被审查的源码 commit;核查源码时固定以该提交为事实基线。
|
||||
|
||||
## 2. 审查独立性:先源码,后已有报告
|
||||
- 首先依据 Git V12→V13 改动、当前调用链及实际代码独立扫描,不从旧报告结论直接生成 findings。
|
||||
- 然后阅读 docs/20_调查/Particle_V13_严格代码审查报告_v1.md,逐项复核 F01–F10:确认、修正、否定或标记需动态验证。
|
||||
- 不能复制上一份报告来冒充独立 review。需要发现旧报告遗漏、误判、证据不足及其他模块的潜在风险。
|
||||
- 交叉阅读 STEP-088~094 的任务、设计、Step 记录、Codex 报告及相关发布/验证事实。
|
||||
- 当任务书与代码冲突,分别说明“设计意图”和“实际实现”;不能把历史数据冒充 V13 测量。
|
||||
- 可以读取 Integration repo 的 contract/status/compatibility,但不得修改该仓库;发现跨平台契约问题只提升级建议。
|
||||
|
||||
## 3. 强制审查范围 A:Renderer 与 V13 Segment
|
||||
- 通用 RenderSegment plan、JSON parser/resolver、配置切换、missing/reorder/hidden boundary、valid/invalid/recovery。
|
||||
- Style 全局 Z-order、layerIndex、LayerGroup ordinal、opaque reverse、translucent forward、depth/stencil/blending、debug/legacy/3D 与 offscreen composite 的一致性。
|
||||
- 任意 R/C 交错:全 R、全 C、R-C-R、C-R-R、R-R-C、R-C-R-C-R、相邻 R-R;缓存 OFF 和资源失败的 full fallback。
|
||||
- Retained cache 独立生命周期、build/reuse/invalidate/release、topology generation、content fingerprint、diagnostics counters 的归属。
|
||||
- V12 连续区间兼容映射、单 Continuous Fast Path、多个 Continuous 安全 fallback,注意 silent fallback 与正常渲染的真实成本。
|
||||
- 特别核查 Segment pass 分割后的 alpha/depth/stencil 语义、source/layer mutation 与下一帧 stale GPU object 风险。
|
||||
## 4. 强制审查范围 B:GPU、粒子和时间场
|
||||
- OpenGL 与 Metal OffscreenTexture、Framebuffer、Texture、render pass、commit/present/wait、resource destroy/context loss;区分通用 Core 与平台实现。
|
||||
- 是否存在 CPU/GPU 同步等待、重复 GPU allocation、每帧 upload/UBO/drawable 重建、纹理引用失效和资源峰值。
|
||||
- Particle State A/B、Trail、Field T0/T1/MIX、spatial sampling、forecast/playback、zoom-out reseed、viewport bounds 和 resize/orientation。
|
||||
- Wind/Wave/Current/Rain 等 canonical FieldProfile;V3/V4 encoded/decoded field、QBIN decoder、projection/materialization、binding/validity/missing data。
|
||||
- 检查异常或损坏输入、极大网格/时间、浮点 NaN/Inf、整数溢出、文件/缓冲区生命周期、重复/乱序/过期 field、安全失败和内存占用。
|
||||
- 需要从 Source→Runtime→RenderLayer→Renderer→GPU 按真实调用链追踪,不接受只搜索名称的浅层判断。
|
||||
|
||||
## 5. 强制审查范围 C:高频、线程和 Host API
|
||||
- 每帧 CPU 分配/深复制/锁竞争/字符串或 JSON 构造、LayerGroup 遍历复杂度、静态 CPU Fast Path 命中与错过原因。
|
||||
- style/source/tile/image/glyph/placement 失效范围是否正确,segment-local 与全局 revision 的边界是否一致。
|
||||
- map-scoped lifetime、prepared RenderTree、Runtime/renderer/control state 的并发、线程归属、异步回调、销毁后引用和重复 attach/detach。
|
||||
- Android Java/Kotlin/JNI 与 iOS ObjC++/Metal 的桥接接口、错误归一化、RN TurboModule typed API、ABI/codegen 与版本锁。
|
||||
- Test Host 的依赖及 lockfile、构建结果的可复现性、release manifest/npm/AAR/XCFramework 的版本一致性。
|
||||
- 不将业务 provider、HTTP、Route/ETA 带入 Shared Core;不建议建立新的隐藏私有桥接或第二套协议。
|
||||
|
||||
## 6. 强制审查问题清单
|
||||
- 是否存在 V13 上线后能够导致画面层级错误、透明度/遮挡错误、丢帧、黑屏、闪烁、过期 cache composite、GPU/CPU 泄漏、崩溃或错误数据的实际源码路径?
|
||||
- 非法配置是否真正 fail closed?同一 index 换不同 Segment 时旧 cache/diagnostics 归属是否错误?
|
||||
- Retained contentDirty 是否被误当作 GPU allocation invalid?Metal 逐 cache wait 是否可证实?需要哪些 profiler 证据才能决定修改?
|
||||
- 高频 Fast Path 是否复制 full diagnostics、扫描 Style 或反复线性搜索?有无更重的热点在既有报告中遗漏?
|
||||
- 多段 retained 是否存在全 viewport depth/stencil 过度分配;是否有峰值预算、安全回退及 resource accounting?
|
||||
- source/tile 局部更新是否引起不必要全 retained rebuild?若建议局部化,必须给出不破坏 correctness 的依赖证据。
|
||||
- 单 Continuous 与多 Continuous 的 Frame 处理在相同真实 Style 下有什么可测的差异?不能外推 STEP-090 历史 A/B 数字。
|
||||
- release、Test Host、RN/iOS/Android 版本与实际产物是否能被独立复现?尚未测试的必须标 NOT RUN。
|
||||
|
||||
## 7. 审查方法
|
||||
1. 建立 V12→V13 变更与模块地图,阅读入口、类型定义、状态机、调用方和析构路径。
|
||||
2. 针对每个怀疑的问题制作最短触发条件和源码调用链;尝试找反例,禁止只因为名称相似就报告漏洞。
|
||||
3. 按已证实缺陷、源码确定的成本、真实运行影响待验证、显式设计限制、文档/环境偏差分类。
|
||||
4. 为每条 finding 写严重度 P0/P1/P2/P3、置信度、源文件精确行范围、触发场景、影响、最小安全修复方向和验收方法。
|
||||
5. 独立比较上一份 F01–F10,标为 CONFIRMED / QUALIFIED / DISPROVED / NEEDS_RUNTIME,说明依据和新增发现。
|
||||
6. 对潜在收益只给复杂度/估算表达和建议基准测试,不提供无 profiler 支持的 FPS/CPU/GPU 节省百分比。
|
||||
7. 所有未亲自执行的测试必须标 HISTORY / NOT RUN;已有报告的 PASS 只能列为历史证据,不替代当前独立执行。
|
||||
|
||||
## 8. 运行与修改边界
|
||||
- 禁止修改任何生产源码、shader、公共 API、构建配置、package/lockfile、release manifest、tag、发布产物或 Integration 文件。
|
||||
- 禁止运行耗时 CMake/Xcode/Gradle 编译、安装、设备操作和大型测试;由 RDC/用户另行验证。
|
||||
- 允许只读 git show/log/diff、文件检索、源码静态分析和查看既有日志、测试源码、报告。
|
||||
- 如判断必须借助 profiler、真机或编译才可定论,写精确验证方案而不是擅自实施。
|
||||
- 仅允许新增本 STEP 中文调查/Step/完成报告,按规定追加根目录粒子开发记录;不要顺手修复发现的问题。
|
||||
## 9. 完成报告格式(必须实际落盘)
|
||||
- 执行摘要:哪些是确定缺陷、哪些是性能成本、哪些只能动态验证;不能只给结论口号。
|
||||
- 基线与范围:审查 commit、分支、release manifest、审查的主要文件/代码路径、未覆盖范围。
|
||||
- Finding 总表:编号、严重度、证据类别、问题、触发、影响、代码定位、修复方向、验证方法。
|
||||
- 逐条完整分析:至少包含最短可复核调用链及确认或推翻依据;新问题与旧报告重复项区分。
|
||||
- V13 与 V12 架构/性能成本比较:不确定的写待测;不要虚构性能数值。
|
||||
- F01~F10 独立复核矩阵:原结论、当前意见、依据、是否需补充/修正。
|
||||
- 缺失测试与运行态矩阵:GPU correctness、CPU/GPU frame time、allocation/peak memory、30/60 FPS、长时间稳定性、各平台状态。
|
||||
- 优先级清单:分 correctness gate、无需设备的安全优化、GPU 资源/同步、较大架构改造。
|
||||
- 具体建议的下一轮 Codex 修复批次(只建议,不创建修复任务,不改源码)。
|
||||
- Git:起始/最终 HEAD、docs-only diff 清单、commit SHA/message、push 结果、remote HEAD、最终 status。
|
||||
- 结论状态:调查 PASS/PARTIAL/BLOCKED 与 V13 产品运行态是否通过必须分开。
|
||||
|
||||
## 10. 必做输出和固定路径
|
||||
- 调查主文档:docs/20_调查/STEP-095_V13独立严格代码与性能调查_v1.md。
|
||||
- 阶段记录:docs/50_Step记录/STEP-095_V13独立严格代码与性能审查_v1.md。
|
||||
- Codex 完成报告:docs/报告/STEP-095_V13独立严格代码与性能审查_Codex完成报告_v1.md。
|
||||
- 追加:根目录 粒子开发记录.md,短而具体地记录本次调查变化、现状及下一步,不覆盖历史。
|
||||
- 若同主题相同文件已经存在,改用递增版本号并在报告写明,不覆盖旧报告。
|
||||
- 以上文件全部中文;任何报告必须明确区分事实、推断和待验证。
|
||||
|
||||
## 11. Git 收尾
|
||||
- 先确保用户现有 dirty/untracked 不被覆盖;只有本任务新增/追加文档允许 staged。
|
||||
- 不执行 git reset --hard、git clean、force push、tag 移动或向 upstream push。
|
||||
- 对所有新增文档进行路径、目录、内容、交叉引用检查;git diff --check。
|
||||
- 仅 git add 上述具体文档和本次追加的开发记录;scoped commit 后 push origin particle/main。
|
||||
- push 失败或 HEAD 与远端不一致时,不得写 PASS;明确失败与恢复建议。
|
||||
- 记录源代码相对于原 V13 源码提交没有改变,禁止把 docs-only commit 误写成新 V13 软件发布。
|
||||
- 最终由 RDC 独立检查报告证据、Git 与任务边界,Codex 不自行宣布正式产品验收。
|
||||
|
||||
## 12. 停止条件
|
||||
- 无法确认当前 commit、源代码状态与所审查发布版本对应时,停止并报告 BLOCKED。
|
||||
- 遇到跨项目协议变更/需要修改业务源码时,只形成接口阻塞或修复建议,不扩大任务。
|
||||
- 证据不足时明确 NEEDS_RUNTIME,不应为了凑数量虚构问题。
|
||||
- 重大 P0 或明确破坏性问题优先直接写进报告,但本任务仍只读,不自动修复。
|
||||
59
docs/40_任务/STEP-096_V13隔离盲审_Codex任务_v1.md
Normal file
59
docs/40_任务/STEP-096_V13隔离盲审_Codex任务_v1.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# STEP-096 V13 隔离盲审 — Codex 任务 v1
|
||||
|
||||
## 0. 工作性质与隔离目标
|
||||
|
||||
这是针对已冻结 V13 源码的全新、只读、源码优先的代码与性能审查。发现必须来自当前隔离目录内的源代码和规范,不允许从任何既有审查结论、聊天记录或外部仓库导入问题清单。不得为凑数量制造 finding。
|
||||
|
||||
- 执行模型:全新 Codex TUI 会话,`gpt-6-astra`,reasoning effort=`high`;RDC 负责实际启动与记录,Codex 报告仅记录能核实的启动事实。
|
||||
- 唯一工作目录:`/Users/tei/dev/MapLibreParticleV13Blind096`。
|
||||
- 目标源码版本:`ee0b89e711bf01335b3aa71d36def785cd97fec3`;由 RDC 从该 Git 对象导出**无 .git 元数据**的源码快照。当前隔离目录不是主仓库、不是 Git worktree;不得在此运行 git,也不得用其他路径反查仓库历史。
|
||||
- 唯一任务说明:本文件。用户和外部 RDC 将在审查开始前检查隔离树不含已有调查、阶段记录、完成报告、任务历史或开发日志。
|
||||
- 禁止访问隔离目录之外的所有源码、文档、Git metadata、会话日志、终端回放、网页、连接器、用户历史或其他 AI 输出;不要尝试恢复缺失文档。对任何未在快照内的证据写 NOT AVAILABLE。
|
||||
- 不得读取、搜索或比较其他轮次的审查材料;**本任务只有盲审,不包含解除盲态后的交叉比较。** 完成后的文件由 RDC 先冻结散列,再独立移交正式仓库。
|
||||
|
||||
## 1. 只读入口与事实层级
|
||||
|
||||
先读取隔离目录的 `AGENTS.md`、`PARTICLE_BASELINE.md`、`docs/00_项目规范/`、`docs/00_项目总则/README.md`、其中列出的当前有效总则和本任务。隔离快照故意不提供历史 Step、调查/完成报告;读取要求在盲审范围内以当前有效规范和实际源码为准,不得向目录外补读。规范包含历史发布坐标时,以本快照的 release manifest、源码和目标 SHA 为实现事实,区别说明规范意图与实际代码。
|
||||
|
||||
允许读取:隔离树内的 C++/GL/Metal/Android/iOS/RN 代码、头文件、shader、构建配置、测试源码、release manifest,以及当前有效规范/架构设计。历史测试输出和运行日志不可用;阅读测试源码不等于执行测试。不得浏览 `docs/20_调查`、`docs/50_Step记录`、`docs/报告`、既有历史任务或开发日志(这些输入在隔离树内也应不存在)。不引用主仓库的当前 HEAD 作为 V13 源码 SHA。
|
||||
|
||||
## 2. 审查目标与探索方法
|
||||
|
||||
从目录和公开 API 识别实际架构,自行建立模块和调用链地图;不要按照预设缺陷名称凑检索结果。对每个候选问题至少追踪入口、状态所有权、调用方、正常路径、错误路径、销毁路径,并寻找反例。分别审查:
|
||||
|
||||
1. Style 顺序和分段解析/更新/绘制语义;任意连续渲染策略交错、透明/深度/裁剪及缓存一致性。
|
||||
2. Renderer→gfx→OpenGL/Metal 的目标、命令、资源和生命周期;稳定/变化帧、失败恢复、尺寸变化及上下文切换。
|
||||
3. GPU 粒子、颜色场、时空插值与场数据链;输入合法性、数值边界、时间状态和显示状态。
|
||||
4. Source/Runtime/Field/QBIN、数据快照、线程与内存所有权;重复、乱序、异常或极大输入与 fail-closed。
|
||||
5. Android/iOS/RN 的公开 API、错误传播、生命周期和版本/产物引用;区分源码可证事实与安装环境需要的验证。
|
||||
6. 高频执行成本:CPU、锁、分配、遍历、复制、GPU 同步/资源、UI 线程以及资源上界。查找实际热点路径,但没有 profiler 不得断言运行时热点排序或节省百分比。
|
||||
7. 兼容与工程边界:既有行为、上游薄接入、Style Z-order 唯一事实来源;业务下载/HTTP/provider 属于 Host,不进入 Native。
|
||||
|
||||
如同一问题已有安全 fallback、缓存稳定复用或数据校验,必须写出反例和限制,而不是仅报告表面上可疑的符号。源代码路径和行号必须在隔离快照内真实存在。
|
||||
|
||||
## 3. 每条 finding 的证据契约
|
||||
|
||||
- 按发现顺序自行编号 `B01`、`B02`……;不要采用其他报告的任何编号。
|
||||
- 列:P0/P1/P2/P3、类型(确定缺陷/源码确定成本/设计限制/环境或文档偏差/需运行验证)、置信度、触发条件、最短源码调用链与精确路径/行号、反例、影响边界、安全修复方向、最小验证方案。
|
||||
- 区分“代码中的风险路径已经证实”和“用户画面、GPU、CPU、OOM、崩溃、性能收益已经实测”;后者本轮一概不可假定。
|
||||
- 如果推断尚缺关键调用链,降为 HYPOTHESIS/NEEDS_RUNTIME,不以 P0/P1 明确事故冒充。
|
||||
- 不要求 finding 数量、也不要求与任何报告相同;实际不存在证据就明确无此类 finding。
|
||||
- 不复制包含商业/个人机密的数据;路径与定位信息足够即可。
|
||||
|
||||
## 4. 验证边界与停止条件
|
||||
|
||||
本任务只允许静态读取、轻量文件搜索、源码/语法/引用自检。禁止生产代码、测试代码、shader、manifest、package/lock、工具链改动;禁止 CMake/Xcode/Gradle、Codegen、Jest、设备、安装、性能测试、发布、Git 写入。历史构建/测试结果没有暴露,不得编造。所有未运行门槛标 `NOT RUN`,运行效果标 `NEEDS_RUNTIME`。如必须依赖平台设备、profiler 或竞态注入,写可执行验证矩阵,停止在证据边界。
|
||||
|
||||
只能写以下三份中文文件,写完确认三份存在、正文和相对链接有效、所引用路径/行号有效、无错误引用和行尾空格后退出:
|
||||
|
||||
1. `docs/20_调查/STEP-096_V13隔离盲审调查_v1.md`:架构地图、独立 finding 总表与证据、反例、风险、性能模型、未覆盖范围、运行验证矩阵、分批建议。开头明确声明仅使用隔离输入。
|
||||
2. `docs/50_Step记录/STEP-096_V13隔离盲审_v1.md`:状态、输入版本、检查范围、只读工作过程、结果、三份文件清单、未运行验证、阻塞与下一步。
|
||||
3. `docs/报告/STEP-096_V13隔离盲审_Codex完成报告_v1.md`:执行结果 PASS/PARTIAL/BLOCKED(仅表示盲审文档交付)、finding 索引、证据质量、输入限制、构建测试 NOT RUN、源码未变、交付/移交方案。
|
||||
|
||||
本隔离树无 Git metadata,**不要在报告内伪造 Git commit/push/remote/clean 状态**。正式仓库移交、`粒子开发记录.md` 追加、四个文件的 scoped commit/push、最终 SHA 和远端核对均由 RDC 在盲审文件冻结之后执行。Codex 不得自行打开正式仓库进行移交,避免在写稿前接触比较材料。
|
||||
|
||||
## 5. 盲审冻结与移交验收(由 RDC 执行,不是 Codex 自行执行)
|
||||
|
||||
Codex 停止后,RDC 验证隔离树的文件清单,计算三份原始报告 SHA-256 并记录时间;冻结后不得由同一会话基于其他资料重写 finding。随后独立把三份报告完整复制到正式 `particle/main` 同路径,追加根目录 `粒子开发记录.md`,执行引用、边界、git diff --check、scoped commit、push `origin particle/main`、远端 HEAD/clean 核查。禁止修改已冻结正文,若必须校正移交元数据,应写入独立移交记录,不得改写发现表。
|
||||
|
||||
文档交付 PASS 不等于 V13 Android/iOS GPU 运行态或性能 PASS。若 Codex 无法访问输入、丢失上下文或额度阻断,停止、保留已成品并标 PARTIAL/BLOCKED;不得使用其他会话已有审查结果补齐。
|
||||
153
docs/40_任务/STEP-097_V13三轮审查问题合并修复_Codex任务_v1.md
Normal file
153
docs/40_任务/STEP-097_V13三轮审查问题合并修复_Codex任务_v1.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# STEP-097 V13 三轮审查问题合并修复 — Codex 任务 v1
|
||||
|
||||
状态:**计划 / 待执行**。本文件只定义后续代码修复任务;创建本文件不代表任何缺陷已经修复、编译通过或 V13 运行验收通过。
|
||||
|
||||
## 0. 目标、基线及本轮决策
|
||||
|
||||
目标:对 V13 三轮源码调查的问题去重、复核、按安全依赖顺序修改;保留全部原始编号和证据归属,不以“多份报告都说有问题”取代源码验证。先消除渲染与天气数据的正确性风险,再处理输入安全、Host 生命周期和可证明的热路径成本。保留通用连续 Render Segment、Style Z-order 唯一顺序、每个 RETAINED 独立 cache;不得退回旧 Bottom/Continuous/Top 固定三明治模型。
|
||||
|
||||
本次用户决定:**暂不执行 V13 正式运行态确认**。本任务结束的最高状态只能是 \`IMPLEMENTED / WAITING_RDC_BUILD_AND_RUNTIME_VALIDATION\`;不能写 Android/iOS GPU、FPS、长期稳定性、正式发布或 NavSea 接入 PASS。本任务不要求 Codex 编译、安装、操控设备或执行性能测试。运行态集中验证清单由 Codex准备,之后单独授权执行,避免小变动引发多次 Android 编译/安装。
|
||||
|
||||
实际源码基线以执行时 \`particle/main\`、\`origin/particle/main\` 和 clean worktree 为准;编写任务书时 HEAD 为 \`b5c1c4879a9d36608242e788afab5012bd9388f2\`,V13 软件冻结源码为 \`ee0b89e711bf01335b3aa71d36def785cd97fec3\`,功能提交 \`016fd1fff6a33cf795c10d4f5c5413d308383677\`。当前 \`.13\` 软件已发布,所有修复在后续开发提交中完成,**不得原地覆盖、移动或重新发布 \`.13\` 的 tag/manifest/产物**。若起始 HEAD 已前进,先核实源代码差异并报告,不用旧行号机械覆盖。
|
||||
|
||||
审查输入,必须逐份读取:
|
||||
1. \`docs/20_调查/Particle_V13_严格代码审查报告_v1.md\`(F01–F10,含历史实测)。
|
||||
2. \`docs/20_调查/STEP-095_V13独立严格代码与性能调查_v1.md\`(N01–N07、C01–C07、O01、E01/E02)。
|
||||
3. \`docs/20_调查/STEP-096_V13隔离盲审调查_v1.md\`(B01–B13;**已冻结的独立盲审原文,不得因修复结果修改其 findings**)。
|
||||
4. STEP-094、095、096 的任务/Step/完成报告与 release manifest,按真实性区分历史 PASS 与本轮 NOT RUN。
|
||||
|
||||
开始前读根 \`AGENTS.md\`、\`PARTICLE_BASELINE.md\`、\`docs/00_项目规范/\`、\`docs/00_项目总则/README.md\` 指定的四份有效总则。通过 RDC 确认 \`navsea-particle-integration\` 已同步到最新 \`main\`,并只读检查 \`README.md\`、\`WORKFLOW.md\`、\`coordination/TEAM_MODEL.md\`、\`contracts/\`、\`rfc/\`、\`adr/\`、\`status/\`、\`compatibility/\`。创建任务时 Integration main 为 \`39ad8415fbdb76c1822460bb09f3a3f35fe141e0\`,C-001 / RFC-001 为既有合同,不把其中历史 \`.5\` 误认为当前软件发布 \`.13\`。本任务**不修改 Integration、NavSea 仓库**,新增跨项目公共语义须先有协调/RFC/ADR/Contract 决定。
|
||||
|
||||
## 1. 去重修复总表与执行分类
|
||||
|
||||
状态枚举:\`CODE_FIX\`=本任务可实施;\`CONDITIONAL\`=只有满足设计/证据门槛才动;\`DESIGN_ONLY\`=不作为既有缺陷盲改;\`COORDINATION\`=需跨项目负责人协调。Codex 应将表中每项逐条写入实施矩阵,落实文件、测试入口、commit、实际状态;确认某项不成立也必须提供反证,不能直接删除。
|
||||
|
||||
| 合并问题 | 三轮原编号 | 初始分类 | 目标与安全边界 |
|
||||
| --- | --- | --- | --- |
|
||||
| 01 跨 render target 的 stencil CPU/GPU 状态失配 | N01 / B01 | CODE_FIX | target/pass 世代一致;首帧、R-R/R-C-R、cache ON/OFF |
|
||||
| 02 Metal depth/stencil clear 与附件不完整 | N02 / B02 | CODE_FIX | 全部 RenderPass descriptor/combined attachment;不得只改颜色 |
|
||||
| 03 LayerGroup render 异常后缓存错误标成功 | N03 / B12 | CODE_FIX | 失败不得 \`markBuildSucceeded\` 或 reuse;允许安全恢复 |
|
||||
| 04 encoded PLAY 缺失唯一 production clock 驱动 | N04 / B06 | CODE_FIX | map/source owner 单一单调推进;无双重 tick |
|
||||
| 05 GPU 粒子场身份未覆盖 V/validity | B13 | CODE_FIX | 两时间端点完整内容身份;正确复用和局部上传 |
|
||||
| 06 8-bit 零向量偏差 | B07 | CONDITIONAL | 修复零表达和编码/解码一致性,不引入未验证的格式破坏 |
|
||||
| 07 Mercator 东向位移缺纬度系数 | B08 | CODE_FIX | U/V 相同物理单位下正确度量,保留既定时间倍率 |
|
||||
| 08 JSON 嵌入 NUL/重复键被接受 | N05 | CODE_FIX | 长度感知解析;非法计划清旧状态、fail-closed |
|
||||
| 09 坏数值场先入库、物化失败替换好投影 | N06 | CODE_FIX | 事务式校验/发布;允许合法 no-data/validity=0 |
|
||||
| 10 Android 重复 attach listener 积累 | N07 | CODE_FIX | 幂等 attach/detach、移除监听、对象释放 |
|
||||
| 11 热路径完整 Diagnostics 深复制 | F01 / C01 | CODE_FIX | 内部轻快照与对外完整 pull 分离,线程一致性 |
|
||||
| 12 Metal retained/普通 State/Trail 同步等待 | F02 / C02 / B03 | CONDITIONAL | 先记录同步点/依赖;不可直接删除 wait |
|
||||
| 13 内容 dirty 导致相同尺寸 GPU allocation 重建 | F03 / C03 / B04 | CODE_FIX | contentDirty 与 resourceInvalid 分离;清屏正确性先通过 |
|
||||
| 14 retained GPU 资源无总预算 | F04 / C04 | CONDITIONAL | 安全估算、分配失败完整 fallback;独立 R 不合并 |
|
||||
| 15 encoded store 缺总 key/byte 预算 | C04 / B11 | CONDITIONAL | 保留绑定窗口和旧读者;若拒绝结果需新公共语义则停止 |
|
||||
| 16 ordinal 前缀 O(S×G)/fast 扫全层 | F07 / C05 | CODE_FIX | topology generation 缓存索引,不能改变全局顺序 |
|
||||
| 17 相同 time/mix-only 重物化和 UI 同步处理 | C06 / B05 / B10 | CODE_FIX(异步部分 CONDITIONAL) | 先幂等和不可变端点复用;owner-thread 安全发布 |
|
||||
| 18 drawable/tweaker 重建与窗口端点复制 | C07 | CONDITIONAL | 只改可证明稳定的对象生命周期,避免新 stale 引用 |
|
||||
| 19 旧 cache 的 release 记入新 topology | F09 / O01 | CODE_FIX | identity/generation 归属、历史/当前计数分离 |
|
||||
| 20 Test Host RN \`.10\` / native \`.13\` 错配 | F05 / E01 | CODE_FIX | 默认 package/lock 与插件精确锁定;不把旧 TS 报错误归因 |
|
||||
| 21 多 Continuous 退出 CPU Fast Path | F06 | DESIGN_ONLY | 保留安全 full CPU fallback,后续真实 A/B 再立项 multi-C |
|
||||
| 22 source/tile 等保守全局失效 | F08 | DESIGN_ONLY | 没有完整依赖证明不得缩窄,后续 profiling/依赖设计 |
|
||||
| 23 Integration compatibility/status 历史口径 | F10 / E02 | COORDINATION | 向粒子负责人提交接入证据请求,不擅改或声称 NavSea 支持 |
|
||||
| 24 正式共享地理 Ocean Mask 尚未形成 | STEP-095 §7 / B09 | COORDINATION | 跨 Host/Shared Core/双平台显示契约需单独 RFC/ADR,不用合成 fixture 冒充正式实现 |
|
||||
|
||||
注意:B13 与 N06 **不是同一缺陷**;GPU 内容身份遗漏与 encoded ingress 事务发布必须分别修复和验证。B07/B08 是粒子数值正确性,不等于 V13 分段回归。所有 P1/P2 是调查建议优先级,不是运行事故证明。
|
||||
|
||||
## 2. 固定顺序与不可跨越的检查点
|
||||
|
||||
严格按 \`基线复核 → A正确性 → B输入/生命周期 → C安全性能 → D交付\` 串行进行。前一阶段没有明确代码完成和静态检查结果,禁止推进下一阶段;失败、进程超时、状态未知不能当作成功。任一阶段发现需要更改冻结公共 API、QBIN/FieldProfile 语义或破坏跨平台 ABI,**停在该问题**,记录接口阻塞和建议,不自行加入 Adapter/Normalizer、隐藏通道或不兼容 fallback。不得为了凑齐总表自动进行大架构重构。
|
||||
|
||||
### 阶段 0:基线与定点反证
|
||||
|
||||
1. 确认 branch/HEAD/origin、dirty/untracked、submodule、manifest、V13 tag;对照三轮行号回读实际源码,建立 \`问题→当前源码→最短复现→反例→修复文件→目标回归\` 表。
|
||||
2. 确认 GL/Metal/RN/Test Host 的实际入口和平台条件,核对是否已有后续修改解决某项;有明确反证者记 \`NOT_REPRODUCIBLE_ON_CURRENT_HEAD\` 并附定位,不重复修复。
|
||||
3. 固定公开语义:\`fieldId/generation/validTime\`、U 东向/V 北向、T0/T1/MIX 与 OLD_T1→NEW_T0 reuse、Style Z-order、每个 R 独立 cache、颜色与粒子分离、天气获取由 Host 所有。必要的内部对象身份可以新增,禁止创造第二套公共场协议。
|
||||
4. 在 \`docs/30_设计/STEP-097_V13三轮问题去重与修复矩阵_v1.md\` 先写入逐项状态和安全不变量;后续随源码实现持续更新。这不是重新撰写第四轮审查。
|
||||
|
||||
### 阶段 A1:Renderer 正确性(01/02/03/19)
|
||||
|
||||
- **01 stencil**:明确记录属于当前 render target/pass,切换附件时重置/分代 CPU mask/ID 记录;GPU clear 与 CPU 映射同世代。保留同一 target 中安全的 tile mask 复用,不能中途全局清掉仍需使用的 stencil。针对相同 tiles 的 R-R、R-C-R、C-R-R、首次 build/rebuild/reuse/cache OFF 加最小可执行回归入口。
|
||||
- **02 Metal**:从 gfx RenderPass descriptor 到 MTL descriptor 完整传递 color/depth/stencil 的 load/clear/store;正确处理 combined depth-stencil 和 device/simulator 附件存在性,创建失败显式不发布可用缓存。不能只依赖默认 loadAction 或 color isRenderable。补 descriptor/附件状态与 fallback 可观察测试入口;不宣称 iOS runtime PASS。
|
||||
- **03 错误缓存**:使 layer-group 绘制失败结果贯穿 opaque/translucent、observer、finishBuild;部分绘制不得发布为 valid,不得在下一帧 reuse。正常 fallback/retry 能恢复;保留已有资源准备失败回退,不吞异常或改成进程崩溃。
|
||||
- **19 计数归属**:cache 创建/析构使用稳定 identity + topology generation,旧资源 release 不记入同 index 的新段;聚合统计与当前 topology 统计分离。连续模式切换、R→C、相同 index 换边界均有验证入口。诊断错误不夸大为纹理本体泄漏。
|
||||
- 本阶段有任何 stencil/Metal/异常正确性未闭合时,**不得开始资源复用/异步 GPU 性能改造**。
|
||||
|
||||
### 阶段 A2:Field/粒子正确性(04/05/06/07)
|
||||
|
||||
- **04 PLAY**:只允许一个 map/source owner 推进 forecast clock,基于单调时间处理 PLAY/PAUSE、速度、恢复前后台、clear/destroy;平台 timer 仅调度/唤醒,不允许 WeatherColor 与 Particle 各推进一次。只 setTime 一次后 PLAY 自动移动 time/mix;保持 Host 显式 setTime 路径语义。
|
||||
- **05 B13**:在两个端点的 GPU 更新门槛覆盖 U、V、validity、grid、encoding/解释语义及来源版本;generation、fieldID、dataVersion 不能代替完整内容身份。只变 mix 不应上传场,真正跨窗时旧 T1→新 T0 安全复用,只上传需要的新端点,不能每 tick 重建;无效/旧 revision 不可污染有效窗口。
|
||||
- **06 B07**:先确认当前 GL/Metal 纹理格式、GPU 精度与 CPU/shader 编解码约束;以零、微风、正负抵消及 NaN/validity fixture 定量约束误差。选择可准确表达零的**内部**表示或明确等效方案,GL/Metal 统一;如果必须改变公共 encoding、需要不支持的图形格式或改变已发布输入 ABI,则不强行实施,记录 \`BLOCKED_FORMAT_DECISION\`。
|
||||
- **07 B08**:按 eastward U / northward V m/s 推导 Web Mercator 度量,对 U/V 一致应用纬度修正、符号、极区夹紧和既有 visual time scale。建立 0°/45°/60°、东/西/南/北及极区有限值的解析对照;不得更改 Host 输入向量单位。
|
||||
|
||||
### 阶段 B:输入与平台生命周期(08/09/10/20)
|
||||
|
||||
- **08 N05**:JSON strategy/startLayerId 必须使用长度感知字符串;拒绝内嵌 NUL、重复键、未知 schema/strategy、缺失/逆序边界。无效配置 fail-closed、旧计划清除与恢复语义保持,Android/iOS/RN 错误归一化不分叉。
|
||||
- **09 N06**:在已解码 frame 真正替换 Store 前,按既有 profile/validity/no-data 语义验证有效 cell 的有限 U/V 和 scale/offset 运算不溢出;事务式提交,失败返回明确错误并保留好旧帧、投影及 GPU 显示。不把合法 validity=0 或规范允许的 noData 当成错误;不同时间/高 revision 的合法行为不改变。
|
||||
- **10 N07**:同 View 重复 attach/detach 幂等;map listener 有明确 remove/dispose owner 和 map/destroy 世代。测试入口覆盖 100 次复用、切 view、销毁中的异步回调,不产生悬挂访问或多重 dispose。
|
||||
- **20 E01**:核实真实发布坐标、Host \`package.json\` 和 lockfile、plugin 注入的 Native 版本;将**默认** Test Host 消费组合对齐经核对的 \`.13\` 精确配对,不将临时 node_modules/本地软链当作成功。若需要 install/codegen/Gradle,则准备 RDC/用户命令并标 \`NOT RUN\`;不发布、不覆盖现有 \`.13\` 产物、不改 NavSea。
|
||||
|
||||
### 阶段 C:有边界的性能与资源(11–18)
|
||||
|
||||
严格先做只读热点/资源依赖记录,再实施不会改变正确性和公共契约的优化,优先把同一阶段兼容变更集中,避免每个小点都要求一次 Android APK。
|
||||
|
||||
1. **11 轻量 Diagnostics**:内部 fast/render 热路径只取同一 generation 的轻量一致状态;完整 Style/Segments 深复制仅在外部 pull 时构造。避免用若干无同步原子值组成不一致快照;低频 pull schema 兼容。
|
||||
2. **16 ordinal/扫描**:预计算 global ordinal 和粒子/待更新 group 列表,topology/style reorder 后失效重建;保持 opaque reverse、translucent forward、隐藏层及全局 Z-order,不能用局部 ordinal 替代全局编号。
|
||||
3. **17 物化与 UI**:相同 requestedTime 和相同不可变端点早退;mix-only 仅更新 mix,复用完整内容 identity(U/V/validity/grid/encoding),与 B13 修复共用身份。双 binding 共享安全的不可变端点,不能跨 generation 复用旧数据。Android/iOS 解码和 CRC 若转 worker,必须保存 map/source 世代、owner-thread 事务发布、有限队列、取消和 destroy race;若涉及跨平台公共异步返回语义或不能证明生命周期,先完成同步路径早退,异步部分标 \`DEFERRED_DESIGN\`。
|
||||
4. **13 retained allocation**:仅在 A1 的 target clear/错误语义已经正确后,分离内容 dirty 与资源 key invalid;同尺寸、同 gfx context/format 可清除重绘;resize/context loss/关闭明确释放。相邻 R-R 仍是独立 cache。
|
||||
5. **14/15 预算**:显式估算 retained color+depth/stencil+pending 与 fieldStore key/byte 上界,处理整数溢出和在途旧快照。无法分配必须走原设计的安全 fallback 或拒绝;不得静默丢弃仍绑定窗口。若新增拒绝状态无法用现有 public error contract 表达,保留安全上界/诊断并提交接口阻塞,禁止私改 RN/Native 双方错误语义。
|
||||
6. **18 Drawable**:仅在能证明版本/target/UBO 生命周期正确时保留可复用 drawable/tweaker、减少 field endpoint 深复制;区分 tick、reseed、resize、context loss 与窗口轮换。未能证明不产生 stale resources 时不做大重构。
|
||||
7. **12 Metal wait**:仅盘点 retained build、State/Trail 等实际同步点、命令和资源依赖,准备 Metal capture/性能基准。未经设备/renderer 验证**不得直接删 \`waitUntilCompleted\`、改成无法证明安全的异步 present**。可在现有顺序中去除明确重复且不改变依赖的等待,但须提供静态资源所有权证据;否则标 \`DEFERRED_PROFILE_AND_RUNTIME\`。
|
||||
|
||||
## 3. 本任务明确不进行的架构/协调事项
|
||||
|
||||
- **F06 multi-C**:保留既有安全 full CPU fallback 和 GPU retained reuse,不为达到指标改变 Style 顺序或暗中合并 C/R;新的可组合 multi-C fast path 单独设计、后续 A/B 验证。
|
||||
- **F08 精细失效**:保留 source/tile/glyph/image/placement 的保守全局 invalidation,直到存在完整依赖与变更序列回归;不因性能冲动将其裁窄。
|
||||
- **B09 Ocean Mask**:现有 synthetic fixture 不是正式共享地理 Mask。本任务只形成 Host→Shared Core→两层消费及跨平台一致性需求、最小数据边界和阻塞报告,交粒子负责人按 Integration C-001/RFC/ADR 决定;**不借任务顺便建立新的公开 mask API、导入 provider 或让 MapLibre 理解 NavSea 业务**。
|
||||
- **F10/E02**:只输出“需 Integration 更新 Compatibility/Status”的事实与建议,并提供当前软件版本与真正验证状态;不修改 Integration 文档或宣称 NavSea 已支持。
|
||||
- 不扩大为 renderer 全架构重写、QBIN 格式版本升级、RN 第二套桥、全量新主题、NavSea业务变动、新软件版本发布或复原旧三明治设计。
|
||||
|
||||
## 4. 验证准备、执行责任与验收矩阵
|
||||
|
||||
Codex 负责按实际代码添加**现有框架下必要的定向 C++/平台测试源码或最小 fixture**、静态核对、命令和期望结果;仅有调用说明但没有针对关键回归的测试入口,不能把相应问题标代码完成。可按现有项目测试结构写回归,但本任务**不包含 Jest,也不运行 Jest**。不修改旧断言来掩盖真实失败,不删除测试、不降低测试门槛。
|
||||
|
||||
| 验证集合 | 必备情形/最低证据 |
|
||||
| --- | --- |
|
||||
| Renderer A1 | 同 source/same tiles R-R/R-C-R、主/离屏 stencil;Metal clear/load/store、combined attachment;异常后禁止 reuse;R→C/拓扑更换计数 |
|
||||
| Field A2 | 一次 setTime 后 PLAY/PAUSE/rate;T0/T1 boundary reuse;同 identity 仅 V 或 validity 变化;零/微速量化、0°/45°/60° Mercator |
|
||||
| 输入 B | NUL/重复键/invalid→valid;好旧场→坏高 revision 且旧场保留;重复 attach/销毁;Host 默认版本与 lockfile |
|
||||
| CPU C | pull-only diagnostics、reorder/ordinal、相同时间/仅 mix、窗口换端点、UI owner 世代安全 |
|
||||
| 资源 C | mutation vs resize vs context loss、allocation fail、R×viewport×fieldKey 阶梯、清理恢复、GL/Metal resource ownership |
|
||||
| 集中运行态(后续) | Android OpenGL + iOS Metal 各自像素/数值/时间/资源复现;30/60 FPS、p50/p95/p99、峰值/长稳、方向/缩放、状态恢复;按平台独立记录 |
|
||||
|
||||
**现在不执行** CMake、Xcode、Gradle、Codegen、Test Host install、APK/AAR、ADB、安装、真实设备、GPU capture、profiler、NavSea 集成或正式 V13 运行确认。Codex 结束时给出可复制的 RDC 验证命令(以本仓现有 runner/build 入口为准);Android Win11 手工 Gradle/ADB 和 iOS 设备验证依后续授权,不在本任务自动触发。不得把 STEP-094 历史 43/43、旧报告 69/69 或任何已发布 manifest PASS 写成本任务结果。
|
||||
|
||||
RDC 在每个代码批次之后独立审查文件/静态 diff;未来获授权再集中编译/运行,而不是在本轮分阶段频繁请求重装 APK。未来完整 Android 编译应遵守 \`tools/particle_android_validation/build_validation_apk.sh\` 当前权威流水线与用户手工运行安排。Metal 修改必须留给 Mac 同 commit 验证,Android 结果不得替 iOS PASS。
|
||||
|
||||
## 5. 允许修改与明确禁止
|
||||
|
||||
允许(仅与已复核问题直接相关,实际存在的路径以源码核对为准):
|
||||
- \`src/mbgl/renderer/\`、\`src/mbgl/gl/\`、\`src/mbgl/mtl/\`、\`include/mbgl/\`、所需 gfx backend;
|
||||
- \`src/mbgl/particle/\`、\`src/mbgl/style/sources/raster_array_source.cpp\`、\`src/mbgl/renderer/layers/\`、对应 GL/Metal shader及其标准生成源;
|
||||
- Android Java/Kotlin/JNI、iOS ObjC++、RN 公共实现及 Test Host 本任务限域依赖文件;
|
||||
- 对应定向测试源码/fixture、下述交付文档和开发记录。
|
||||
涉及 shader generator 必须改其权威源而非只手改自动生成产物,并附原生成/校验命令由 RDC 后续执行。
|
||||
|
||||
禁止:修改 Integration、NavSea、项目总则、旧三份调查/任务/报告、已发布 manifest/tag/tgz/AAR/XCFramework、无关 package/lock、官方上游基线;禁止 \`git reset --hard\`、\`git clean\`、force push、push upstream;禁止把公开 API/FieldProfile/QBIN breaking 变更悄悄塞进修复。不能自行把 V13 版本重命名为下一发布版,下一版本号属于独立发布任务。
|
||||
|
||||
## 6. 必做输出与报告路径
|
||||
|
||||
执行时必须建立、更新且全部中文:
|
||||
1. \`docs/30_设计/STEP-097_V13三轮问题去重与修复矩阵_v1.md\`:保留表中全部 24 行、旧编号、实际源码定位、修复/不修理由、源码状态、测试入口、commit、平台影响、运行待测项。分阶段附可量化的变更前/后操作次数模型,不虚构 FPS/内存收益。
|
||||
2. \`docs/50_Step记录/STEP-097_V13三轮审查问题合并修复_v1.md\`:阶段 0/A1/A2/B/C/D 检查点、状态、实际改动、阻塞、下一步。每一阶段结束后追加短而具体的事实,未完成不得勾选。
|
||||
3. \`docs/60_测试/STEP-097_V13修复后集中验证清单_v1.md\`:按 Android GL、iOS Metal、通用 Core/测试入口、后续 GPU 性能分层记录**尚未执行**的精确命令、fixtures、预期输出和复核责任。
|
||||
4. \`docs/报告/STEP-097_V13三轮审查问题合并修复_Codex完成报告_v1.md\`:按规定详列开始/结束 HEAD、每批提交、文件清单、各问题状态、跨项目阻塞、静态检查实际命令和结果、所有构建/运行 \`NOT RUN\`、软件发布 \`NOT RUN\`、下一步集中 RDC 验证。
|
||||
5. 在根目录 \`粒子开发记录.md\` **仅追加**实际进展;不得覆盖历史。
|
||||
6. 若实际触发跨 Contract 阻塞,额外写 \`docs/报告/STEP-097_公共接口阻塞报告_v1.md\`,明确现有 C-001/RFC-001、拟议语义、为什么无法安全局部修、Android/iOS/NavSea 各自责任;本地只记录并提升级建议,不假装协调决定已被接受。
|
||||
|
||||
完成状态只能细分为 \`CODE_IMPLEMENTED_STATIC_CHECKED\`、\`DEFERRED_DESIGN\`、\`BLOCKED_CONTRACT\`、\`NOT_REPRODUCIBLE_WITH_EVIDENCE\`、\`WAITING_RDC_BUILD_AND_RUNTIME\`;没有执行的验证永远写 \`NOT RUN\`,产品级 \`PASS\` 禁用。
|
||||
|
||||
## 7. Git 收尾与停止条件
|
||||
|
||||
在当前 \`particle/main\` 上执行,先保护所有已有 dirty/untracked;只 stage 本任务变更。按 A1、A2、B、C 各自边界形成 scoped commit,文档随阶段更新;阶段失败不能越过到下一阶段,不可混入不相关工作。每个成功阶段允许 push \`origin particle/main\` 供 RDC 直接核对,失败必须确认进程/状态并修复,不能以 timeout 当作已失败后盲目重复 push。最终 \`git diff --check\`、提交内容、远端 HEAD、clean status 明确记录。push 失败不得宣称交付完成;提交并不代表运行验收。
|
||||
|
||||
触发以下任一条件则停止相应实现并生成阻塞事实:需要新增/改变公共契约而无 Integration 决定;必须升级工具链/MapLibre;不能证明跨 GL/Metal 正确或资源所有权;修复需要删除已有功能/测试或强制绕过安全 fallback;当前 HEAD 与所需源码不符且不能安全合并;既有用户改动会被覆盖;任一关键问题没有明确退出条件。必要时整任务停在已完成的 scoped checkpoint,报告 PARTIAL,不靠继续扩大代码量冒充完成。
|
||||
|
||||
交付后由 RDC 独立审查并按后续授权集中执行构建、回归和设备验收。**本任务不发布下一版软件,不提前关闭 V13 GPU 运行态。**
|
||||
141
docs/40_任务/STEP-098_QBIN_V4单帧首显与八帧滚动缓存_Codex任务_v1.md
Normal file
141
docs/40_任务/STEP-098_QBIN_V4单帧首显与八帧滚动缓存_Codex任务_v1.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# STEP-098:QBIN V4 单帧首显与按 fieldKey 独立的八帧滚动缓存 — Codex 任务 v1
|
||||
|
||||
状态:**任务书 / 待契约闸门通过后执行**。本任务书不代表代码已经修改、编译或双平台运行验收通过。
|
||||
|
||||
## 0. 目标与执行环境
|
||||
|
||||
本任务落实已讨论的目标模型:**单帧可独立显示;第二帧到达后支持正常时间插值;后续时间片在 Native 内部进入每个 fieldKey 独立的、按 validTime 升序排序的固定 8 帧滚动缓存。** 满 8 帧提交新的合法时间片时应自动淘汰,不应因正常满额返回 `REJECTED_CAPACITY`。
|
||||
|
||||
当前用户维护和修改的源码以 **Mac** 为准:`/Users/tei/dev/MapLibreParticleLab`,分支 `particle/main`。编写本任务书时 Mac HEAD / origin HEAD 均为 `0d13552bed84f427dd43cae9856391bd26622179`。执行前重新查询 Git 事实,不以该 SHA 或本文行号覆盖后续提交;不要拿 Linux 旧工作树代替 Mac 代码。任务 MD 的目标归档位置为 `docs/40_任务/STEP-098_QBIN_V4单帧首显与八帧滚动缓存_Codex任务_v1.md`;不要创建 `docs/任务/`。
|
||||
|
||||
本阶段以 **Shared Core 公共语义和现有 RN V4 公共接口**为实施目标,不在 NavSea/Weather Server 中实施下载策略;Android 与 iOS 使用同一共享语义,平台专项编译/实体机验收分别另行完成。不得因 Mac 为编辑机而建立 iOS 专属 Core 行为。
|
||||
|
||||
### 固定边界
|
||||
|
||||
- RN:负责获取预报目录、QBIN 文件、本地下载缓存、用户选择的预报时间、异步命令 latest-wins 和 UI。RN 已提供 `FieldRuntime.submitEncodedField(...)`、`FieldRuntime.setEncodedForecastTime(...)`,**先 await 前者成功,再 await 后者**;无需新增组合提交 API。
|
||||
- Shared Core/Native:负责 QBIN 校验、存储、每 fieldKey 内排序/淘汰/复用、单帧/双帧解析、颜色与粒子投影、GPU 资源正确性。
|
||||
- 不增加 Native→RN 索取数据回调;Native 不接入 provider、HTTP、manifest、下载、缓存文件管理或 NavSea 业务语义。
|
||||
- 不修改 QBIN 文件格式、Rice 解码语义、现有公开方法签名、Style Z-order 或 V3 路径,不在 JS 伪造第二帧,也不在 Host 手动清空 8 帧来实现正常拖动。
|
||||
|
||||
## 1. 首要闸门:Integration 公共契约
|
||||
|
||||
1. 先经 RDC 检查并同步 `/Users/tei/dev/navsea-particle-integration` 的最新 `origin/main`(`git fetch origin main` + 确认可 fast-forward;尊重现有本地改动)。读取该仓库 `README.md`、`WORKFLOW.md`、`coordination/TEAM_MODEL.md`、`contracts/`、`rfc/`、`adr/`、`status/`、`compatibility/`、`coordination/active/COORD-004/` 和 `instructions/PARTICLE_PROJECT_INSTRUCTIONS.md`。
|
||||
2. 编写本任务书时 Integration HEAD 为 `318f8ccf911ac3136c3086a1d14e61f83e2d4bce`;`RFC-003_编码场两帧首显与增量补帧语义.md` 仍处于 DRAFT,其第一条要求至少两帧、满额返回容量拒绝,与本任务新目标**明确冲突**。本任务书不是已经接受的跨平台 RFC。
|
||||
3. **开始改公共源码前,必须由粒子负责人/Integration 按既定流程正式更新 RFC-003、COORD/Contract 或 ADR,接受下述单帧和滚动淘汰语义,明确 Android/iOS 共用。** Codex 不得自行把 DRAFT 当 ACCEPTED,也不得静默更改 Integration 文档替代负责人决定。
|
||||
4. 如果执行时仍没有已接受且不冲突的正式依据:先在 `docs/报告/STEP-098_公共契约阻塞报告_v1.md` 写出当前 SHA、冲突条目、拟实施语义与所需协调决策,**停止功能修改及 push**,状态记为 `BLOCKED_CONTRACT`,交回粒子负责人。不得绕过闸门做 Android/iOS 两套实现。
|
||||
5. 若 Integration 已更新且批准,记录精确文档路径、版本与 commit SHA,后续实现以正式版本为准;如正式结论与本文不同,应停止并回报,不自行择一。
|
||||
|
||||
## 2. 开始前必须核对的 Mac 实际基线
|
||||
|
||||
先读根项目规范、`PARTICLE_BASELINE.md`(如存在)、`docs/00_项目规范/`、`docs/00_项目总则/README.md` 指定总则、与 STEP-097 的正在实施范围。读取并建立文件/函数对照表:
|
||||
|
||||
- `include/mbgl/particle/field/field_runtime.hpp`、`src/mbgl/particle/field/field_runtime.cpp`:`FieldSeries`、`EncodedFieldStore`、`resolve`、容量及 revision/identity。
|
||||
- `src/mbgl/particle/field/field_binding.cpp`、`src/mbgl/particle/field/field_projection_materialization.cpp`:单/双帧投影、独立绑定和物化。
|
||||
- `src/mbgl/style/sources/raster_array_source.cpp` 及相关头文件:`submitEncodedField`、`setRequestedForecastTime`、`refreshV4RendererState`、现有 projection equality/notification。
|
||||
- `src/mbgl/renderer/layers/render_weather_color_layer.cpp`、`render_particle_validation_layer.cpp` 及 shader/texture 资源:单帧/双帧下颜色、粒子、轨迹和 GPU 复用。
|
||||
- `packages/maplibre-particle-rn/src/FieldRuntime.ts`、`NativeWeatherRuntimeBridge.ts`、Android Kotlin bridge/JNI、iOS `WeatherRuntimeBridge.mm`/`MLNParticleFieldRuntime.mm`:确认两个现有公开调用完整传递结果。
|
||||
- `test/particle/field_runtime.test.cpp`、`test/particle/field_binding.test.cpp`、`test/source/raster_array_v4_host_api.test.cpp` 及现有 Renderer 定向验证入口。
|
||||
|
||||
编写任务书时源码事实:`FieldSeries::submit` 对第 9 个不同时间直接 `RejectedCapacity`;`resolve` 对 `frames.size()<2` 返回空;`submitEncodedField` 已会刷新投影但不修改 forecast time。以上仅供定位,若新 HEAD 有变化应以实际代码为准。若 STEP-097 仍在改相同的 runtime/projection/Renderer 文件,先确认依赖顺序,不得并行覆盖或回滚已完成的修复;尤其保留 STEP-097 的输入校验、forecast clock、GPU 身份、资源生命周期等新修正。
|
||||
|
||||
## 3. 正式目标语义
|
||||
|
||||
### 3.1 缓存所有权、容量与排序
|
||||
|
||||
- `EncodedFieldStore` 按 `fieldKey` 分别持有 `FieldSeries`。`wind`、`wave`、`rain`、`tide` 使用不同 key 时分别最多 8 帧;同 key 的多个 Layer/Binding 共享该序列。不能把四类天气合为“全局 8 帧”。
|
||||
- 每帧按 `frameContentIdentity.validTimeMs` **升序**保存,提交顺序不影响排序;不得假设固定一小时/三小时间隔。
|
||||
- 已有相同 `validTime`:先走现有 Duplicate、identity conflict、stale、合法 revision 替换语义;不额外占一个槽,不因为满额淘汰别的帧。
|
||||
- 不同时间且不足 8 帧:先完整校验,按时间有序插入。
|
||||
- 不同时间且已满 8 帧:在成功通过解码、profile、grid/plane、身份和相邻兼容性校验后,选择淘汰一帧并原子提交新有序集合。正常满额替换成功返回现有 `ACCEPTED_INSERTED`(或正式合同要求的**既有**成功语义),不返回 `REJECTED_CAPACITY`。为了 TypeScript/ABI 向后兼容可以保留旧结果枚举,但正常容量=8的有效插入路径不再产生它;不可偷偷更名公共结果。
|
||||
- 淘汰只作用于正在提交的 `fieldKey`,不影响其他天气种类、Bindings、requestedForecastTime、V3 数据或 Style。
|
||||
|
||||
### 3.2 双向淘汰,按新提交帧时间取中心
|
||||
|
||||
由于既定调用顺序是先 `submitEncodedField()`、后 `setEncodedForecastTime()`,**提交阶段不能依赖尚未下发的新目标时间**。本版使用“新提交帧 validTime 为中心,淘汰时间距离最远的端点”的确定性规则;新帧本身必须留下。对于已升序的满额序列,只在最早端或最晚端候选之间选择;若距离相同,按正式合同规定稳定决胜,允许优先避免淘汰当前显示端点,但不可牺牲新提交帧。不得永远只删除最旧帧。
|
||||
|
||||
必须覆盖:
|
||||
|
||||
- 原有示意 `13:00 ... 21:00` 共 8 帧;提交 `22:00` → 删除最早端 `13:00`,插入 `22:00`,最终仍 8 帧。
|
||||
- 已向后滚动后提交 `12:00` → 删除最远的最晚端,重新保留 `12:00`;向过去拖动同样成立。
|
||||
- 已满时提交位于内部的新时刻 → 仍保持有序、8 帧和确定的端点淘汰。
|
||||
- 今天跳到后天某一时刻 → 接收该时刻 QBIN,而不是要求 RN 先清空旧窗口或填齐 8 帧。之后逐帧提交,Native 自行滚动。
|
||||
- 乱序提交、Duplicate、同时间新 revision 不应造成意外淘汰。
|
||||
- 插入不兼容数据或解码失败时,**零淘汰、零 Store/Projection/Clock 修改**;不得先删除旧帧再验证。
|
||||
|
||||
### 3.3 单帧首显与双帧升级
|
||||
|
||||
- 当请求时间精确对应某个已验证 `validTime`,即使整个 Store 只有一帧,或它与其余 7 帧相隔很远无法形成合理双帧窗口,也应能够显示该帧:颜色为这一帧的值;有有效 U/V(或规范允许的方向向量)时粒子可持续运动,**天气时间不变不等于粒子停止**;仅标量帧不能伪装成可平流向量。
|
||||
- 单帧在运行时具有真实单端点语义。可在内部使用同一 Snapshot 作为 T0/T1、`mix=0` 来复用现有双纹理 shader,但不得向 RN 要求伪造第二个 QBIN 或捏造第二个不同 validTime;优先共享解码结果、物化 Snapshot 和 GPU 纹理,避免同一单帧无意义地双份常驻。
|
||||
- 第二帧补入后,如果目标时间被合法的相邻端点覆盖,则自动升级为双帧插值,保持既有 `T0/T1/MIX`,端点、mix 与实际预报时间对应;如果并未覆盖,保持合法单帧或 fail-closed,不能强行把不相关的旧帧插值进去。
|
||||
- 目标时间落在两帧之间且有合法覆盖时执行正常时间插值;目标时间既不精确匹配可用单帧、也没有合法双帧覆盖时返回明确 unavailable,不使用“最近旧帧”冒充该时刻。RN 若选择最近预报帧首显,必须以该帧实际 `validTime` 作为已显示时刻;用户日历选中时间与实际显示时间不可混称。例如用户初始选择 13:30,只有最近的 12:00 QBIN 时,RN 可先提交该帧并调用现有 `setEncodedForecastTime(12:00)` 显示 12:00 静态预报,仍将 13:30 保留为业务层待完成目标;等 15:00 帧到达后再设置 `setEncodedForecastTime(13:30)` 进入插值。不得在 Native 悄悄修改 `requestedForecastTime`,也不得将临时 12:00 画面标成 13:30 的插值天气。
|
||||
- 不能把跨 dataset/reference/Profile 的帧混合成一个时间序列;沿用已有权威身份契约。不凭固定时间间隔猜测有效帧,也不能用任意远离的旧时间片推断新的天气。若当前元数据不足以明确区分“同一批次的合法双端点”与“跨批次伪兼容”,作为契约阻塞项报告,不能自行新造隐含协议。
|
||||
- 明确 `fieldKey` 之间可有不同 validTime 覆盖。颜色与粒子在同一全局请求时刻分别 resolve,各自可处于单帧、双帧或 unavailable;不得因一个 fieldKey 无数据清空其他 key 的合法投影。
|
||||
|
||||
### 3.4 两个现有 RN API 的行为不变
|
||||
|
||||
正常时间轴操作为:
|
||||
|
||||
1. RN 从本地缓存或服务器取得对应 QBIN,先 `await FieldRuntime.submitEncodedField(...)`。
|
||||
2. 仅在 `ACCEPTED_INSERTED`、`ACCEPTED_REPLACED_REVISION` 或可确认的 `DUPLICATE` 等成功结果后,`await FieldRuntime.setEncodedForecastTime(reactTag, targetTimeMs)`。
|
||||
3. Native 的 submit **不擅自修改目标时间**,setTime 不触发网络下载。已缓存时间直接 setTime 即可;无需每次重新 submit。
|
||||
4. 两调用非原子,可能在两次调用之间短暂显示旧时间、单帧或暂时不可用,**不可对外保证无闪烁**。不得把旧时刻天气伪标为新目标。RN 侧 latest-wins、目标版本和展示状态属于后续 Host 接入范围;本任务不私改 NavSea。
|
||||
5. 不新增 `submitAndSetTime`、Native→RN 数据请求事件、逐帧删除 RN API 或新的 provider 参数;保留现有 `clearEncodedField()` / `clearAllEncodedFields()` 作为显式重置能力,而不是正常滚动手段。
|
||||
|
||||
### 3.5 Renderer 与资源安全
|
||||
|
||||
- 正常向前/向后补帧且当前已选 pair 和 mix 未变化时,避免无条件 rebuild、重复 GPU 上传、清空 trail 或发布无意义 Source revision;复用 existing `sameProjection`/GPU content identity。
|
||||
- 单帧→双帧、双帧窗口变化和 revision 更新必须正确刷新,shader 无非法 T1/null;若相同 Snapshot 对应 T0/T1,避免重复上传同一内容。支持 GL/Metal 共享语义,不能只修 Android Renderer。
|
||||
- 淘汰旧帧不能销毁仍被当前 projection/GPU 更新流程持有的不可变 Snapshot;通过正常引用计数和资源所有权安全释放。切换不同窗口时遵守现有 trail/particle reset 规则,不错误承诺跨任意远跳跃保留轨迹。
|
||||
- 坏帧不允许覆盖已验证的好投影;生命周期关闭、clear、style reload、map 销毁与异步提交有明确所有权,不使旧请求重新点亮已 OFF 的画面。
|
||||
|
||||
### 3.6 内存边界
|
||||
|
||||
- 8 帧是**每 fieldKey 的数量上限**,不是无限制内存保证。每个 decoded frame 由原始 Plane 字节、validity bitset 和元数据构成;参与显示的端点另有 Float32 物化数组、图像、纹理和粒子/trail 资源。不得错误地用压缩 QBIN 大小表示运行内存。
|
||||
- 不在本任务未经批准添加任意固定全局 MiB 上限或第二套容量公开错误码。为风/浪/雨/潮各 8 帧、单端点/双端点和跳跃替换提供 CPU/GPU 常驻及峰值测量入口或现有计数;记录可计算的 decoded byte 占用、资源复用/释放及最坏边界风险。
|
||||
- 评估单帧 T0=T1 情况下重复物化/重复上传;保证淘汰与资源 release 后无持续增长。若测量显示 32 帧和显示资源会触及移动端内存风险,报告下一步“全局字节预算/多 fieldKey 优先级”设计建议,**不可把内存申请失败伪装成正常 REJECTED_CAPACITY**。
|
||||
|
||||
## 4. 实施顺序与测试矩阵
|
||||
|
||||
阶段 A:只读复核与契约闸门 → 阶段 B:Core Store 滚动淘汰及单帧 resolver → 阶段 C:Binding、物化、Renderer 单/双帧和资源复用 → 阶段 D:已有 RN Bridge 端到端语义检查 → 阶段 E:文件、报告、Git 收尾。前一阶段未明确通过不进入后一阶段;命令 timeout/进程未知不能算通过。发生公共 ABI、QBIN、profile、dataset 身份或无法证实跨平台一致性的阻塞时停止扩大范围。
|
||||
|
||||
至少具备以下定向验证;优先扩展既有 C++ 源级测试和可复用 Test Host 场景,不新建无关测试框架:
|
||||
|
||||
| 编号 | 场景 | 必须得到的结果 |
|
||||
|---|---|---|
|
||||
| T01 | 只有一帧,target=validTime | 投影合法;色场可显示;有 U/V 时粒子可动;实际时刻正确 |
|
||||
| T02 | 首帧后补入第二帧,target 在两者中间 | 单帧→双帧,MIX 正确;不用伪造第二帧 |
|
||||
| T03 | 8 帧未满乱序提交 | 按 validTime 升序;各 key 相互隔离 |
|
||||
| T04 | 满 8 帧,向未来提交新帧 | 淘汰最早端、保留新帧,计数仍 8,无正常容量拒绝 |
|
||||
| T05 | 满 8 帧,向过去提交新帧 | 淘汰最晚端、保留新帧,计数仍 8 |
|
||||
| T06 | 满 8 帧,提交中间时刻 | 确定性端点淘汰;排序/唯一性不变 |
|
||||
| T07 | 今天→后天单帧→后续补帧→返回今天 | 新时刻单帧可用,后续升级双帧;旧数据需 RN 重交时可正常滚动,不越界、不混批 |
|
||||
| T08 | Duplicate、同时间合法新 revision、stale、identity conflict | 保留现有判定;重复/替换不淘汰其他时刻 |
|
||||
| T09 | 错 QBIN、Profile/Grid/Plane 不兼容、无效向量 | 拒绝且 Store、forecast clock、原投影不被破坏 |
|
||||
| T10 | 目标时间位于已缓存相邻两帧之间 | 正常插值,mix 首/中/末精确 |
|
||||
| T11 | 目标不被合法帧覆盖也不精确匹配 | unavailable,不把最近帧冒充目标时间 |
|
||||
| T12 | 新帧在当前 pair 外围、当前投影不变 | requestedTime、当前 pair/mix 保持;避免无意义资源重建 |
|
||||
| T13 | 新帧改变当前 bracket,或当前端点被淘汰 | 正确更新;不沿用失效旧 GPU 数据、错误轨迹 |
|
||||
| T14 | `wind` 满额后提交 `wave`;同 key Color+Particle | 独立 8 帧;其他 key 不受影响;同 key 不复制 Store |
|
||||
| T15 | 单/双端点 Materialization/GL/Metal 资源 | 单帧无需两份独立 QBIN/CPU/GPU 内容;复用、release 与上下文丢失恢复正确 |
|
||||
| T16 | 连续 100+ 次双向滚动、反复 clear、切 Runtime | 帧上限不破、无持续内存增长和悬挂引用 |
|
||||
| T17 | RN 两步调用成功与失败分支 | 既有 API/TypeScript/Android/iOS bridge 类型兼容,失败不继续 setTime;不新增公开 API |
|
||||
|
||||
注意:Native 静态/定向测试只能证明对应源码路径,不等于手机 GPU 首帧或 NavSea UI 通过。对尚未执行的 Android/iOS 真机、滚动拖动、跨天回退、内存峰值必须标 `NOT RUN / WAITING_USER_VALIDATION`。
|
||||
|
||||
## 5. 修改范围与明确禁止项
|
||||
|
||||
允许根据当前真实源码在上述 Shared Core/Source/Renderer 及对应现有测试文件做必要的最小修改;仅在证明是既有 API 传递障碍时修改 Android/iOS bridge,保持方法签名和结果语义。可在 `docs/30_设计/` 记录淘汰不变量与单帧/双帧选择表,在 `docs/50_Step记录/` 记录实施进度。
|
||||
|
||||
禁止:修改 NavSea、Server、天气 provider、下载缓存、Manifest、日历 UI;重新生成 PBF、修改 Style Z-order、强行更改 QBIN/QBV3 格式;新建第二套 FieldStore/Timeline、额外 JS Normalizer/Wrapper、私有 Native 通道、自动请求 RN 数据、提交单帧伪造第二份不同时间、给四类天气合用 8 帧、让正常满额仍返回容量拒绝。不允许顺带完成 STEP-097 的未授权项,也不允许覆盖现有发布 tag/manifest 或把源码通过称为正式发布通过。
|
||||
|
||||
## 6. 构建、交付与 Git
|
||||
|
||||
- 本次编译、Gradle/Pod/Xcode 构建、APK/IPA 安装、ADB/设备操作及实际 GPU 验收由用户人工执行。Codex 可做源码、`git diff --check`、格式/静态检查及环境许可的低成本 C++ 定向测试;**不得擅自启动完整 Android/iOS 编译或设备操作**,也不得把未运行项目写成 PASS。
|
||||
- 准备一份 Windows 11 可直接执行的 Android 手动验证清单,ADB 命令如需涉及当前虚拟机必须显式 `adb -s 192.168.200.139 ...`;iOS 只列 Mac 用户手工构建/设备验收条件。合并一轮验证 T01–T17,不为单个按钮或日志要求反复重装。
|
||||
- 必做输出:`docs/30_设计/STEP-098_QBIN_V4单帧与八帧滚动缓存设计_v1.md`、`docs/50_Step记录/STEP-098_QBIN_V4单帧与八帧滚动缓存_v1.md`、`docs/报告/STEP-098_QBIN_V4单帧与八帧滚动缓存_Codex完成报告_v1.md`。目录不存在先创建;同主题报告已存在则递增版本,不覆盖旧报告。全部中文。
|
||||
- 报告明确记录:当前 HEAD、获接受的 Integration 协议版本/commit、真实改动列表、API/ABI 是否变化、每项 T01–T17 执行状态、`REJECTED_CAPACITY` 新语义、单帧显示实际范围、CPU/GPU 内存测量与未验证项、下一步 Android/iOS/Host 责任。最多只能报告 `IMPLEMENTED / WAITING_PLATFORM_RUNTIME_VALIDATION`;契约闸门未过则 `BLOCKED_CONTRACT`。
|
||||
- 完成可实施阶段且检查通过后,只提交本次任务相关变更到 **Mac 当前项目 `particle/main`**,并 push 到公开 `origin/particle/main`。提交前检查未跟踪/用户既有文件,不得 `git add -A` 混入无关变更;先核验 `origin` 是否前进,冲突时停下处理而非强推。完成报告必须写最终 commit hash、commit message、push 结果和远端 branch/HEAD;push 失败不能声称完成。若被契约闸门阻塞,不提交或 push 功能代码。
|
||||
|
||||
## 7. 最终验收口径
|
||||
|
||||
用户能在 RN 维持两个现有 API 和顺序:第一帧即显示其真实有效时间;第二帧具备合法覆盖时升级插值;后续任意顺序提交,Native 每 key 自动保持最多 8 帧。向未来提交淘汰最早,向过去提交淘汰最晚;正常已满不会 `REJECTED_CAPACITY`;四类天气互不挤占。无效帧零副作用、时间语义不造假、Core 不掌握下载/日历。Renderer 不因正常补帧无谓清屏或持续泄漏。Android 和 iOS 分别通过用户实际设备验证后,才能由 Integration 更新兼容性及正式状态。
|
||||
690
docs/40_任务/STEP-100_SourceScoped_RenderSegment局部失效_Codex任务_v2.md
Normal file
690
docs/40_任务/STEP-100_SourceScoped_RenderSegment局部失效_Codex任务_v2.md
Normal file
@@ -0,0 +1,690 @@
|
||||
# STEP-100 Source → RenderSegment 局部 Revision / Invalidation / Readiness 与独立 Fallback — Codex 任务 v2
|
||||
|
||||
## 0. 任务性质
|
||||
|
||||
这是 STEP-099 之后的正式修复任务。
|
||||
|
||||
STEP-099 已经完成纯诊断并 CLOSED / PASS,已确认 V17 的核心问题:
|
||||
|
||||
1. 动态 GeoJSON Source/Tile mutation 会推进 global retained content revision;
|
||||
2. 即使 R0 不依赖该 GeoJSON,R0 retained cache 仍跟着 invalid/build;
|
||||
3. GeoJSON 更新期间约 200 ms 的 transient `isLoaded=false` 会通过 global `RenderOrchestrator::isLoaded()` 把整个 retained path 拉成 `style-not-loaded`;
|
||||
4. 当前 renderer 还存在 `useRetainedRanges` all-or-none gate:一个 RETAINED Segment fallback 时,会使其他本来可安全 reuse 的 RETAINED Segment 一起退出 retained path;
|
||||
5. STEP-099 diagnostics 已能正确给 source mutation 计算 `affectedSegmentIndices`,但该依赖关系仍只是诊断,不参与正式 invalidation。
|
||||
|
||||
本任务目标是把 **Source/Tile 变化对 RETAINED Segment 的影响正式局部化**。
|
||||
|
||||
本任务不是 V18 发布任务。Codex 只负责实现与源码级说明;Android/iOS 构建、APK、ADB、设备测试、最终 Git push、V18 release 由 RDC/ChatGPT 在 Codex 完成后独立执行。
|
||||
|
||||
未经用户明确要求,本任务 **不加入、不运行 Jest**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 冻结基线
|
||||
|
||||
工作目录:
|
||||
|
||||
`/Users/tei/dev/MapLibreParticleLab-step100`
|
||||
|
||||
分支:
|
||||
|
||||
`step100-segment-local-source-invalidation`
|
||||
|
||||
开始基线必须重新核对,当前预期:
|
||||
|
||||
- `origin/particle/main`
|
||||
- HEAD:`c4b481fd2c5b5da20d31f6ae54011f0552d7dbd0`
|
||||
- STEP-099 implementation:`8a5214d92dd2f8899db76d2194da42999fa0c87d`
|
||||
- V17 frozen tag:`maplibre-particle/rn/13.4.1-particle.17`
|
||||
- V17 frozen commit:`e8b451fe70054107773e82484c80e90df3889147`
|
||||
|
||||
禁止修改、移动、覆盖 V17 tag / release worktree。
|
||||
|
||||
STEP-100 是 V17 之后的新开发,最终只有 RDC 验收通过后才允许进入 V18 发布。
|
||||
|
||||
开始前必须读取:
|
||||
|
||||
- 当前仓库实际 HEAD / status / diff;
|
||||
- 全局 Codex AGENTS;
|
||||
- `ARCHITECTURE.md`(若存在);
|
||||
- STEP-092 / 094 / 099 的任务、Step 记录、完成报告;
|
||||
- 当前:
|
||||
- `renderer_impl.cpp`
|
||||
- `render_orchestrator.*`
|
||||
- `retained_underlay_cache.*`
|
||||
- `retained_underlay_runtime_state.hpp`
|
||||
- `render_segment.*`
|
||||
- STEP-099 diagnostics 与 Android fixture;
|
||||
- 当前 RenderSegment / retained cache / particle fast path tests。
|
||||
|
||||
不得凭历史印象修改。
|
||||
|
||||
---
|
||||
|
||||
## 2. 最终架构原则
|
||||
|
||||
长期 RenderSegment 模型继续保持通用模型:
|
||||
|
||||
```text
|
||||
Style Z-order
|
||||
↓
|
||||
R / C / R / C / R / ...
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- R = RETAINED
|
||||
- C = CONTINUOUS
|
||||
|
||||
本任务只用 **R-C-R** 做最小验证,不把 R-C-R 固化成最终架构。
|
||||
|
||||
必须保持:
|
||||
|
||||
1. Style layer 顺序是唯一 Z-order 真相;
|
||||
2. Renderer 不理解 AIS / Route / Weather / GeoJSON 等业务名;
|
||||
3. Source → Segment 依赖由当前实际 Style / RenderLayer / Source 关系推导;
|
||||
4. 一个 Source 可依赖多个 Segment;
|
||||
5. source-less layer 不制造假 dependency;
|
||||
6. unknown/global dependency 必须 fail closed;
|
||||
7. 相邻 Segment、多个 C、多个 R 的通用模型不能被破坏;
|
||||
8. 本任务不实现 multi-continuous Fast Path。
|
||||
|
||||
---
|
||||
|
||||
## 3. 本任务必须实现的核心目标
|
||||
|
||||
### 3.1 Source/Tile mutation 局部化
|
||||
|
||||
假设:
|
||||
|
||||
```text
|
||||
R0 depends [staticSource]
|
||||
C depends [particleField]
|
||||
R1 depends [dynamicGeoJson]
|
||||
```
|
||||
|
||||
当:
|
||||
|
||||
```text
|
||||
dynamicGeoJson mutation
|
||||
```
|
||||
|
||||
正式 renderer 必须达到:
|
||||
|
||||
```text
|
||||
R0:
|
||||
content/revision 不因该 source mutation 改变
|
||||
cache 不 invalid
|
||||
cache 不 rebuild
|
||||
持续 reuse
|
||||
|
||||
R1:
|
||||
revision/invalidation 正常变化
|
||||
按需要 build/reuse/fallback
|
||||
|
||||
C:
|
||||
正常 continuous render
|
||||
```
|
||||
|
||||
禁止继续出现:
|
||||
|
||||
```text
|
||||
dynamicGeoJson mutation
|
||||
↓
|
||||
global revision 变化
|
||||
↓
|
||||
R0 + R1 一起 rebuild
|
||||
```
|
||||
|
||||
### 3.2 保留 global revision 兼容信号
|
||||
|
||||
STEP-100 不要求删除现有 global `retainedUnderlayContentRevision`。
|
||||
|
||||
为了保持:
|
||||
|
||||
- diagnostics;
|
||||
- 现有 fast-path correctness;
|
||||
- 旧 API / compatibility;
|
||||
- conservative fallback;
|
||||
|
||||
允许并推荐继续维护 global revision。
|
||||
|
||||
但 **RETAINED Segment cache 的正式内容 revision / invalidation 不能再只依赖这个 global source/tile revision**。
|
||||
|
||||
当前 `retainedUnderlayRevisionTracker` 同时消费 global content revision 与 `renderTreeParameters.loaded`。STEP-100 必须明确拆成两种用途:
|
||||
|
||||
```text
|
||||
global retained revision / tracker
|
||||
-> compatibility / Fast Path / diagnostics
|
||||
|
||||
segment-local retained content state
|
||||
-> full CPU renderer 的每个 RETAINED Segment Build / Reuse / Fallback
|
||||
```
|
||||
|
||||
**Segment-local cache content state 禁止继续直接消费:**
|
||||
|
||||
- global source/tile content revision;
|
||||
- global `renderTreeParameters.loaded`。
|
||||
|
||||
否则即使 source-scoped revision 已实现,R0 仍会被无关 GeoJSON transient unloaded 或 global revision 连带失效。
|
||||
|
||||
允许 global revision / global tracker继续按原逻辑变化;禁止为了让测试 PASS 而停止 global revision 增长或放宽 Fast Path。
|
||||
|
||||
### 3.3 per-RETAINED-Segment readiness
|
||||
|
||||
不得修改任何 Source `isLoaded()` 定义。
|
||||
|
||||
不得修改:
|
||||
|
||||
```cpp
|
||||
renderTreeParameters->loaded = updateParameters->styleLoaded && isLoaded();
|
||||
```
|
||||
|
||||
这个 global loaded 仍作为 MapLibre 现有全局兼容事实。
|
||||
|
||||
但 retained segment 的 cache Build/Reuse/Fallback 必须有自己的安全 readiness 判断。
|
||||
|
||||
最低语义:
|
||||
|
||||
- Style 本身未 ready:fail closed;
|
||||
- image manager / sprite / glyph 等无法安全定位到单 Segment 的全局前置条件未 ready:fail closed;
|
||||
- Segment 已知依赖的 source 中任一缺失或 `isLoaded()==false`:仅该 Segment fail closed;
|
||||
- Segment 已知依赖的 source `hasFadingTiles()==true`:仅该 Segment视为 unstable;
|
||||
- **不被该 Segment 引用的 source unloaded 或 fading,不得让该 Segment cache fallback/rebuild**;
|
||||
- `retainedRangeIsStable(begin,end)` 的 source fading 检查必须按该 range 的真实 source dependency 缩小,不能继续遍历全部 `renderSources` 作为 full-CPU retained gate;
|
||||
- Fast Path 原有 global fading gate 保持不变;
|
||||
- source ID 无法解析、dependency 缺失或真正无法证明安全时 fail closed。
|
||||
|
||||
必须区分:
|
||||
|
||||
1. **已知 Source dependency**;
|
||||
2. **symbol/global prerequisite**(glyph/image/placement 等);
|
||||
3. **真正 unknown dependency**。
|
||||
|
||||
STEP-099 的 `unknownDependencies` 目前会把 symbol 相关条件标为 unknown。STEP-100 不得机械地把“存在 symbol”解释成永久 fallback;symbol 所需 glyph/image/placement 应作为明确的 global prerequisite/fail-closed 条件处理。
|
||||
|
||||
因此 T3 中一个:
|
||||
|
||||
```text
|
||||
no layer
|
||||
enabled=false
|
||||
isLoaded=false
|
||||
```
|
||||
|
||||
的 RasterArray source 可以继续让 global `RenderOrchestrator::isLoaded()==false`,但不得因此让与它无依赖关系的 R0/R1 cache 全部 `style-not-loaded`。
|
||||
|
||||
本 Step 的运行态硬验收范围是 **MapMode::Continuous**(粒子正式实时渲染模式)。当前非 Continuous / still 路径如果在 global unloaded 时提前返回,本 Step 不强行改变其兼容语义;完成报告必须明确该适用范围。
|
||||
|
||||
### 3.4 RETAINED Segment 独立决策
|
||||
|
||||
当前 V17 路径存在类似:
|
||||
|
||||
```text
|
||||
useRetainedRanges = all retained segments supported
|
||||
```
|
||||
|
||||
的 all-or-none 语义。
|
||||
|
||||
STEP-100 必须改为每个 RETAINED Segment 独立拥有:
|
||||
|
||||
- readiness/support decision;
|
||||
- Build / Reuse / Fallback;
|
||||
- content/source revision;
|
||||
- invalidation reason。
|
||||
|
||||
如果:
|
||||
|
||||
```text
|
||||
R0 = Reuse
|
||||
R1 = Fallback
|
||||
```
|
||||
|
||||
则本帧必须允许:
|
||||
|
||||
```text
|
||||
R0 composite cached texture
|
||||
C live render
|
||||
R1 live render fallback
|
||||
```
|
||||
|
||||
而不是退化为“整个 Style 全部 live render”。
|
||||
|
||||
如果某 retained cache 自身 build 失败,应优先只 fallback 该 Segment;只有无法证明局部安全的 context/global resource failure 才允许全 retained fail closed。
|
||||
|
||||
---
|
||||
|
||||
## 4. Source-scoped revision 设计要求
|
||||
|
||||
Codex 必须基于当前代码选择最小、稳定设计,并在完成报告说明实际方案。
|
||||
|
||||
推荐方向但不强制具体类名:
|
||||
|
||||
- 为 Source/Tile mutation 维护 source-scoped monotonic revision;
|
||||
- keyed by stable sourceId,但必须处理 source 删除后同 ID recreate,不能让新 source 错误继承可复用的旧 cache identity;可用 generation/epoch 或保留单调 tombstone 语义实现;
|
||||
- source diff、feature-state mutation、tile changed 等所有当前会进入 Source/Tile retained content invalidation 的正式路径都必须覆盖;
|
||||
- 能定位 sourceId 的 mutation 只推进对应 source revision;无法定位 sourceId 的 mutation继续 global fail closed;
|
||||
- Segment content revision 由:
|
||||
- 该 Segment 的 dependent source revisions;
|
||||
- 当前 Segment layer fingerprint / layer mutation;
|
||||
- 必须全局影响 retained 的 global revision 类别;
|
||||
- topology / camera / viewport 等既有安全条件
|
||||
共同决定。
|
||||
|
||||
Source/Tile mutation 不应推进“所有 Segment 都消费的 retained cache revision”。
|
||||
|
||||
必须避免:
|
||||
|
||||
- 用屏幕位置猜 dependency;
|
||||
- 在 renderer 中硬编码 source/layer ID;
|
||||
- 从 STEP-099 diagnostics JSON 反向驱动正式 renderer;
|
||||
- 建立 diagnostics 与 production 两套互相漂移的 dependency 真相。
|
||||
|
||||
如果需要提取 production-grade dependency helper,应让 diagnostics 和正式逻辑共享同一只读推导源。正式 renderer 不得从 diagnostics JSON 或 `RetainedUnderlayRuntimeState` 反向取 dependency。
|
||||
|
||||
Source-scoped revision 属正式 correctness state,不得沿用 STEP-099 diagnostics 的“best-effort 记录失败可忽略”语义。正式 revision/dependency state 若无法可靠更新,必须 conservative global invalidation/fail closed。
|
||||
|
||||
---
|
||||
|
||||
## 5. Global mutation 仍可保守处理
|
||||
|
||||
本任务只要求 **Source/Tile mutation** 与明确 Source readiness 局部化。
|
||||
|
||||
以下无法安全局部化的事件,本轮允许继续 invalidate/fail closed 全 retained:
|
||||
|
||||
- context reset;
|
||||
- style reload / topology change;
|
||||
- image mutation;
|
||||
- glyph mutation;
|
||||
- sprite/image manager 全局 readiness;
|
||||
- placement / symbol fade / glyph/image/sprite 全局状态;
|
||||
- camera / transform / viewport / resize / pixel ratio;
|
||||
- debug mode;
|
||||
- 3D / unsupported / legacy ordering;
|
||||
- 无法定位 dependency 的 mutation;
|
||||
- 真正 unknown/global dependency。
|
||||
|
||||
注意:**Source 自身的 loaded / fading 不属于这里的 global fail-closed 项**;若 source dependency 可定位,必须按 Segment 局部判断。Fast Path 的 global source/fading gate除外,本轮保持原状。
|
||||
|
||||
完成报告必须列出:
|
||||
|
||||
- 已局部化的 mutation 类型;
|
||||
- 仍为 global 的 mutation 类型;
|
||||
- 为什么保守处理。
|
||||
|
||||
---
|
||||
|
||||
## 6. Fast Path 本轮严格不改
|
||||
|
||||
STEP-099 已确认 Fast Path 仍有 global gates:
|
||||
|
||||
- global update signature(sources/layers/images 等);
|
||||
- global content revision;
|
||||
- global fade;
|
||||
- pendingChanges;
|
||||
- placement/light/non-particle transition/repaint;
|
||||
- annotation mutation;
|
||||
- topology/boundary/revision/cache validity;
|
||||
- `singleContinuousSegment`。
|
||||
|
||||
STEP-100 **禁止修改这些 gate 的语义**。
|
||||
|
||||
允许:
|
||||
|
||||
- Source mutation 后 Fast Path miss;
|
||||
- transient source readiness 时 Fast Path fail closed;
|
||||
- full CPU renderer 被调用。
|
||||
|
||||
但进入 full CPU renderer 后,必须能做到:
|
||||
|
||||
```text
|
||||
R0 retained cache reuse
|
||||
R1 rebuild/fallback
|
||||
```
|
||||
|
||||
这正是本任务验收目标。
|
||||
|
||||
Multi-continuous Fast Path 明确不在本任务。
|
||||
|
||||
---
|
||||
|
||||
## 7. Render 顺序与混合 Build/Reuse/Fallback
|
||||
|
||||
实现 per-segment mixed decisions 时必须保持原 MapLibre 绘制语义:
|
||||
|
||||
- Style Z-order;
|
||||
- global layerIndex;
|
||||
- opaque reverse traversal;
|
||||
- translucent forward traversal;
|
||||
- currentLayer;
|
||||
- depthRangeSize;
|
||||
- legacy item 不双绘;
|
||||
- debug overlay 不重复;
|
||||
- retained texture composite 不改变 segment 顺序。
|
||||
|
||||
推荐的逻辑行为:
|
||||
|
||||
```text
|
||||
for segment in Style order:
|
||||
RETAINED + Build:
|
||||
build cache
|
||||
composite cache
|
||||
RETAINED + Reuse:
|
||||
composite cache
|
||||
RETAINED + Fallback:
|
||||
live render only this range
|
||||
CONTINUOUS:
|
||||
live render this range
|
||||
```
|
||||
|
||||
实际代码可按现有 RenderPass 安全实现,不要求机械照搬伪代码。
|
||||
|
||||
必须特别检查:
|
||||
|
||||
- 一个 R fallback 不得导致另一个 R live redraw;
|
||||
- resource prepare 在 Build 前失败时,必须先把该 Segment 本帧 decision 改为 Fallback,再进入主 buffer 绘制;
|
||||
- retained build 中途失败时,必须 invalidate/标记该 Segment失败并把该 Segment 本帧 decision 改为 Fallback,禁止 composite 旧纹理;
|
||||
- 如果失败意味着 context / shared GPU state 已不可信,允许全局 fail closed;
|
||||
- fallback range 的 legacy items 只绘一次,不得因 per-segment 主循环重复 render;
|
||||
- debug overlay 全帧只绘一次;
|
||||
- Segment 间 Opaque/Translucent 语义不得被破坏;
|
||||
- 相邻 R、多个 C、透明叠加与 legacy scene 不能因 mixed execution 出现漏绘/双绘。
|
||||
|
||||
推荐执行顺序:先完成各 Segment resource prepare 与必要离屏 Build;再 clear 主 buffer;最后按 Style Segment 顺序逐段选择 cached composite 或 live range render。
|
||||
|
||||
---
|
||||
|
||||
## 8. Diagnostics 必须继续可证明局部性
|
||||
|
||||
保留 STEP-099 diagnostics,并 additive 增加足够字段,使 RDC 能验证正式行为。
|
||||
|
||||
每个 RETAINED Segment 至少应能观察:
|
||||
|
||||
- dependentSourceIds;
|
||||
- segment readiness / support;
|
||||
- segment-local source/content revision 或等价可证明值;
|
||||
- Build / Reuse / Fallback 的**最终本帧 execution decision**;
|
||||
- build/reuse/fallback/invalidation count;
|
||||
- last invalidation/fallback reason。
|
||||
|
||||
Source mutation 继续输出:
|
||||
|
||||
- sourceId;
|
||||
- sequence/reason/action;
|
||||
- affectedSegmentIndices。
|
||||
|
||||
要求:
|
||||
|
||||
```text
|
||||
step099-dynamic mutation
|
||||
affected=[R1]
|
||||
R0 revision unchanged
|
||||
R0 cache reuse++
|
||||
R0 build unchanged
|
||||
R0 invalidation unchanged
|
||||
```
|
||||
|
||||
Diagnostics 仍是 pull/event low-frequency,不允许逐帧高频 spam。
|
||||
|
||||
---
|
||||
|
||||
## 9. Android Test App
|
||||
|
||||
复用 STEP-099,不另建独立 App。
|
||||
|
||||
至少保留并完善:
|
||||
|
||||
### T1 STATIC
|
||||
|
||||
验证静态 R-C-R:
|
||||
|
||||
- loaded settle;
|
||||
- R0/R1 build 一次后稳定 reuse;
|
||||
- 不产生持续 rebuild。
|
||||
|
||||
### T2 GEOJSON_1HZ
|
||||
|
||||
两个 GeoJSON source 交错,各约 1 Hz。
|
||||
|
||||
重点:
|
||||
|
||||
- global loaded transient false window 可以继续存在;
|
||||
- 只引用某 source 的 retained Segment 才允许因该 source mutation 改变;
|
||||
- 无关 retained Segment build/invalidation 不得跟随增长。
|
||||
|
||||
### T3 PERMANENT_UNREADY
|
||||
|
||||
保留一个:
|
||||
|
||||
- no layer;
|
||||
- enabled=false;
|
||||
- isLoaded=false;
|
||||
- no field submission
|
||||
|
||||
的 RasterArray source。
|
||||
|
||||
预期:
|
||||
|
||||
- global renderOrchestratorLoaded 仍可为 false;
|
||||
- 但无关 R0/R1 retained cache 不应因它持续 `style-not-loaded` fallback;
|
||||
- 能安全判断依赖的 retained Segment应继续 build/reuse。
|
||||
|
||||
### T4 R-C-R(主验收)
|
||||
|
||||
必须保持清晰依赖:
|
||||
|
||||
```text
|
||||
R0 -> dedicated static source only
|
||||
C -> WeatherColor/Particle field
|
||||
R1 -> dedicated dynamic GeoJSON source
|
||||
```
|
||||
|
||||
运行动态 GeoJSON 约 1 Hz。
|
||||
|
||||
必须调整 fixture,避免 STEP-099 中 R1 再复用 R0 的 static source:
|
||||
|
||||
- R0 static source 只出现在 R0;
|
||||
- R1 dynamic source 只出现在 R1;
|
||||
- 若 MapLibre 内建 annotation source/layer 自动进入 R1,必须在 diagnostics 中列明,并保证验收窗口内 annotation 不发生 mutation;
|
||||
- 不允许用业务 ID 特判 renderer。
|
||||
|
||||
RDC 验收区间必须支持“settled baseline → 60 s pure dynamic-source mutation window → final”对比。每个 aggregate sample 同时记录 mutation source/sequence、各 Segment local revision、readiness、最终 execution decision 与 cache counters。
|
||||
|
||||
---
|
||||
|
||||
## 10. STEP-100 硬验收标准
|
||||
|
||||
### A. R0 locality
|
||||
|
||||
T4 settle 后记录 baseline。
|
||||
|
||||
随后至少 60 s dynamic GeoJSON:
|
||||
|
||||
- R0 `buildDelta == 0`
|
||||
- R0 `invalidationDelta == 0`
|
||||
- R0 `reuseDelta > 0`
|
||||
- R0 segment-local revision/content state 不因 R1 dynamic source mutation 改变
|
||||
- 即使某采样点 global `renderOrchestratorLoaded=false`,只要 R0 自身 prerequisites/dependencies ready,R0 final decision 仍可为 Reuse
|
||||
|
||||
如果为了安全出现明确 global event,应在日志中可解释并重新选择纯动态 mutation 区间;不能把 global event 混入后声称 PASS。
|
||||
|
||||
### B. R1 reacts
|
||||
|
||||
同一时间窗:
|
||||
|
||||
- R1 必须能观察到 source mutation;
|
||||
- R1 revision / build / invalidation / fallback 中至少有与动态 source 对应的实际变化;
|
||||
- `affectedSegmentIndices` 必须只包含真实依赖 Segment。
|
||||
|
||||
### C. global revision compatibility
|
||||
|
||||
- global `retainedUnderlayContentRevision` 允许并预期继续增长;
|
||||
- 不能通过停止 global revision 增长来制造 R0 PASS。
|
||||
|
||||
### D. readiness locality
|
||||
|
||||
T2 transient unloaded 或 T3 permanent unrelated unloaded 时:
|
||||
|
||||
- global loaded 可 false;
|
||||
- R0 不得仅因无关 source 的 unloaded 而 invalid/rebuild/fallback;
|
||||
- R0 cache 必须继续可复用。
|
||||
|
||||
### E. correctness
|
||||
|
||||
- 无 crash / ANR;
|
||||
- 无 stale cache composite;
|
||||
- 无 Segment Z-order 错乱;
|
||||
- 无 fallback range 双绘;
|
||||
- Cache OFF 仍安全走 live renderer;
|
||||
- plan invalid 仍 fail closed。
|
||||
|
||||
任一硬门槛失败,STEP-100 不得宣布 PASS,不得进入 V18。
|
||||
|
||||
---
|
||||
|
||||
## 11. Native 非 Jest 测试要求
|
||||
|
||||
Codex 必须新增或扩展现有 C++ 测试源码,至少覆盖:
|
||||
|
||||
1. source A mutation 只改变依赖 A 的 Segment revision;
|
||||
2. source A 被多个 Segment 共享时,对应多个 Segment 都变化;
|
||||
3. unrelated Segment revision 不变;
|
||||
4. source-less Segment 不制造 dependency;
|
||||
5. unknown/global dependency fail closed;
|
||||
6. unrelated source unloaded 不使安全 Segment readiness false;
|
||||
7. dependent source unloaded 使对应 Segment readiness false;
|
||||
8. R0 Reuse + R1 Fallback 的 mixed decision;
|
||||
9. 单 Segment build failure 不 composite stale cache;
|
||||
10. query diagnostics 不改变 revision/cache state;
|
||||
11. global non-source invalidation 仍影响所有 retained;
|
||||
12. source 删除后同 ID recreate 不误复用旧 Segment cache identity;
|
||||
13. dependent source fading 只影响对应 Segment;unrelated source fading 不影响安全 Segment;
|
||||
14. Cache OFF / invalid plan 行为不回归;
|
||||
15. 至少有一组能证明实际 mixed render 没有漏绘/双绘的 renderer-level draw-count、pixel 或现有等价测试入口;不能只用 decision unit test 代替。
|
||||
|
||||
不得修改测试断言去适应错误实现。
|
||||
|
||||
---
|
||||
|
||||
## 12. 允许修改范围
|
||||
|
||||
允许:
|
||||
|
||||
- RenderOrchestrator source mutation/readiness bookkeeping;
|
||||
- RenderSegment dependency production helper;
|
||||
- Renderer retained per-segment revision/readiness/decision;
|
||||
- RetainedUnderlayCache/RuntimeState 为局部状态增加必要字段;
|
||||
- additive diagnostics JSON/TS;
|
||||
- Android Validation Test App STEP-099 fixture/log;
|
||||
- 相关 Native C++ tests;
|
||||
- STEP-100 Step 记录/完成报告/开发记录。
|
||||
|
||||
禁止:
|
||||
|
||||
- NavSea;
|
||||
- Integration;
|
||||
- V17 tag/release worktree;
|
||||
- Source `isLoaded()` 定义;
|
||||
- Particle field/provider/network;
|
||||
- Fast Path gate semantic;
|
||||
- `singleContinuousSegment`;
|
||||
- multi-continuous Fast Path;
|
||||
- 自动改变 Style Z-order;
|
||||
- provider/business-specific source/layer ID;
|
||||
- unrelated cleanup/refactor;
|
||||
- Jest。
|
||||
|
||||
---
|
||||
|
||||
## 13. Codex 执行边界
|
||||
|
||||
Codex 本轮负责:
|
||||
|
||||
1. 读取实际源码和任务书;
|
||||
2. 实现代码;
|
||||
3. 编写 Native 非 Jest 测试源码;
|
||||
4. 更新 Android Test App 验证入口;
|
||||
5. 执行轻量静态检查,如 `git diff --check`;
|
||||
6. 生成中文完成报告。
|
||||
|
||||
Codex **不得执行**:
|
||||
|
||||
- Android Gradle / APK;
|
||||
- ADB / 安装 / 实机;
|
||||
- Xcode / iOS device;
|
||||
- V18 release;
|
||||
- tag;
|
||||
- push;
|
||||
- Jest。
|
||||
|
||||
本轮 Codex 完成报告固定:
|
||||
|
||||
`docs/报告/STEP-100_SourceScoped_RenderSegment局部失效_Codex完成报告_v1.md`
|
||||
|
||||
Step 记录固定:
|
||||
|
||||
`docs/50_Step记录/STEP-100_SourceScoped_RenderSegment局部失效_v1.md`
|
||||
|
||||
完成报告必须写清:
|
||||
|
||||
- 实际 Codex model / reasoning effort;
|
||||
- start HEAD;
|
||||
- 修改文件;
|
||||
- 生产设计;
|
||||
- source revision/readiness 规则;
|
||||
- mixed retained decision 规则;
|
||||
- Fast Path 明确未改;
|
||||
- 测试源码新增情况;
|
||||
- 实际执行 / NOT RUN;
|
||||
- 风险与 RDC 验收点;
|
||||
- 未 commit / 未 push(如果确实如此)。
|
||||
|
||||
Codex 不 commit、不 push。由 RDC 验证通过后统一 Git 收尾。
|
||||
|
||||
---
|
||||
|
||||
## 14. RDC / ChatGPT 后续独立验收
|
||||
|
||||
Codex 返回后由 RDC 负责:
|
||||
|
||||
1. source/diff 审计;
|
||||
2. Native Mac 编译;
|
||||
3. Native focused + broad related C++ tests;
|
||||
4. Android x86_64 OpenGL validation APK 单次构建;
|
||||
5. Mac ADB 直连 FZG1-2;
|
||||
6. 自动跑 T1/T2/T3/T4;
|
||||
7. 解析 R0/R1 build/reuse/invalidation/revision/readiness;
|
||||
8. 确认硬验收标准 A~E;
|
||||
9. 若失败,回到 STEP-100 修复,不发布;
|
||||
10. 若全部 PASS,完成 STEP-100 Git push;
|
||||
11. 再按现有统一发行流程升级到:
|
||||
`maplibre-particle/rn/13.4.1-particle.18`
|
||||
12. V18 build/release/public artifact 验证通过后才宣布 V18 发布完成。
|
||||
|
||||
---
|
||||
|
||||
## 15. 停止条件
|
||||
|
||||
Codex 遇到以下任一情况必须停止并报告,不得扩大范围:
|
||||
|
||||
- 要修改 Source `isLoaded()` 才能继续;
|
||||
- 需要放宽 Fast Path gate;
|
||||
- 必须改变 singleContinuous / multi-continuous;
|
||||
- production dependency 无法可靠从现有 Style/RenderLayer/Source 推导;
|
||||
- per-segment fallback 会破坏 RenderPass/Z-order 且无法在当前范围安全解决;
|
||||
- 发现需要 NavSea/Integration 配合;
|
||||
- 需要业务 layer/source ID;
|
||||
- 需要改变 V17 release。
|
||||
|
||||
---
|
||||
|
||||
## 16. 本任务最终原则
|
||||
|
||||
STEP-100 的目标不是让“全局世界看起来 loaded”。
|
||||
|
||||
目标是:
|
||||
|
||||
> **即使某个 GeoJSON Source 正在变化或短暂 unready,只要 R0 与它没有依赖关系,R0 retained cache 就必须继续安全 reuse,不能被迫一起刷新。**
|
||||
|
||||
Fast Path 是否还能 miss,是后续独立问题。
|
||||
|
||||
只有这一点经过 Native + Android R-C-R 实测成立,才允许进入 V18 发布。
|
||||
405
docs/40_任务/STEP-101_RenderOrchestrator销毁生命周期修复_Codex任务_v1.md
Normal file
405
docs/40_任务/STEP-101_RenderOrchestrator销毁生命周期修复_Codex任务_v1.md
Normal file
@@ -0,0 +1,405 @@
|
||||
# STEP-101 RenderOrchestrator teardown 生命周期修复 — Codex 任务 v1
|
||||
|
||||
## 0. 任务目的
|
||||
|
||||
修复 Particle V18 Android 在正常 Renderer teardown / MapView reset 时发生的 Native SIGABRT。
|
||||
|
||||
现场已在 NavSea Android x86_64 设备稳定复现:
|
||||
|
||||
```text
|
||||
Fatal signal 6 (SIGABRT)
|
||||
thread: RenderThread 11
|
||||
|
||||
FORTIFY:
|
||||
pthread_mutex_lock called on a destroyed mutex
|
||||
|
||||
libmaplibre.so
|
||||
std::__ndk1::mutex::lock()
|
||||
...
|
||||
mbgl::android::MapRenderer::resetRenderer()
|
||||
```
|
||||
|
||||
V17 正常,V18 接入后稳定在启动约 9~11 秒触发。
|
||||
|
||||
本任务只修 Particle Native 生命周期;禁止在 NavSea 增加绕路、private wrapper、禁止阻止正常 MapView reset。
|
||||
|
||||
未经用户明确要求:不加入、不执行 Jest。
|
||||
|
||||
---
|
||||
|
||||
## 1. 冻结基线
|
||||
|
||||
工作树:
|
||||
|
||||
`/Users/tei/dev/MapLibreParticleLab-step101`
|
||||
|
||||
分支:
|
||||
|
||||
`step101-render-orchestrator-teardown`
|
||||
|
||||
起点必须是:
|
||||
|
||||
`origin/particle/main = d29e84c1a90daeea104b389d28b000c4f017d08d`
|
||||
|
||||
即正式 V18 Final HEAD。
|
||||
|
||||
V18 已发布,不得覆盖、重发或移动:
|
||||
|
||||
- `maplibre-particle/rn/13.4.1-particle.18`
|
||||
- Android `13.4.1-particle.18`
|
||||
- iOS `6.28.0-particle.18`
|
||||
|
||||
修复通过后由 RDC/ChatGPT 发布新不可变版本 V19。
|
||||
|
||||
---
|
||||
|
||||
## 2. 已知根因证据
|
||||
|
||||
V18 STEP-100 新增:
|
||||
|
||||
```cpp
|
||||
RenderSegmentRevisionBook retainedSegmentRevisions;
|
||||
mutable std::mutex readinessDiagnosticsMutex;
|
||||
uint64_t readinessEventSequence;
|
||||
std::unordered_map<std::string, SourceReadinessEventState> sourceReadinessEvents;
|
||||
RenderMutationDiagnostics lastReadinessMutation;
|
||||
```
|
||||
|
||||
多个 RenderSource / Tile observer callback 会进入:
|
||||
|
||||
```cpp
|
||||
advanceRetainedSourceRevision(...)
|
||||
recordReadinessMutation(...)
|
||||
onTileChanged(...)
|
||||
onTileAction(...)
|
||||
buildReadinessDiagnostics(...)
|
||||
retainedSegmentSourceState(...)
|
||||
```
|
||||
|
||||
并 lock:
|
||||
|
||||
```cpp
|
||||
readinessDiagnosticsMutex
|
||||
```
|
||||
|
||||
V18 当前成员声明中:
|
||||
|
||||
```text
|
||||
renderSources
|
||||
renderLayers
|
||||
...
|
||||
retainedSegmentRevisions
|
||||
...
|
||||
readinessDiagnosticsMutex
|
||||
sourceReadinessEvents
|
||||
...
|
||||
threadPool
|
||||
```
|
||||
|
||||
C++ 成员按声明反序析构,因此 readiness state / mutex 会早于 renderSources 销毁。
|
||||
|
||||
而 RenderSource 在创建时:
|
||||
|
||||
```cpp
|
||||
renderSource->setObserver(this);
|
||||
```
|
||||
|
||||
observer 指向 RenderOrchestrator。
|
||||
|
||||
当前 `RenderOrchestrator::~RenderOrchestrator()` 只:
|
||||
|
||||
1. contextLost 时标记 layer;
|
||||
2. `threadPool.waitForEmpty()`;
|
||||
|
||||
没有在 readiness state 仍有效时显式 detach RenderSource observer / 清理 renderSources。
|
||||
|
||||
因此 V18 存在明确生命周期危险:
|
||||
|
||||
```text
|
||||
readinessDiagnosticsMutex destroyed
|
||||
↓
|
||||
renderSources still destroying/alive
|
||||
↓
|
||||
late tile/source callback
|
||||
↓
|
||||
RenderOrchestrator::onTile*
|
||||
↓
|
||||
lock destroyed mutex
|
||||
↓
|
||||
FORTIFY SIGABRT
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 修复原则
|
||||
|
||||
不要只依赖调整成员声明顺序。
|
||||
|
||||
本任务应建立显式 teardown contract:
|
||||
|
||||
```text
|
||||
RenderOrchestrator destructor begins
|
||||
↓
|
||||
stop / drain deferred scheduler work
|
||||
↓
|
||||
detach observer from every RenderSource
|
||||
↓
|
||||
destroy/clear renderSources while all orchestrator state is still alive
|
||||
↓
|
||||
only then continue member teardown
|
||||
```
|
||||
|
||||
同时 `RenderSource::setObserver(nullptr)` 必须安全恢复到 NullObserver。
|
||||
|
||||
目标是即使未来成员声明顺序变化,Source callback 也不能在 owner teardown 后访问 RenderOrchestrator。
|
||||
|
||||
---
|
||||
|
||||
## 4. 必做实现
|
||||
|
||||
### 4.1 RenderSource nullptr observer 语义
|
||||
|
||||
当前:
|
||||
|
||||
```cpp
|
||||
void RenderSource::setObserver(RenderSourceObserver* observer_) {
|
||||
observer = observer_;
|
||||
}
|
||||
```
|
||||
|
||||
改成与 Style Source / Layer 一致的安全语义:
|
||||
|
||||
```cpp
|
||||
observer = observer_ ? observer_ : &nullObserver;
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- constructor 仍默认 NullObserver;
|
||||
- `setObserver(nullptr)` 是合法 detach;
|
||||
- onTileChanged / onTileError / onTileAction 不需要加 null 分支;
|
||||
- 不吞掉正常有 observer 时的回调。
|
||||
|
||||
必须加非 Jest C++ 回归测试验证 nullptr detach 后 callback 不 crash、不调用旧 observer。
|
||||
|
||||
### 4.2 RenderOrchestrator 显式 teardown
|
||||
|
||||
在 `RenderOrchestrator::~RenderOrchestrator()` 中,在 readiness mutex/state 仍确定存活时:
|
||||
|
||||
1. 保留现有 contextLost layer handling;
|
||||
2. 等待已有 deferred work 完成;
|
||||
3. 对所有 `renderSources` 执行 observer detach;
|
||||
4. 显式 `renderSources.clear()`;
|
||||
5. 必要时对 glyph/image observer 也做已有 API 支持的 detach,但不要扩大范围;
|
||||
6. 不允许 teardown callback 再进入 RenderOrchestrator source callback。
|
||||
|
||||
顺序必须结合当前 TaggedScheduler / RenderSource / TilePyramid 的真实生命周期检查,而不是机械照抄上述编号。
|
||||
|
||||
如果存在“clear renderSources 本身可能产生新的 scheduler work”,需要设计第二次 drain 或更安全顺序,并在报告解释。
|
||||
|
||||
### 4.3 生命周期状态可选保护
|
||||
|
||||
如果仅 detach + explicit clear 仍无法证明并发安全,允许增加最小 teardown flag,例如 atomic bool,但只作为二次防线。
|
||||
|
||||
禁止用 flag 代替 observer detach。
|
||||
|
||||
禁止 catch FORTIFY;destroyed mutex abort 不是 C++ exception。
|
||||
|
||||
---
|
||||
|
||||
## 5. 不得修改
|
||||
|
||||
- STEP-100 Source→Segment locality 语义;
|
||||
- per-segment revision;
|
||||
- per-segment readiness;
|
||||
- mixed Build/Reuse/Fallback;
|
||||
- Fast Path gate;
|
||||
- singleContinuousSegment;
|
||||
- Source::isLoaded;
|
||||
- Style Z-order;
|
||||
- provider/network;
|
||||
- NavSea;
|
||||
- MapRenderer::resetRenderer 正常生命周期;
|
||||
- Android Activity 为避免 reset 而加 workaround。
|
||||
|
||||
---
|
||||
|
||||
## 6. 必做测试
|
||||
|
||||
### 6.1 Native 非 Jest
|
||||
|
||||
至少覆盖:
|
||||
|
||||
1. RenderSource 默认 NullObserver 可安全接 callback;
|
||||
2. setObserver(real) 正常转发;
|
||||
3. setObserver(nullptr) 后不再调用旧 observer;
|
||||
4. detach 后 onTileChanged / onTileError / onTileAction 安全;
|
||||
5. RenderOrchestrator teardown 有至少一个含 RenderSource 的场景;
|
||||
6. source callback 与 teardown 边界不访问已销毁 readiness state;
|
||||
7. STEP-100 locality 相关 focused tests 不回归。
|
||||
|
||||
如果现有测试设施不方便直接构造完整 RenderOrchestrator teardown,允许提取最小可测试 teardown helper,但禁止为了测试重构大范围生产结构。
|
||||
|
||||
### 6.2 Android Test App
|
||||
|
||||
在现有 ParticleCoreLayerValidationActivity / Validation App 增加专门 STEP-101 teardown 场景。
|
||||
|
||||
场景必须真实触发 Native Renderer teardown,而不是只 clear field。
|
||||
|
||||
至少提供:
|
||||
|
||||
```text
|
||||
T1 RESET_ONCE
|
||||
T2 RESET_LOOP_20
|
||||
T3 RESET_WITH_DYNAMIC_GEOJSON
|
||||
T4 RESET_WITH_DIAGNOSTICS
|
||||
```
|
||||
|
||||
目标:
|
||||
|
||||
- recreate/destroy MapView 或等价真实 renderer.reset;
|
||||
- 每轮等待足够让 source/tile work 进入正常运行;
|
||||
- T3 同时保持动态 GeoJSON/source mutation;
|
||||
- T4 在 reset 前调用一次 RendererRuntime diagnostics,模拟 NavSea probe 时序;
|
||||
- 20 次循环无 SIGABRT / crash / ANR;
|
||||
- reset 后新 renderer 能继续正常显示 / 获取 diagnostics。
|
||||
|
||||
不得通过延迟 reset、禁用 observer、关闭 GeoJSON 来逃避问题。
|
||||
|
||||
---
|
||||
|
||||
## 7. NavSea 现场等价验收
|
||||
|
||||
RDC 在 Codex 完成后独立执行。
|
||||
|
||||
Android 设备:
|
||||
|
||||
`192.168.200.139:5555`
|
||||
|
||||
必须显式 `adb -s`。
|
||||
|
||||
最终至少执行:
|
||||
|
||||
1. Validation App STEP-101 T1~T4;
|
||||
2. crash buffer 检查;
|
||||
3. 20 次 reset/recreate;
|
||||
4. 安装 V19 进 NavSea 后做真实启动;
|
||||
5. 保留 NavSea 当前 `key={map-style-generation-...}` 正常 reset 触发条件;
|
||||
6. 保留/启用一次 RenderSegment diagnostics probe A/B;
|
||||
7. 连续至少 5 次冷启动,每次观察 >30 秒;
|
||||
8. 不允许出现:
|
||||
- `pthread_mutex_lock called on a destroyed mutex`
|
||||
- SIGABRT RenderThread
|
||||
- `MapRenderer::resetRenderer` teardown crash。
|
||||
|
||||
---
|
||||
|
||||
## 8. STEP-100 回归验收
|
||||
|
||||
修复 teardown 后必须确认 V18 的核心 locality 没被破坏。
|
||||
|
||||
至少复测 T4 R-C-R:
|
||||
|
||||
无关 R0:
|
||||
|
||||
```text
|
||||
buildDelta = 0
|
||||
invalidationDelta = 0
|
||||
reuseDelta > 0
|
||||
```
|
||||
|
||||
动态 R1 正常 build/invalidation。
|
||||
|
||||
禁止通过删除 mutex/readiness/revision 功能来修 crash。
|
||||
|
||||
---
|
||||
|
||||
## 9. Codex 执行边界
|
||||
|
||||
Codex负责:
|
||||
|
||||
- 只读检查真实 V18 代码;
|
||||
- 实现最小 Native 修复;
|
||||
- 编写/更新非 Jest C++ tests;
|
||||
- 更新 Android Validation STEP-101 场景;
|
||||
- 静态检查;
|
||||
- 生成报告。
|
||||
|
||||
Codex不负责:
|
||||
|
||||
- NavSea 修改;
|
||||
- V19 release;
|
||||
- tag;
|
||||
- publish;
|
||||
- 覆盖 V18;
|
||||
- Jest。
|
||||
|
||||
Mac Native 开发/编译原则保持;不要使用 Linux Native 环境。
|
||||
|
||||
---
|
||||
|
||||
## 10. 完成报告
|
||||
|
||||
生成:
|
||||
|
||||
`docs/报告/STEP-101_RenderOrchestrator销毁生命周期修复_Codex完成报告_v1.md`
|
||||
|
||||
Step 记录:
|
||||
|
||||
`docs/50_Step记录/STEP-101_RenderOrchestrator销毁生命周期修复_v1.md`
|
||||
|
||||
报告必须写:
|
||||
|
||||
- root cause;
|
||||
- start HEAD;
|
||||
- 修改文件;
|
||||
- destructor teardown 顺序;
|
||||
- observer detach 语义;
|
||||
- 为什么不依赖成员声明顺序;
|
||||
- tests added;
|
||||
- 实际运行 / NOT RUN;
|
||||
- STEP-100 locality 是否保持;
|
||||
- 风险;
|
||||
- 未发布 V19(Codex 阶段)。
|
||||
|
||||
Codex阶段不 push,由 RDC 验收后统一 commit/push。
|
||||
|
||||
---
|
||||
|
||||
## 11. V19 发布门槛
|
||||
|
||||
仅当:
|
||||
|
||||
- Native compile PASS;
|
||||
- focused C++ tests PASS;
|
||||
- Android Validation teardown loop PASS;
|
||||
- STEP-100 locality regression PASS;
|
||||
- NavSea V19 真实 5 次冷启动 >30s PASS;
|
||||
- crash buffer 无 destroyed mutex;
|
||||
|
||||
才允许发布:
|
||||
|
||||
- RN / Android:`13.4.1-particle.19`
|
||||
- iOS:`6.28.0-particle.19`
|
||||
- tag:`maplibre-particle/rn/13.4.1-particle.19`
|
||||
|
||||
V18 保持不可变,不覆盖、不删除。
|
||||
|
||||
---
|
||||
|
||||
## 12. 最终原则
|
||||
|
||||
正常:
|
||||
|
||||
```text
|
||||
MapView unmount
|
||||
Style generation replacement
|
||||
renderer.reset()
|
||||
Activity recreate
|
||||
```
|
||||
|
||||
都是合法生命周期。
|
||||
|
||||
Particle Native 必须做到:
|
||||
|
||||
> Renderer teardown 永远不能因为 observer late callback 访问已销毁 mutex/state 而杀掉宿主 App。
|
||||
510
docs/40_任务/STEP-101_RenderOrchestrator销毁生命周期修复_Codex任务_v2.md
Normal file
510
docs/40_任务/STEP-101_RenderOrchestrator销毁生命周期修复_Codex任务_v2.md
Normal file
@@ -0,0 +1,510 @@
|
||||
# STEP-101 RenderOrchestrator teardown 生命周期修复 — Codex 任务 v1
|
||||
|
||||
## 0. 任务目的
|
||||
|
||||
修复 Particle V18 Android 在正常 Renderer teardown / MapView reset 时发生的 Native SIGABRT。
|
||||
|
||||
现场已在 NavSea Android x86_64 设备稳定复现:
|
||||
|
||||
```text
|
||||
Fatal signal 6 (SIGABRT)
|
||||
thread: RenderThread 11
|
||||
|
||||
FORTIFY:
|
||||
pthread_mutex_lock called on a destroyed mutex
|
||||
|
||||
libmaplibre.so
|
||||
std::__ndk1::mutex::lock()
|
||||
...
|
||||
mbgl::android::MapRenderer::resetRenderer()
|
||||
```
|
||||
|
||||
V17 正常,V18 接入后稳定在启动约 9~11 秒触发。
|
||||
|
||||
本任务只修 Particle Native 生命周期;禁止在 NavSea 增加绕路、private wrapper、禁止阻止正常 MapView reset。
|
||||
|
||||
未经用户明确要求:不加入、不执行 Jest。
|
||||
|
||||
---
|
||||
|
||||
## 1. 冻结基线
|
||||
|
||||
工作树:
|
||||
|
||||
`/Users/tei/dev/MapLibreParticleLab-step101`
|
||||
|
||||
分支:
|
||||
|
||||
`step101-render-orchestrator-teardown`
|
||||
|
||||
起点必须是:
|
||||
|
||||
`origin/particle/main = d29e84c1a90daeea104b389d28b000c4f017d08d`
|
||||
|
||||
即正式 V18 Final HEAD。
|
||||
|
||||
V18 已发布,不得覆盖、重发或移动:
|
||||
|
||||
- `maplibre-particle/rn/13.4.1-particle.18`
|
||||
- Android `13.4.1-particle.18`
|
||||
- iOS `6.28.0-particle.18`
|
||||
|
||||
修复通过后由 RDC/ChatGPT 发布新不可变版本 V19。
|
||||
|
||||
---
|
||||
|
||||
## 2. 已知根因证据
|
||||
|
||||
V18 STEP-100 新增:
|
||||
|
||||
```cpp
|
||||
RenderSegmentRevisionBook retainedSegmentRevisions;
|
||||
mutable std::mutex readinessDiagnosticsMutex;
|
||||
uint64_t readinessEventSequence;
|
||||
std::unordered_map<std::string, SourceReadinessEventState> sourceReadinessEvents;
|
||||
RenderMutationDiagnostics lastReadinessMutation;
|
||||
```
|
||||
|
||||
多个 RenderSource / Tile observer callback 会进入:
|
||||
|
||||
```cpp
|
||||
advanceRetainedSourceRevision(...)
|
||||
recordReadinessMutation(...)
|
||||
onTileChanged(...)
|
||||
onTileAction(...)
|
||||
buildReadinessDiagnostics(...)
|
||||
retainedSegmentSourceState(...)
|
||||
```
|
||||
|
||||
并 lock:
|
||||
|
||||
```cpp
|
||||
readinessDiagnosticsMutex
|
||||
```
|
||||
|
||||
V18 当前成员声明中:
|
||||
|
||||
```text
|
||||
renderSources
|
||||
renderLayers
|
||||
...
|
||||
retainedSegmentRevisions
|
||||
...
|
||||
readinessDiagnosticsMutex
|
||||
sourceReadinessEvents
|
||||
...
|
||||
threadPool
|
||||
```
|
||||
|
||||
C++ 成员按声明反序析构,因此 readiness state / mutex 会早于 renderSources 销毁。
|
||||
|
||||
而 RenderSource 在创建时:
|
||||
|
||||
```cpp
|
||||
renderSource->setObserver(this);
|
||||
```
|
||||
|
||||
observer 指向 RenderOrchestrator。
|
||||
|
||||
当前 `RenderOrchestrator::~RenderOrchestrator()` 只:
|
||||
|
||||
1. contextLost 时标记 layer;
|
||||
2. `threadPool.waitForEmpty()`;
|
||||
|
||||
没有在 readiness state 仍有效时显式 detach RenderSource observer / 清理 renderSources。
|
||||
|
||||
因此 V18 存在明确生命周期危险:
|
||||
|
||||
```text
|
||||
readinessDiagnosticsMutex destroyed
|
||||
↓
|
||||
renderSources still destroying/alive
|
||||
↓
|
||||
late tile/source callback
|
||||
↓
|
||||
RenderOrchestrator::onTile*
|
||||
↓
|
||||
lock destroyed mutex
|
||||
↓
|
||||
FORTIFY SIGABRT
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 修复原则
|
||||
|
||||
不要只依赖调整成员声明顺序。
|
||||
|
||||
本任务应建立显式 teardown contract:
|
||||
|
||||
```text
|
||||
RenderOrchestrator destructor begins
|
||||
↓
|
||||
stop / drain deferred scheduler work
|
||||
↓
|
||||
detach observer from every RenderSource
|
||||
↓
|
||||
destroy/clear renderSources while all orchestrator state is still alive
|
||||
↓
|
||||
only then continue member teardown
|
||||
```
|
||||
|
||||
同时 `RenderSource::setObserver(nullptr)` 必须安全恢复到 NullObserver。
|
||||
|
||||
目标是即使未来成员声明顺序变化,Source callback 也不能在 owner teardown 后访问 RenderOrchestrator。
|
||||
|
||||
---
|
||||
|
||||
## 4. 必做实现
|
||||
|
||||
### 4.1 RenderSource nullptr observer 语义
|
||||
|
||||
当前:
|
||||
|
||||
```cpp
|
||||
void RenderSource::setObserver(RenderSourceObserver* observer_) {
|
||||
observer = observer_;
|
||||
}
|
||||
```
|
||||
|
||||
改成与 Style Source / Layer 一致的安全语义:
|
||||
|
||||
```cpp
|
||||
observer = observer_ ? observer_ : &nullObserver;
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- constructor 仍默认 NullObserver;
|
||||
- `setObserver(nullptr)` 是合法 detach;
|
||||
- onTileChanged / onTileError / onTileAction 不需要加 null 分支;
|
||||
- 不吞掉正常有 observer 时的回调。
|
||||
|
||||
必须加非 Jest C++ 回归测试验证 nullptr detach 后 callback 不 crash、不调用旧 observer。
|
||||
|
||||
### 4.2 RenderOrchestrator 显式 teardown
|
||||
|
||||
在 `RenderOrchestrator::~RenderOrchestrator()` 中,在 readiness mutex/state 仍确定存活时:
|
||||
|
||||
1. 保留现有 contextLost layer handling;
|
||||
2. 等待已有 deferred work 完成;
|
||||
3. 对所有 `renderSources` 执行 observer detach;
|
||||
4. 显式 `renderSources.clear()`;
|
||||
5. 必要时对 glyph/image observer 也做已有 API 支持的 detach,但不要扩大范围;
|
||||
6. 不允许 teardown callback 再进入 RenderOrchestrator source callback。
|
||||
|
||||
顺序必须结合当前 TaggedScheduler / RenderSource / TilePyramid 的真实生命周期检查,而不是机械照抄上述编号。
|
||||
|
||||
如果存在“clear renderSources 本身可能产生新的 scheduler work”,需要设计第二次 drain 或更安全顺序,并在报告解释。
|
||||
|
||||
### 4.3 生命周期状态可选保护
|
||||
|
||||
如果仅 detach + explicit clear 仍无法证明并发安全,允许增加最小 teardown flag,例如 atomic bool,但只作为二次防线。
|
||||
|
||||
禁止用 flag 代替 observer detach。
|
||||
|
||||
禁止 catch FORTIFY;destroyed mutex abort 不是 C++ exception。
|
||||
|
||||
---
|
||||
|
||||
## 5. 不得修改
|
||||
|
||||
- STEP-100 Source→Segment locality 语义;
|
||||
- per-segment revision;
|
||||
- per-segment readiness;
|
||||
- mixed Build/Reuse/Fallback;
|
||||
- Fast Path gate;
|
||||
- singleContinuousSegment;
|
||||
- Source::isLoaded;
|
||||
- Style Z-order;
|
||||
- provider/network;
|
||||
- NavSea;
|
||||
- MapRenderer::resetRenderer 正常生命周期;
|
||||
- Android Activity 为避免 reset 而加 workaround。
|
||||
|
||||
---
|
||||
|
||||
## 6. 必做测试
|
||||
|
||||
### 6.1 Native 非 Jest
|
||||
|
||||
至少覆盖:
|
||||
|
||||
1. RenderSource 默认 NullObserver 可安全接 callback;
|
||||
2. setObserver(real) 正常转发;
|
||||
3. setObserver(nullptr) 后不再调用旧 observer;
|
||||
4. detach 后 onTileChanged / onTileError / onTileAction 安全;
|
||||
5. RenderOrchestrator teardown 有至少一个含 RenderSource 的场景;
|
||||
6. source callback 与 teardown 边界不访问已销毁 readiness state;
|
||||
7. STEP-100 locality 相关 focused tests 不回归。
|
||||
|
||||
如果现有测试设施不方便直接构造完整 RenderOrchestrator teardown,允许提取最小可测试 teardown helper,但禁止为了测试重构大范围生产结构。
|
||||
|
||||
### 6.2 Android Test App
|
||||
|
||||
在现有 ParticleCoreLayerValidationActivity / Validation App 增加专门 STEP-101 teardown 场景。
|
||||
|
||||
场景必须真实触发 Native Renderer teardown,而不是只 clear field。
|
||||
|
||||
至少提供:
|
||||
|
||||
```text
|
||||
T1 RESET_ONCE
|
||||
T2 RESET_LOOP_20
|
||||
T3 RESET_WITH_DYNAMIC_GEOJSON
|
||||
T4 RESET_WITH_DIAGNOSTICS
|
||||
```
|
||||
|
||||
目标:
|
||||
|
||||
- recreate/destroy MapView 或等价真实 renderer.reset;
|
||||
- 每轮等待足够让 source/tile work 进入正常运行;
|
||||
- T3 同时保持动态 GeoJSON/source mutation;
|
||||
- T4 在 reset 前调用一次 RendererRuntime diagnostics,模拟 NavSea probe 时序;
|
||||
- 20 次循环无 SIGABRT / crash / ANR;
|
||||
- reset 后新 renderer 能继续正常显示 / 获取 diagnostics。
|
||||
|
||||
不得通过延迟 reset、禁用 observer、关闭 GeoJSON 来逃避问题。
|
||||
|
||||
---
|
||||
|
||||
## 7. NavSea 现场等价验收
|
||||
|
||||
RDC 在 Codex 完成后独立执行。
|
||||
|
||||
Android 设备:
|
||||
|
||||
`192.168.200.139:5555`
|
||||
|
||||
必须显式 `adb -s`。
|
||||
|
||||
最终至少执行:
|
||||
|
||||
1. Validation App STEP-101 T1~T4;
|
||||
2. crash buffer 检查;
|
||||
3. 20 次 reset/recreate;
|
||||
4. 安装 V19 进 NavSea 后做真实启动;
|
||||
5. 保留 NavSea 当前 `key={map-style-generation-...}` 正常 reset 触发条件;
|
||||
6. 保留/启用一次 RenderSegment diagnostics probe A/B;
|
||||
7. 连续至少 5 次冷启动,每次观察 >30 秒;
|
||||
8. 不允许出现:
|
||||
- `pthread_mutex_lock called on a destroyed mutex`
|
||||
- SIGABRT RenderThread
|
||||
- `MapRenderer::resetRenderer` teardown crash。
|
||||
|
||||
---
|
||||
|
||||
## 8. STEP-100 回归验收
|
||||
|
||||
修复 teardown 后必须确认 V18 的核心 locality 没被破坏。
|
||||
|
||||
至少复测 T4 R-C-R:
|
||||
|
||||
无关 R0:
|
||||
|
||||
```text
|
||||
buildDelta = 0
|
||||
invalidationDelta = 0
|
||||
reuseDelta > 0
|
||||
```
|
||||
|
||||
动态 R1 正常 build/invalidation。
|
||||
|
||||
禁止通过删除 mutex/readiness/revision 功能来修 crash。
|
||||
|
||||
---
|
||||
|
||||
## 9. Codex 执行边界
|
||||
|
||||
Codex负责:
|
||||
|
||||
- 只读检查真实 V18 代码;
|
||||
- 实现最小 Native 修复;
|
||||
- 编写/更新非 Jest C++ tests;
|
||||
- 更新 Android Validation STEP-101 场景;
|
||||
- 静态检查;
|
||||
- 生成报告。
|
||||
|
||||
Codex不负责:
|
||||
|
||||
- NavSea 修改;
|
||||
- V19 release;
|
||||
- tag;
|
||||
- publish;
|
||||
- 覆盖 V18;
|
||||
- Jest。
|
||||
|
||||
Mac Native 开发/编译原则保持;不要使用 Linux Native 环境。
|
||||
|
||||
---
|
||||
|
||||
## 10. 完成报告
|
||||
|
||||
生成:
|
||||
|
||||
`docs/报告/STEP-101_RenderOrchestrator销毁生命周期修复_Codex完成报告_v1.md`
|
||||
|
||||
Step 记录:
|
||||
|
||||
`docs/50_Step记录/STEP-101_RenderOrchestrator销毁生命周期修复_v1.md`
|
||||
|
||||
报告必须写:
|
||||
|
||||
- root cause;
|
||||
- start HEAD;
|
||||
- 修改文件;
|
||||
- destructor teardown 顺序;
|
||||
- observer detach 语义;
|
||||
- 为什么不依赖成员声明顺序;
|
||||
- tests added;
|
||||
- 实际运行 / NOT RUN;
|
||||
- STEP-100 locality 是否保持;
|
||||
- 风险;
|
||||
- 未发布 V19(Codex 阶段)。
|
||||
|
||||
Codex阶段不 push,由 RDC 验收后统一 commit/push。
|
||||
|
||||
---
|
||||
|
||||
## 11. V19 发布门槛
|
||||
|
||||
仅当:
|
||||
|
||||
- Native compile PASS;
|
||||
- focused C++ tests PASS;
|
||||
- Android Validation teardown loop PASS;
|
||||
- STEP-100 locality regression PASS;
|
||||
- NavSea V19 真实 5 次冷启动 >30s PASS;
|
||||
- crash buffer 无 destroyed mutex;
|
||||
|
||||
才允许发布:
|
||||
|
||||
- RN / Android:`13.4.1-particle.19`
|
||||
- iOS:`6.28.0-particle.19`
|
||||
- tag:`maplibre-particle/rn/13.4.1-particle.19`
|
||||
|
||||
V18 保持不可变,不覆盖、不删除。
|
||||
|
||||
---
|
||||
|
||||
## 12. 最终原则
|
||||
|
||||
正常:
|
||||
|
||||
```text
|
||||
MapView unmount
|
||||
Style generation replacement
|
||||
renderer.reset()
|
||||
Activity recreate
|
||||
```
|
||||
|
||||
都是合法生命周期。
|
||||
|
||||
Particle Native 必须做到:
|
||||
|
||||
> Renderer teardown 永远不能因为 observer late callback 访问已销毁 mutex/state 而杀掉宿主 App。
|
||||
|
||||
---
|
||||
|
||||
# 13. Codex 6-Sol High 架构评审修订(v2,优先级高于前文冲突表述)
|
||||
|
||||
只读评审结论:`APPROVE_WITH_CHANGES`。
|
||||
|
||||
## 13.1 根因表述修正
|
||||
|
||||
当前已经**代码层证实**存在生命周期漏洞:`readinessDiagnosticsMutex` / readiness state 的析构顺序早于 `renderSources`,而 `RenderSource` observer 仍指向 `RenderOrchestrator`;source/tile teardown 可同步或延迟回调进入 `onTileAction/onTileChanged` 并访问 mutex。
|
||||
|
||||
该漏洞与现场 `pthread_mutex_lock called on a destroyed mutex` 高度吻合;在没有完整符号化 crash stack 精确到具体 callback 前,不把“唯一现场根因”写成数学意义上的唯一证明。
|
||||
|
||||
## 13.2 teardown 必须双 drain
|
||||
|
||||
最低安全顺序调整为:
|
||||
|
||||
```text
|
||||
contextLost layer handling
|
||||
↓
|
||||
第一次 threadPool.waitForEmpty()
|
||||
↓
|
||||
detach RenderSource observers
|
||||
↓
|
||||
detach GlyphManager / ImageManager observers
|
||||
↓
|
||||
renderSources.clear() // readiness mutex/state 此时仍存活
|
||||
↓
|
||||
第二次 threadPool.waitForEmpty()
|
||||
↓
|
||||
继续既有 backend/context render-job teardown
|
||||
↓
|
||||
成员析构
|
||||
```
|
||||
|
||||
原因:source/tile destruction 本身可能投递新的 tagged cleanup/deferred work,第一次 drain 不能覆盖 clear 过程中产生的新任务。
|
||||
|
||||
同时必须审计并在报告说明:`TaggedScheduler::waitForEmpty()` 不等于清空 `runOnRenderThread` 队列;后者依赖现有 backend/context render-job 生命周期。不得声称双 drain 自动证明所有 render-thread job 已清空。
|
||||
|
||||
## 13.3 Observer detach 范围
|
||||
|
||||
除了所有 `RenderSource`:
|
||||
|
||||
- `glyphManager->setObserver(nullptr)`;
|
||||
- `imageManager->setObserver(nullptr)`;
|
||||
|
||||
也应在 owner teardown 时显式 detach(前提是现有 API 的 nullptr 已安全回 NullObserver)。
|
||||
|
||||
`RenderSource::setObserver(nullptr)` 必须改为 NullObserver fallback。
|
||||
|
||||
不得直接给 `TilePyramid` / `Tile` observer 写 nullptr,除非其 API 本身有同等安全语义;本任务不扩展到这一步。
|
||||
|
||||
## 13.4 teardown flag
|
||||
|
||||
默认不增加 teardown atomic flag。
|
||||
|
||||
若实现审计发现真实并发 callback 不受 renderer thread / tagged scheduler 串行保证约束,必须先报告;atomic flag 只能二次防护,不能替代 observer detach、drain 和可等待取消。
|
||||
|
||||
## 13.5 Android Validation 证据升级
|
||||
|
||||
T1~T4 每一轮必须能证明发生了**真实 Native renderer reset**:
|
||||
|
||||
- 旧 MapView detach / SurfaceView detach;
|
||||
- `nativeReset()`;
|
||||
- `MapRenderer::resetRenderer()` 进入/完成;
|
||||
- 新 renderer style ready;
|
||||
- 新 renderer diagnostics 可用。
|
||||
|
||||
不能把 `setStyle()`、clear field、只读 diagnostics 当成 teardown 测试。
|
||||
|
||||
推荐按代次销毁并重建 MapView,或 Activity recreate;T2 至少 20 次已确认 reset。
|
||||
|
||||
T3:每轮 reset 前保持动态 GeoJSON/source mutation。
|
||||
|
||||
T4:每轮 reset 前调用一次 Native diagnostics,模拟 NavSea 8 秒 probe 的等价条件。
|
||||
|
||||
每轮应等待 source/tile 确实进入运行态,而不是只按固定 sleep 机械计数。
|
||||
|
||||
## 13.6 测试必须覆盖真实 source teardown
|
||||
|
||||
空 `RenderOrchestrator` 析构测试不够。
|
||||
|
||||
非 Jest C++ 测试至少需要一个含 RenderSource / tile callback 的 teardown 场景,使 source cancellation / callback 能在销毁边界发生,并验证旧 observer 不再收到回调。
|
||||
|
||||
## 13.7 正常 source erase / clearData 审计
|
||||
|
||||
必须只读检查 `clearData()` 和正常 source erase/replacement 路径,因为它们也会销毁 RenderSource。
|
||||
|
||||
本任务主要修 owner teardown;不得未经证明把 destructor 修复泛化为“所有 source replacement 异步问题均已解决”。若发现相同 observer 生命周期漏洞在正常 erase 路径存在,应做最小同源修复或明确 BLOCK,不得隐藏。
|
||||
|
||||
## 13.8 V19 候选验收顺序
|
||||
|
||||
V19 发布门槛修正为:
|
||||
|
||||
```text
|
||||
STEP-101 source code PASS
|
||||
→ 构建未公开的 V19 candidate artifacts
|
||||
→ Validation App 实机 PASS
|
||||
→ NavSea 使用 candidate 做 5 次冷启动/teardown 验收
|
||||
→ 公开发布不可变 V19
|
||||
→ public readback
|
||||
→ finalize/tag
|
||||
```
|
||||
|
||||
不得先公开 V19 再做 NavSea 验收。
|
||||
156
docs/40_任务/粒子连续渲染静态底图缓存与动画快路径优化规划_v1.md
Normal file
156
docs/40_任务/粒子连续渲染静态底图缓存与动画快路径优化规划_v1.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# MapLibreParticleLab 粒子连续渲染静态底图缓存与动画快路径优化规划 v1
|
||||
|
||||
## 1. 目标
|
||||
STEP-086 已确认:Particle / WeatherColor 的 needsRepaint 会通过全局 repaint 让完整 MapLibre scene 连续出帧。React 不会每帧重新提交 PBF,但 Native Renderer 会重复进入 source/layer orchestration,并重新提交静态 PBF/fill/line/symbol draw。
|
||||
|
||||
本规划目标:
|
||||
- 地图真实变化时仍走 Full Frame。
|
||||
- Camera、Style、Tile 稳定且只有 WeatherColor/Particle 动画时,复用 Static Underlay。
|
||||
- NavSea / React 不参与 Renderer cache 管理。
|
||||
- 不改变 NavSea ↔ Particle C-001 公共边界。
|
||||
|
||||
## 2. 当前关键代码
|
||||
必须以当前 origin/particle/main 实际代码为准,重点包括:
|
||||
- src/mbgl/renderer/layers/render_particle_validation_layer.cpp
|
||||
- src/mbgl/renderer/layers/render_weather_color_layer.cpp
|
||||
- src/mbgl/renderer/render_orchestrator.cpp/.hpp
|
||||
- src/mbgl/renderer/renderer.cpp
|
||||
- src/mbgl/renderer/renderer_impl.cpp/.hpp
|
||||
- src/mbgl/map/map_impl.cpp
|
||||
- include/mbgl/renderer/render_target.hpp
|
||||
- src/mbgl/renderer/render_target.cpp
|
||||
- src/mbgl/renderer/layers/render_heatmap_layer.cpp
|
||||
- src/mbgl/renderer/layers/render_hillshade_layer.cpp
|
||||
|
||||
## 3. 核心结构
|
||||
不增加永久 public static/dynamic layer flag。采用 Renderer 内部 retained dynamic boundary:
|
||||
- WeatherColor 是 boundary candidate。
|
||||
- RasterParticle 是 boundary candidate。
|
||||
- 同时存在时取最小 layerIndex。
|
||||
- boundary 之前连续 LayerGroup 前缀作为 Static Underlay。
|
||||
- boundary 及之后继续实时绘制。
|
||||
- 没有 boundary 时完全保持原 Renderer。
|
||||
|
||||
Layer 顺序、parameters.currentLayer、depthRangeSize、opaque/translucent 原始 ordinal 必须保持,不允许对子集从 0 重新编号而改变透明/depth 语义。
|
||||
|
||||
## 4. Static Underlay Cache
|
||||
建议 Renderer-owned helper:
|
||||
- src/mbgl/renderer/retained_underlay_cache.hpp
|
||||
- src/mbgl/renderer/retained_underlay_cache.cpp
|
||||
|
||||
职责:
|
||||
- offscreen renderable / texture
|
||||
- viewport size
|
||||
- boundary
|
||||
- valid/invalid
|
||||
- invalidation reason
|
||||
- build/reuse/fallback 计数
|
||||
- compositor resources
|
||||
|
||||
不改变 Heatmap/Hillshade RenderTarget 现有行为;复用现有 gfx::OffscreenTexture / RenderTarget / Drawable / shader abstraction。
|
||||
|
||||
## 5. Texture Composite
|
||||
使用独立语义的 RetainedUnderlayCompositeShader:
|
||||
- viewport/fullscreen quad
|
||||
- sample cached RGBA texture
|
||||
- 正确 premultiplied alpha
|
||||
- 不做 map transform / color ramp / Particle 业务
|
||||
|
||||
不要长期复用 LocationIndicatorTexturedShader。
|
||||
|
||||
## 6. STEP-088
|
||||
先只消除静态地图重复 GPU draw,仍保留:
|
||||
- Map::Impl::onUpdate
|
||||
- UpdateParameters
|
||||
- createRenderTree
|
||||
- source update / TilePyramid update
|
||||
- source/layer prepare
|
||||
- updateLayers
|
||||
- LayerGroup tweaker/upload
|
||||
|
||||
Render 阶段:
|
||||
1. cache invalid:boundary 前 LayerGroups render 到 offscreen。
|
||||
2. cache valid:跳过 boundary 前静态 draw。
|
||||
3. main framebuffer clear。
|
||||
4. composite cached underlay。
|
||||
5. boundary 及之后 WeatherColor/Particle/动态 overlay 正常绘制。
|
||||
6. present。
|
||||
|
||||
## 7. STEP-089
|
||||
在 STEP-088 实机通过后再做 production invalidation:
|
||||
- Camera center/zoom/bearing/pitch
|
||||
- viewport/pixelRatio
|
||||
- style/layer add/remove/reorder/change
|
||||
- source/tile/bucket/drawable 变化
|
||||
- sprite/image/glyph
|
||||
- symbol placement/fade
|
||||
- context/surface recreation
|
||||
- 3D/terrain/custom/debug fallback
|
||||
- OpenGL/Metal 一致生命周期
|
||||
|
||||
## 8. STEP-090 条件步骤
|
||||
只有 STEP-089 后真实 NavSea profile 仍证明主要 CPU 落在:
|
||||
- Map::Impl::onUpdate
|
||||
- createRenderTree
|
||||
- Vector/PBF source update
|
||||
- TilePyramid update
|
||||
- source/layer prepare
|
||||
才进入 Animation-only fast path。
|
||||
|
||||
如果 STEP-089 已满足性能目标,不执行 STEP-090。
|
||||
|
||||
## 9. STEP-088 安全边界
|
||||
PoC reuse 至少要求:
|
||||
- 2D
|
||||
- Camera fingerprint 连续一致
|
||||
- viewport 不变
|
||||
- style loaded
|
||||
- boundary 不变
|
||||
- context/resource 正常
|
||||
|
||||
以下至少 fallback full render:
|
||||
- has3D
|
||||
- Debug Overdraw
|
||||
- cache texture/compositor 创建失败
|
||||
- viewport resize
|
||||
- boundary change
|
||||
- context 不可用
|
||||
- legacy RenderItem ordering 无法证明安全
|
||||
- 其他不能证明 cache 正确的状态
|
||||
|
||||
原则:不确定就 full render,不用 stale cache 换性能。
|
||||
|
||||
## 10. 观测
|
||||
复用 include/mbgl/particle/renderer_debug_counters.hpp,建议增加:
|
||||
- FullRendererFrame
|
||||
- RetainedUnderlayBuild
|
||||
- RetainedUnderlayReuse
|
||||
- RetainedUnderlayFallback
|
||||
- RetainedUnderlayInvalidation
|
||||
- RetainedUnderlayStaticGroupDraw
|
||||
- RetainedDynamicGroupDraw
|
||||
|
||||
固定 Camera + Particle 约 30 FPS 时,理想:
|
||||
- RetainedUnderlayBuild ≈ 1
|
||||
- RetainedUnderlayReuse 持续增长
|
||||
- ParticleSimulationAdvance 持续增长
|
||||
- StaticGroupDraw 建 cache 后不随 Particle FPS 增长
|
||||
- DynamicGroupDraw 持续增长
|
||||
|
||||
## 11. 不允许
|
||||
- React RAF 驱动
|
||||
- Host renderFastFrame/cacheMapNow/invalidateBasemap API
|
||||
- NavSea 手工管理 renderer cache
|
||||
- 修改 C-001
|
||||
- 永久 public static/dynamic flag
|
||||
- 改变 Style layer ordering
|
||||
- STEP-088 就跳过 source/update/prepare
|
||||
- Android/iOS 不同 renderer semantics
|
||||
- stale cache
|
||||
|
||||
## 12. 执行顺序
|
||||
1. STEP-088:PoC + observability
|
||||
2. 用户 Android/NavSea 真机验证
|
||||
3. STEP-089:production invalidation/fallback
|
||||
4. 用户再次 profile
|
||||
5. 只有 CPU orchestration 仍是瓶颈时才 STEP-090
|
||||
122
docs/50_Step记录/STEP-088_静态底图缓存PoC与Renderer观测_v1.md
Normal file
122
docs/50_Step记录/STEP-088_静态底图缓存PoC与Renderer观测_v1.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# STEP-088 静态底图缓存 PoC 与 Renderer 观测 v1
|
||||
|
||||
## 状态
|
||||
|
||||
进行中
|
||||
|
||||
`IMPLEMENTED / WAITING FOR ANDROID VALIDATION`
|
||||
|
||||
## 目标
|
||||
|
||||
在固定 Camera、Style/Tile 稳定、2D 且存在 WeatherColor/RasterParticle 动态层时,把动态边界之前的连续普通 LayerGroup 前缀缓存为 retained underlay。Particle Tick 仍进入现有 MapLibre update/createRenderTree/prepare/upload 主链,但绘制阶段复用静态底图纹理,只实时绘制动态边界及其上方内容。
|
||||
|
||||
## 基线
|
||||
|
||||
- 分支:`particle/main`
|
||||
- 起始 HEAD:`f47469d2ab77cdb959ecfe4081dc571fa1ab78f9`
|
||||
- 实现提交:`8b9ab303228c307612c47f7f3379ad06206ea14e`
|
||||
- 当前版本坐标:`13.4.1-particle.10`
|
||||
|
||||
## 必做项
|
||||
|
||||
- [x] Renderer-internal dynamic boundary。
|
||||
- [x] LayerGroup before/from、forward/reverse 与 original ordinal。
|
||||
- [x] Renderer-owned underlay cache 与 OpenGL/Metal compositor。
|
||||
- [x] full-render fallback 与 legacy ordering 保护。
|
||||
- [x] 7 个 Renderer debug counters。
|
||||
- [x] Native 定向测试代码与 CMake 注册。
|
||||
- [x] Mac OpenGL/Metal 编译及 4/4 定向测试由 RDC 完成。
|
||||
- [ ] Android/NavSea 实机视觉、counters 与性能验证。
|
||||
|
||||
## 允许与禁止范围
|
||||
|
||||
允许修改 Renderer/Core/gfx backend/shader/CMake/Native test 和本 Step 文档;必要的 `test/gl/gl_functions.test.cpp` macOS 平台条件小修纳入本次。禁止 Android/NavSea/React 业务修改、Host API、C-001、STEP-089、STEP-090、production 全量 invalidation 与 animation-only fast path。
|
||||
|
||||
## 当前问题
|
||||
|
||||
STEP-086 已确认 Particle/WeatherColor 的 `needsRepaint()` 会唤醒完整 MapLibre frame。固定 Camera 下仍重复执行 source/layer orchestration、prepare、LayerGroup opaque/translucent draw 与 present;复杂 NavSea Style 因而持续重复提交大量静态 fill/line/symbol/PBF draw。
|
||||
|
||||
## 实现结构
|
||||
|
||||
- `RenderLayer` 新增仅 Renderer 内部使用的 `startsRetainedDynamicRange()`;WeatherColor 与 RasterParticle 返回 true,不增加 Host/public API。
|
||||
- `RenderOrchestrator` 保存最小动态 boundary,并提供 before/from、forward/reverse 的 LayerGroup 分段 visitor;callback 保留完整序列 original ordinal。
|
||||
- Renderer-owned `RetainedUnderlayCache` 持有 RGBA offscreen texture、depth/stencil renderable、compositor LayerGroup、key、valid 状态和 build/reuse/fallback/invalidation 数据。
|
||||
- 新增独立 `RetainedUnderlayCompositeShader`,OpenGL/Metal 均为 fullscreen textured quad,只采样 premultiplied RGBA,不做 map transform、color ramp 或 Particle 业务。
|
||||
- OpenGL OffscreenTexture 补齐可选 depth/stencil attachment;通用 gfx Context 的四参数 offscreen 接口由 OpenGL、Metal、Vulkan、WebGPU 后端显式覆盖。
|
||||
- legacy `RenderLayer::render()` 当前仅 LocationIndicator 需要保护;若它位于 boundary 以下则 full-render fallback。
|
||||
|
||||
## Boundary
|
||||
|
||||
- WeatherColor 与 RasterParticle 都是 candidate。
|
||||
- 两者同时存在时取实际 ordered layer 序列中的最小 index。
|
||||
- only Particle 时使用 Particle index。
|
||||
- 无 candidate 时 retained path disabled,完整原 Renderer 路径不变。
|
||||
- 不依赖 React layer ID/name,不由 Host/NavSea 传入。
|
||||
|
||||
## Underlay Cache Flow
|
||||
|
||||
1. 保留 `Renderer::render → createRenderTree`、source update/prepare、layer prepare、`updateLayers/processChanges`、LayerGroup tweaker/upload。
|
||||
2. 先按原顺序处理 independent RenderTargets。
|
||||
3. cache invalid 时,把 boundary 前 LayerGroups 以原始 ordinal/depth 映射分别执行 opaque reverse 与 translucent forward,绘制到透明 clear 的 offscreen。
|
||||
4. main framebuffer 仍按原 `backgroundColor/depth/stencil` 语义 clear;background optimized-as-color 路径未改。
|
||||
5. composite cached underlay,再按完整序列 original ordinal 绘制 boundary 及之后的 opaque/translucent groups。
|
||||
6. legacy items、debug overlay、postRender 与 present 保持原位置。
|
||||
7. fixed key 且安全条件连续成立时复用;viewport、camera matrix 或 boundary 改变时重新 build。
|
||||
|
||||
## Fallback
|
||||
|
||||
以下状态不复用缓存:无 boundary、静态前缀为空、style 尚未 loaded、3D、任意 debug options、静态层 transition/repaint、placement/fade、boundary 以下 legacy ordering、offscreen/compositor 创建失败。不能建立安全资源时计数并回到完整绘制路径。
|
||||
|
||||
## Renderer 观测
|
||||
|
||||
在既有 debug-only counters 中新增:
|
||||
|
||||
- `FullRendererFrame`
|
||||
- `RetainedUnderlayBuild`
|
||||
- `RetainedUnderlayReuse`
|
||||
- `RetainedUnderlayFallback`
|
||||
- `RetainedUnderlayInvalidation`
|
||||
- `RetainedUnderlayStaticGroupDraw`
|
||||
- `RetainedDynamicGroupDraw`
|
||||
|
||||
`NDEBUG` 下仍为 no-op;未增加 Host API,也未增加逐帧日志。
|
||||
|
||||
## 构建与测试
|
||||
|
||||
以下由 RDC / 普通 Terminal 在当前 STEP-088 实现上完成:
|
||||
|
||||
- `cmake --build build-step088-opengl --target mbgl-test-runner -j3`:成功。
|
||||
- STEP-088 定向测试:4/4 成功,覆盖 boundary、LayerGroup range/original ordinal、build/reuse/resize/boundary invalidation、3D/resource failure fallback。
|
||||
- `cmake --build build-ios --config Debug --target mbgl-core -j3`:iOS/Metal device arm64 `BUILD SUCCEEDED`。
|
||||
- `git diff --check`:成功。
|
||||
- RDC 为 Mac OpenGL test-runner 链接补充 `test/gl/gl_functions.test.cpp` Apple 平台条件:macOS 不引用仅 iPhone/Simulator 提供的 `glInvalidateFramebuffer` / `glInvalidateSubFramebuffer`。
|
||||
|
||||
Codex 收尾阶段按新规则未再启动任何 build/test。Android Gradle、AAR/APK、ADB、Android 设备、NavSea 实机与 Jest 均未执行。
|
||||
|
||||
## PoC 限制
|
||||
|
||||
- 本 Step 只消除稳定场景下静态前缀的重复 GPU draw;没有跳过 Map update、createRenderTree、source/TilePyramid update、prepare、tweaker 或 upload。
|
||||
- production 级 style/layer/source/tile/bucket/drawable、sprite/image/glyph、context/surface 等完整 invalidation 属于 STEP-089;本 PoC 只允许在固定 Camera、Style/Tile 稳定条件下判断 reuse。
|
||||
- 未做 animation-only CPU fast path;STEP-090 未执行。
|
||||
- 未做通用任意 static/dynamic RenderGraph,也未修改 NavSea、React 或 C-001。
|
||||
|
||||
## 用户 Android/NavSea 手工验证项
|
||||
|
||||
1. 固定 Camera、复杂真实 Style、Particle 约 30 FPS,确认视觉与原路径一致。
|
||||
2. 观察 `RetainedUnderlayBuild ≈ 1`、`RetainedUnderlayReuse` 和 `ParticleSimulationAdvance` 持续增长。
|
||||
3. 确认建 cache 后 `RetainedUnderlayStaticGroupDraw` 不随 Particle FPS 增长,`RetainedDynamicGroupDraw` 持续增长。
|
||||
4. 分别验证 only Particle、WeatherColor + Particle 的两种 layer 顺序。
|
||||
5. pan/zoom/resize 后确认 invalidation/rebuild,不能出现 stale underlay。
|
||||
6. 验证 3D、debug 与 boundary 以下 legacy ordering 会安全回到 full render。
|
||||
7. 对比 Particle OFF/ON 的 draw calls、RenderThread CPU、main/JS CPU 与画面叠放顺序。
|
||||
|
||||
## 当前结果与下一步
|
||||
|
||||
STEP-088 实现与 Mac OpenGL/Metal、Native 定向测试已完成;Android/NavSea 运行态证据尚缺。因此当前不满足进入 STEP-089 的验收条件,不得进入 STEP-089/090。下一步仅由用户/RDC 完成上述 Android/NavSea 手工验证并回传事实。
|
||||
|
||||
## 必做输出与阻塞
|
||||
|
||||
- Step 记录:本文。
|
||||
- Codex 完成报告:`docs/报告/STEP-088_静态底图缓存PoC与Renderer观测_Codex完成报告_v1.md`。
|
||||
- 开发记录:已追加 `粒子开发记录.md`。
|
||||
- 阻塞:Android/NavSea 实机验证尚未执行;这是关闭 STEP-088 和进入 STEP-089 的唯一当前验收阻塞。
|
||||
139
docs/50_Step记录/STEP-088_静态底图缓存PoC与Renderer观测_v2.md
Normal file
139
docs/50_Step记录/STEP-088_静态底图缓存PoC与Renderer观测_v2.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# STEP-088 静态底图缓存 PoC 与 Renderer 观测 v2
|
||||
|
||||
## 状态
|
||||
|
||||
进行中
|
||||
|
||||
`ANDROID TEST APP PASS / WAITING FOR NAVSEA VALIDATION`
|
||||
|
||||
STEP-088 尚未关闭,也不得据此进入 STEP-089/090。
|
||||
|
||||
## 本版目的
|
||||
|
||||
本文在 v1 实现记录基础上,记录 Android MapLibreAndroidTestApp 实机发现的 placement fallback 问题、Renderer 修正、Mac 最终复验和 Android Test App 运行态验收。v1 中的 retained underlay 架构、范围和 PoC 限制继续有效。
|
||||
|
||||
## 基线
|
||||
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab`
|
||||
- 分支:`particle/main`
|
||||
- v1 实现提交:`8b9ab303228c307612c47f7f3379ad06206ea14e`
|
||||
- debug counters 日志提交:`e7d109246a225eb1c1b2f177aa30e3c7fcdf658e`
|
||||
- Android placement-scoping 修正提交:`1d1a7915e0886c46be40f4fc9a2eff13192f804b`
|
||||
- 当前版本坐标:`13.4.1-particle.10`
|
||||
|
||||
## Android 首轮问题
|
||||
|
||||
较早安装的 STEP-088 APK 在 fixed camera / only Particle 场景持续 full-render fallback:
|
||||
|
||||
```text
|
||||
frame=1200 build=0 reuse=0 fallback=1199 invalidation=1199
|
||||
staticDraw=0 dynamicDraw=0 particleAdvance=1199
|
||||
```
|
||||
|
||||
根因是 retained eligibility 无条件受全局 placement transition、`placementChanged` 和 `symbolFadeChange` 约束。即使 retained static prefix 没有 symbol/placement layer,动态范围或全局 placement 状态也会阻止 cache build/reuse。
|
||||
|
||||
## Renderer 修正
|
||||
|
||||
- `RenderOrchestrator::retainedUnderlayUsesPlacement(boundary)` 只检查 boundary 之前的 retained static prefix。
|
||||
- static prefix 不使用 placement 时,全局 placement transition、`placementChanged` 和 `symbolFadeChange` 不再阻止 retained cache。
|
||||
- static prefix 真正使用 placement 时,上述 placement stability 保护仍然生效并安全 fallback。
|
||||
- 原有 3D、debug、legacy ordering、style-not-loaded、static transition、resource/compositor failure 等 fallback 不变。
|
||||
- debug-only `ParticleRetained` 周期日志保留 `reason=<lastInvalidationReason>`,用于区分 `first-frame`、`camera-or-boundary`、`style-not-loaded`、`static-transition`、`static-placement` 等原因;Release/NDEBUG 语义不变。
|
||||
|
||||
## Android Validation 构建辅助
|
||||
|
||||
`platform/android/MapLibreAndroidTestApp/build.gradle.kts` 新增测试专用 property:
|
||||
|
||||
```text
|
||||
maplibre.validation.stripNativeDebugSymbols=true
|
||||
```
|
||||
|
||||
默认 Debug 行为不变,仍保留 native debug symbols。只有 Validation 构建显式启用该 property 时才允许 strip native symbols;x86_64 Debug APK 由约 375 MB 降至约 57 MB。该改动不改变 Renderer、debug counters 或 `NDEBUG` 语义。
|
||||
|
||||
## RDC Mac 最终复验
|
||||
|
||||
- `cmake --build build-step088-opengl --target mbgl-test-runner -j3`:PASS / no work to do;RDC 已确认当前 placement-scoping 源码已编入 object。
|
||||
- STEP-088 定向测试:4/4 PASS:
|
||||
- `RetainedDynamicBoundary.SelectsFirstCandidate`
|
||||
- `RetainedLayerGroupRange.PreservesForwardReverseOriginalOrdinals`
|
||||
- `RetainedUnderlayCache.BuildReuseAndInvalidation`
|
||||
- `RetainedUnderlayCache.UnsupportedAndResourceFailureFallBackSafely`
|
||||
- `cmake --build build-ios --config Debug --target mbgl-core -j3`:`BUILD SUCCEEDED`。
|
||||
- `git diff --check`:PASS。
|
||||
|
||||
上述命令均由 RDC 执行;Codex 文档与 Git 收尾阶段未重新执行 build/test。
|
||||
|
||||
## Android Test App 环境
|
||||
|
||||
- 设备:`192.168.200.139:5555`
|
||||
- 设备标识:FZG1-2
|
||||
- 系统:Android 13
|
||||
- ABI:x86_64
|
||||
- App:MapLibreAndroidTestApp OpenGL Debug x86_64
|
||||
- Validation strip property:启用
|
||||
- 构建、安装、ADB 与 Activity 启动:均成功
|
||||
|
||||
## Android Test App 运行态结果
|
||||
|
||||
### Fixed camera / only Particle
|
||||
|
||||
```text
|
||||
frame=840 build=1 reuse=836 fallback=2 invalidation=3
|
||||
reason=first-frame staticDraw=2 dynamicDraw=5022 particleAdvance=839
|
||||
```
|
||||
|
||||
后续 `reuse` 持续增长,`staticDraw` 保持不变,证明 retained static prefix 首次 build 后被复用。
|
||||
|
||||
### Camera DATELINE
|
||||
|
||||
- 变化前:`build=1`、`staticDraw=2`。
|
||||
- camera transition 后:`build=2`、`staticDraw=4`。
|
||||
- 随后 `reuse` 重新持续增长。
|
||||
- 观测原因:`reason=camera-or-boundary`。
|
||||
|
||||
结果符合 camera change → transition 期间安全 fallback/invalidate → 稳定后 rebuild 一次 → reuse。
|
||||
|
||||
### REAL WEATHER
|
||||
|
||||
- `REAL_WEATHER_INITIAL_WINDOW_READY` 正常。
|
||||
- `WEATHER_COLOR_FIELD_READY` 正常。
|
||||
- GSI raster + WeatherColor + Particle 正常进入 retained path。
|
||||
- 代表数据:`frame=6960 build=3 reuse=6907 fallback=49 invalidation=52 staticDraw=6 particleAdvance=6959`。
|
||||
- fixed camera 后 `reuse` 持续增长,`staticDraw` 保持 6。
|
||||
- `TARGET_FPS_CHANGED target=FPS_30` 成功,缓存继续 reuse。
|
||||
|
||||
### Resize / rotation
|
||||
|
||||
viewport 完成 `1280x680 → 800x1160 → 1280x680`。过渡期间出现安全的 `style-not-loaded` fallback;稳定后 rebuild 并继续 reuse。设备最终恢复 `accelerometer_rotation=1`、`user_rotation=0`。
|
||||
|
||||
### STATIC / ACTIVE
|
||||
|
||||
- STATIC 时 RenderThread 约 0%。
|
||||
- ACTIVE 后恢复 continuous render,retained reuse 继续增长且 `staticDraw` 保持不变。
|
||||
- 旧 Test App 设备上 ACTIVE RenderThread 约 96%~107%,仅作为 Test App 对照,不代表 NavSea 最终性能。
|
||||
|
||||
### 异常扫描
|
||||
|
||||
未发现 `FATAL EXCEPTION`、`SIGSEGV`、`SIGABRT`、ANR、`GL_INVALID`、`EGL_BAD` 或 OOM。
|
||||
|
||||
## 当前验收边界
|
||||
|
||||
Android MapLibreAndroidTestApp 运行态验证为 PASS,但它不能替代 NavSea 复杂真实 Style 的最终验证。以下仍待 NavSea 完成:
|
||||
|
||||
1. 真实复杂 Style 的视觉与 layer ordering。
|
||||
2. 大量 PBF/fill/line/symbol 下 retained static prefix 的 `staticDraw` 稳定性。
|
||||
3. Particle OFF/ON 的 RenderThread、main、JS CPU 对比。
|
||||
4. pan/zoom/resize/fallback 后无 stale underlay。
|
||||
5. only Particle 与 WeatherColor + Particle 在真实产品 layer stack 中的一致性。
|
||||
|
||||
## PoC 边界
|
||||
|
||||
- 未实现 STEP-089 production 全量 invalidation。
|
||||
- 未实现 STEP-090 animation-only CPU fast path。
|
||||
- 未修改 NavSea、React、Host API 或 C-001。
|
||||
- Android Test App PASS 不构成 STEP-088 overall CLOSED/PASS。
|
||||
- NavSea 验证完成前不得进入 STEP-089/090。
|
||||
|
||||
## 当前结论
|
||||
|
||||
placement-scoping 修正解决了 Android 上 static prefix 不含 placement 时的每帧 fallback。Mac OpenGL/Metal、Native 定向测试及 Android Test App 运行态均已通过;STEP-088 当前唯一剩余 Gate 是 NavSea 复杂真实 Style 验证。
|
||||
65
docs/50_Step记录/STEP-088_静态底图缓存PoC与Renderer观测_v3.md
Normal file
65
docs/50_Step记录/STEP-088_静态底图缓存PoC与Renderer观测_v3.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# STEP-088 静态底图缓存 PoC 与 Renderer 观测 v3
|
||||
|
||||
## 状态
|
||||
|
||||
**ANDROID / NATIVE VALIDATION PASS;STEP-088 收尾。**
|
||||
|
||||
STEP-088 已验证 retained underlay 能停止静态 LayerGroup 的重复 GPU draw,并补充了 Release 可用的运行时 Cache ON/OFF、最小诊断接口、Android/iOS Native API、React Native RendererRuntime,以及 Android Test App 的真实 NavSea PBF 性能基线工具。
|
||||
|
||||
## 本轮新增能力
|
||||
|
||||
- Retained Underlay 默认开启,可按 Map 实例运行时开启/关闭。
|
||||
- 关闭时立即失效缓存并恢复原始 full-render;重新开启后在满足条件时重新 build/reuse。
|
||||
- Release 保留最小 pull-based diagnostics:enabled/build/reuse/fallback/invalidation/lastReason。
|
||||
- Debug 继续保留详细 ParticleRetained 周期日志。
|
||||
- RN 新增 RendererRuntime:
|
||||
- setRetainedUnderlayEnabled(reactTag, enabled)
|
||||
- getRetainedUnderlayDiagnostics(reactTag)
|
||||
- Android Test App 新增:
|
||||
- NavSea 正式 63 层 PBF Style;
|
||||
- Particle 位置 BOTTOM / AFTER_SEA / AFTER_CLIP / BEFORE_SYMBOL_CLUSTER / TOP;
|
||||
- runtime Cache ON/OFF;
|
||||
- PBF ON/OFF;
|
||||
- PURE PARTICLE;
|
||||
- PURE PBF;
|
||||
- CUSTOM PARTICLE COUNT,范围 1..10000。
|
||||
|
||||
## 关键 Android 性能证据
|
||||
|
||||
测试设备:Android 13 / x86_64 / FZG1-2,固定唐津 Camera,Particle target FPS 30。
|
||||
|
||||
- PURE PARTICLE,2048 粒子,无 PBF:
|
||||
- RenderThread ≈ 10.2%
|
||||
- Process ≈ 13.7%
|
||||
- Main ≈ 2.5%
|
||||
- PURE PBF,63 层正式 Style,无 Particle:
|
||||
- RenderThread ≈ 0%
|
||||
- Process ≈ 0%
|
||||
- NavSea Style 保留、60 个 navsea_delivery layer 全隐藏、Particle 2048:
|
||||
- RenderThread ≈ 25.5%
|
||||
- Process ≈ 29.1%
|
||||
- PBF + Particle,AFTER_CLIP:
|
||||
- Cache ON RenderThread ≈ 96.5%,Process ≈ 102.2%
|
||||
- Cache OFF RenderThread ≈ 99.5%,Process ≈ 111.8%
|
||||
- PBF + Particle,BEFORE_SYMBOL_CLUSTER:
|
||||
- Cache ON RenderThread ≈ 96.5%,Process ≈ 102.8%
|
||||
- Cache OFF RenderThread ≈ 100.2%,Process ≈ 114.2%
|
||||
- TOP 因 static-placement/static-transition 持续 fallback,不能作为稳定 retained 场景。
|
||||
|
||||
结论:STEP-088 能消除静态 GPU draw,但主要 CPU 成本仍来自 Particle continuous repaint 下 PBF/vector/static renderer pipeline 被反复处理。该证据支持下一阶段先做 STEP-089 invalidation/revision 基础,再进入 STEP-090 Static CPU Fast Path。
|
||||
|
||||
## 验证结果
|
||||
|
||||
- Android x86_64 stripped Debug Validation APK:BUILD SUCCESSFUL,约 57 MB,实机通过。
|
||||
- Mac OpenGL mbgl-test-runner:PASS。
|
||||
- STEP-088 directed tests:5/5 PASS。
|
||||
- iOS Metal mbgl-core:BUILD SUCCEEDED。
|
||||
- iOS ios-sdk-static:BUILD SUCCEEDED。
|
||||
- RN package TypeScript:PASS。
|
||||
- RN Test Host TypeScript:PASS。
|
||||
- RN iOS bridge 单独编译:被 Test Host 仍引用旧 STEP-083 本地 SwiftPM 包阻塞;旧包缺少本轮新增 MLNMapView retained-underlay API。当前源码 iOS SDK 已通过编译,因此该项记录为测试宿主产物过期,不阻塞 STEP-089。
|
||||
- git diff --check:PASS。
|
||||
|
||||
## 下一步
|
||||
|
||||
进入 STEP-089:建立静态内容 invalidation/revision 可靠判据,只做 fast-path 前置安全基础,不提前实现 STEP-090 的 CPU fast path。
|
||||
117
docs/50_Step记录/STEP-089_静态渲染Revision与Invalidation基础层_v1.md
Normal file
117
docs/50_Step记录/STEP-089_静态渲染Revision与Invalidation基础层_v1.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# STEP-089 静态渲染 Revision 与 Invalidation 基础层 v1
|
||||
|
||||
## 状态
|
||||
|
||||
**CLOSED / PASS**
|
||||
|
||||
STEP-089 已完成 retained underlay 的静态状态 revision / invalidation 基础层,为 STEP-090 Static CPU Fast Path 提供可靠的安全门控。
|
||||
|
||||
## 核心实现
|
||||
|
||||
- 新增单调递增的静态 revision / generation。
|
||||
- retained cache build 时记录 buildRevision。
|
||||
- reuse 前要求 buildRevision 与当前 staticRevision 一致。
|
||||
- 新增稳定 topology snapshot:
|
||||
- dynamic boundary;
|
||||
- static prefix 中每个 LayerGroup 的 layerIndex;
|
||||
- groupName;
|
||||
- groupType。
|
||||
- 真正的 LayerGroup 增删、排序、index 变化会推进 topologyRevision。
|
||||
- Renderer 内部 transient drawable churn 不再直接推进 static revision。
|
||||
- 等价的 stencil tile set 不因 shared_ptr identity 变化而误失效。
|
||||
- 支持并记录以下语义失效来源:
|
||||
- camera / viewport / pixel ratio;
|
||||
- style / layer / source mutation;
|
||||
- tile content change;
|
||||
- image / sprite / glyph;
|
||||
- placement / fade;
|
||||
- context reset;
|
||||
- debug options;
|
||||
- 3D / unsupported custom/static layer。
|
||||
- unsupported / uncertain 场景继续 fail-closed。
|
||||
- STEP-088 runtime Cache ON/OFF 行为保持不变。
|
||||
- 未实现 STEP-090 CPU fast path。
|
||||
|
||||
## Android 实机关键验证
|
||||
|
||||
测试场景:
|
||||
- Android 13 / x86_64;
|
||||
- NavSea 正式 63 层 PBF Style;
|
||||
- Particle = AFTER_CLIP;
|
||||
- 固定 Camera:Karatsu;
|
||||
- Cache ON。
|
||||
|
||||
### 稳态
|
||||
|
||||
修复前曾出现:
|
||||
- drawable-mutation 每帧失效;
|
||||
- layer-group-mutation 每帧失效;
|
||||
- staticRevision 每帧递增;
|
||||
- build 每帧递增;
|
||||
- reuse=0。
|
||||
|
||||
最终版本:
|
||||
|
||||
```text
|
||||
frame=480 build=2 reuse=302 staticRevision=21 buildRevision=21
|
||||
frame=600 build=2 reuse=422 staticRevision=21 buildRevision=21
|
||||
frame=720 build=2 reuse=542 staticRevision=21 buildRevision=21
|
||||
```
|
||||
|
||||
确认:
|
||||
- staticRevision 稳定;
|
||||
- buildRevision == staticRevision;
|
||||
- build 不再增长;
|
||||
- reuse 持续增长。
|
||||
|
||||
### Camera 变化
|
||||
|
||||
相机变化后:
|
||||
- staticRevision: 41 -> 44;
|
||||
- build: 18 -> 19;
|
||||
- buildRevision = 44;
|
||||
|
||||
随后稳定:
|
||||
|
||||
```text
|
||||
build=19
|
||||
reuse=3519 -> 3639 -> 3759 -> 3879
|
||||
staticRevision=44
|
||||
buildRevision=44
|
||||
```
|
||||
|
||||
确认真实变化会使 revision 前进并重建一次,稳定后恢复 reuse。
|
||||
|
||||
## Native 测试
|
||||
|
||||
Retained directed tests:12/12 PASS。
|
||||
|
||||
覆盖:
|
||||
- dynamic boundary;
|
||||
- layer group range;
|
||||
- cache build/reuse/invalidation;
|
||||
- runtime Cache ON/OFF;
|
||||
- revision 单调;
|
||||
- 无语义变化时 revision 不前进;
|
||||
- 等价 intra-frame remove/add 不推进 topology;
|
||||
- 持久 topology 变化只推进一次;
|
||||
- ordering/index 变化推进 topology;
|
||||
- buildRevision 匹配才允许 reuse;
|
||||
- placement/fade 活跃时拒绝 stale cache。
|
||||
|
||||
## 构建验证
|
||||
|
||||
- Mac OpenGL mbgl-test-runner:PASS。
|
||||
- Metal/iOS mbgl-core:BUILD SUCCEEDED。
|
||||
- Android x86_64 stripped Debug Validation APK:BUILD SUCCESSFUL,约 57 MB。
|
||||
- Android 实机稳态 reuse:PASS。
|
||||
- Android Camera invalidation -> rebuild -> reuse:PASS。
|
||||
- git diff --check:PASS。
|
||||
|
||||
## 结论
|
||||
|
||||
STEP-089 已建立可信的 static revision gate。后续 STEP-090 可以在该 gate 为稳定状态时尝试跳过静态 PBF/vector CPU processing;089 本身不做 CPU fast path。
|
||||
|
||||
## 下一步
|
||||
|
||||
进入 STEP-090:Static CPU Fast Path。
|
||||
231
docs/50_Step记录/STEP-090_Particle-only_Static_CPU_Fast_Path_v1.md
Normal file
231
docs/50_Step记录/STEP-090_Particle-only_Static_CPU_Fast_Path_v1.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# STEP-090 Particle-only Static CPU Fast Path v1
|
||||
|
||||
## 状态
|
||||
|
||||
**CLOSED / PASS**
|
||||
|
||||
STEP-090 已完成 Particle-only Static CPU Fast Path。该实现建立在 STEP-088 retained underlay GPU cache 与 STEP-089 static revision/invalidation gate 之上,在满足严格安全条件时,于 `Renderer::render()` 的 `createRenderTree()` 之前命中 fast path,复用上一帧已 prepare 的 RenderTree,并跳过静态 PBF/vector 的完整 CPU pipeline。
|
||||
|
||||
## 核心实现
|
||||
|
||||
- fast-path gate 位于 `Renderer::render()`,早于 `RenderOrchestrator::createRenderTree()`。
|
||||
- full frame 成功后缓存 prepared RenderTree。
|
||||
- fast frame 命中时:
|
||||
- 不重新 createRenderTree;
|
||||
- 不执行完整 RenderTree::prepare;
|
||||
- 不执行 full static layer update;
|
||||
- 只更新 Particle 动态 layer;
|
||||
- 静态 underlay 继续使用 retained cache;
|
||||
- 保持 Particle simulation / draw / present 正常运行。
|
||||
- full path 创建新 RenderTree 前先清除旧 prepared tree,避免引用旧 RenderLayer/RenderSource 生命周期。
|
||||
- context lost / clearData / reduceMemoryUse / style reload 时清除 prepared tree。
|
||||
- 没有新增 Host/RN 手工 fast-frame API;仍使用已有 retainedUnderlayEnabled 作为整体优化 ON/OFF A/B 开关。
|
||||
|
||||
## Particle-only 安全 gate
|
||||
|
||||
只有下列条件同时满足才允许 fast path:
|
||||
|
||||
- prepared RenderTree 存在;
|
||||
- retained runtime enabled;
|
||||
- retained cache valid;
|
||||
- static/build/content/topology revision 一致;
|
||||
- Camera / viewport / pixelRatio 一致;
|
||||
- UpdateParameters 的非时间语义输入一致;
|
||||
- style loaded;
|
||||
- dynamic boundary 一致;
|
||||
- static prefix stable;
|
||||
- 当前 placement/fade transition 已结束;
|
||||
- 无 pending renderer changes;
|
||||
- 无 non-particle transition;
|
||||
- 无 non-particle needsRepaint;
|
||||
- 无 fading tiles;
|
||||
- Particle 当前确实 needsRepaint;
|
||||
- 非 still-image request;
|
||||
- Continuous MapMode;
|
||||
- debug rendering disabled;
|
||||
- 当前场景支持 fast path;
|
||||
- annotation 无 pending mutation。
|
||||
|
||||
任一不满足均立即走 full path。
|
||||
|
||||
## placement/fade 修正
|
||||
|
||||
首轮实机显示 fast path 永久被 `placement-or-fade` 阻止。调查确认 prepared RenderTree 上一 full frame 的 `placementChanged / symbolFadeChange` 是历史帧事件状态,不应永久阻止后续已稳定帧。
|
||||
|
||||
最终改为以当前 `placementController.hasTransitions(timePoint)` 为权威判据:
|
||||
|
||||
- 当前 placement/fade transition 活跃:full path;
|
||||
- transition 结束且 STEP-089 revision / camera / tile / style 等语义 gate 均稳定:允许 fast path。
|
||||
|
||||
这保留了 active symbol fade/placement 的安全回退,同时避免上一帧 placement pass 永久锁死 fast path。
|
||||
|
||||
## Android 性能实测
|
||||
|
||||
设备:Android 13 / x86_64 / FZG1-2。
|
||||
|
||||
场景:NavSea 正式 63 层 PBF Style,Particle=2048。
|
||||
|
||||
### AFTER_CLIP
|
||||
|
||||
FastPath ON:
|
||||
|
||||
```text
|
||||
PROC_CPU ≈ 27.7%
|
||||
MAIN_CPU ≈ 2.9%
|
||||
RENDER_CPU ≈ 21.7%
|
||||
```
|
||||
|
||||
同 APK、同场景、Cache/FastPath OFF:
|
||||
|
||||
```text
|
||||
PROC_CPU ≈ 111.4%
|
||||
MAIN_CPU ≈ 2.8%
|
||||
RENDER_CPU ≈ 97.0%
|
||||
```
|
||||
|
||||
RenderThread 从约 97% 降至约 21.7%。
|
||||
|
||||
稳定日志:
|
||||
|
||||
```text
|
||||
fastHit 持续增长
|
||||
fastMiss 固定
|
||||
particleFull 固定
|
||||
fastReason=none
|
||||
buildRevision == staticRevision
|
||||
retained build 固定
|
||||
retained reuse 持续增长
|
||||
```
|
||||
|
||||
代表日志:
|
||||
|
||||
```text
|
||||
frame=2880 ... fastHit=2670 fastMiss=210 particleFull=210 fastReason=none
|
||||
frame=3000 ... fastHit=2790 fastMiss=210 particleFull=210 fastReason=none
|
||||
frame=3240 ... fastHit=3030 fastMiss=210 particleFull=210 fastReason=none
|
||||
```
|
||||
|
||||
### BEFORE_SYMBOL_CLUSTER
|
||||
|
||||
稳定后 fast path 命中。
|
||||
|
||||
```text
|
||||
PROC_CPU ≈ 26.3%
|
||||
MAIN_CPU ≈ 2.7%
|
||||
RENDER_CPU ≈ 20.3%
|
||||
```
|
||||
|
||||
说明优化不依赖单一 AFTER_CLIP 边界。
|
||||
|
||||
## 安全回退验证
|
||||
|
||||
### Cache OFF / ON
|
||||
|
||||
Cache OFF:
|
||||
- enabled=false;
|
||||
- fastHit 停止增长;
|
||||
- fastMiss / particleFull 持续增长;
|
||||
- fastReason=retained-disabled;
|
||||
- RenderThread 回到约 97%。
|
||||
|
||||
重新 Cache ON:
|
||||
- cache rebuild 一次;
|
||||
- buildRevision 恢复匹配;
|
||||
- fastReason=none;
|
||||
- fastHit 恢复持续增长。
|
||||
|
||||
### Camera pan
|
||||
|
||||
pan 前:
|
||||
- staticRevision/buildRevision 稳定;
|
||||
- particleFull 固定;
|
||||
- fastHit 持续增长。
|
||||
|
||||
pan 时:
|
||||
- revision 前进;
|
||||
- build / invalidation / fastMiss / particleFull 增加;
|
||||
- full path 正常执行。
|
||||
|
||||
稳定后:
|
||||
- build 固定;
|
||||
- fastHit 恢复持续增长;
|
||||
- particleFull 再次固定。
|
||||
|
||||
### TOP symbol-heavy
|
||||
|
||||
TOP 将全部 63 层置于 Particle 下方。
|
||||
|
||||
实测:
|
||||
- retained buildRevision=0;
|
||||
- reason=static-transition / static-placement;
|
||||
- fastHit 不增长;
|
||||
- fastMiss / particleFull 持续增长;
|
||||
- 保守 full path 生效。
|
||||
|
||||
说明 symbol-heavy static prefix 未被错误复用。
|
||||
|
||||
### Viewport / orientation
|
||||
|
||||
原始设备设置:
|
||||
- accelerometer_rotation=1;
|
||||
- user_rotation=0。
|
||||
|
||||
强制旋转后:
|
||||
- style/cache invalid;
|
||||
- full path;
|
||||
- 新 RenderThread 创建;
|
||||
- cache rebuild;
|
||||
- 随后 fastReason=none,fastHit 恢复。
|
||||
|
||||
恢复原始旋转设置后再次经历同样的 full → rebuild → fast-hit 恢复流程。
|
||||
|
||||
设备设置最终已恢复:
|
||||
- accelerometer_rotation=1;
|
||||
- user_rotation=0。
|
||||
|
||||
## 2026-09-23 长时间运行异常复测
|
||||
|
||||
此前长时间实机运行后,2026-09-23 08:23:27 曾出现一次:
|
||||
|
||||
`ActivityTaskManager: Force removing ActivityRecord{...ParticleCoreLayerValidationActivity}: app died, no saved state`。
|
||||
|
||||
当时 frame counter 已约 1,868,000,fast path 长时间持续正常;同期未检出 `FATAL EXCEPTION`、`Fatal signal`、`SIGSEGV`、`SIGABRT`、ANR、OOM、`GL_INVALID` 或 `EGL_BAD`。因此该事件没有足够证据归因于 STEP-090 fast path,也不能据此判定具体 crash root cause。
|
||||
|
||||
按收尾要求重新启动 Test App,并进入 `NAVSEA PBF / AFTER_CLIP / CACHE ON` 后连续复测约 3 分钟:
|
||||
|
||||
- PID 始终为 29295,Activity 始终保持 top resumed;
|
||||
- `build=2`,`staticRevision=18`,`buildRevision=18`;
|
||||
- `fastReason=none`;
|
||||
- `particleFull=218` 全程固定;
|
||||
- `fastHit` 从约 20662 持续增长到 33622;
|
||||
- 6 个连续 30 秒 RenderThread 样本为 21.2% / 21.3% / 21.5% / 21.7% / 21.8% / 22.0%;
|
||||
- 复测结束后再次扫描 FATAL / signal / ANR / GL / EGL / OOM / app died 关键字,均未发现异常。
|
||||
|
||||
结论:08:23 的 `app died` 记录为**长时间单次异常,当前 3 分钟短时稳定性复测未复现**。该异常不阻塞 STEP-090 关闭,但后续如安排长时间 soak,应继续观察。
|
||||
|
||||
## 构建与测试
|
||||
|
||||
- git diff --check:PASS。
|
||||
- Mac OpenGL mbgl-test-runner:PASS。
|
||||
- retained + ParticleFastPath directed tests:21/21 PASS。
|
||||
- Android x86_64 stripped Debug Validation APK:BUILD SUCCESSFUL,约 57 MB。
|
||||
- Android 安装:PASS。
|
||||
- Android A/B CPU:PASS。
|
||||
- Camera / Cache / TOP / viewport 回退:PASS。
|
||||
- 异常日志扫描:
|
||||
- 无 FATAL EXCEPTION;
|
||||
- 无 ANR;
|
||||
- 无 SIGSEGV/SIGABRT;
|
||||
- 无 GL_INVALID / EGL_BAD;
|
||||
- 无 OOM。
|
||||
- Metal/iOS mbgl-core:BUILD SUCCEEDED。
|
||||
- 临时 ParticleFastPathLife / ParticleFastPathDecision 日志:已移除。
|
||||
- 未新增 Host/RN fast-frame API。
|
||||
|
||||
## 结论
|
||||
|
||||
STEP-090 达成目标:Particle continuous repaint 不再强制静态 PBF/vector 每帧重新执行完整 CPU render-tree/source/layer pipeline。
|
||||
|
||||
在真实 NavSea PBF 测试场景中,RenderThread CPU 从约 97% 降到约 20~22%,已经接近此前 PURE PARTICLE / PBF-hidden 基线级别。
|
||||
|
||||
下一阶段不应立即扩展成通用多动态层调度;应先基于当前 Android/iOS 实际集成结果决定是否需要新的 profiling / production hardening 阶段。
|
||||
@@ -0,0 +1,53 @@
|
||||
# STEP-091 Zoom Out Viewport Particle Reseed v1
|
||||
|
||||
## 状态
|
||||
|
||||
**完成(Codex)/ 等待 RDC Android 实机验收**
|
||||
|
||||
## 基线
|
||||
|
||||
- 分支:`step091-zoom-out-reseed`
|
||||
- Starting HEAD:`297b9126200cea3da3bdef2faacedcd9845f6d08`
|
||||
- Implementation commit:`4286c16a53d4f49001f3b41ed4638d3c6edc95b3`
|
||||
- STEP-090:`CLOSED / PASS`,retained underlay 与 Particle-only CPU fast path 未修改。
|
||||
|
||||
## 实现结果
|
||||
|
||||
- camera invalidation 首帧保存一次稳定 `startZoom`,连续 camera 变化不覆盖。
|
||||
- 延用 150 ms camera stable debounce;稳定后先计算当前 `viewportRespawnDomain`、`viewportDensityActive` 与新 `respawnBounds`。
|
||||
- 当且仅当 `endZoom < startZoom - 0.05` 且 viewport domain 有效时,一次性设置 `stateSeeded=false`、`trailSeeded=false`。下一次既有 State/Trail pass 使用新的 `respawnBounds` 完成全体 State initial seed 与 Trail 清理。
|
||||
- `0.05` 仅过滤 sub-step 浮点/相机抖动,明显小于一次有意 pinch 或 zoom level 变化。
|
||||
- Zoom In、Pan、Rotate、Pitch 以及 epsilon 内抖动不触发 State reseed。
|
||||
- Zoom Out 后 domain fallback 时,本轮请求立即消费并记录 `INVALID_RESPAWN_DOMAIN`,不会留到后续 Pan。
|
||||
- `resetSequence()` 会取消尚未稳定消费的 gesture 状态,避免真实 simulation reset 后残留请求。
|
||||
|
||||
## 边界保持
|
||||
|
||||
- 未调用 Zoom Out `resetSequence()`。
|
||||
- 未重建 State/Trail GPU resource,未重新上传 Field,未 reset Timeline/T0/T1/MIX。
|
||||
- 未修改 shader、UBO layout、公共 Layer/Host/RN API。
|
||||
- 未修改 WeatherColor/RasterParticle 分层、Runtime Field Window 或 continuous cadence。
|
||||
- 未修改 STEP-089 revision/invalidation 与 STEP-090 fast-path gate;camera 变化期间既有 full-path、稳定后 fast-hit 恢复语义保持。
|
||||
|
||||
## 诊断
|
||||
|
||||
成功时单次输出:
|
||||
|
||||
`ParticleDensity: event=VIEWPORT_RESEED reason=ZOOM_OUT ... stateReseed=true trailReseed=true fieldResubmit=false timelineReset=false gpuResourceRebuild=false`
|
||||
|
||||
fallback 时单次输出 `action=SKIP skipReason=INVALID_RESPAWN_DOMAIN`,并明确 State/Trail 未 reseed。
|
||||
|
||||
## 测试与构建
|
||||
|
||||
- Mac mini / Darwin x86_64,OpenGL Debug CMake/Ninja 配置:PASS。
|
||||
- 精确编译 `render_particle_validation_layer.cpp.o`:PASS。
|
||||
- 精确编译 `particle_viewport_reseed.test.cpp.o`:PASS。
|
||||
- 独立原生 C++ directed executable:8/8 PASS,覆盖 Zoom Out、Zoom In、Pan、Rotate/Pitch、epsilon、fallback 消费、一次 gesture 一次触发、下一 gesture 刷新 startZoom。
|
||||
- `git diff --check`:PASS。
|
||||
- 全量 `mbgl-test-runner` 冷链接不是本 Step 必需验证;首次新 build tree 在受影响 objects 已通过后停止于 182/659,未据此声称全量 runner PASS。
|
||||
- Android Gradle/APK/ADB/设备:未执行,交由 RDC 串行验收。
|
||||
- Jest:未运行、未新增。
|
||||
|
||||
## 当前结果与下一步
|
||||
|
||||
Codex 阶段满足一次性 viewport State/Trail reseed 的实现与 Mac 定向验证要求。下一步由 RDC 构建 OpenGL Debug Test APK,在正式 RasterParticleLayer 场景验证 Zoom Out 立即铺开、单次日志、无 GL/EGL/crash,以及 camera stable 后 ParticleFastPath fast hit 恢复。
|
||||
@@ -0,0 +1,80 @@
|
||||
# STEP-092 Continuous Range 可退化分段 Retained Cache v1
|
||||
|
||||
## 状态
|
||||
|
||||
**进行中:Codex 实现完成,等待 RDC 独立编译与运行验证**
|
||||
|
||||
## 目标与基线
|
||||
|
||||
- 分支:`particle/main`
|
||||
- 起始 HEAD:`b29e564944197072a0effaec36ca95c48fe0649b`
|
||||
- 起始 `origin/particle/main`:`b29e564944197072a0effaec36ca95c48fe0649b`
|
||||
- Implementation commit:`a9eaec6b65cc26aa70e03002bdc1326c46cad245`(`feat: add continuous retained render ranges`),已 push 至 `origin/particle/main`。
|
||||
- Style Layer 顺序是唯一 Z-order;App 只提交现有 `startLayerId` / `endLayerId` 闭区间。
|
||||
- 保留未配置 explicit range 时的 `startsRetainedDynamicRange()` legacy underlay 行为。
|
||||
|
||||
## 实现结构
|
||||
|
||||
- `RetainedUnderlayRuntimeState` 是 map-scoped range config 所有者;使用 mutex 保护 ID/config/resolution,config revision 单调递增。
|
||||
- `RenderOrchestrator::createRenderTree()` 基于完整 `orderedLayers`(包含 hidden layer)把 ID 解析为 Style index;不存在、倒序或 Style 未 ready 均 fail closed,不保留旧 index。
|
||||
- 内部统一使用 `[continuousBegin, continuousEndExclusive)`:Bottom、Continuous、Top 均由 Style index 自然退化。
|
||||
- 现有 cache 泛化为带 `begin/endExclusive` 与 Bottom/Top role 的 retained range cache;两个 cache 对象不等于两个 GPU cache,texture/render target/compositor 只在对应 range 实际存在时 lazy 创建。
|
||||
- explicit valid 时完全忽略 legacy boundary;explicit invalid 时不用 legacy 猜测,走完整 Renderer。
|
||||
|
||||
## 四种结构
|
||||
|
||||
| Style 结构 | Render Range | Retained GPU cache |
|
||||
|---|---:|---:|
|
||||
| Bottom + Continuous + Top | 3 | 2 |
|
||||
| Bottom + Continuous | 2 | 1(Bottom) |
|
||||
| Continuous + Top | 2 | 1(Top) |
|
||||
| Continuous only | 1 | 0 |
|
||||
|
||||
`startLayerId == endLayerId` 是合法 single-layer Continuous。hidden endpoint 仍决定 topology;range 是否存在只看 Style index 区间,不看当前 drawable/group 是否为空。
|
||||
|
||||
## 绘制与 global index
|
||||
|
||||
- cache build 的 opaque 使用区间 reverse traversal,translucent 使用 forward traversal。
|
||||
- callback ordinal 来自完整 `layerGroupsByLayerIndex`,`parameters.currentLayer` 与 `depthRangeSize` 沿用全 Style 语义,不对子区间重新编号。
|
||||
- main 顺序固定为:Bottom composite(若有)→ Continuous opaque/translucent → Top composite(若有)。
|
||||
- Top 与 Bottom 均使用透明 RGBA offscreen、现有 direct sample compositor 和 premultiplied alpha blend;未新增 land-mask 或业务判断。
|
||||
|
||||
## Invalidation 与 fallback
|
||||
|
||||
- camera/viewport/pixel ratio/style/layer/source/tile/image/glyph/placement/context/debug/3D 等继续进入 STEP-089 的 content/topology/static revision 模型。
|
||||
- range config revision、continuous begin/end、Bottom/Top topology 纳入 revision;配置变化会释放实际存在 cache 并使 prepared tree/fast gate 失配。
|
||||
- endpoint 删除、missing、倒序与 Style 未 ready:diagnostics 给出原因,cache key 变空并释放资源,full renderer 安全 fallback;后续 Style 恢复会重新解析。
|
||||
- correctness-first:无法只归因 Bottom 或 Top 时,两侧实际存在 cache 可同时失效。
|
||||
|
||||
## Particle-only CPU Fast Path
|
||||
|
||||
- gate 只要求实际存在的 retained cache valid;Bottom/Top 不存在时为 vacuous success,不产生 cache-missing miss。
|
||||
- fast frame 仅遍历 Continuous 区间,仍使用全局 ordinal。
|
||||
- explicit Continuous 中只允许 Renderer 已知的 WeatherColor/RasterParticle dynamic candidate;出现未知 Layer 时 fail closed 到 full CPU frame,但 range cache/Z-order 仍保持。
|
||||
- Continuous-only 可进入 0-cache fast gate;是否最终命中仍需满足 Particle、revision、placement、scene 等既有条件。
|
||||
|
||||
## API 与 diagnostics
|
||||
|
||||
- C++/Android/iOS/RN 增加 `setContinuousLayerRange(..., startLayerId, endLayerId)`;空 ID 拒绝,相同 ID 接受。
|
||||
- 保留 `setRetainedUnderlayEnabled()` 与 `getRetainedUnderlayDiagnostics()`。
|
||||
- pull diagnostics 增加 configured/valid、IDs/index、Bottom/Top present/build/reuse/invalidation;保留 aggregate fallback 与 `lastReason`,无 per-frame Release 日志。
|
||||
|
||||
## Directed tests
|
||||
|
||||
已新增源码覆盖:single/multi layer、missing/reversed、hidden endpoint、reorder/delete、四种 topology、0/1/2 cache decision、无 cache 的 fast gate、config revision 与 explicit/legacy 排他。
|
||||
|
||||
## 待 RDC 执行
|
||||
|
||||
```bash
|
||||
cmake --build build-step088-opengl --target mbgl-test-runner -j3
|
||||
build-step088-opengl/mbgl-test-runner \
|
||||
--gtest_filter='ContinuousLayerRange.*:RetainedDynamicBoundary.*:RetainedLayerGroupRange.*:RetainedUnderlayCache.*:RetainedUnderlayRevision.*:ParticleFastPath.*'
|
||||
cmake --build build-ios --target ios-sdk-static -- -quiet
|
||||
git diff --check
|
||||
```
|
||||
|
||||
RDC 还需独立核验 OpenGL/Metal 透明 Top composite、0/1/2 texture/FBO 生命周期和 stable frame 复用。Android Gradle/APK/ADB/设备项未由 Codex 执行。
|
||||
|
||||
## 当前结论
|
||||
|
||||
源码与 directed test 已实现并完成 scoped implementation push;在 RDC 返回 OpenGL、Metal、Native test 实际结果前,本 Step 不判 PASS。
|
||||
@@ -0,0 +1,99 @@
|
||||
# STEP-092 Continuous Range 可退化分段 Retained Cache v2
|
||||
|
||||
## 状态
|
||||
|
||||
**CLOSED / PASS(Mac Native 验证闭环)**
|
||||
|
||||
本版在 v1 的 Codex 实现基础上补入 RDC 独立编译、测试、修复与双后端核验结果。Android Gradle/APK/ADB/实体机与 Metal GPU frame capture 未执行,不伪造为已验证。
|
||||
|
||||
## Git 基线
|
||||
|
||||
- 分支:`particle/main`
|
||||
- 起始 HEAD:`b29e564944197072a0effaec36ca95c48fe0649b`
|
||||
- Codex implementation:`a9eaec6b65cc26aa70e03002bdc1326c46cad245`
|
||||
- Codex 文档:`b89c8c8cb4d941abdcd704b278284e209ba7399b`
|
||||
- RDC 验证修复:`4908244d2`(`fix: close STEP-092 native validation issues`)
|
||||
- 最终文档收尾提交在本文件提交后回读,以最终交付结果为准。
|
||||
|
||||
## 已实现架构
|
||||
|
||||
- Style Layer 顺序是唯一 Z-order 事实来源。
|
||||
- App/RN 只提交现有 `startLayerId` / `endLayerId` 闭区间。
|
||||
- Native 基于完整 `orderedLayers` 解析 index;hidden endpoint 仍参与 topology。
|
||||
- `startLayerId == endLayerId` 为合法 single-layer Continuous。
|
||||
- 内部统一为 `[continuousBegin, continuousEndExclusive)`。
|
||||
- 四种结构自然退化:
|
||||
- Bottom + Continuous + Top → 3 Render Range / 2 retained caches
|
||||
- Bottom + Continuous → 2 Render Range / 1 Bottom cache
|
||||
- Continuous + Top → 2 Render Range / 1 Top cache
|
||||
- Continuous only → 1 Render Range / 0 retained cache
|
||||
- explicit valid 时不混用 legacy boundary;explicit invalid 时 fail closed,不复用 stale index/cache。
|
||||
- global layerIndex / ordinal / `currentLayer` / `depthRangeSize` 保持全 Style 语义。
|
||||
- Opaque reverse、Translucent forward 保持。
|
||||
- Particle-only Fast Path 只检查实际存在的 retained ranges。
|
||||
|
||||
## RDC 编译与测试结果
|
||||
|
||||
### OpenGL test-runner
|
||||
|
||||
第一次编译失败:
|
||||
- `renderer_impl.cpp:873-874`
|
||||
- 原因:循环变量被收窄为 `RenderItem&` 后访问不存在的 `index`。
|
||||
|
||||
第二次编译失败:
|
||||
- 改为 `LayerRenderItem&` 后,`hasRenderPass/render` 是 private override。
|
||||
|
||||
RDC 最终修复:
|
||||
- 用 `LayerRenderItem` 读取 `index`;
|
||||
- 通过 `RenderItem&` 调用公开虚接口。
|
||||
|
||||
第三轮:
|
||||
- `cmake --build build-step088-opengl --target mbgl-test-runner -j3`
|
||||
- 结果:PASS,exit code 0。
|
||||
### Native directed tests
|
||||
|
||||
命令:
|
||||
`build-step088-opengl/mbgl-test-runner --gtest_filter='ContinuousLayerRange.*:RetainedDynamicBoundary.*:RetainedLayerGroupRange.*:RetainedUnderlayCache.*:RetainedUnderlayRevision.*:ParticleFastPath.*'`
|
||||
|
||||
第一轮:28/29 PASS。
|
||||
唯一失败:
|
||||
- `RetainedUnderlayCache.RuntimeControlDisablesAndReenablesMapScopedCache`
|
||||
- 原因:`setEnabled(false)` 已记录 aggregate invalidation,STEP-092 range cache 同步又调用 aggregate `invalidate()`,造成重复计数。
|
||||
|
||||
RDC 修复:
|
||||
- 保留 legacy aggregate invalidation 只记录一次;
|
||||
- 对实际 range cache 单独记录 Bottom/Top invalidation;
|
||||
- 不改变 runtime-disabled 语义。
|
||||
|
||||
第二轮:**29/29 PASS**。
|
||||
|
||||
覆盖 single/multi range、missing/reversed endpoint、hidden endpoint、reorder/delete、四种 topology、0/1/2 cache decision、explicit/legacy 排他、Fast Path、revision 与 runtime control。
|
||||
|
||||
## Metal / iOS
|
||||
|
||||
- `build-ios/CMakeCache.txt`:`MLN_WITH_METAL=ON`、`MLN_WITH_OPENGL=OFF`。
|
||||
- Xcode target 编译定义包含 `MLN_RENDER_BACKEND_METAL=1`。
|
||||
- `cmake --build build-ios --target ios-sdk-static -- -quiet`
|
||||
- 结果:PASS,exit code 0。
|
||||
- 警告仅包括既有 EAGL deprecated、bundle identifier 等,不构成 STEP-092 编译失败。
|
||||
## GPU resource / composite 核验
|
||||
|
||||
RDC 对实际源码路径独立核验:
|
||||
- Bottom/Top holder 可常驻,但 GPU texture/render target/compositor 只在 `prepareResources()` 被调用时 lazy 创建。
|
||||
- `prepareResources()` 只在对应 cache decision 为 Build/Reuse 时调用;不存在的 Range 为 Disabled,不调用资源准备。
|
||||
- 同 viewport 且已有 `offscreenTexture` 时不重建 texture;已有 `compositeLayerGroup` 时不重建 compositor。
|
||||
- retained target clear color 为透明 `(0,0,0,0)`。
|
||||
- composite 使用 `gfx::ColorMode::alphaBlended()`,其因子为 `One / OneMinusSrcAlpha`,对应 premultiplied-alpha。
|
||||
- OpenGL 与 Metal `RetainedUnderlayCompositeShader` 均直接采样 RGBA texture。
|
||||
- Final 顺序为 Bottom composite → Continuous → Top composite。
|
||||
- 29 项 directed tests 已验证 0/1/2 cache decision 与 stable reuse/invalidation 语义。
|
||||
|
||||
## 未执行项
|
||||
|
||||
- Android Gradle / AAR / APK / ADB / 安装 / 实体机视觉:未执行。
|
||||
- Metal GPU frame capture、iOS 实机 runtime resource counter:未执行;当前仓库无现成可直接运行的 STEP-092 Metal GPU runtime test target。
|
||||
- 未执行 Jest。
|
||||
|
||||
## 结论
|
||||
|
||||
STEP-092 的 Mac Native 实现、OpenGL 编译、Native directed tests、Metal-only iOS 编译、资源生命周期代码路径、透明 Top composite 与 Git 修复闭环均已通过。未执行的平台实体机/Frame Capture 项保留为后续产品级验证,不改变本 Step 的 Mac Native CLOSED / PASS 结论。
|
||||
238
docs/50_Step记录/STEP-093_ContinuousRange运行态综合验收_v1.md
Normal file
238
docs/50_Step记录/STEP-093_ContinuousRange运行态综合验收_v1.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# STEP-093 Continuous Range 运行态综合验收 v1
|
||||
|
||||
## 1. 当前状态
|
||||
|
||||
**IMPLEMENTED / WAITING_RUNTIME_VALIDATION**
|
||||
|
||||
Codex 已完成 Android 运行态验收入口、跨平台 diagnostics 补强、Fast Path fail-closed 原因观测、定向 Native 测试源码和 RDC 操作手册。依照仓库职责分工与本轮用户指令,Codex 未执行 Android/iOS 长时间编译、安装、ADB、模拟器/真机操作或视觉验收。因此 Android 为 `WAITING_ANDROID_RUNTIME_VALIDATION`,iOS/Metal 为 `WAITING_IOS_RUNTIME_VALIDATION`;本版不能标记 `CLOSED / PASS`。
|
||||
|
||||
## 2. 起始 Git 基线
|
||||
|
||||
- Repository:`MapLibreParticleLab`
|
||||
- 机器:`Mac-mini-tei.lan`
|
||||
- 路径:`/Users/tei/dev/MapLibreParticleLab`
|
||||
- Branch:`particle/main`
|
||||
- Starting HEAD:`8a5d88008c142e2a80e0ff602d228e0e67de2d83`
|
||||
- Starting `origin/particle/main`:`8a5d88008c142e2a80e0ff602d228e0e67de2d83`
|
||||
- STEP-092 基线:`CLOSED / PASS(Mac Native 验证闭环)`
|
||||
- STEP-093 implementation commit:`b3ec8edc87ece404b5f7b3706f5acf2a70de6862`
|
||||
- 起始工作树差异:仅有本任务书 `docs/40_任务/STEP-093_ContinuousRange运行态综合验收_Codex任务_v1.md` 为 untracked;已保留并纳入本 Step 文档提交。
|
||||
|
||||
## 3. 实际调查到的测试入口
|
||||
|
||||
Android 已有入口为 `MapLibreAndroidTestApp` 的 `ParticleCoreLayerValidationActivity`。本 Step 复用该 Activity、既有 Host-submitted particle fixture、`weather-color` 和普通 Style layer,不建立新测试框架,也不引入特殊 layer type。
|
||||
|
||||
新增底部 `STEP 093` 菜单与常驻 diagnostics 区。入口支持:
|
||||
|
||||
- A:Bottom + Continuous + Top;
|
||||
- B:Bottom + Continuous;
|
||||
- C:Continuous + Top;
|
||||
- D:Continuous only;
|
||||
- E:Single-layer Continuous;
|
||||
- Continuous/Bottom/Top add 与 reorder;
|
||||
- 普通 layer 跨 Bottom/Continuous/Top 移动;
|
||||
- start/end endpoint visibility、删除与恢复;
|
||||
- missing start、missing end、reversed、style loading 期间配置、empty ID、恢复 valid;
|
||||
- Camera preset 循环覆盖 pan/zoom/rotate/pitch;
|
||||
- Continuous 内未知普通 layer 触发 Fast Path fail-closed;
|
||||
- 1 秒一次的屏幕 pull diagnostics;仅用户点 `READ DIAGNOSTICS` 或场景切换时写一条日志,不制造逐帧日志风暴。
|
||||
|
||||
Android Activity 声明增加 `orientation|keyboardHidden|screenSize` config changes,使 RDC 可在不重建 Map/Style 的前提下用旋转或尺寸变化观察 viewport invalidation。
|
||||
|
||||
iOS 当前只有正式 `MLNMapView` diagnostics API 与 RN bridge 字段补齐,没有新增专用 STEP-093 iOS validation 页面。因此 iOS runtime 必须后续由 RDC 准备实际入口并执行,不能由 Android 结果推导。
|
||||
|
||||
## 4. 本轮新增/修改内容
|
||||
|
||||
### 4.1 Renderer diagnostics
|
||||
|
||||
在既有 map-scoped diagnostics 上追加,而非另造一套状态源:
|
||||
|
||||
- Range:`continuousBegin`、`continuousEndExclusive`、`rangeFallback`;
|
||||
- Bottom/Top:`cacheState`、build/reuse/invalidation、last invalidation reason;
|
||||
- 资源生命周期:texture create、composite create、resource release;
|
||||
- Fast Path:eligible、active、fallback reason、hit、miss、full CPU frame;
|
||||
- 未知 Continuous layer 的明确原因:`unsupported-continuous-layer`。
|
||||
|
||||
这些 counter 是 Map 生命周期内累计事件数,不是 GPU memory byte 数。它们用于证明 stable frame 不重复创建资源及 invalidation 后能重建/reuse;不能替代 profiler 或 GPU capture。
|
||||
|
||||
### 4.2 跨端暴露
|
||||
|
||||
- Android JNI/Java:diagnostics 字符串数组由 20 项扩为 39 项,Java 构造索引与 Native 顺序逐项一致;
|
||||
- iOS ObjC++:`MLNRetainedUnderlayDiagnostics` 暴露同组字段;
|
||||
- RN Android/iOS/TypeScript/Test Host:透传并显示同组字段;
|
||||
- 原有 aggregate build/reuse/fallback/invalidation 字段保持,不删除兼容语义。
|
||||
|
||||
### 4.3 Android 可视场景
|
||||
|
||||
测试 Style 中仅使用普通 background/fill/line、已有 WeatherColor 与 RasterParticle:
|
||||
|
||||
- Bottom:深色 background + 青色 fill;
|
||||
- Continuous:WeatherColor + Particle;
|
||||
- Top:半透明黄色 fill、opaque 红色 fill、白色粗 line;
|
||||
- fail-closed:在 Continuous 内加入普通 fill;
|
||||
- cross-range:同一个普通 fill ID 分别位于 endpoint 前、endpoint 间、endpoint 后。
|
||||
|
||||
最终 Z-order 仍完全由 Style layer 数组顺序决定。App 只调用 `setContinuousLayerRange(startLayerId, endLayerId)`;没有 anchor/marker、业务 layer metadata 或 Renderer 业务 ID 硬编码。
|
||||
|
||||
### 4.4 定向测试源码
|
||||
|
||||
新增 `RetainedUnderlayCache.RuntimeValidationDiagnosticsExposeRangeResourcesAndFastPath`,覆盖 Continuous index、cache state、资源 counter、Fast Path counter;既有 Fast Path gate 测试增加 `UnsupportedContinuousLayer` 原因断言。
|
||||
|
||||
本轮未运行 test runner。STEP-092 既有 29/29 PASS 是历史基线,不能当作新增测试已通过;RDC 运行后预期相关 filter 共 30 项,但必须以实际 runner 输出为准。
|
||||
|
||||
## 5. 静态一致性审查
|
||||
|
||||
Codex 已完成:
|
||||
|
||||
- Native diagnostics 定义、runtime state、Android JNI/Java、iOS、RN 字段名与顺序逐项比对;
|
||||
- 四种 topology 与 single-layer 的 Style layer 顺序/endpoint 位置审查;
|
||||
- mutation、cross-range、endpoint、invalid、camera、fail-closed 菜单接线审查;
|
||||
- absent Bottom/Top 仍沿用 STEP-092 `Disabled` / lazy resource 路径,没有新增固定 GPU 分配;
|
||||
- Fast Path eligibility 与 decision counter 接入既有 gate,失败仍进入 full CPU frame;
|
||||
- `xmllint --noout`:Android Manifest 与 layout PASS;
|
||||
- `git diff --check`:PASS。
|
||||
|
||||
未执行:C++/Java/Kotlin/ObjC++/TypeScript 编译、Native tests、Gradle、APK、ADB、Android/iOS runtime。静态审查不等于运行态 PASS。
|
||||
|
||||
## 6. RDC Android 构建、安装与启动
|
||||
|
||||
在仓库根目录执行:
|
||||
|
||||
```bash
|
||||
./tools/particle_android_validation/build_validation_apk.sh
|
||||
adb devices
|
||||
adb -s <serial> install -r artifacts/maplibre-particle/android-validation/MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk
|
||||
adb -s <serial> shell am start -n org.maplibre.android.testapp/.activity.particle.ParticleCoreLayerValidationActivity
|
||||
```
|
||||
|
||||
可选定向日志:
|
||||
|
||||
```bash
|
||||
adb -s <serial> logcat -c
|
||||
adb -s <serial> logcat -v threadtime ParticleValidation:I MapLibre:I '*:S'
|
||||
```
|
||||
|
||||
若使用非 x86_64 真机,当前 validation script 的固定 ABI 不适用;RDC 应按项目现行 Android 构建规则选择相符 ABI,并在证据中记录实际 APK、commit、ABI、设备与 renderer backend。
|
||||
|
||||
## 7. RDC Native 精确回归命令
|
||||
|
||||
如 RDC 使用 STEP-092 的现有 OpenGL build tree:
|
||||
|
||||
```bash
|
||||
cmake --build build-step088-opengl --target mbgl-test-runner -j3
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='ContinuousLayerRange.*:RetainedDynamicBoundary.*:RetainedLayerGroupRange.*:RetainedUnderlayCache.*:RetainedUnderlayRevision.*:ParticleFastPath.*'
|
||||
```
|
||||
|
||||
必须记录真实 test 数与结果;不能直接抄写“30/30”。
|
||||
|
||||
## 8. Android 逐项运行手册
|
||||
|
||||
### 8.1 通用观察方法
|
||||
|
||||
启动 Activity 后点 `STEP 093`。每次切场景等待 Style READY,再观察屏幕 diagnostics;需要固化证据时点 `READ DIAGNOSTICS` 并保存日志/截图。稳定性判断建议至少间隔 10–30 秒采两次快照:
|
||||
|
||||
- Particle 持续运动;
|
||||
- 对存在的 cache,build/texture/composite 不再增长,reuse 与 Fast hit 应增长;
|
||||
- 不存在的 range 显示 `present=false`、`state=disabled`,对应资源创建数不因稳定帧增长;
|
||||
- counter 为累计值,切场景时应比较增量,不要求绝对值归零。
|
||||
|
||||
### 8.2 Topology A–E
|
||||
|
||||
1. A:选 `A BOTTOM + CONTINUOUS + TOP`。预期 range valid、Bottom/Top present、cache count 2;opaque 红块完整盖住 Particle,黄色半透明区域正确混合,白线在最上层,无黑边/halo/错误变暗。
|
||||
2. B:选 `B BOTTOM + CONTINUOUS`。预期 Bottom present、Top absent、cache count 1;Top state disabled,Top 资源不增长,Fast Path 不因 Top absent 被阻断。
|
||||
3. C:选 `C CONTINUOUS + TOP`。预期 Bottom absent、Top present、cache count 1;Bottom state disabled;Top 仍正确覆盖 Particle。
|
||||
4. D:选 `D CONTINUOUS ONLY`。预期 valid、Bottom/Top 均 absent、cache count 0、不是 fallback;Particle 正常运行,Fast Path 可命中。
|
||||
5. E:选 `E SINGLE-LAYER CONTINUOUS`。预期 start ID 等于 end ID、startIndex 等于 endIndex、`continuousEndExclusive = startIndex + 1`,不 fallback。
|
||||
|
||||
### 8.3 Mutation 与跨边界
|
||||
|
||||
在 `MUTATION / FAST PATH` 子菜单依次执行:
|
||||
|
||||
- `CONTINUOUS ADD` → `CONTINUOUS REORDER` → `RESTORE DEFAULT`:恢复动作也等价验证非 endpoint layer 删除;
|
||||
- `BOTTOM ADD` → `BOTTOM REORDER` → restore;
|
||||
- `TOP ADD` → `TOP REORDER` → restore;
|
||||
- `CROSS → BOTTOM` → `CROSS → CONTINUOUS` → `CROSS → TOP`。
|
||||
|
||||
每次检查 endpoint ID 重新解析出的 index、present topology、invalidation/build/reuse 增量及最终画面顺序;不得出现旧 index、旧 cache 或自动按 layer 类型修正 range。
|
||||
|
||||
### 8.4 Endpoint 与 visibility
|
||||
|
||||
在 `ENDPOINT / VISIBILITY` 子菜单:
|
||||
|
||||
- start visible ↔ hidden、end visible ↔ hidden:index 与 topology 不变,hidden endpoint 仍是 endpoint;
|
||||
- `DELETE START`:range invalid、fallback、无 crash/stale cache;`RESTORE START` 后恢复 valid 并 rebuild/reuse;
|
||||
- 对 end 执行同样步骤。
|
||||
|
||||
### 8.5 Invalid explicit range
|
||||
|
||||
在 `INVALID RANGE` 子菜单依次执行 missing start、missing end、reversed、configure while style loading、empty ID、restore valid:
|
||||
|
||||
- missing/reversed/style-not-ready:不得猜测 endpoint,不复用 stale cache,diagnostics 给出原因;
|
||||
- empty ID:当前 API 应同步返回 `accepted=false`;
|
||||
- restore 后无需重建 Map instance,应重新 valid、build、reuse。
|
||||
|
||||
### 8.6 Camera/viewport
|
||||
|
||||
连续点四次 `CAMERA NEXT`,覆盖 target/zoom/bearing/pitch 组合。每次检查 invalidation → build → reuse,topology 不变,无错位、黑屏或 Particle/地图不同步。
|
||||
|
||||
旋转设备或改变模拟器窗口尺寸,确认同一 Activity/Map 上产生 viewport invalidation;如设备支持可改变 display scale/pixel ratio,并单独记录是否实际触发。
|
||||
|
||||
### 8.7 Fast Path fail-closed
|
||||
|
||||
先恢复 A,稳定后确认 eligible/active 与 hit 增长。再选 `FAST FAIL-CLOSED`:
|
||||
|
||||
- Continuous Range 仍 valid;
|
||||
- fallback reason 为 `unsupported-continuous-layer`;
|
||||
- Fast Path 不 active,full CPU frame 增长;
|
||||
- 普通未知 layer 不被跳过或移出 Continuous;
|
||||
- Bottom/Top 与最终 Z-order 仍正确。
|
||||
|
||||
### 8.8 长时间运行
|
||||
|
||||
建议 RDC 在可用设备上连续运行 15–30 分钟,循环 topology、camera、mutation、endpoint、visibility、invalid/restore、再次稳定:观察 texture/composite create 与 release 增量、系统内存/GPU 工具可获得的数据、黑屏/闪屏/stale cache/偶发层序/Particle 恢复。若没有可靠 GPU byte 数,明确记录限制,不伪造内存数字。
|
||||
|
||||
## 9. PASS / FAIL 判定
|
||||
|
||||
- `PASS`:必须有实际设备/模拟器操作、画面确认与 diagnostics/log 证据,并满足该行预期。
|
||||
- `FAIL`:实际结果违背预期;保留首个真实错误、场景、截图/日志和 commit。
|
||||
- `WAITING_*`:入口已准备但平台尚未执行。
|
||||
- 编译 PASS、静态检查 PASS、另一平台 PASS 均不能替代当前平台 runtime PASS。
|
||||
|
||||
## 10. 验收矩阵
|
||||
|
||||
| 类别 | 场景 | Android | iOS/Metal | 关键证据 | 结论 |
|
||||
|---|---|---|---|---|---|
|
||||
| Topology | Bottom+Continuous+Top | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | A 按钮、range/index、2 cache、画面截图待采集 | NOT RUN |
|
||||
| Topology | Bottom+Continuous | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | B 按钮、Top disabled/资源增量待采集 | NOT RUN |
|
||||
| Topology | Continuous+Top | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | C 按钮、Bottom disabled/资源增量待采集 | NOT RUN |
|
||||
| Topology | Continuous-only | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | D 按钮、valid + 0 cache + 非 fallback 待采集 | NOT RUN |
|
||||
| Range | Single-layer Continuous | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | E 按钮、start=end、endExclusive=start+1 待采集 | NOT RUN |
|
||||
| Z-order | Top covers Particle | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | opaque/semi-transparent/line 视觉截图待采集 | NOT RUN |
|
||||
| Cache | Stable reuse | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | 两次 counter 快照待采集 | NOT RUN |
|
||||
| Invalidation | Pan/Zoom/Rotate/Pitch | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | camera preset 前后 invalid/build/reuse 待采集 | NOT RUN |
|
||||
| Mutation | Continuous internal mutation | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | add/reorder/restore diagnostics 待采集 | NOT RUN |
|
||||
| Mutation | Cross-range move | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | cross layer 三位置的 index/Z-order 待采集 | NOT RUN |
|
||||
| Endpoint | start missing/recovery | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | invalid reason + restore valid 待采集 | NOT RUN |
|
||||
| Endpoint | end missing/recovery | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | invalid reason + restore valid 待采集 | NOT RUN |
|
||||
| Visibility | hidden endpoint topology | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | visible/hidden index 不变待采集 | NOT RUN |
|
||||
| Invalid | missing/reversed range | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | fallback/lastReason/no stale cache 待采集 | NOT RUN |
|
||||
| Fast Path | eligible | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | eligible/active/hit/full counter 待采集 | NOT RUN |
|
||||
| Fast Path | unknown layer fail-closed | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | unsupported reason/full CPU frame 待采集 | NOT RUN |
|
||||
| Alpha | Top premultiplied composite | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | transparent/opaque/semi/line 视觉证据待采集 | NOT RUN |
|
||||
| Stability | repeated operations / long run | WAITING_ANDROID_RUNTIME_VALIDATION | WAITING_IOS_RUNTIME_VALIDATION | 长时间 counter/内存/画面记录待采集 | NOT RUN |
|
||||
|
||||
## 11. 问题、限制与未执行项
|
||||
|
||||
- 未发现需要修改 STEP-092 数学定义、Style 顺序、0/1/2 cache 或 Fast Path 核心模型的架构阻塞。
|
||||
- diagnostics 是累计 counter,不是 per-scene reset;RDC 应使用前后增量。
|
||||
- 资源 release counter 表示 holder 释放事件,不是释放字节数。
|
||||
- Android 场景 Style reload 是可重复的测试手段;跨场景比较必须等待 READY。
|
||||
- 当前 iOS 没有专用 STEP-093 面板,只有 diagnostics API/bridge;这是明确待办,不虚报 iOS runtime 能力完成。
|
||||
- Android/iOS build、Native tests、APK、安装、ADB、真机/模拟器、视觉、长时间运行均 NOT RUN。
|
||||
|
||||
## 12. 下一步
|
||||
|
||||
1. RDC 对 implementation commit `b3ec8edc87ece404b5f7b3706f5acf2a70de6862` 执行 Native 精确回归与 Android build/runtime 矩阵。
|
||||
2. 若 Android 核心矩阵全部实际通过,可更新为 `ANDROID CLOSED / PASS + IOS WAITING_RUNTIME_VALIDATION`。
|
||||
3. 准备 iOS/Metal 对等 validation UI 或可重复操作入口,再独立执行 iOS 矩阵。
|
||||
4. 只有任务书 24 节全部完成、Git 与文档闭环后,才能更新为 `CLOSED / PASS`。
|
||||
66
docs/50_Step记录/STEP-094_通用RenderSegment分段渲染与Style分类诊断_v1.md
Normal file
66
docs/50_Step记录/STEP-094_通用RenderSegment分段渲染与Style分类诊断_v1.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# STEP-094 通用 RenderSegment 分段渲染与 Style 分类诊断 v1
|
||||
|
||||
## 状态
|
||||
|
||||
**PASS / CLOSED(RDC 代码与构建验收完成)**
|
||||
|
||||
Codex 完成实现后,RDC 已独立完成 OpenGL Native 编译与定向 tests、Metal-only iPhoneOS 编译、RN TypeScript/codegen、Android Validation APK 构建与产物自检。规定的代码/编译验收门槛全部通过;Android/iOS 实体机运行与视觉验证本 STEP 未执行,明确保留为 NOT RUN。
|
||||
|
||||
## 起始基线
|
||||
|
||||
- 机器:`Mac-mini-tei.lan`
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab`
|
||||
- 分支:`particle/main`
|
||||
- 起始 HEAD:`8fbb892a1236a55329ed9ef5a612640c9363d3f1`
|
||||
- 起始 `origin/particle/main`:同上
|
||||
- V12 tag:`maplibre-particle/rn/13.4.1-particle.12`(`7eaff6e12`)
|
||||
- 起始工作树:clean
|
||||
|
||||
## 实现结果
|
||||
|
||||
- 新增 typed Core `RenderSegmentPlan` / resolved plan / diagnostics model。
|
||||
- explicit plan、V12 Continuous Range 与 legacy boundary 统一进入一个 resolver。
|
||||
- Style boundary ID 在 RenderOrchestrator 按完整 orderedLayers 解析;hidden Layer 仍参与。
|
||||
- Renderer 改为 topology-sized cache vector;每个 Retained Segment 独立 lazy GPU cache,Continuous 无 retained resource。
|
||||
- Segment 按 Style 顺序执行,global layerIndex/original ordinal/currentLayer/depthRangeSize 保持。
|
||||
- Layer Impl fingerprint 支持明确 retained Segment 内的 layer-local invalidation;Source/Tile 等仍保守全 retained invalidation。
|
||||
- 单 Continuous 保留既有 Particle Fast Path;多 Continuous fail closed 到 full CPU frame。
|
||||
- pull diagnostics 返回完整 Style→Segment→Layer、config/topology revision、coverage 与每个 retained cache 统计。
|
||||
- Android/iOS/RN 沿既有 WeatherRuntimeBridge 增加 plan 与 diagnostics JSON ABI;RN public API 为 typed object/discriminated union。
|
||||
- RN Test Host 增加 `Apply C-R plan` 与 `Fetch Segment Diagnostics`,只在用户操作时拉取一次。
|
||||
|
||||
## 定向测试源码
|
||||
|
||||
在既有 `retained_underlay_cache.test.cpp` 增加 RenderSegmentPlan/Diagnostics tests,覆盖:全 Retained、全 Continuous、C-R-R、R-R-C、R-C-R-C-R、相邻 R 不合并、missing/duplicate/reversed、hidden boundary、插入 Layer membership、boundary 删除/恢复、compat R-C-R/Continuous-only、visibility 不推进 topology revision、Cache OFF diagnostics、完整 coverage/flatten、Continuous cache=null、严格 JSON strategy/schema。
|
||||
|
||||
RDC 已执行过滤集:8 个 suite、43/43 tests PASS;其中 RenderSegmentPlan 9、RenderSegmentDiagnostics 4,并同时覆盖 V12 Continuous/Retained/Fast Path 回归。
|
||||
|
||||
## RDC 验证结果
|
||||
|
||||
- OpenGL:`mbgl-test-runner` 编译 PASS(exit 0);定向过滤集 43/43 PASS。
|
||||
- Metal/iPhoneOS:确认 `MLN_WITH_METAL=ON`、`MLN_WITH_OPENGL=OFF`;`ios-sdk-static` 编译 PASS(exit 0)。
|
||||
- RN package TypeScript:PASS;RN Test Host 使用本工作树 package typecheck PASS。
|
||||
- RN codegen:顶层 `npx react-native codegen` 因工程未安装 `@react-native-community/cli` 无法作为入口;未修改依赖,改用当前已安装 RN 0.85 的 `@react-native/codegen` combine/generate-all 正式底层入口 PASS,并确认 Android Java/JNI、C++ JSI 与 iOS ObjC++ 生成物均含 `setRenderSegmentPlanJson` / `getRenderSegmentDiagnosticsJson`。
|
||||
- Android Validation APK:标准脚本 PASS;OpenglDebug / x86_64 / strip=true;APK 60,294,298 bytes;`libmaplibre.so` 39,421,736 bytes;SHA-256 `848c3196c64cee6dfb1e9627f4af32b6e235fdd82afd1cfb8474c7fc5d591c71`。
|
||||
- Android/iOS 实体机 runtime 与视觉:NOT RUN。
|
||||
- Jest:明确不加入、不执行。
|
||||
|
||||
## RDC 验证重点
|
||||
|
||||
1. Core 编译:重点检查 rapidjson header、RenderLayer Impl metadata、JNI String ABI 与 ObjC++ selector。
|
||||
2. Tests:验证 resolver/diagnostics 新 suites 及 V12 原有 Continuous/Retained/Fast Path suites。
|
||||
3. OpenGL/Metal:确认 N retained resource、相邻 retained 不合并、稳定帧不重复创建。
|
||||
4. RN codegen:确认新增两个 primitive JSON methods 在 Android/iOS generated Spec 与实现 selector 对齐。
|
||||
5. Runtime:拉取 snapshot,核对 `styleLayers` 与 flatten(segment.layers) 完全一致;Cache OFF 仍保留 topology。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 多 Continuous Fast Path 未优化,按任务边界明确 fail closed。
|
||||
- Source/Tile mutation 无安全通用 dependency graph,仍全 retained invalidation。
|
||||
- 旧 diagnostics 仅兼容摘要;新 API 才是多 Segment 权威事实。
|
||||
- 多 Continuous Fast Path 仍按设计 fail closed,不是本 STEP 缺陷。
|
||||
- Android/iOS 真机运行和视觉结果不在本次 PASS 证据内。
|
||||
|
||||
## 下一步
|
||||
|
||||
实现代码已以 `016fd1fff`(`feat: add generic render segment model`)提交并 push。完成报告与本 Step 记录作为独立文档收尾 commit 提交;后续如需产品级 runtime/视觉确认,应另做 Android/iOS 运行态验收,不反向改变本 STEP 的代码验收结论。
|
||||
50
docs/50_Step记录/STEP-095_V13独立严格代码与性能审查_v1.md
Normal file
50
docs/50_Step记录/STEP-095_V13独立严格代码与性能审查_v1.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# STEP-095 V13 独立严格代码与性能审查 v1
|
||||
|
||||
日期:2026-09-25(Asia/Tokyo)。任务:[STEP-095 任务书](../40_任务/STEP-095_V13独立严格代码与性能审查_Codex任务_v1.md)。调查:[独立源码与性能调查](../20_调查/STEP-095_V13独立严格代码与性能调查_v1.md)。报告:[Codex 完成报告](../报告/STEP-095_V13独立严格代码与性能审查_Codex完成报告_v1.md)。
|
||||
|
||||
## 1. 合同、环境与基线
|
||||
|
||||
只读独立审查 V13,先源码后旧报告。仅新增三份指定中文文档并追加开发记录;不修复源码,不执行构建、测试、codegen、打包、安装、设备或 profiler。按 AGENTS 已读当前基线、项目规范、有效总则及 STEP-088~094 相关任务/设计/记录/报告。
|
||||
|
||||
机器 `Mac-mini-tei.lan`,仓库 `/Users/tei/dev/MapLibreParticleLab`,分支 `particle/main`;起始工作树 clean,HEAD 和实际 origin 均 `05878d662308b9a92c20c433c3cd2943d74ae718`。冻结源码 `ee0b89e711bf01335b3aa71d36def785cd97fec3`,功能提交 `016fd1fff6a33cf795c10d4f5c5413d308383677`。RN/Android `13.4.1-particle.13`、iOS `6.28.0-particle.13`;manifest 源码准备提交 `ddf20dec9fe9cd05b77274827b6825829500dd5a` 至冻结点无生产源码变化。冻结点至起始 HEAD 仅有两份文档。
|
||||
|
||||
用户在本次同一会话恢复指令中明确指定 `gpt-6-astra high`,与任务要求一致。记录的是用户指定;没有独立的底层模型/effort 启动参数证明,不声称完成模型独立核验。原 285 行调查稿保留,仅校正模型记录、一个源码路径及补充精确桥接引用;不重写调查、不扩展搜索范围。
|
||||
|
||||
## 2. 本阶段交付
|
||||
|
||||
- 沿 Segment→Runtime→Renderer→GPU、Source→Field Runtime→RenderLayer→GPU 追踪,检查平台桥接、版本锁及 Integration 只读状态。
|
||||
- 新增 N01~N07:跨 target stencil、Metal depth/stencil 契约、失败 build 标成功、V4 PLAY 接线、JSON 严格性、数值入库语义、重复 attach listener。
|
||||
- C01~C07 区分源码成本与运行影响:完整快照、Metal wait、dirty 重分配、预算、线性扫描、全网格物化/UI、drawable 与场复制。
|
||||
- O01 给出旧 cache 计数污染新 topology 的具体链;E01/E02 区分 Host 错配与协调文档历史状态。
|
||||
- F01~F10 独立复核:6 项 CONFIRMED(F01/02/03/04/07/09)、4 项 QUALIFIED(F05/06/08/10)。不把设计限制、历史 TS 失败或计数错归扩大为新实测事故。
|
||||
- 给出 V12/V13 成本模型、缺失测试矩阵及后续修复批次;未创建修复任务,未修改 Integration。
|
||||
|
||||
## 3. 验证与未完成边界
|
||||
|
||||
本轮实际构建、测试、codegen、安装和设备验证全部 NOT RUN。STEP-094 构建/43 tests、旧报告 69 tests 与 TS 结果只列 HISTORY。源码查验不替代运行验证,缺陷触发的实际像素、故障注入、性能/峰值等保留 NEEDS_RUNTIME。
|
||||
|
||||
RDC 优先复核:同 source 同 tiles 的 R-R/R-C stencil;Metal attachment/load/clear;build 失败不得 reuse;只设一次时间后 PLAY 自动推进。其后验证完整 topology、invalid/recovery、30/60 FPS、峰值/长稳、销毁恢复。精确既有命令及证据要求见调查第 9 节;本轮未执行其中任何命令。
|
||||
|
||||
调查内容完成,产品运行态不判 PASS。外部模型参数、产物独立下载/重建、NavSea 实际接入、Vulkan、正式共享海陆 mask 均保留未核实范围,不作能力承诺。
|
||||
|
||||
## 4. Git 收尾
|
||||
|
||||
写稿时调查内容完成,Git 交付待提交/推送,整体暂记 PARTIAL;实际结果在本节追加,未完成 push 不预写 PASS。
|
||||
|
||||
仅四个允许路径:调查、本文、完成报告、根开发记录末尾追加。先验证引用/证据路径和 `git diff --check`,仅具体路径 staged,主提交 message 为 `docs: audit V13 source and performance for STEP-095`,push `origin particle/main`,不移动 tag、不 force push、不向 upstream push。
|
||||
|
||||
提交对象无法在自身正文中预先记录最终 SHA。采用主文档提交/推送后追加已知主提交证据,再提交收尾文档的方式;最后收尾提交 SHA、remote HEAD 和 clean status 在最终回复给出,可用 `git rev-parse HEAD` 与 `git ls-remote origin refs/heads/particle/main` 复核。
|
||||
|
||||
### 文档提交前检查
|
||||
|
||||
- 原调查仍为285行;新增Step/完成报告与开发记录追加齐备。9处相对文档链接、63处带行号源码引用的路径及行号范围检查通过;对校正引用回读源码,未执行产品测试。
|
||||
- 开发记录原内容逐字节保留,仅末尾追加;工作树只含四个允许路径,原暂存区为空。`git diff --check` exit 0;新增文档亦检查无行尾空白,staged后再执行cached检查。
|
||||
|
||||
### 实际主提交与远端回读(收尾事实)
|
||||
|
||||
- 主提交:`ad5f92c48ea353c18548c79d223e9a06888330d3`,message:`docs: audit V13 source and performance for STEP-095`;仅四个允许文档,417行增加,无源码变化。
|
||||
- 提交前 `git diff --cached --check` exit 0;主提交后 `git diff --check` exit 0。一次提交遇到瞬时index.lock,回读时锁已自行消失,重试成功;没有删锁或执行破坏性Git操作。
|
||||
- `git push origin particle/main` exit 0,远端从 `05878d662` 前进至 `ad5f92c48`;随后 `git ls-remote origin refs/heads/particle/main` exit 0,返回完整主提交SHA,与本地HEAD及origin tracking一致。
|
||||
- 主提交推送核对后的 `git status --porcelain=v1` 为空:工作树clean。冻结V13至主提交只含本轮四文档和既有审查/任务两文档,无生产源码、shader、API、依赖、build、manifest或产物变化。
|
||||
- 调查内容及主交付:**PASS**;V13产品运行态仍 **NOT RUN / 未验收**。无构建、测试、安装或设备操作。
|
||||
- 本条和完成报告的结果追加由后续scoped收尾提交保存,message:`docs: record STEP-095 delivery verification`。其自身最终SHA不能预写进自身内容;最终回复记录该收尾提交、再次push/remote核对和clean status。此说明不将尚未发生的第二次push冒充已完成。
|
||||
56
docs/50_Step记录/STEP-096_V13隔离盲审_v1.md
Normal file
56
docs/50_Step记录/STEP-096_V13隔离盲审_v1.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# STEP-096 V13 隔离盲审 v1
|
||||
|
||||
## 状态与范围
|
||||
|
||||
状态:**完成(仅隔离盲审文档交付)**。日期:2026-09-25。
|
||||
|
||||
本阶段按[当前任务书](../40_任务/STEP-096_V13隔离盲审_Codex任务_v1.md)独立读取隔离快照源码和当前规范,形成调查、Step 与完成报告。完成不表示 V13 构建、测试、GPU 画面或性能通过。未做代码修复,后续验证全部 NOT RUN。
|
||||
|
||||
## 输入与可核实环境
|
||||
|
||||
| 项目 | 事实及限制 |
|
||||
|---|---|
|
||||
| 唯一工作目录 | `/Users/tei/dev/MapLibreParticleV13Blind096` |
|
||||
| 机器观察值 | `Mac-mini-tei.lan` |
|
||||
| 目标导出 SHA | `ee0b89e711bf01335b3aa71d36def785cd97fec3`,来自任务书;未用 Git 对象独立核验 |
|
||||
| 版本声明 | [manifest](../../release/maplibre-particle/release-manifest.json#L4):revision 13,RN/Android `13.4.1-particle.13`,iOS `6.28.0-particle.13` |
|
||||
| 产物 sourceCommit 声明 | `ddf20dec9fe9cd05b77274827b6825829500dd5a`,不作为本快照 HEAD |
|
||||
| 当前 branch / Git HEAD / clean / remote | NOT AVAILABLE;本轮未运行 Git |
|
||||
| 实际 TUI、screen、模型和 effort 启动记录 | NOT AVAILABLE;未访问目录外配置、会话日志或终端回放补证 |
|
||||
|
||||
已读取 AGENTS.md、PARTICLE_BASELINE.md、`docs/00_项目规范/`、总则 README 及所列当前有效总则,包括当前基线与开发路线 v2,以及本任务。当前总则中的旧发布坐标与快照 `.13` 声明分开记录,不重写总则或历史。未打开外部路径、历史调查/Step/完成报告、既有审查材料或开发日志;本轮输出目录只用于写入和检查本轮三份新稿。
|
||||
|
||||
## 只读过程
|
||||
|
||||
1. 从公开 API 和目录建立 Style/分段、Renderer/gfx、场 ingress/runtime、平台 bridge 与 GPU 数据路径地图。
|
||||
2. 追踪正常、拒绝、fallback、失效和销毁路径,区分 owner、共享不可变快照与 GPU 资源;对候选寻找反例,不按问题数量补齐。
|
||||
3. 阅读 C++、GLSL、Metal、Android、iOS、RN、测试源码和配置;核对公开 PLAY 调用方、纹理更新条件、attachment 状态、CRC/容量、线程派发与同步点。
|
||||
4. 排除颜色投影仅看第一分量、强引用环、关闭缓存同步遗漏、纯重排遗漏等未成立候选;保留粒子独立前置更新门槛的完整证据。
|
||||
5. 写入三份中文报告;对本轮新稿进行文件存在、相对链接、引用行号和行尾空格的轻量静态检查。未启动构建、测试 runner、设备或 profiler。
|
||||
|
||||
## 结果
|
||||
|
||||
调查形成 **13 条独立 finding**:P1 四条(B01、B02、B06、B13),P2 九条;无 P0。类型为确定缺陷七条、源码确定成本四条、设计限制两条。这里的“确定”限定源码控制流/数值语义,不代表已发生用户事故;全部运行效果 NEEDS_RUNTIME。
|
||||
|
||||
主要正确性证据为跨渲染目标 stencil 记录、Metal depth/stencil clear 请求传递、encoded 播放推进、粒子完整内容身份、8-bit 零值和 Mercator 度量、retained 失败提交。成本和设计限制涉及 Metal 同步等待、资源重复分配、重复物化、UI 同步处理、共享地理 mask 与 store 总预算。每条的条件、精确路径/行号、反例、影响边界、安全修复与验证方案见调查报告。
|
||||
|
||||
## 三份交付文件
|
||||
|
||||
| 文件 | 内容 |
|
||||
|---|---|
|
||||
| [调查报告](../20_调查/STEP-096_V13隔离盲审调查_v1.md) | 架构地图、B01—B13 证据、反例、性能模型、覆盖缺口、验证矩阵、分批建议 |
|
||||
| [本 Step 记录](STEP-096_V13隔离盲审_v1.md) | 范围、输入、只读过程、结果、未运行验证、后续责任 |
|
||||
| [Codex 完成报告](../报告/STEP-096_V13隔离盲审_Codex完成报告_v1.md) | 交付状态、发现索引、证据质量、边界与移交方案 |
|
||||
|
||||
## 未运行验证与阻塞
|
||||
|
||||
- CMake/Ninja/Xcode/Gradle、编译、链接、codegen、打包:**NOT RUN**。
|
||||
- C++/GL/Metal/Jest 测试、设备安装/启动、GPU capture、性能与内存测量:**NOT RUN**。
|
||||
- Git 状态、commit、push、远端核对和产物公网回读:**NOT RUN**。
|
||||
- 外部仓库、上游差异、真实安装环境、历史日志、启动配置:**NOT AVAILABLE**。
|
||||
|
||||
这些是任务约束和证据边界,不阻塞文档交付;它们阻止把本报告升级为运行态或发布验收。未确认 Vulkan 全链、全部图层组合及竞态行为,详见调查覆盖缺口。源码未写入;没有 Git diff 或导出对象逐文件比对证据,不声称已核验工作树 clean。
|
||||
|
||||
## 下一步与停止
|
||||
|
||||
Codex 在三份报告自检后停止,不继续修复、追加开发日志或解盲比较。RDC 随后冻结三份原稿的 SHA-256 与时间,独立移交正式仓库同路径、追加 `粒子开发记录.md`,执行四文件 scoped commit/push 和远端核查;这些动作本轮均未执行。新代码任务优先处理 B01/B02/B06/B13,并补 B12 失败恢复;其余数值/域语义和性能/容量工作按调查报告分批推进。验证场景和已有 runner 的定向执行命令已写入调查报告,交由后续 RDC 阶段执行。
|
||||
81
docs/50_Step记录/STEP-097_V13三轮审查问题合并修复_v1.md
Normal file
81
docs/50_Step记录/STEP-097_V13三轮审查问题合并修复_v1.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# STEP-097 V13 三轮审查问题合并修复 v1
|
||||
|
||||
状态:进行中。目标:按阶段修复三轮审查的当前源码问题,保护 C-001 与既有公开协议。基线:Mac-mini-tei.lan,`/Users/tei/dev/MapLibreParticleLab`,`particle/main`,起始 `720d309364628f85049cf9969d71c71e8dfacce9`,起始工作树 clean,子模块无偏离。当前 `.13` 发布 tag 指向 `ee0b89e71`;不修改发布坐标。
|
||||
|
||||
| 检查点 | 状态 | 实际事实、阻塞和下一步 |
|
||||
| --- | --- | --- |
|
||||
| 阶段 0 基线与反证 | 完成(静态) | 读取三轮审查、STEP-094~096 交付及 C-001/RFC-001 本地副本。当前 HEAD 比任务书基线只新增任务文档;远端 particle/main 经 `git ls-remote` 核对一致。建立 24 项矩阵。Integration 远端同步状态待 RDC 复核。 |
|
||||
| A1 Renderer 正确性 | Android/Core 检查点完成,iOS runtime 用户豁免 | `e67e4961b` 记录 01/02/03/19 的部分代码;19 有 GTest。01/02/03 缺少任务要求的专用可执行像素/附件/故障注入测试入口;且未进行 GL/Metal 编译。故 A1 不判完成,严格停止于此,不进入 A2。 |
|
||||
| A2 Field/粒子正确性 | Android/Core 验证完成;iOS runtime 用户豁免 | 04–07 在当前 GPT-6 Luna High 可见会话实现,RDC 独立验证完成;详见本记录末尾补记及 `docs/报告/STEP-097_A2_阶段验证记录_v3.md`。A2 达到 Android/Core 阶段门槛,不能据此关闭整个 STEP-097。 |
|
||||
| B 输入/生命周期 | 计划 | 尚未开始。 |
|
||||
| C 安全性能 | 计划 | 尚未开始;必须先完成 A1 正确性。 |
|
||||
| D 交付 | 部分完成 | 已写 24 项矩阵、集中验证清单、Step、本轮完成报告和开发记录。所有构建、测试、运行、发布均 NOT RUN;后续 RDC 独立审查。 |
|
||||
|
||||
允许范围与禁止范围按任务书。未完成项目不勾选。RDC 后续执行构建及运行;本轮不运行构建或 Jest。
|
||||
|
||||
## 2026-09-25 RDC/FZG1 A1 APK 实际验证补记
|
||||
|
||||
- 用户授权在 FZG1 而非 Win11 虚拟机测试。Mac RDC 以 HEAD `91c2491b0` 的 clean 基线执行标准 OpenGL x86_64 Validation APK 脚本:Gradle `BUILD SUCCESSFUL in 15m 59s`,APK 60,295,184 bytes,SHA-256 `8677197c5f83a22747cb51f0c261edffc5db4678fc25b3f366474f6ecb94dc99`。
|
||||
- 经 `adb -s 192.168.200.139:5555` 在 Panasonic FZG1-2 覆盖安装 `Success`;设备内安装 APK SHA-256 与产物一致。保存原包备份、20 张截图/UI XML、ADB 操作日志与实时 Logcat;设备实际验证见 `docs/报告/STEP-097_A1_FZG1测试APK与实机验证报告_v1.md`。
|
||||
- R-C-R、R-C、C-R、C-only、single continuous、缓存 OFF/ON、Camera、缺失 START/END 后恢复、TOP_REORDER、跨连续区与 fail-closed 均已做**既有场景冒烟**,未发现本次测试窗口内 FATAL/SIGSEGV/SIGABRT。
|
||||
- B/D 场景 top 不可见而诊断仍 topPresent=true 的现象已记录为待复核,不凭此断言确定像素错误;01 同 tile fixture、02 Metal、03 render exception 注入、19 GTest 仍未完成。因此 **A1 保持阻塞/PARTIAL,A2/B/C 未开始**。此补记更新了之前 Codex 报告中“全部测试 NOT RUN”的后续事实,不追溯修改旧报告。
|
||||
|
||||
## 2026-09-26 RDC 第 19 项 GTest 实跑补记
|
||||
|
||||
- Mac 同一 `particle/main` HEAD `0d13552bed84f427dd43cae9856391bd26622179` 将 `build-step088-opengl/mbgl-test-runner` 增量编译/链接完成:95/95 steps,exit 0,261.74 秒;构建警告记录于原始日志,不影响测试退出状态。
|
||||
- 定向 `RenderSegmentDiagnostics.OldCacheReleaseCannotChargeReplacementAtSameIndex`:**1/1 PASS、exit 0**;相关 `RenderSegmentDiagnostics.*`:**5/5 PASS、exit 0**。测试证明旧 revision 释放不污染新 topology 相同 index 的当前段计数。详细报告:`docs/报告/STEP-097_A1_第19项GTest运行验证报告_v1.md`。
|
||||
- 19 的**已存在 GTest 实测门槛通过**,但不等于 GPU 内存无泄漏或整项资源实机验收;01/02/03 专用测试入口仍缺,A1 依然 PARTIAL,不进入 A2/B/C。本补记覆盖此前 Codex 与 FZG1 报告中的历史 `GTest NOT RUN` 记录,不改写旧报告。
|
||||
|
||||
## 2026-09-26 FZG1 第 19 项设备辅助验收
|
||||
|
||||
- Mac RDC 确认 APK 源码 `91c2491b0` 到当前 `237a3e660` 只改变文档,复用原 OpenGL x86_64 APK;在 FZG1-2(ADB `192.168.200.139:5555`)执行 `install -r` 得 `Success`,设备内 APK SHA-256 `8677197c5f83a22747cb51f0c261edffc5db4678fc25b3f366474f6ecb94dc99` 与源文件相同。
|
||||
- 两轮 A/B/C/D/E/A 拓扑循环及 TOP_REORDER、CROSS_CONTINUOUS、CACHE OFF/ON、缺失 START 后恢复完成:20 张 1920×1200 截图与 20 份 UI XML;18 个有效检查点 `valid=true/fallback=false`、预期无效检查点 fail-closed。B/T `textureCreate-resourceRelease` 在现有诊断的 19 个场景均为 0 或 1;实时日志无 FATAL/SIGSEGV/SIGABRT。
|
||||
- 设备辅助冒烟 **PASS**,前述 19 GTest **1/1 PASS**;没有 on-device GTest、逐 revision GPU 内部观测或显存泄漏证明。B/D `topPresent=true` 与视觉不一致的既有疑点仍待单独核查;01/02/03 专项测试未闭合,A1 整体继续 PARTIAL。报告:`docs/报告/STEP-097_A1_第19项FZG1运行验证报告_v1.md`。
|
||||
|
||||
## 2026-09-26 Codex 继续 A1 测试入口
|
||||
|
||||
- 新增 Android 同源同 tile 的 R-R/R-C-R/C-R-R 本地 fixture 与诊断菜单;新增 Metal 附件/clear GTest 源码;新增 LayerGroup 正反范围异常、observer 与 cache 失败后重试 GTest 源码。生产 Metal clear 逻辑抽成仅内部调用的辅助函数,使测试检查同一路径,不调整公开协议。
|
||||
- 本轮只做静态检查;构建、GTest、Jest、设备像素、Metal validation 均 **NOT RUN**。01/02/03 仍需 RDC 编译与设备验证;A1 保持 PARTIAL,A2/B/C 未启动。第 19 项原有 RDC 1/1 PASS 不受影响。
|
||||
|
||||
## 2026-09-26 A1 收口,允许进入 A2
|
||||
|
||||
- A1 新 Android APK 源码 `8c006ac83`,构建/安装/设备内 SHA-256 复核成功;FZG1 18 个同源同 tile RR/RCR/CRR 检查点、中心/区域外像素采样均一致,脚本 exit 0,实时日志无崩溃。03 原生异常恢复 GTest 2/2 PASS;19 已有 GTest 1/1 和诊断组 5/5 PASS。
|
||||
- Metal host 可编译,但无 Metal device,两项测试均 SKIP;用户明确不做 iOS runtime,本轮验收按用户决定接受,不能记为 iOS 实测 PASS。B/D 的 topPresent 历史疑点保留;详见 `docs/报告/STEP-097_A1_Android专项验收报告_v1.md`。
|
||||
- A1 Android/Core 阶段完成;下一步严格 A2→B→C,每阶段保留独立 RDC 构建及验证。STEP-098 未启动、未提交;`.13` 发布冻结不动。
|
||||
|
||||
## 2026-09-26 A2 RDC 中途验证,尚未进入 B
|
||||
|
||||
- Codex GPT-6-Sol medium 在 A2 四项范围内写入生产/测试源码但触及当期使用额度,未完成最终审查/提交。RDC OpenGL 原生编译 exit 0、16/16 定向 GTest PASS;Shader generator 双次 exit 0/幂等;Metal host 源码编译 exit 0、同样 16/16 Core GTest PASS,**没有 iOS runtime**。
|
||||
- A2 仍 PARTIAL;完整差异和构建/测试日志位于 `artifacts/maplibre-particle/step097-a2-rdc/`,中文事实报告:`docs/报告/STEP-097_A2_阶段验证记录_v1.md`。B/C 均未启动;用户未跟踪 STEP-098 与已发布 `.13` 未动。已安排 Codex 额度恢复后严格串行继续。
|
||||
|
||||
## 2026-09-26 A2 Core/Android 最终验证收口(B/C 未启动)
|
||||
|
||||
- Codex 配置:GPT-6 Luna High,可见会话。仓库 `/Users/tei/dev/MapLibreParticleLab`,`particle/main`,验证基线 HEAD `5bb1a0e6353c5dfe8db8a61bbeba9cef447280ef`;A2 源码保持 dirty、未提交。Integration `main` 已有报告基线 `318f8ccf911ac3136c3086a1d14e61f83e2d4bce`,C-001 未改。
|
||||
- 04–07 完成:唯一 Core forecast clock 加 synchronous notification reentry guard;严格 GPU endpoint identity 与 mix-only immutable snapshot reuse;内部 RGBA8 signed zero/byte 解码;Mercator U/V 同纬度度量。旧 `sameFieldGpuContent` 的语义未改变,修正了与其 version-agnostic payload 等价定义相冲突的旧测试断言。新增 source 回归覆盖 50 次 mix ticks 不重物化、endpoint/revision 改变触发 materialize、clear fail-closed 与重入单 tick。
|
||||
- RDC 独立结果:原 58/58 完整回归 PASS;当前 broad filter OpenGL 121/121 与 Metal host 121/121 PASS;三项 A2 定向测试两平台各 3/3 PASS;shader generator 连跑两次幂等。Metal host 仅 Mac Core runner,不是 iOS 设备验证。
|
||||
- 标准 Android OpenGL Debug x86_64 APK `61,753,803` bytes,SHA-256 `27bf4a6cc191bd86c4d1c10398478d81c2999edd61fe7242e40e5188516b0ca5`;已安装到 FZG1-2(`192.168.200.139:5555`),设备 APK hash 相同。V4 Golden Wind test-only fixture 覆盖 load、seek 25/75、speed 2x/1x、PLAY 30 秒、responsive UI、PAUSE、stable pause、replay、clear、reload、再次 PLAY/PAUSE;runner exit 0、17 PNG、24 steps、ANR=0。15 秒截图可见粒子。
|
||||
- Mix-only 计数按两个稳定资源 epoch 记录:clear/reload 前 upload/reset=`6/2` 稳定;clear/reload 后合法增至 `10/4`,reload 后 playback 稳定。不能说跨 clear/reload 零上传或零 reset。APK source diff SHA-256 与 `mix-reuse-build-source.diff` 一致:`18927a5f30a88f5c1c3a00e0017473dc33b04151c227e4baf52250dc4ad2e6a2`。
|
||||
- 历史异常保留:12:43 首次 FZG1 ANR 根因为 source notification 同步重入 forecast tick,当前 `forecastTickInProgress` 与 `RasterArrayV4ClockSourceNotificationReentryDoesNotAdvanceTwice` 覆盖此项;后续有过无 ANR 但主线程高负载、PLAY/TIME 无响应的失败试跑,栈指向 forecast tick 内重复投影物化。最终 mix reuse APK 通过本次闭环。历史 debuggerd、失败试跑和最终日志均保留在 `artifacts/maplibre-particle/step097-a2-final-validation/`。
|
||||
- A1 与 A2 的 Android/Core 阶段检查完成;iOS runtime 明确按用户决定记 `USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`,不写成 iOS PASS。STEP-097 整体仍 PARTIAL:B/C 未开始,Jest 未运行,STEP-098 未运行,`.13` 未发布/覆盖。Git commit/push 留给 RDC 独立 QA 后执行。
|
||||
|
||||
### 2026-09-26 A2 源码独立 scoped commit 核验
|
||||
|
||||
- A2 的 26 个生产源码、shader、TestApp-only V4 Golden fixture 和定向测试文件已固定为 `f49ffa68a17e0a505199bdbeccf07f8543f3a05f` 并推送,`origin/particle/main` 回读一致;没有 stage 用户未跟踪 STEP-098。完整代码/设备证据仍见 `docs/报告/STEP-097_A2_阶段验证记录_v3.md`,不因 Git 提交改变原 APK 的 `git_dirty=true` 来源事实。STEP-097 整体 PARTIAL,B/C 待用户指令。
|
||||
|
||||
## 2026-09-26 B 阶段源码、Core 复核与 Native 版本阻塞(未收口)
|
||||
|
||||
- Codex 两次自动任务(Android 第 10 项销毁监听、旧数值夹具纠正)均使用 `codex exec --json` + GPT-6 Luna High,均有 `turn.completed` 和 exit 0,JSONL/status.json 已持久化于 `artifacts/maplibre-particle/step097-b10-codex/` 与 `step097-b-fixture-codex/`。
|
||||
- 08 N05:长度感知 JSON/NUL/重复键/非法配置清除与恢复测试入口已实施;09 N06:提交前有限数值验证和旧有效修订保护已实施;10 N07:View owner、世代隔离与新 Map 销毁 observer 测试源码已实施;20 E01:Host package/lock 均指向正式 .13,正式 tarball SHA-256 与 SRI 已核对。
|
||||
- RDC Mac Core 构建 PASS,四项 B 定向 GTest 4/4 PASS;broad 初次 132/134,真实原因是旧四单元 16-bit 测试借用了 4-byte UInt8 storage;仅修为合法 8-byte little-endian fixture 后复编与扩大回归 **134/134 PASS**,未放宽生产校验。
|
||||
- 硬阻塞:B10 新 `MapLibreMap.addOnDestroyListener()` 不存在于已发布 .13 source commit `ddf20dec9`,现有 RN Gradle 默认正式 .13 AAR 无法提供该符号;本地新源码 Android 测试不能替代正式 RN consumer 精确配对构建。禁止借此修改 .13 发行产物。
|
||||
- Linux Android 采用远端 `b1239ac3b` 新独立 worktree,并从 Mac 传入 B scoped patch/测试,原 Linux dirty 工作区未动。首次 Gradle 仅因工作树缺少 ANDROID_HOME 失败,按既有 SDK 路径重试;结果见 B 专项报告的后续记录。
|
||||
- 当前 B = PARTIAL / BLOCKED_NATIVE_AAR_COMPATIBILITY,尚未 scoped commit/push;C 与 STEP-098 均不启动,iOS runtime 保持用户豁免。详见 `docs/报告/STEP-097_B_独立验收与版本阻塞报告_v1.md`。
|
||||
|
||||
### 2026-09-26 B 阶段 Mac RDC 最新核验(仍阻塞)
|
||||
- B10 `codex exec --json` 终态 `turn.completed`、exit 0,完整 JSONL/status.json 持久化;旧 16-bit fixture 纠正任务亦同。RDC 复编后 B 定向 6/6、扩展 139/139 PASS。
|
||||
- Android `MapLibreMapDestroyListenerTest` 2/2 PASS,Native Java 编译 exit 0;隔离 Kotlin owner 4/4 PASS,均非默认 RN consumer 集成验收。日志和 XML 见 `artifacts/maplibre-particle/step097-b-rdc/`。
|
||||
- .13 release source 无新 `MapLibreMap.addOnDestroyListener()`,新 RN Kotlin 源码默认依赖 .13 AAR;B 保持 `PARTIAL / BLOCKED_NATIVE_AAR_COMPATIBILITY`。Linux 隔离 Gradle 仍待最终状态;未启动 C、STEP-098,也未修改 .13 发布资产。
|
||||
|
||||
### 2026-09-26 B scoped Git 阻塞检查点
|
||||
- Mac RDC 在独立 worktree 对 21 个 B 文件逐项 SHA-256 一致检查后,提交并推送 `step097-b-blocked-20260926`:`e463ba04ce956a54eed02b77f981eeeedd9a5534`,远端回读一致;`particle/main` 仍为 `b1239ac3b`,原 dirty/untracked(含 STEP-098)未动。
|
||||
- 该提交只冻结 PARTIAL/BLOCKED 的源码及证据,不是 B PASS;C 与 STEP-098 仍禁止启动,.13 发布资产不变。
|
||||
137
docs/50_Step记录/STEP-098_QBIN_V4单帧与八帧滚动缓存_v1.md
Normal file
137
docs/50_Step记录/STEP-098_QBIN_V4单帧与八帧滚动缓存_v1.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# STEP-098 QBIN V4 单帧与八帧滚动缓存
|
||||
|
||||
状态:**进行中(Codex 源码交付;等待 RDC 独立构建/定向测试及平台验收)**
|
||||
日期:2026-09-26(JST)
|
||||
|
||||
## 目标与基线
|
||||
|
||||
- 目标:单帧真实时刻首显、合法同批双帧插值、每 `fieldKey` 最多八帧有序滚动,保留现有 RN API 两步调用。
|
||||
- 仓库:Mac `/Users/tei/dev/MapLibreParticleLab`;branch `particle/main`;开工 HEAD `b1239ac3b1554943200e6fbf14321459ccc86527`,结束时 HEAD 未变。
|
||||
- Integration:`main` / `origin/main`=`1dd576d3304e267e53ed64d31f9711d2350ce10a`;RFC-004、ADR-001、C-002、COORD-005 均已读。旧 RFC-003 阻塞由本轮明确的新接受契约解除;旧阻塞报告保留不改。
|
||||
- 当前 Android Native/RN 坐标仍为 `13.4.1-particle.13`;本次未改版本或发布资产。iOS 基础版本 `6.28.0`。
|
||||
- 工作树开工已有 STEP-097 B 修改/未跟踪文件、STEP-098 原任务与旧阻塞报告;保留原样。`git submodule status` 全部与记录 SHA 一致。
|
||||
|
||||
## 本轮完成
|
||||
|
||||
- `FieldSeries` 在 exact validTime 返回真实单帧窗口;非精确时间只解析本地相邻 pair。
|
||||
- 按 key 区分共存 schema 与插值批次身份。新批次可进入滚动序列并按精确时间单帧显示;插值要求 fieldID、reference time、resolved profile ID/version 与 grid/plane schema 全部相同。Ingress 保存已验证 profile 身份。
|
||||
- 满额候选集合先按新帧时间选择远端点,等距删除原最早端;通过 schema 邻接校验后原子 swap。拒绝帧不先淘汰。八帧以上正常插入使用既有成功结果。
|
||||
- 新增 decoded raw-plane + validity 字节计数;单帧 T0/T1 共享一份 materialized Snapshot,WeatherColor/Particle 首次 GPU 安装共享相同 endpoint 资源。
|
||||
- 扩展 Runtime、Binding、Ingress、RasterArraySource 定向测试代码,覆盖单帧/双帧/滚动/跨批次/原子拒绝/内存计数/重复清理场景;详见设计和完成报告矩阵。
|
||||
- RN TypeScript、Android Kotlin/JNI、iOS bridge 静态核对:旧 API 名称、参数、返回类型和两步顺序不变;未修改 bridge。
|
||||
- 新增中文设计文档与本报告;开发记录仅追加本轮事实。
|
||||
|
||||
## 修改范围
|
||||
|
||||
STEP-098 本轮源代码与测试:
|
||||
|
||||
- `include/mbgl/particle/field/field_runtime.hpp`
|
||||
- `src/mbgl/particle/field/field_runtime.cpp`
|
||||
- `src/mbgl/particle/field/field_projection_materialization.cpp`
|
||||
- `src/mbgl/particle/ingress/qbin_v2_decoder.cpp`
|
||||
- `src/mbgl/renderer/layers/render_particle_validation_layer.cpp`
|
||||
- `src/mbgl/renderer/layers/render_weather_color_layer.cpp`
|
||||
- `test/particle/field_binding.test.cpp`
|
||||
- `test/particle/field_runtime.test.cpp`
|
||||
- `test/particle/qbin_v2_decoder.test.cpp`
|
||||
- `test/source/raster_array_v4_host_api.test.cpp`
|
||||
|
||||
文档/记录:
|
||||
|
||||
- `docs/30_设计/STEP-098_QBIN_V4单帧与八帧滚动缓存设计_v1.md`
|
||||
- `docs/50_Step记录/STEP-098_QBIN_V4单帧与八帧滚动缓存_v1.md`
|
||||
- `docs/报告/STEP-098_QBIN_V4单帧与八帧滚动缓存_Codex完成报告_v1.md`
|
||||
- 根目录 `粒子开发记录.md`(追加)
|
||||
|
||||
未修改用户原任务书、STEP-097 的矩阵/记录/清单/桥接文件、NavSea/Server、QBIN 格式、版本锁、tag、manifest、制品。`test/particle/field_runtime.test.cpp` 与 `test/source/raster_array_v4_host_api.test.cpp` 开工前已有 B 阶段 dirty 内容;原 B 的 fixture 修正保留,本轮只叠加 STEP-098 测试,并为 Source 测试 QBIN helper 增加 forecast reference 参数。
|
||||
|
||||
## API / ABI 与限制
|
||||
|
||||
- RN 和平台 bridge 公开方法签名/枚举名称:未变。
|
||||
- `DecodedFieldFrame` 增加了解析 profile 身份字段,C++ 结构二进制布局变化;Native/RN 配套版本必须同源码重编。没有用 `.13` 旧 AAR 验证该结构。
|
||||
- `decodedByteSize()` 只计原始 plane storage 与 validity bitset,不含 profile 元数据、CPU 物化数组、图像、GPU texture、粒子/trail。没有执行 CPU/GPU 峰值或长时间泄漏测量。
|
||||
- 单帧 Particle 的实际 GPU 运动、GL/Metal texture 复用、context loss 和设备视觉尚待平台实测。
|
||||
|
||||
## RDC 分阶段执行命令(本轮未运行)
|
||||
|
||||
### B:Shared Core Store / Ingress / Binding
|
||||
|
||||
```bash
|
||||
ninja -C build-step088-opengl mbgl-test-runner
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='ParticleFieldRuntime.*:ParticleFieldBinding.*:ParticleEncodedFieldIngress.*'
|
||||
```
|
||||
|
||||
### C:Projection / Source / Renderer 与 OpenGL、Metal host 回归
|
||||
|
||||
```bash
|
||||
ninja -C build-step088-opengl mbgl-test-runner
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='Particle*.*:Source.RasterArray*'
|
||||
ninja -C build-step097-metal-host mbgl-test-runner
|
||||
build-step097-metal-host/mbgl-test-runner --gtest_filter='Particle*.*:Source.RasterArray*'
|
||||
```
|
||||
|
||||
先完成 B 并检查结果,再执行 C;Metal host GTest 不等于 iOS 设备 Metal 通过。未通过/退出未知时停在原阶段。Codex 本轮只运行 `git diff --check`,未运行上述命令、Android Gradle、iOS/Xcode、Jest、设备或发布。
|
||||
|
||||
### D:RN / 平台验证前置条件
|
||||
|
||||
RN 端确认现有顺序为成功 await `submitEncodedField(...)` 后才 await `setEncodedForecastTime(...)`;失败提交不得调用 setTime。T17 使用 V15 同源码 Native/RN 配套构建和 Host 手工验证,不能用现有 `.13` AAR 替代。
|
||||
|
||||
Windows 11 用户手工验收(待相同 V15 Test Host APK、RN 包与 Native AAR 可用后):
|
||||
|
||||
```powershell
|
||||
cd D:\React\MapLibreParticleRNTestHost-V4
|
||||
npm install <RDC提供的同版本V15 RN package tgz路径>
|
||||
npx expo prebuild --platform android
|
||||
cd android
|
||||
$env:ANDROID_HOME='C:\Users\taibe\AppData\Local\Android\Sdk'
|
||||
$env:ANDROID_SDK_ROOT=$env:ANDROID_HOME
|
||||
.\gradlew.bat :app:assembleDebug
|
||||
adb -s 192.168.200.139 install -r <同源码V15 Test Host APK路径>
|
||||
adb -s 192.168.200.139 shell am force-stop org.maplibre.particle.rntesthost
|
||||
adb -s 192.168.200.139 shell am start -n org.maplibre.particle.rntesthost/.MainActivity
|
||||
```
|
||||
|
||||
在 Host 可复用的本地 QBIN 场景中一次完成:首帧单帧显示/粒子持续运动、标量无粒子、第二帧中点 mix、乱序 8 帧、未来/过去/内部插入与淘汰、跨 reference/profile 日切首帧及回退、duplicate/revision/stale/损坏帧、wind/wave key 隔离、当前 endpoint 淘汰清场、100+ 次滚动及 clear。逐项记录真实请求时间与实际显示 validTime、T0/T1/mix、提交结果、异常日志、进程内存趋势。ADB 全部显式指定 `192.168.200.139`;不得将 Test Host 冒充 NavSea 验收。
|
||||
|
||||
iOS 由 Mac 用户使用同一冻结源码和配套产物手工构建;在 Metal 设备验证单帧首显、单帧向量运动、双帧 mix、跨批次 unavailable、双向淘汰、key 隔离、坏帧不覆盖好投影、clear/context 恢复及长时间内存趋势。Metal host runner 结果不能替代该设备证据。
|
||||
|
||||
## T01–T17 状态
|
||||
|
||||
测试覆盖映射见设计文档 §6。以下“代码覆盖”仅说明测试源码中存在场景;**实际运行均为 NOT RUN**。
|
||||
|
||||
| 项 | 代码覆盖状态 | 实际运行 |
|
||||
| --- | --- | --- |
|
||||
| T01 | 已覆盖 | NOT RUN |
|
||||
| T02 | 已覆盖 | NOT RUN |
|
||||
| T03 | 已覆盖 | NOT RUN |
|
||||
| T04 | 已覆盖 | NOT RUN |
|
||||
| T05 | 已覆盖 | NOT RUN |
|
||||
| T06 | 已覆盖 | NOT RUN |
|
||||
| T07 | 已覆盖 | NOT RUN |
|
||||
| T08 | 部分覆盖;现有 revision/stale 入口 + 满额分支,尚无单一 T08 全覆盖测试 | NOT RUN |
|
||||
| T09 | 部分覆盖;profile/decode/numeric/grid 拒绝及原子性,错误向量仍由既有 binding 测试覆盖 | NOT RUN |
|
||||
| T10 | 部分覆盖;pair mix 0.25/0.5/0.99 已覆盖,精确 T1 以单帧表示 | NOT RUN |
|
||||
| T11 | 已覆盖 | NOT RUN |
|
||||
| T12 | 已覆盖 | NOT RUN |
|
||||
| T13 | 已覆盖 | NOT RUN |
|
||||
| T14 | 已覆盖 | NOT RUN |
|
||||
| T15 | 部分覆盖;materialization/renderer 源码有覆盖,GPU/context loss 未运行 | NOT RUN |
|
||||
| T16 | 部分覆盖;120 次 Core Store 双向滚动/clear 及 byte count 已覆盖,Runtime 切换/GPU 泄漏未运行 | NOT RUN |
|
||||
| T17 | 部分覆盖;bridge 静态检查,自动化成功/失败序列与配套消费构建未运行 | NOT RUN |
|
||||
|
||||
## 当前结果与下一步
|
||||
|
||||
- 当前结果:**源码已实施,等待 RDC 阶段 B 独立构建和测试;没有 PASS 结论。**
|
||||
- `git diff --check`:通过(只代表空白/patch 格式检查)。
|
||||
- 未完成:所有新增 C++ 测试执行、OpenGL/Metal host 构建、Android/RN 同版本配套构建、iOS/Metal 设备验证、窗口拖动/跨天/内存实测、V15 pipeline、commit/push。
|
||||
- 剩余阻塞:当前仅为 RDC 尚未运行本轮实现;`.13` 不能与新增 C++ Frame layout 配对,需后续同源码新版本构建。
|
||||
- 下一责任:RDC 按 B→C 顺序执行并反馈真实日志;Android、iOS 设备与 Test Host 验收在后续配套阶段完成。当前停止于 Codex 源码交付,不进入 V15 pipeline。
|
||||
|
||||
|
||||
## 2026-09-27 RDC 独立构建与定向测试补充(覆盖“本轮 NOT RUN”的后续事实)
|
||||
|
||||
- Codex `turn.completed` / exit 0 后,RDC 先执行 B;初次 runner 无 ninja PATH、Source 测试访问 private store、两处 fixture Grid 不合法,均已按首个真实错误做定向修复,记录见 `docs/报告/STEP-098_RDC定向验收报告_v1.md`。
|
||||
- B OpenGL Core/Binding/Ingress 编译 PASS,19/19 GTest PASS。
|
||||
- C1 OpenGL Host 扩展 `Particle*.*:Source.RasterArray*` 119/119 PASS。
|
||||
- C2 Metal Host 编译 PASS,同 filter 119/119 PASS。
|
||||
- 源级测试不等于 Android RN consumer/iOS 真机 GPU、内存/交互通过;V15 Pipeline 此时尚未启动。下一关必须为同源码发布构建和分端实测。
|
||||
175
docs/50_Step记录/STEP-099_RenderSource就绪与RenderSegment失效诊断_v1.md
Normal file
175
docs/50_Step记录/STEP-099_RenderSource就绪与RenderSegment失效诊断_v1.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# STEP-099 RenderSource 就绪与 RenderSegment 失效诊断
|
||||
|
||||
状态:**CLOSED / PASS**
|
||||
日期:2026-10-06(JST)
|
||||
|
||||
## 基线
|
||||
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab-step099`
|
||||
- 分支:`step099-render-source-diagnostics`
|
||||
- frozen release:`maplibre-particle/rn/13.4.1-particle.17`
|
||||
- frozen release commit:`e8b451fe70054107773e82484c80e90df3889147`
|
||||
- implementation commit:`8a5214d92dd2f8899db76d2194da42999fa0c87d`
|
||||
- Integration/NavSea:未修改。
|
||||
|
||||
## 本 Step 完成内容
|
||||
|
||||
- Renderer readiness snapshot additive 暴露 style/image manager/render orchestrator/sprite readiness、fading、global content revision、per-source 状态和最近 mutation。
|
||||
- per-source diagnostics 暴露 sourceId/type/enabled/isLoaded;tile-backed source 提供 tileCount/incomplete/loaded/fading。
|
||||
- RenderSegment diagnostics 只读推导 `dependentSourceIds` / `unknownDependencies`;source-less layer 不制造 source dependency,共享 source 可出现在多个 segment。
|
||||
- mutation sequence/source/tile action/reason 只消费既有 renderer 事件;正式 revision 增长逻辑不变。
|
||||
- 正式 loaded 判定保持:
|
||||
`renderTreeParameters->loaded = updateParameters->styleLoaded && isLoaded();`
|
||||
- diagnostics snapshot 不进入正式 loaded/cache/fast path/segment execution。
|
||||
- RN 仅增加 diagnostics 类型字段;没有 NavSea 私有 API,没有改变业务 API。
|
||||
- Android Test App 提供 T1 STATIC、T2 GEOJSON_1HZ、T3 PERMANENT_UNREADY、T4 R-C-R。
|
||||
|
||||
## Native 验证
|
||||
|
||||
Metal host `RenderSegmentDiagnostics.*`:
|
||||
|
||||
- 6/6 PASS;
|
||||
- 新增 `ReadinessSnapshotMapsSourcesToSegmentsWithoutSideEffects` PASS;
|
||||
- 覆盖 source readiness、tile stats、source→segment、shared source、source-less 与重复 query 无副作用。
|
||||
|
||||
最终源码再次尝试 Metal configure 时当前 Mac 缺 `libuv` pkg-config,因此第二次 host rerun 未进入 build;不虚报 PASS。最终代码已经被 Android 全量 native build 成功编译并由 T1~T4 运行覆盖。
|
||||
|
||||
Vulkan:因当前 Mac 缺 `libuv`,NOT RUN tests。
|
||||
|
||||
Jest:NOT RUN,用户未要求。
|
||||
|
||||
## Android 构建
|
||||
|
||||
首轮 Android NDK 暴露:
|
||||
|
||||
```text
|
||||
error: missing field 'tileCount' initializer
|
||||
[-Werror,-Wmissing-designated-field-initializers]
|
||||
```
|
||||
|
||||
只补全 `RenderSourceReadinessStats` 默认空字段显式初始化。
|
||||
|
||||
随后 native build 通过,但 strip 阶段因 Mac 磁盘仅剩约 2 GiB:
|
||||
|
||||
```text
|
||||
No space left on device
|
||||
```
|
||||
|
||||
仅清理可再生 Gradle 9.5.1 cache / 失败中间产物后恢复约 12 GiB,再次增量构建:
|
||||
|
||||
- `BUILD SUCCESSFUL in 4m 54s`
|
||||
- 86 actionable tasks:7 executed / 79 up-to-date
|
||||
- OpenGL Debug / x86_64
|
||||
- APK:61,923,024 bytes
|
||||
- libmaplibre.so:39,602,856 bytes
|
||||
- SHA-256:
|
||||
`5de73f0358b835a78d3ae7e2ac22f4efdb2a66775b8c63654a8bb7313957d90a`
|
||||
|
||||
设备:
|
||||
|
||||
- `192.168.200.139:5555`
|
||||
- FZG1-2
|
||||
- Android 13 / API 33 / x86_64
|
||||
|
||||
## T1~T4 结果
|
||||
|
||||
### T1 STATIC
|
||||
|
||||
- 1 个启动 false window;
|
||||
- avg/max = 204 ms;
|
||||
- 随后 global loaded 稳定为 true;
|
||||
- retained cache 可持续 reuse;
|
||||
- fast path 可进入 active。
|
||||
|
||||
### T2 GEOJSON_1HZ
|
||||
|
||||
- 两个 GeoJSON source 交错更新,总 update 122 次,约 61 次/源;
|
||||
- 10 个 loaded=false window;
|
||||
- avg 203 ms;
|
||||
- max 205 ms;
|
||||
- open 0;
|
||||
- 约 60 s 中 false 总时长约 2.03 s,约 3.4%。
|
||||
|
||||
结论:存在 readiness thrash,但不是“几乎永远 false”。
|
||||
|
||||
### T3 PERMANENT_UNREADY
|
||||
|
||||
`step099-unready-raster-array`:
|
||||
|
||||
- 无 layer;
|
||||
- enabled=false;
|
||||
- isLoaded=false;
|
||||
- 其他主要 source loaded。
|
||||
|
||||
结果:
|
||||
|
||||
- global renderOrchestratorLoaded=false 持续;
|
||||
- 观察结束 open false window = 21,459 ms;
|
||||
- retained cache 持续 style-not-loaded fallback。
|
||||
|
||||
结论:单个与 segment 无关的 permanently unready source 足以拉低 V17 全局 loaded。
|
||||
|
||||
### T4 R-C-R
|
||||
|
||||
- R0 deps=[step099-static]
|
||||
- C deps=[particle-fixture]
|
||||
- R1 deps=[org.maplibre.annotations, step099-dynamic, step099-static]
|
||||
- dynamic update:61 次
|
||||
|
||||
结果:
|
||||
|
||||
- loaded false windows:4
|
||||
- avg 203 ms
|
||||
- max 207 ms
|
||||
- contentRevision:1760 → 2540
|
||||
- diagnostics 对 dynamic source 正确给出 affected=[2]
|
||||
- R0 不依赖 dynamic source,但:
|
||||
- build 1 → 121
|
||||
- invalidation 20 → 200
|
||||
|
||||
结论:dependency diagnostics 已能表达 source-local 影响,但 V17 正式 content revision/cache invalidation 仍为 global。
|
||||
|
||||
## Q1~Q7
|
||||
|
||||
1. Q1:**YES**。permanently unloaded source 会造成全局 style-not-loaded。
|
||||
2. Q2:**YES**。1 Hz GeoJSON 会产生约 200 ms readiness false window;T2 avg 203 / max 205 ms。
|
||||
3. Q3:**NO(本次观测)**。双 1 Hz source 并未让 global loaded 几乎始终 false。
|
||||
4. Q4:**YES**。GeoJSON tile mutation 推进 global content revision,并使 retained cache 重建/失效。
|
||||
5. Q5:**YES**。不引用动态 GeoJSON 的 R0 仍受 global revision 影响;源码还确认 `retainedRangeIsStable` 全局检查所有 source fading。
|
||||
6. Q6:fast path 仍有 global update signature(sources/layers/images 等)、global content revision、global fade、pendingChanges、placement/light/non-particle transition/repaint、annotation mutation、retained revision/cache/topology/boundary,以及 singleContinuous gate。
|
||||
7. Q7 blocker:
|
||||
- runtime confirmed:`PERMANENT_SOURCE_UNLOADED`、`DYNAMIC_SOURCE_READINESS_THRASH`、`GLOBAL_CONTENT_REVISION`
|
||||
- source confirmed:`GLOBAL_FADE`、`GLOBAL_UPDATE_SIGNATURE`
|
||||
- OTHER:pending/placement/light/repaint/annotation/topology/boundary/singleContinuous。
|
||||
|
||||
## 证据
|
||||
|
||||
忽略目录:
|
||||
|
||||
`artifacts/maplibre-particle/android-validation/`
|
||||
|
||||
- `build-info.txt`
|
||||
- `build.log`
|
||||
- `MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk`
|
||||
- `step099-logcat.txt`
|
||||
- `step099-device-info.txt`
|
||||
- `step099-metal-gtest.log`
|
||||
|
||||
STEP-099 log SHA-256:
|
||||
|
||||
`0f15d75dde77e7dd2f4d98c46a4c1501a628205e8aae62130b0cb96db6b8482f`
|
||||
|
||||
## 边界结论
|
||||
|
||||
本 Step 未修改:
|
||||
|
||||
- retained cache 算法;
|
||||
- fast path gate;
|
||||
- singleContinuous / multi-continuous;
|
||||
- segment-local invalidation 正式语义;
|
||||
- RenderSegment execution;
|
||||
- NavSea;
|
||||
- Source isLoaded;
|
||||
- RN 业务 API。
|
||||
|
||||
下一阶段应拆分处理 global source readiness、source/segment-local content revision/invalidation,再独立处理 fast-path global gates。
|
||||
132
docs/50_Step记录/STEP-100_SourceScoped_RenderSegment局部失效_v1.md
Normal file
132
docs/50_Step记录/STEP-100_SourceScoped_RenderSegment局部失效_v1.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# STEP-100 SourceScoped RenderSegment 局部失效记录 v1
|
||||
|
||||
状态:**CLOSED / PASS(Android runtime)**
|
||||
日期:2026-10-06
|
||||
|
||||
## 基线
|
||||
|
||||
- worktree:`/Users/tei/dev/MapLibreParticleLab-step100`
|
||||
- branch:`step100-segment-local-source-invalidation`
|
||||
- start HEAD:`c4b481fd2c5b5da20d31f6ae54011f0552d7dbd0`
|
||||
- V17 tag:`maplibre-particle/rn/13.4.1-particle.17`
|
||||
- V17 frozen commit:`e8b451fe70054107773e82484c80e90df3889147`
|
||||
|
||||
## 实现
|
||||
|
||||
- Source/Tile mutation 增加 source-scoped revision;
|
||||
- global retained content revision 继续保留;
|
||||
- production renderer 与 diagnostics 共用 source→segment dependency;
|
||||
- RETAINED Segment 独立 readiness / stability / Build / Reuse / Fallback;
|
||||
- unrelated source unloaded/fading 不再拖垮该 Segment;
|
||||
- full CPU mixed execution 支持 R0 Reuse + C Live + R1 Fallback;
|
||||
- Fast Path gate、singleContinuous、多 Continuous语义未改;
|
||||
- Source::isLoaded 与 global renderTree loaded 语义未改。
|
||||
|
||||
## Android Build
|
||||
|
||||
标准 validation pipeline 最终:
|
||||
|
||||
```text
|
||||
RC=0
|
||||
BUILD SUCCESS
|
||||
variant=OpenglDebug
|
||||
abi=x86_64
|
||||
apk_bytes=61956640
|
||||
libmaplibre_bytes=39636472
|
||||
sha256=86e22bf081bf7d3196f0bf0e7c56af51bde01e9f0b01e0bab238cb794273315a
|
||||
```
|
||||
|
||||
RDC 另修复 validation script 的 pipefail/SIGPIPE 141:
|
||||
`awk ... {print $1; exit}` → 完整消费管道。
|
||||
|
||||
## FZG1-2
|
||||
|
||||
- Android 13
|
||||
- API 33
|
||||
- x86_64
|
||||
- ADB:`192.168.200.139:5555`
|
||||
|
||||
### T1 STATIC
|
||||
|
||||
- false window:1
|
||||
- avg/max:203 ms
|
||||
- R0/R1 build=1 后持续 reuse
|
||||
|
||||
### T4 R-C-R 主验收
|
||||
|
||||
窗口:
|
||||
- baseline:tick 8
|
||||
- final:tick 130
|
||||
- 约 61.6 s
|
||||
|
||||
global revision:
|
||||
`128 → 973`
|
||||
|
||||
R0:
|
||||
```text
|
||||
build 1 → 1
|
||||
invalidation 20 → 20
|
||||
reuse 132 → 4042
|
||||
sourceRevision unchanged
|
||||
```
|
||||
|
||||
R1:
|
||||
```text
|
||||
build 5 → 135
|
||||
invalidation 26 → 211
|
||||
```
|
||||
|
||||
结论:
|
||||
- R0 buildDelta=0
|
||||
- R0 invalidationDelta=0
|
||||
- R0 reuseDelta=+3910
|
||||
- global revision 仍增长
|
||||
- dynamic source affected=[2]
|
||||
|
||||
**主硬门槛 PASS。**
|
||||
|
||||
### T2 GEOJSON 1HZ
|
||||
|
||||
- 约 35 s
|
||||
- falseWindowCount=6
|
||||
- avg=203 ms
|
||||
- max=205 ms
|
||||
- R0 build=1 / invalidation=18 保持稳定,reuse 增长
|
||||
|
||||
### T3 PERMANENT UNREADY
|
||||
|
||||
global loaded=false 持续约 21.468 s。
|
||||
|
||||
unready source:
|
||||
- no layer
|
||||
- enabled=false
|
||||
- isLoaded=false
|
||||
|
||||
R0/R1:
|
||||
- build=1 不变
|
||||
- invalidation=19 不变
|
||||
- reuse:11 → 1200
|
||||
- decision=reuse
|
||||
|
||||
说明 global loaded 兼容事实仍为 false,但无关 retained cache 继续安全复用。
|
||||
|
||||
## Native Host Test
|
||||
|
||||
Codex 已新增 `RenderSegmentLocality.*` 测试源码。
|
||||
|
||||
当前 Intel Mac 缺 `libuv`,Metal host configure 被环境阻塞。RDC 曾尝试 Homebrew 安装,但发现会连带升级 CMake/OpenSSL/Python 且 Intel 无 bottle 支持,已主动停止。
|
||||
|
||||
因此:
|
||||
- Native host GTest:ENV BLOCKED / NOT RUN
|
||||
- Android full native build:PASS
|
||||
- Android runtime:PASS
|
||||
|
||||
Jest:NOT RUN。
|
||||
|
||||
## 结论
|
||||
|
||||
STEP-100 功能目标成立:
|
||||
|
||||
> GeoJSON Source 更新只刷新依赖它的 retained segment;无关 retained segment 不再一起刷新。
|
||||
|
||||
允许进入 V18 unified release。
|
||||
54
docs/50_Step记录/STEP-101_RenderOrchestrator销毁生命周期修复_v1.md
Normal file
54
docs/50_Step记录/STEP-101_RenderOrchestrator销毁生命周期修复_v1.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# STEP-101 RenderOrchestrator 销毁生命周期修复记录 v1
|
||||
|
||||
状态:**CLOSED / PASS(Native + Android runtime)**
|
||||
日期:2026-10-07
|
||||
|
||||
## 目标与基线
|
||||
|
||||
- 工作区:`/Users/tei/dev/MapLibreParticleLab-step101`
|
||||
- 分支:`step101-render-orchestrator-teardown`
|
||||
- start HEAD:`d29e84c1a90daeea104b389d28b000c4f017d08d`
|
||||
- implementation commit:`64cf8f44350eb8ec4b45360db4d3f9505ac4ac0b`(`fix: make render orchestrator teardown safe`),已 push 到 `origin/particle/main` 并回读一致。
|
||||
- 工作合同:`docs/40_任务/STEP-101_RenderOrchestrator销毁生命周期修复_Codex任务_v2.md`,第 13 节优先于前文冲突项。
|
||||
- V18 为不可变已发布版本;本阶段未创建、发布或标记 V19。
|
||||
- 目标:修复 RenderSource/Tile 销毁时 observer 可能回调已析构的 RenderOrchestrator readiness mutex/state 的生命周期漏洞。
|
||||
|
||||
V18 成员声明使 readiness mutex/state 早于 `renderSources` 自动析构。Tile 销毁时同步发出 `Cancelled` 回调的路径可达,且与现场 Android destroyed mutex SIGABRT 高度吻合;没有完整符号化栈与修复后等价实机结果,暂不把它写成唯一确认的现场根因。
|
||||
|
||||
## 必做项进度
|
||||
|
||||
- [x] `RenderSource::setObserver(nullptr)` 恢复 NullObserver,保留正常 observer 转发。
|
||||
- [x] Orchestrator 析构:保留 contextLost layer handling;第一次 tagged drain;detach RenderSource、GlyphManager、ImageManager observer;在 readiness state 存活时清理 sources;第二次 tagged drain。
|
||||
- [x] 静态审计独立 render-job 队列:`waitForEmpty()` 不排空该队列;OpenGL context 析构的 `runRenderJobs(true)` 属于后续 backend 清理,测试中也单独调用 `runRenderJobs()`。不能以双 drain 代替 render-job 验收。
|
||||
- [x] 新增非 Jest C++ 测试源码:默认/真实/detach observer、三种 tile callback、Cancelled、source 清理后第二次 drain、render-job 单独处理和重复 teardown。helper 像生产代码一样注册 Orchestrator observer,并断言 source 销毁前旧 observer 已被替换。
|
||||
- [x] Android Validation 增加 T1 RESET_ONCE、T2 RESET_LOOP_20、T3 RESET_WITH_DYNAMIC_GEOJSON、T4 RESET_WITH_DIAGNOSTICS;通过 Activity recreate 触发 MapView 销毁/新建,并输出 SurfaceView detach、nativeReset、resetRenderer、style ready 与 diagnostics 日志供逐次配对。
|
||||
- [x] 只读审计正常 `renderSources.erase()` / `clearData()`:两者在 Orchestrator 存活期间执行;TileCache 析构等待其延迟释放任务结束。本阶段没有扩大其行为修改,运行态异步边界仍待 RDC 验证。
|
||||
- [x] 静态核对 STEP-100 locality、Fast Path、`singleContinuousSegment`、`Source::isLoaded()` 等代码未被本次 diff 修改。
|
||||
- [x] Native:Android full native build PASS;focused Android C++ TestRunner 6/6 PASS。Mac host 因 libuv 环境阻塞,未虚报。
|
||||
- [x] Android Validation APK build PASS;T1~T4 实机 PASS;T2 完整 20 次真实 reset/recreate,crash buffer 为空。
|
||||
- [x] STEP-100 T4 R-C-R locality 回归 PASS:R0 buildDelta=0、invalidationDelta=0、reuseDelta=+3878。
|
||||
- [ ] V19 candidate、NavSea 五次冷启动与正式 V19 发布:属于后续发行 Gate,尚未执行。
|
||||
|
||||
## 允许与禁止范围
|
||||
|
||||
实际变更限于 Orchestrator/RenderSource 生命周期、对应 C++ 测试、Android Validation 场景及 Java/JNI reset 观测日志。未修改 NavSea、provider/network、STEP-100 locality 实现、发布坐标或 V18 产物。未运行 Jest。
|
||||
|
||||
## 检查与当前结果
|
||||
|
||||
本次验证仅包含源码 diff 静态审计与 `git diff --check`。构建、链接、GTest、Gradle/APK、ADB、设备、NavSea、发布均 **NOT RUN**;源码交付不能记为运行态 PASS。完成报告:`docs/报告/STEP-101_RenderOrchestrator销毁生命周期修复_Codex完成报告_v1.md`。
|
||||
|
||||
## 阻塞与下一步
|
||||
|
||||
当前没有已观测编译失败;实际编译与场景行为仍未知。由 RDC 按完成报告的命令独立编译、运行 Native 测试、Android T1~T4、STEP-100 locality,并核对每次 `RESET_REQUEST` 对应 SurfaceView detach、`nativeReset`、`resetRenderer` 和新 renderer ready。随后才进入 V19 candidate 与 NavSea 等价验收;全部门槛满足后才可考虑正式发布。
|
||||
|
||||
|
||||
## RDC 最终证据
|
||||
|
||||
- Android Validation APK:61,957,678 bytes;`libmaplibre.so` 39,637,512 bytes;SHA-256 `14cbd35ed0bc1366389ddea69925901013e3b209ed3575462421a7920c73c721`。
|
||||
- T1~T4 teardown:全部 PASS;T2 20 次 reset;动态 GeoJSON / diagnostics 条件均无 crash。
|
||||
- STEP-100 locality:global revision 62→894;R0 build 1→1、invalidation 19→19、reuse 12→3890;PASS。
|
||||
- Android C++ focused TestRunner:4 个 `RenderSegmentLocality` + 2 个 `RenderOrchestratorTeardown` 全部 PASS;status=0。
|
||||
- Mac host GTest:ENV BLOCKED(本机缺可用 libuv pkg-config),不影响 Android full native/device 闭环结论。
|
||||
- Jest:NOT RUN。
|
||||
|
||||
**STEP-101 CLOSED / PASS。** 下一阶段进入 V19 candidate → NavSea 等价冷启动 Gate → 不可变公开发行。
|
||||
73
docs/50_Step记录/V15_iOS_WeatherColor纹理生命周期修复_v1.md
Normal file
73
docs/50_Step记录/V15_iOS_WeatherColor纹理生命周期修复_v1.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# V15 iOS WeatherColor 纹理生命周期修复
|
||||
|
||||
状态:**进行中(RDC Mac 定向验证 PASS;等待 iPad 人工验收)**
|
||||
日期:2026-09-27(JST)
|
||||
|
||||
## 目标与基线
|
||||
|
||||
- 仅修复 V15 WeatherColor 的 Metal 纹理绑定生命周期,不涉及 V12/V13、JavaScript、NavSea API、Android 行为或发行版本/tag。
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab`;机器:Mac mini、Darwin 24.6.0、x86_64;分支:`particle/main`。
|
||||
- 开工 HEAD:`cd71640a84901212ce4681e296f027befdff4ee0`;`maplibre-particle/rn/13.4.1-particle.15` peeled commit 与 HEAD 相同;`origin/particle/main` 同步于 HEAD。
|
||||
- manifest 的 V15 `sourceCommit` 为 `1fdc5df625d405233703136eb2dc12032d932da2`;后续 finalize release 生成的 V15 tag 指向 HEAD `cd71640a84901212ce4681e296f027befdff4ee0`。这两个坐标分别表示 manifest 源码提交与后续 tag HEAD,不构成冲突;本任务相关已跟踪源码在两提交间相同,tag 与发行内容未改。
|
||||
- 开工时仅有用户提供的 V15 任务书为未跟踪文件;始终保留。现有 submodule 均已初始化,本轮未修改。
|
||||
- 执行模型/推理档位:GPT-6 Luna High。
|
||||
|
||||
## 根因与修改
|
||||
|
||||
Metal LayerGroup 在 drawable 的 `upload()` 后才执行 Tweaker。旧实现于 Tweaker 的 `execute()` 才向既有 drawable 调用 `setTexture()`;当 endpoint 纹理已在 WeatherColor `update()` 中更换、wrap 范围不变时,drawable 的上传阶段仍持有旧纹理,随后 draw 才首次绑定新纹理,可能触发 `assert(!textureDirty)`。
|
||||
|
||||
本轮修改:
|
||||
|
||||
- 在 `update()` 进入资源更新时保存 drawable 原有 T0/T1 实际纹理指针;资源 endpoint 安装完成后比较新旧指针。
|
||||
- 把 wrap 区间变化或任一纹理 endpoint 指针变化统一作为重建条件;重建在 WeatherColor update 阶段执行,builder 在 drawable 上传前安装正确纹理。
|
||||
- 移除 WeatherColor Tweaker 的三处 `setTexture()`;Tweaker 只写 UBO 中的 mix、opacity、color stops、scale/bounds 等参数。
|
||||
- 新增 `WeatherColorTextureLifecycle` 定向测试,覆盖无变化、单端/双端替换、T0/T1 交叉复用、T0=T1 合并/保持、wrap 改变、隐藏期间 endpoint 替换后恢复、空资源首次创建与释放。
|
||||
|
||||
动态 mix/opacity/stops 不参与 endpoint 比较,因此这些值单独变化不会触发 drawable 重建或纹理上传。Resources 清空后下次更新会重新创建纹理和 drawables;隐藏期间不更新时,恢复后的 endpoint 比较仍在下一次 update、drawable upload 之前发生。
|
||||
|
||||
## 修改文件
|
||||
|
||||
- `include/mbgl/renderer/layers/weather_color_texture_lifecycle.hpp`(新增)
|
||||
- `src/mbgl/renderer/layers/render_weather_color_layer.cpp`
|
||||
- `test/renderer/weather_color_texture_lifecycle.test.cpp`(新增)
|
||||
- `test/CMakeLists.txt`
|
||||
- `docs/50_Step记录/V15_iOS_WeatherColor纹理生命周期修复_v1.md`(新增)
|
||||
- `docs/报告/V15_iOS_WeatherColor纹理生命周期修复_Codex完成报告_v1.md`(新增)
|
||||
- `粒子开发记录.md`(追加)
|
||||
|
||||
未修改任务书、用户其他文件、公共 API/ABI、Style Z-order、shader slot、版本锁、release tag、ZIP/manifest、Android、RN/JS、NavSea、V12/V13 或粒子 advection/State/Trail/Retained 代码。
|
||||
|
||||
## 本阶段检查与状态
|
||||
|
||||
- 已完成只读源码调用链复核和 diff 静态核对准备。
|
||||
- `git diff --check`:由 Codex 执行,结果记录于完成报告。
|
||||
- RDC 首次 OpenGL runner build:**FAIL**,`weather_color_texture_lifecycle.test.cpp:60:17` 因 `nullptr` 参数无法推导模板类型;RDC 反馈 production renderer object 已编译。Codex 仅为该断言显式指定 `<int>`。RDC 随后 OpenGL build retry exit 0、定向 GTest 6/6 PASS;Metal host build exit 0、定向 GTest 6/6 PASS。原始日志位于 `/tmp/navsea_v15_weathercolor_validation/`(`opengl_build.log`、`opengl_build_retry.log`、`opengl_test.log`、`metal_host_build.log`、`metal_host_test.log`)。Codex 未运行构建或测试。
|
||||
- iOS App/Xcode 编译、签名、安装、Metal 真机渲染和 iPad 事故场景:**等待用户手工验收**。
|
||||
- 现有仓库内未找到 `STEP_RECORD.md`;按根 `AGENTS.md` 追加 `粒子开发记录.md`,没有新建或猜造该文件。
|
||||
- RDC host 验证已完成,用户授权的 scoped commit/push 已完成;当前只等待用户手工 iPad 验收。
|
||||
|
||||
## RDC Mac 定向验证命令
|
||||
|
||||
先按 OpenGL,再按 Metal host 顺序执行:
|
||||
|
||||
```bash
|
||||
cmake --build build-step088-opengl --target mbgl-test-runner --parallel 3
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='WeatherColorTextureLifecycle.*'
|
||||
cmake --build build-step097-metal-host --target mbgl-test-runner --parallel 3
|
||||
build-step097-metal-host/mbgl-test-runner --gtest_filter='WeatherColorTextureLifecycle.*'
|
||||
```
|
||||
|
||||
RDC 已按顺序完成 OpenGL 与 Metal host 验证,结果均 PASS(各 6/6)。Metal host 测试仅验证 host 可执行代码,不等于 iOS 设备 Metal PASS。当前等待用户手工 iPad 验收;本阶段不再构建。
|
||||
|
||||
## Git 收尾
|
||||
|
||||
- scoped 实现提交:`5af91fe7a105f0ac047e18db1dc063c9ffd69d44`(`fix: 修复 V15 WeatherColor Metal 纹理生命周期`),仅纳入本任务 7 个文件。
|
||||
- 已推送 `origin/particle/main`;`git ls-remote` 回读的远端 HEAD 为 `5af91fe7a105f0ac047e18db1dc063c9ffd69d44`。
|
||||
- 验证结果补录提交:`4712e1752af2a8238def5839c8754270de4195c9`(`docs: 记录 V15 WeatherColor RDC 验证与推送`),包含 Step、完成报告、开发记录;推送后远端 HEAD 已回读为该提交。
|
||||
- 未跟踪 V15 任务书未提交;V15 release tag 和发行内容未改。当前仍 **WAITING FOR MANUAL IPAD VALIDATION**。
|
||||
|
||||
## 2026-09-27 v16 发行构建兼容补充
|
||||
|
||||
- 统一发行 v16 的 Android 四 ABI 与 Maven Local 构建通过;首次 iOS Bazel 编译在 render_weather_color_layer.cpp 报找不到新增的 weather_color_texture_lifecycle.hpp。
|
||||
- 根因:bazel/core.bzl 的 MLN_CORE_HEADERS 为显式清单,新增头文件未列入;仅追加该头文件路径,不改 WeatherColor 算法或公开 API。
|
||||
- iOS Bazel 构建需由 RDC 在同一 v16 独立 worktree 重新验证;未通过前不得宣称 v16 发布或 iPad 修复通过。
|
||||
130
docs/60_测试/STEP-097_V13修复后集中验证清单_v1.md
Normal file
130
docs/60_测试/STEP-097_V13修复后集中验证清单_v1.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# STEP-097 V13 修复后集中验证清单 v1
|
||||
|
||||
状态:**STEP-097 整体 PARTIAL;A1 与 A2 的 Android/Core 检查已完成,B/C 未开始。** 下方较早段落按当时事实保留;后续 RDC 结果以本文末尾带日期的更新为准。Metal host Core 测试不等于 iOS runtime,iOS runtime 按用户豁免记 `USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`。
|
||||
|
||||
## 通用 Core 与现有测试入口(RDC)
|
||||
|
||||
在本仓 Mac 当前 checkout,使用已配置的现有 build 目录;若目录不存在,先按当前构建规范配置。此处不自动运行:
|
||||
|
||||
```sh
|
||||
cmake --build build-step088-opengl --target mbgl-test-runner -j3
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='RenderSegmentPlan.*:RenderSegmentDiagnostics.*:RetainedUnderlayCache.*:RetainedUnderlayRevision.*:ParticleFastPath.*'
|
||||
```
|
||||
|
||||
新入口 `RenderSegmentDiagnostics.OldCacheReleaseCannotChargeReplacementAtSameIndex` 预期同 index 的旧 revision release 不计入新 topology,当前 revision release 计 1。`RetainedUnderlayCache.UnsupportedAndResourceFailureFallBackSafely` 为旧入口,用于资源失败后的基础状态;尚不能替代真实 layer render 异常注入。失败保留完整日志,回传首个错误和日志路径。
|
||||
|
||||
## Android OpenGL(Particle-Android / RDC,NOT RUN)
|
||||
|
||||
```sh
|
||||
./tools/particle_android_validation/build_validation_apk.sh
|
||||
```
|
||||
|
||||
RDC 应使用同一 commit 生成 APK,再由后续授权的 Win11/模拟器操作按标准流水线安装。现有 Test Host 仅有 C-R 控制,尚缺同一 vector source/same tiles 的 R-R、R-C-R、C-R-R fixture,故该 APK 即使构建成功也不能关闭 01/03。需比较 cache ON/OFF 的首帧、重建、reuse、主目标与离屏目标像素;失败后 diagnostics 不得 Ready,下帧可恢复。记录 Renderer、ABI、SHA-256、截图与日志。不得引用 STEP-094 历史 43/43 作为本轮结果。
|
||||
|
||||
## iOS Metal(Particle-iOS / RDC,NOT RUN)
|
||||
|
||||
```sh
|
||||
cmake --build build-ios --target ios-sdk-static -- -quiet
|
||||
```
|
||||
|
||||
同 commit 的 iOS simulator 与真机需开启 Metal API Validation,捕获 retained R-R/R-C-R 的 color/depth/stencil texture、load/clear/store,特别确认 simulator 的 Depth32Float_Stencil8 同时绑定 depth 和 stencil。故障注入覆盖附件创建失败、encoder 创建失败及 layer render 异常;预期不发布 Ready cache、后续能 fallback/retry。专用自动测试入口尚缺,不能因静态修改或 `ios-sdk-static` 成功就宣称 Metal runtime PASS。
|
||||
|
||||
## 后续 GPU 性能(粒子负责人协调,NOT RUN)
|
||||
|
||||
完成 A1/A2/B/C 代码与平台测试入口后,以相同设备、commit、Style、视口和场数据采样 cache ON/OFF、R×viewport×fieldKey 阶梯,记录 30/60 FPS、p50/p95/p99、GPU/CPU frame time、纹理创建/释放、峰值/长稳内存、方向/缩放/状态恢复。Metal capture 记录 `waitUntilCompleted` 的实际同步点及资源依赖。未经运行不填写数字;F06 multi-C 与 F08 精细失效另行设计。
|
||||
|
||||
## Test Host 依赖(B 阶段后,NOT RUN)
|
||||
|
||||
目前默认 RN `.10` 与 Native `.13` 错配,阶段 B 未开始,不得运行安装并声称配对成功。B 修正 package/lock/SPM 后,RDC 再在 Test Host 目录执行:
|
||||
|
||||
```sh
|
||||
npm ci
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
必须同时检查实际已安装 RN tgz、Android Gradle substitution、iOS SPM exactVersion 与 lockfile;不得以 node_modules 临时软链代替默认安装结果。本清单不含 Jest 命令。
|
||||
|
||||
## 2026-09-25 FZG1 A1 部分执行结果(覆盖上方 Codex 交付时的 NOT RUN 状态)
|
||||
|
||||
- 标准构建脚本已由 Mac RDC 实际执行;HEAD `91c2491b0`,x86_64 OpenGL Debug APK `BUILD SUCCESSFUL`,SHA-256 `8677197c5f83a22747cb51f0c261edffc5db4678fc25b3f366474f6ecb94dc99`。冻结于 `artifacts/maplibre-particle/step097-fzg1/`。
|
||||
- ADB `192.168.200.139:5555` 对 FZG1-2 实际执行覆盖安装、20 组截图/UI XML、既有 STEP-093 topology、CACHE OFF/ON、相机、非法范围恢复与 mutation/fail-closed,日志见 `docs/报告/STEP-097_A1_FZG1测试APK与实机验证报告_v1.md`。
|
||||
- 此处只是设备冒烟;A1 的同 tile stencil fixture、Metal 附件验证、真正 LayerGroup exception 注入与 `OldCacheReleaseCannotChargeReplacementAtSameIndex` GTest **仍 NOT RUN/缺专用入口**;A2/B/C 和软件发布亦未执行。
|
||||
|
||||
## 2026-09-26 第 19 项原生 GTest 已执行
|
||||
|
||||
- RDC 使用当前源码 HEAD `0d13552bed84f427dd43cae9856391bd26622179` 增量重编译 `mbgl-test-runner`:95/95 steps,exit 0;第 19 项 `OldCacheReleaseCannotChargeReplacementAtSameIndex` **1/1 PASS**;`RenderSegmentDiagnostics.*` **5/5 PASS**,两次退出码均为 0。原始日志和分析见 `docs/报告/STEP-097_A1_第19项GTest运行验证报告_v1.md`。
|
||||
- 上方第 19 项“NOT RUN”是 2026-09-25 FZG1 冒烟时点的历史事实,现已由本条更新。01 同 tile、02 Metal、03 render exception 测试仍未闭合;A1 不得整体标 PASS。
|
||||
|
||||
## 2026-09-26 第 19 项 FZG1 设备辅助测试结果
|
||||
|
||||
- 经 RDC 在 FZG1-2 重新覆盖安装与 hash 复核,执行两轮 topology 轮换和资源诊断场景,20 个截图检查点;18 个有效配置均 valid=true/fallback=false,缺失 START fail-closed 后恢复。B/T 的粗粒度 create-release 差额 0/1,实时日志中无应用崩溃记录。详见 `docs/报告/STEP-097_A1_第19项FZG1运行验证报告_v1.md` 与 Mac 忽略目录 `artifacts/maplibre-particle/step097-fzg1-gtest19-device/`。
|
||||
- 这次是 Android Runtime 冒烟,前述第 19 项精确计数归属 GTest 由 Mac 原生测试覆盖;01 同 source/tile stencil、02 Metal、03 绘制异常注入仍缺,A1 整体 PARTIAL,A2/B/C 未实施。
|
||||
|
||||
## 2026-09-26 A1 新入口执行清单(RDC,NOT RUN)
|
||||
|
||||
通用 Core(当前 OpenGL 构建目录):
|
||||
|
||||
```sh
|
||||
cmake --build build-step088-opengl --target mbgl-test-runner -j3
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='RenderOrchestratorError.*:RenderSegmentDiagnostics.OldCacheReleaseCannotChargeReplacementAtSameIndex'
|
||||
```
|
||||
|
||||
预期异常 observer/failed 与 cache 不复用、重试后 Reuse;第 19 项保持 1/1 PASS。Metal GTest `MetalRetainedAttachments.*` 需由 RDC 使用可在 Mac 主机执行且配置 `MLN_WITH_METAL=ON` 的 `mbgl-test-runner` 构建目录执行;先检查所用目录的 CMakeCache、目标和 Metal device,再编译、执行并记录命令与日志。不能用 iOS 静态库构建替代主机 GTest。真机/simulator 的创建失败、encoder 失败与 API Validation 仍单独执行。
|
||||
|
||||
Android 同源 stencil:以本提交标准脚本构建 Validation APK,打开 Particle Core Layer Validation → STEP-093 → STEP-097 SAME SOURCE STENCIL;依次选择 RR、RCR、CRR。每种拓扑在 CACHE ON 首帧、稳定 reuse、REBUILD CURRENT 后,再切 CACHE OFF 与 ON;保存每个检查点截图、`STEP097_STENCIL_PLAN/READY/DIAGNOSTICS` 日志及 `READ SNAPSHOT`。预期 `accepted=true`、`valid=true`、`fallback=false`,多个 retained 段分别 build/reuse,四层同一 source 的轮廓与填充在缓存切换后无残影/漏裁剪。再以相机移动强制重建;记录主目标与离屏结果。此清单是待执行判据,不能据此宣称像素 PASS。真正 GPU 绘制异常及下一帧 full fallback/recovery 需 RDC 专项注入;GTest 状态机不能代替它。
|
||||
|
||||
## 2026-09-26 A1 实测结果(覆盖上方待运行记录)
|
||||
|
||||
- Android/FZG1:新 APK 源码 `8c006ac83`,SHA-256 `48df19cf5a4c4250934efa2596423d3ad9571fe49a65b9b704a81cdb41f58671`;同 source/tile RR/RCR/CRR、重建、缓存 OFF/ON 18/18 状态与像素采样检查通过,无本次捕获的 FATAL。
|
||||
- Core:03 `RenderOrchestratorError.*` 2/2 PASS;19 `OldCacheReleaseCannotChargeReplacementAtSameIndex` 1/1 PASS,诊断组 5/5 PASS。
|
||||
- Metal:编译通过,2/2 SKIP(无 Metal device);用户豁免 iOS runtime,标 `USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`,不是 iOS 运行测试 PASS。详见 `docs/报告/STEP-097_A1_Android专项验收报告_v1.md`。后续 A2/B/C 未执行。
|
||||
|
||||
## 2026-09-26 A2 中途静态/Core 结果(不是 A2 关闭)
|
||||
|
||||
- OpenGL `mbgl-test-runner` 编译 exit 0;定向 16/16 PASS;两次 Shader generator 均 exit 0 且输出一致。Metal host 可编译并执行同一跨 Core GTest 16/16 PASS,未运行 iOS 设备。
|
||||
- 上述结果仅覆盖现有原生测试入口。Codex A2 代码尚未审查/提交,Android 设备/APK 仍 NOT RUN,不能推进 B/C。证据与完整边界见 `docs/报告/STEP-097_A2_阶段验证记录_v1.md`。
|
||||
|
||||
## 2026-09-26 A2 最终验证结果(RDC 独立执行)
|
||||
|
||||
### Core / Shader
|
||||
|
||||
- 原先因旧测试断言与既有语义不一致而失败的 58 项完整集合,在修复断言后 **58/58 PASS**。`sameFieldGpuContent()` 仍表达忽略 producer generation/dataVersion 的解码 payload 等价;`sameFieldGpuEndpoint()` 单独负责严格 GPU endpoint identity。
|
||||
- 当前 broad filter 在 OpenGL 与 Metal host `mbgl-test-runner` 各 **121/121 PASS**。命令:
|
||||
|
||||
```sh
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='Particle*.*:Source.RasterArray*:Source.HostField*:Source.ParticleField*:Source.CanonicalField*'
|
||||
build-step097-metal-host/mbgl-test-runner --gtest_filter='Particle*.*:Source.RasterArray*:Source.HostField*:Source.ParticleField*:Source.CanonicalField*'
|
||||
```
|
||||
|
||||
- 三项 A2 Source 定向回归在 OpenGL 与 Metal host 各 **3/3 PASS**:mix-only 50 ticks 不重物化、真 endpoint 与 revision 变化触发物化、同步通知重入不双推进、clear fail-closed。
|
||||
- `node shaders/generate_shader_code.mjs` 两次执行成功且输出幂等。Metal host 是 Mac 上 Core 测试,不是 iOS device runtime。
|
||||
|
||||
### Android / FZG1-2
|
||||
|
||||
- RDC 使用 `./tools/particle_android_validation/build_validation_apk.sh` 构建标准 OpenGL Debug x86_64 APK;61,753,803 bytes,SHA-256 `27bf4a6cc191bd86c4d1c10398478d81c2999edd61fe7242e40e5188516b0ca5`。FZG1-2 `192.168.200.139:5555` 安装成功,设备 APK SHA-256 相同。
|
||||
- Test-App-only V4 Golden Wind T0/T1:load、seek 25/75、speed 2x/1x、PLAY 30 秒、PLAY 时 UI/TIME 可响应、PAUSE/stable pause、replay、CLEAR、reload、再次 PLAY/PAUSE;脚本 exit 0、17 PNG、24 steps、ANR=0。`05_play_15s.png` 可见粒子。30 秒场景闭环不是长期稳定性或性能测试。
|
||||
- stable mix-only epoch 中 renderer 日志的 `fieldUploadCount=6/resetCount=2` 保持不变;explicit CLEAR/reload 后 upload 合法增至 10,reset 增至 4;reload 后 playback 稳定在该新计数。不能宣称跨 CLEAR/reload 零上传/零 reset。
|
||||
- APK 工作树源 diff 与 `mix-reuse-build-source.diff` SHA-256 均为 `18927a5f30a88f5c1c3a00e0017473dc33b04151c227e4baf52250dc4ad2e6a2`,源码快照一致。原始 build/device/test 日志、steps 与截图位于 `artifacts/maplibre-particle/step097-a2-final-validation/`。
|
||||
|
||||
### 历史失败与范围
|
||||
|
||||
- 12:43 首次 ANR 为 synchronous Source notification 重入 forecast tick;`forecastTickInProgress` guard 与重入 GTest 已修复并覆盖。
|
||||
- 后续无 ANR 但主线程仍高负载、PLAY/TIME 无响应的试跑由 debuggerd 定位到每 tick 重物化大场和深比较;mix-only immutable endpoint reuse 后通过本次 FZG1 闭环。旧失败日志和 debuggerd 保留,不从报告中删除。
|
||||
- 本次不测 iOS runtime;标 `USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`。没有完成 GPU 性能基准、FPS/pXX 或长期运行测试。STEP-097 整体仍 PARTIAL,B/C 未开始;不包含 Jest、STEP-098、发布 `.13`。
|
||||
|
||||
### 2026-09-26 A2 源码独立 scoped commit 核验
|
||||
|
||||
- A2 的 26 个生产源码、shader、TestApp-only V4 Golden fixture 和定向测试文件已固定为 `f49ffa68a17e0a505199bdbeccf07f8543f3a05f` 并推送,`origin/particle/main` 回读一致;没有 stage 用户未跟踪 STEP-098。完整代码/设备证据仍见 `docs/报告/STEP-097_A2_阶段验证记录_v3.md`,不因 Git 提交改变原 APK 的 `git_dirty=true` 来源事实。STEP-097 整体 PARTIAL,B/C 待用户指令。
|
||||
|
||||
## 2026-09-26 B 阶段 RDC 实测与待闭合
|
||||
|
||||
- Mac Core 增量构建 PASS;B 的 JSON 严格校验、非法配置清除与恢复、数值溢出拒绝和合法 validity=0 四项 GTest:**4/4 PASS**。
|
||||
- 扩大回归初次 132/134,旧 16-bit fixture 误用 4-byte UInt8 storage;修正为合法四样本 8-byte 小端之后重新编译,broad **134/134 PASS**,没有放宽生产校验。
|
||||
- Test Host package.json/package-lock.json 的 RN tarball 均为正式 `13.4.1-particle.13`,实包 SHA-256/SRI 一致。
|
||||
- Android 要分别验证本仓新源码的 MapLibreMap destroy listener JUnit 与 RN module 对正式 `.13` AAR 的 compile;前者不能替代后者。正式 .13 source commit 无 `addOnDestroyListener`,因此当前 bridge 存在 ABI 阻塞,不能标 Android consumer PASS。
|
||||
- C、STEP-098、Jest 与正式发布不执行;完整证据见 `docs/报告/STEP-097_B_独立验收与版本阻塞报告_v1.md`。
|
||||
|
||||
## 2026-09-26 B 阶段 RDC 验证更新
|
||||
- Mac 最新 GTest runner:定向 6/6、扩展 139/139 PASS,旧 broad 2 项失败经纠正测试 fixture 后消除;完整日志 `artifacts/maplibre-particle/step097-b-rdc/validation-*.log`。
|
||||
- Android Native OpenGL Debug:`MapLibreMapDestroyListenerTest` 2/2 PASS;`compileOpenglDebugJavaWithJavac` exit 0;owner Kotlin 独立 JVM JUnit 4/4 PASS(源码 SHA 与当前 B10 一致)。隔离单测不能替代 React Native 模块与发布 AAR 精确配对编译。
|
||||
- B10 当前默认 RN Gradle 对已发布 .13 Native AAR 依赖新缺失符号 `addOnDestroyListener`;RN consumer 校验标 `BLOCKED_NATIVE_AAR_COMPATIBILITY`,禁止记录 PASS 或覆盖 .13。Linux 独立 Gradle 状态另记,C 与 STEP-098 均不得越过此门槛。
|
||||
155
docs/报告/STEP-088_静态底图缓存PoC与Renderer观测_Codex完成报告_v1.md
Normal file
155
docs/报告/STEP-088_静态底图缓存PoC与Renderer观测_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# STEP-088 静态底图缓存 PoC 与 Renderer 观测 Codex 完成报告 v1
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
- 状态:`IMPLEMENTED / WAITING FOR ANDROID VALIDATION`
|
||||
- 结论:Retained Static Underlay PoC、OpenGL/Metal compositor、Renderer counters 与 Native 定向测试代码已实现;RDC 已完成 Mac OpenGL/Metal 编译和 4/4 定向测试。Android/NavSea 尚未验证,因此不关闭 Step,也不进入 STEP-089/090。
|
||||
|
||||
## 2. Git 基线
|
||||
|
||||
- 机器:Mac mini,Darwin x86_64
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab`
|
||||
- 分支:`particle/main`
|
||||
- 起始 HEAD:`f47469d2ab77cdb959ecfe4081dc571fa1ab78f9`
|
||||
- STEP-088 实现 Final HEAD:`8b9ab303228c307612c47f7f3379ad06206ea14e`
|
||||
- 实现提交信息:`feat: add retained underlay render cache poc`
|
||||
- 当前版本坐标:`13.4.1-particle.10`
|
||||
- 实现 push:成功,`f47469d2a..8b9ab3032 particle/main -> particle/main`
|
||||
- 远端分支:`origin/particle/main`
|
||||
- 既有 dirty/untracked:保留且未暂存,包括 `AGENTS.md`、两个 modified submodule、`platform/ios/app/MBXAppDelegate.m` 与用户既有任务文档。
|
||||
|
||||
## 3. 开工前只读确认
|
||||
|
||||
已按当前代码确认:
|
||||
|
||||
- RasterParticle 与 WeatherColor 的 `needsRepaint()` 经 `RenderOrchestrator::hasTransitions()` 驱动完整 MapLibre frame。
|
||||
- `Renderer::render()` 每帧仍调用 `createRenderTree()`,随后进入 `Renderer::Impl::render()`。
|
||||
- `updateLayers/processChanges`、LayerGroup tweaker/upload、`visitRenderTargets` 均位于 draw 分段之前,STEP-088 未跳过。
|
||||
- main clear、opaque reverse、translucent forward、legacy items、debug、postRender、present 的原始顺序已核对。
|
||||
- `RenderTarget` / `OffscreenTexture` 以及 Heatmap/Hillshade 的 offscreen + texture composite 模式已作为实现样板,未修改 Heatmap/Hillshade 自身语义。
|
||||
- background layer optimized-as-color 继续通过 main framebuffer `backgroundColor` clear;underlay offscreen 使用透明 clear。
|
||||
|
||||
## 4. 实际实现
|
||||
|
||||
### Boundary
|
||||
|
||||
- `src/mbgl/renderer/render_layer.hpp`:增加 Renderer-internal boundary 与 legacy-rendering virtual hooks。
|
||||
- `render_weather_color_layer.hpp`、`render_raster_particle_layer.hpp`:声明为 boundary candidate。
|
||||
- `render_location_indicator_layer.hpp`:标识 legacy rendering,供 boundary 以下 ordering fallback。
|
||||
- `render_orchestrator.cpp`:在实际 ordered layers 上取最小 candidate index。
|
||||
|
||||
### LayerGroup 分段
|
||||
|
||||
- `render_orchestrator.hpp`:增加 before/from、forward/reverse 四种 visitor,callback 获得完整 LayerGroup 序列 original ordinal。
|
||||
- `renderer_impl.cpp`:`currentLayer = totalGroups - 1 - originalOrdinal`,`depthRangeSize` 仍按完整 group 数计算,避免子集从 0 编号改变 depth/transparency。
|
||||
|
||||
### Cache 与 Renderer Flow
|
||||
|
||||
- 新增 `retained_underlay_cache.hpp/.cpp`:管理 key、valid、RGBA offscreen、compositor group、build/reuse/fallback counts 与 last invalidation reason。
|
||||
- key 包含 viewport、boundary 与 projection/camera matrix。
|
||||
- retained path 只改真正 draw 阶段;createRenderTree、source/layer update/prepare、tweaker/upload 全部保留。
|
||||
- cache build 顺序为 independent targets → static opaque/translucent to offscreen → main clear → cached composite → dynamic opaque/translucent → legacy/debug/postRender/present。
|
||||
|
||||
### Compositor 与 Backend
|
||||
|
||||
- 新增 `RetainedUnderlayCompositeShader` 的 OpenGL GLSL/generated header 与 Metal shader source/metadata。
|
||||
- shader manifest、BuiltIn enum、shader info/defines、GL/Metal backend registration 与 CMake source lists 已接线。
|
||||
- gfx Context 增加四参数 offscreen virtual;OpenGL 补齐 depth/stencil renderbuffer attachment,Metal/Vulkan/WebGPU 明确 override 已有实现。
|
||||
|
||||
### Debug Counters
|
||||
|
||||
在 `include/mbgl/particle/renderer_debug_counters.hpp` 新增:
|
||||
|
||||
- `FullRendererFrame`
|
||||
- `RetainedUnderlayBuild`
|
||||
- `RetainedUnderlayReuse`
|
||||
- `RetainedUnderlayFallback`
|
||||
- `RetainedUnderlayInvalidation`
|
||||
- `RetainedUnderlayStaticGroupDraw`
|
||||
- `RetainedDynamicGroupDraw`
|
||||
|
||||
Release/NDEBUG 仍 no-op,不增加 Host API,不逐帧刷日志。
|
||||
|
||||
## 5. 修改文件
|
||||
|
||||
实现提交共 39 个文件:
|
||||
|
||||
- 构建:`CMakeLists.txt`、`cmake/opengl.cmake`、`cmake/metal.cmake`、`test/CMakeLists.txt`。
|
||||
- Context/Offscreen:`include/mbgl/gfx/context.hpp`、`include/mbgl/{mtl,vulkan,webgpu}/context.hpp`、`src/mbgl/gl/context.cpp/.hpp`、`src/mbgl/gl/offscreen_texture.cpp/.hpp`。
|
||||
- Renderer:`src/mbgl/renderer/render_layer.hpp`、`render_orchestrator.cpp/.hpp`、`renderer_impl.cpp/.hpp`、三个相关 RenderLayer headers、新增 `retained_underlay_cache.cpp/.hpp`。
|
||||
- Shader:`shaders/manifest.json`、两个新增 GLSL、GL/Metal generated/handwritten headers、Metal source、shader source/manifest/defines/info 与 GL/Metal backend registration。
|
||||
- 观测:`include/mbgl/particle/renderer_debug_counters.hpp`。
|
||||
- 测试:新增 `test/renderer/retained_underlay_cache.test.cpp`;RDC 小修 `test/gl/gl_functions.test.cpp` 的 Apple/macOS 符号条件。
|
||||
|
||||
## 6. Fallback 与安全边界
|
||||
|
||||
- 无 boundary:原 Renderer。
|
||||
- static prefix 为空、style 未 loaded、has3D、debug options:full render。
|
||||
- static prefix 存在 transition/repaint、symbol placement/fade 或 source fading tile:full render。
|
||||
- boundary 以下存在 legacy renderer item:full render。
|
||||
- viewport、camera fingerprint、boundary 改变:cache invalid 后 build。
|
||||
- offscreen texture/compositor 创建或 renderable 检查失败:计数并 full-render fallback。
|
||||
|
||||
STEP-088 的成立前提是固定 Camera 且 Style/Tile 稳定。production 级 style/tile/drawable/context invalidation 明确留给 STEP-089,未在本次越界实现。
|
||||
|
||||
## 7. 新增文件
|
||||
|
||||
- `include/mbgl/shaders/gl/retained_underlay_composite.hpp`
|
||||
- `include/mbgl/shaders/mtl/retained_underlay_composite.hpp`
|
||||
- `shaders/retained_underlay_composite.vertex.glsl`
|
||||
- `shaders/retained_underlay_composite.fragment.glsl`
|
||||
- `src/mbgl/renderer/retained_underlay_cache.hpp`
|
||||
- `src/mbgl/renderer/retained_underlay_cache.cpp`
|
||||
- `src/mbgl/shaders/mtl/retained_underlay_composite.cpp`
|
||||
- `test/renderer/retained_underlay_cache.test.cpp`
|
||||
|
||||
## 8. 与任务书差异
|
||||
|
||||
- 功能范围无扩大:未进入 STEP-089/090。
|
||||
- 按新 `AGENTS.md`,长时间 Mac 编译/测试由 RDC 执行,Codex 只负责实现、静态审计、文档与 Git 收尾。
|
||||
- RDC 为完成 Mac OpenGL test-runner 链接,增加了任务范围内必要的 `test/gl/gl_functions.test.cpp` Apple/macOS 平台条件小修。
|
||||
- 没有提交用户对 `AGENTS.md` 的全局流程修改,也没有提交既有无关 dirty/untracked。
|
||||
|
||||
## 9. RDC 构建与测试记录
|
||||
|
||||
- OpenGL:`cmake --build build-step088-opengl --target mbgl-test-runner -j3`,结果成功。
|
||||
- 定向测试:4/4 成功:
|
||||
- `RetainedDynamicBoundary.SelectsFirstCandidate`
|
||||
- `RetainedLayerGroupRange.PreservesForwardReverseOriginalOrdinals`
|
||||
- `RetainedUnderlayCache.BuildReuseAndInvalidation`
|
||||
- `RetainedUnderlayCache.UnsupportedAndResourceFailureFallBackSafely`
|
||||
- iOS/Metal device arm64:`cmake --build build-ios --config Debug --target mbgl-core -j3`,结果 `BUILD SUCCEEDED`。
|
||||
- `git diff --check`:成功。
|
||||
- RDC 小修说明:Darwin 的 gl functions 实现仅在 iPhone/Simulator 定义 `glInvalidateFramebuffer` / `glInvalidateSubFramebuffer`;macOS test-runner 不应无条件引用,故增加 Apple target 条件。该修复仅影响测试平台链接,不改变 Renderer 业务语义。
|
||||
|
||||
按最新 `AGENTS.md` 分工,以上长时间编译/测试由 RDC / 普通 Terminal 执行并回传;Codex 最终收尾没有再次启动 build/test。
|
||||
|
||||
## 10. Android 未执行与无法验证
|
||||
|
||||
以下均未执行:Android Gradle、AAR/APK、ADB、Android 真机/模拟器、NavSea 实机、Jest。因此无法验证复杂 NavSea Style 下的实际视觉、缓存 counters、draw-call 降幅、RenderThread/main/JS CPU 收益以及所有交互 invalidation。
|
||||
|
||||
## 11. PoC 限制、已知问题与未完成事项
|
||||
|
||||
- 未实现 STEP-089 的 production 全量 invalidation。
|
||||
- 未实现 STEP-090 的 Map/update/createRenderTree/source/prepare CPU fast path。
|
||||
- 未修改 NavSea、React、Host API 或 C-001。
|
||||
- Android/NavSea 手工验证待用户执行;结果回传前不得把本 Step 标记为关闭。
|
||||
|
||||
## 12. 生成产物
|
||||
|
||||
本 Step 不生成 AAR/APK/XCFramework/npm artifact;没有发布物、ABI、大小或 SHA-256。Mac 输出仅用于编译/测试验证,不作为发行 artifact。
|
||||
|
||||
## 13. 是否满足进入 STEP-089 条件与下一步建议
|
||||
|
||||
当前不满足。进入 STEP-089 的前置条件是用户/RDC 在 Android/NavSea 复杂真实 Style 上确认:画面与 ordering 正确、fixed Camera 下 build 约一次且 reuse/Particle advance 持续、static group draw 不随 Particle FPS 增长、camera/resize/fallback 不出现 stale cache。STEP-088 未取得这些证据前不得进入 STEP-089;STEP-090 同样未执行。
|
||||
|
||||
下一步只建议执行 STEP-088 Android/NavSea 手工验证,不创建或执行新的 Step。
|
||||
|
||||
## 14. Git 收尾
|
||||
|
||||
- Final implementation commit hash:`8b9ab303228c307612c47f7f3379ad06206ea14e`
|
||||
- Commit message:`feat: add retained underlay render cache poc`
|
||||
- Push result:成功
|
||||
- Remote branch:`origin/particle/main`
|
||||
- STEP-088 implementation Final HEAD:`8b9ab303228c307612c47f7f3379ad06206ea14e`
|
||||
- 本报告与 Step/开发记录由后续独立 docs closeout commit 承载;该提交的精确 hash 因 Git commit 不能自引用,记录在最终响应和远端 HEAD 核验中。
|
||||
172
docs/报告/STEP-088_静态底图缓存PoC与Renderer观测_Codex完成报告_v2.md
Normal file
172
docs/报告/STEP-088_静态底图缓存PoC与Renderer观测_Codex完成报告_v2.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# STEP-088 静态底图缓存 PoC 与 Renderer 观测 Codex 完成报告 v2
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
- 状态:`ANDROID TEST APP PASS / WAITING FOR NAVSEA VALIDATION`
|
||||
- Android MapLibreAndroidTestApp 运行态验证:PASS。
|
||||
- STEP-088 overall:未关闭,不得写作 CLOSED/PASS。
|
||||
- NavSea 复杂真实 Style 尚未验证,因此不得进入 STEP-089/090。
|
||||
|
||||
## 2. Git 基线与提交
|
||||
|
||||
- 机器:Mac mini,Darwin x86_64
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab`
|
||||
- 分支:`particle/main`
|
||||
- 本轮起始 HEAD:`e7d109246a225eb1c1b2f177aa30e3c7fcdf658e`
|
||||
- v1 实现提交:`8b9ab303228c307612c47f7f3379ad06206ea14e`
|
||||
- 本轮代码修正提交:`1d1a7915e0886c46be40f4fc9a2eff13192f804b`
|
||||
- 本轮代码提交信息:`fix: scope retained underlay placement fallback`
|
||||
- 代码 push:成功,`e7d109246..1d1a7915e particle/main -> particle/main`
|
||||
- 远端分支:`origin/particle/main`
|
||||
- v2 文档 closeout commit 与最终 Final HEAD 记录在最终响应中,避免 Git commit 自引用。
|
||||
|
||||
## 3. Android 首轮失败事实
|
||||
|
||||
较早 STEP-088 APK 在 fixed camera / only Particle 场景的代表数据为:
|
||||
|
||||
```text
|
||||
frame=1200 build=0 reuse=0 fallback=1199 invalidation=1199
|
||||
staticDraw=0 dynamicDraw=0 particleAdvance=1199
|
||||
```
|
||||
|
||||
cache 从未 build。审查确认 retained path 无条件受全局 placement transition、`placementChanged` 和 `symbolFadeChange` 约束,导致 static prefix 不含 placement 时仍每帧 fallback。
|
||||
|
||||
## 4. 实际修改文件
|
||||
|
||||
代码提交只包含:
|
||||
|
||||
- `src/mbgl/renderer/render_orchestrator.cpp`
|
||||
- `src/mbgl/renderer/render_orchestrator.hpp`
|
||||
- `src/mbgl/renderer/renderer_impl.cpp`
|
||||
- `platform/android/MapLibreAndroidTestApp/build.gradle.kts`
|
||||
|
||||
文档提交只包含:
|
||||
|
||||
- `docs/50_Step记录/STEP-088_静态底图缓存PoC与Renderer观测_v2.md`
|
||||
- `docs/报告/STEP-088_静态底图缓存PoC与Renderer观测_Codex完成报告_v2.md`
|
||||
|
||||
未修改或提交 `AGENTS.md`、submodule、iOS dirty 文件、STEP-082/083 untracked、既有 untracked STEP-088 task/规划文档或其他用户文件。
|
||||
|
||||
## 5. Renderer placement-scoping 修正
|
||||
|
||||
- `RenderOrchestrator::retainedUnderlayUsesPlacement(boundary)` 只遍历 boundary 之前的 ordered layers,判断 retained static prefix 是否真正依赖 placement。
|
||||
- `retainedUnderlayIsStable(...)` 继续保护 light/static-layer transition;只有 static prefix 使用 placement 时才检查 `placementController.hasTransitions(...)`。
|
||||
- `Renderer::Impl::render(...)` 只有在 static prefix 使用 placement 时才要求 `placementChanged == false` 且 `symbolFadeChange == 0`。
|
||||
- fallback reason 拆分为 `static-transition` 与 `static-placement`,便于区分原因。
|
||||
- 无 boundary、empty prefix、style-not-loaded、3D、debug、legacy ordering、resource/compositor failure 等 STEP-088 fallback 未改变。
|
||||
|
||||
该修正只调整 retained static prefix 的 eligibility,没有实现 production 全量 invalidation,也没有改变动态 boundary、LayerGroup ordinal/depth、draw ordering、Host API 或 C-001。
|
||||
|
||||
## 6. Debug reason 日志
|
||||
|
||||
保留 debug-only `ParticleRetained` 周期日志中的:
|
||||
|
||||
```text
|
||||
reason=<lastInvalidationReason>
|
||||
```
|
||||
|
||||
它复用 `RetainedUnderlayCache::getLastInvalidationReason()`,用于 Android 验证 `first-frame`、`camera-or-boundary`、`style-not-loaded`、`static-transition` 和 `static-placement`。日志仍位于 `#ifndef NDEBUG`,不改变 Release 行为,也不增加 Host API。
|
||||
|
||||
## 7. Android Validation strip property
|
||||
|
||||
MapLibreAndroidTestApp 增加:
|
||||
|
||||
```text
|
||||
maplibre.validation.stripNativeDebugSymbols=true
|
||||
```
|
||||
|
||||
默认 Debug 构建继续 `keepDebugSymbols += "**/*.so"`。只有显式启用 Validation property 时才允许 strip native symbols,使 x86_64 Debug APK 从约 375 MB 降至约 57 MB。Debug counters 与 `NDEBUG` 语义不变。
|
||||
|
||||
## 8. RDC Mac 最终复验
|
||||
|
||||
- `cmake --build build-step088-opengl --target mbgl-test-runner -j3`:PASS / no work to do;源码/object 时间戳已确认当前 placement-scoping 修改已编入 object。
|
||||
- STEP-088 定向测试 4/4 PASS:
|
||||
- `RetainedDynamicBoundary.SelectsFirstCandidate`
|
||||
- `RetainedLayerGroupRange.PreservesForwardReverseOriginalOrdinals`
|
||||
- `RetainedUnderlayCache.BuildReuseAndInvalidation`
|
||||
- `RetainedUnderlayCache.UnsupportedAndResourceFailureFallBackSafely`
|
||||
- `cmake --build build-ios --config Debug --target mbgl-core -j3`:`BUILD SUCCEEDED`。
|
||||
- `git diff --check`:PASS。
|
||||
|
||||
以上均由 RDC 执行。Codex 本次收尾未执行任何新 build/test、Gradle、ADB、Xcode 或设备操作。
|
||||
|
||||
## 9. Android Test App 环境
|
||||
|
||||
- 设备:`192.168.200.139:5555`,FZG1-2
|
||||
- Android:13
|
||||
- ABI:x86_64
|
||||
- 构建:MapLibreAndroidTestApp OpenGL Debug x86_64
|
||||
- Validation strip property:启用
|
||||
- build/install/ADB/Activity:成功
|
||||
|
||||
## 10. Android Test App 运行态结果
|
||||
|
||||
### Fixed camera / only Particle
|
||||
|
||||
```text
|
||||
frame=840 build=1 reuse=836 fallback=2 invalidation=3
|
||||
reason=first-frame staticDraw=2 dynamicDraw=5022 particleAdvance=839
|
||||
```
|
||||
|
||||
后续 `reuse` 持续增长,`staticDraw` 保持不变。
|
||||
|
||||
### Camera DATELINE
|
||||
|
||||
`build` 从 1 增至 2,`staticDraw` 从 2 增至 4;随后 reuse 恢复持续增长,原因为 `camera-or-boundary`。符合 camera transition 后安全 invalidation/fallback、稳定后单次 rebuild/reuse。
|
||||
|
||||
### REAL WEATHER
|
||||
|
||||
`REAL_WEATHER_INITIAL_WINDOW_READY` 和 `WEATHER_COLOR_FIELD_READY` 正常;GSI raster + WeatherColor + Particle 正常进入 retained path。代表数据:
|
||||
|
||||
```text
|
||||
frame=6960 build=3 reuse=6907 fallback=49 invalidation=52
|
||||
staticDraw=6 particleAdvance=6959
|
||||
```
|
||||
|
||||
fixed camera 后 reuse 持续增长、`staticDraw` 保持 6。`TARGET_FPS_CHANGED target=FPS_30` 成功,缓存继续 reuse。
|
||||
|
||||
### Resize / rotation
|
||||
|
||||
viewport `1280x680 → 800x1160 → 1280x680`;过渡期间安全 `style-not-loaded` fallback,稳定后 rebuild/reuse。设备已恢复 `accelerometer_rotation=1`、`user_rotation=0`。
|
||||
|
||||
### STATIC / ACTIVE
|
||||
|
||||
STATIC 时 RenderThread 约 0%;ACTIVE 后恢复 continuous render,retained reuse 继续增长且 `staticDraw` 不再增长。旧 Test App 设备上的 ACTIVE RenderThread 约 96%~107% 仅作 Test App 对照,不代表 NavSea 最终性能。
|
||||
|
||||
### 异常扫描
|
||||
|
||||
未发现 `FATAL EXCEPTION`、`SIGSEGV`、`SIGABRT`、ANR、`GL_INVALID`、`EGL_BAD` 或 OOM。
|
||||
|
||||
## 11. 验收结论与边界
|
||||
|
||||
Android MapLibreAndroidTestApp 已证明:
|
||||
|
||||
- static prefix 不使用 placement 时,全局 placement 状态不再错误阻塞 retained cache;
|
||||
- first build 后能够持续 reuse,static draw 不再随 Particle frame 增长;
|
||||
- camera 和 viewport 变化会安全 invalidation/rebuild;
|
||||
- GSI raster + WeatherColor + Particle、FPS 30、STATIC/ACTIVE 均可运行;
|
||||
- 未发现 crash、ANR、GL/EGL 或 OOM 异常。
|
||||
|
||||
但 Test App 不能替代 NavSea 复杂真实 Style。以下仍无法验证:
|
||||
|
||||
- 真实产品 layer stack 的视觉与 ordering;
|
||||
- 大量 PBF/fill/line/symbol static prefix 的 `staticDraw` 行为;
|
||||
- Particle OFF/ON RenderThread、main、JS CPU 收益;
|
||||
- pan/zoom/resize/fallback 后是否始终无 stale underlay。
|
||||
|
||||
因此 STEP-088 overall 仍为 `ANDROID TEST APP PASS / WAITING FOR NAVSEA VALIDATION`,不是 CLOSED/PASS。
|
||||
|
||||
## 12. 范围与未完成事项
|
||||
|
||||
- 未进入 STEP-089 production invalidation。
|
||||
- 未进入 STEP-090 animation-only fast path。
|
||||
- 未修改 NavSea、React、Host API 或 C-001。
|
||||
- NavSea 复杂真实 Style 验证完成前,不得进入 STEP-089/090。
|
||||
|
||||
## 13. Git 收尾
|
||||
|
||||
- Renderer/Validation fix commit:`1d1a7915e0886c46be40f4fc9a2eff13192f804b`
|
||||
- Commit message:`fix: scope retained underlay placement fallback`
|
||||
- Push result:成功,`e7d109246..1d1a7915e particle/main -> particle/main`
|
||||
- Remote branch:`origin/particle/main`
|
||||
- v2 文档 closeout commit hash、message、push result 与最终 Final HEAD:由最终响应记录。
|
||||
46
docs/报告/STEP-088_静态底图缓存PoC与Renderer观测_Codex完成报告_v3.md
Normal file
46
docs/报告/STEP-088_静态底图缓存PoC与Renderer观测_Codex完成报告_v3.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# STEP-088 静态底图缓存 PoC 与 Renderer 观测 Codex 完成报告 v3
|
||||
|
||||
## 最终结论
|
||||
|
||||
本轮完成 STEP-088 的运行时 Cache 控制、Release 最小诊断、Android/iOS Native API、RN RendererRuntime、NavSea 正式 PBF 测试场景及性能基线。
|
||||
|
||||
Android 实测已经确认:
|
||||
1. retained underlay 可以停止静态 LayerGroup 的重复 draw;
|
||||
2. Cache ON/OFF 可以在同一进程、同一 Map 中运行时切换;
|
||||
3. 静态 draw 不是当前主要 CPU 成本;
|
||||
4. 真正的大头是 Particle continuous repaint 触发后,PBF/vector/static renderer pipeline 被反复处理;
|
||||
5. 因此后续应按规划进入 STEP-089 invalidation/revision,再进入 STEP-090 Static CPU Fast Path。
|
||||
|
||||
## 主要验证
|
||||
|
||||
- PURE PARTICLE:RenderThread ≈ 10.2%
|
||||
- PURE PBF idle:RenderThread ≈ 0%
|
||||
- production Style + PBF layers hidden + Particle:RenderThread ≈ 25.5%
|
||||
- production PBF + Particle:RenderThread ≈ 96~100%
|
||||
- BEFORE_SYMBOL_CLUSTER Cache ON/OFF:
|
||||
- RenderThread 100.2% → 96.5%
|
||||
- Process 114.2% → 102.8%
|
||||
|
||||
## 构建/测试
|
||||
|
||||
- Android x86_64 Validation APK:PASS
|
||||
- OpenGL target:PASS
|
||||
- Directed C++ tests:5/5 PASS
|
||||
- Metal mbgl-core:PASS
|
||||
- ios-sdk-static:PASS
|
||||
- RN TypeScript:PASS
|
||||
- RN Test Host TypeScript:PASS
|
||||
- RN iOS bridge:旧 STEP-083 SwiftPM 测试包缺少新增 MLNMapView API,因此失败;不是当前源码 platform SDK 编译失败,不作为 STEP-089 阻塞项。
|
||||
- git diff --check:PASS
|
||||
|
||||
## Git 收尾
|
||||
|
||||
提交与 push 信息在完成 scoped commit 后补入本文件。
|
||||
|
||||
## Git 收尾记录
|
||||
|
||||
- Code commit: `8384c8bd241bcc70c974449475a9a8ca64a1e626`
|
||||
- Code commit message: `feat: add retained underlay runtime controls`
|
||||
- Remote branch: `origin/particle/main`
|
||||
- Push result: SUCCESS
|
||||
- Final HEAD: `a32e4e1d5b3bc5a5ddc2ff618b964e116265561c`
|
||||
@@ -0,0 +1,63 @@
|
||||
# STEP-089 静态渲染 Revision 与 Invalidation 基础层 Codex 完成报告 v1
|
||||
|
||||
## 最终状态
|
||||
|
||||
**CLOSED / PASS**
|
||||
|
||||
## 完成项
|
||||
|
||||
- 已完成 static revision / buildRevision 门控。
|
||||
- 已完成 topology snapshot / topologyRevision。
|
||||
- 已覆盖 camera、viewport、pixel ratio、style、layer、source、tile、image、glyph、placement/fade、context、debug、3D/custom fail-closed。
|
||||
- 已移除 transient drawable churn 对 static revision 的误触发。
|
||||
- 已修复 equivalent stencil tile set 因 shared_ptr identity 导致的误失效。
|
||||
- 已保留真正 LayerGroup topology 变化的失效能力。
|
||||
- 已保持 STEP-088 Cache ON/OFF 运行时能力。
|
||||
- 未实现 STEP-090 fast path。
|
||||
|
||||
## 实机回归中发现并修复的问题
|
||||
|
||||
1. 首版把 LayerGroup drawable maintenance 当成 semantic mutation,导致 staticRevision 每帧递增。
|
||||
2. stencil tile shared_ptr identity 在正常帧中变化,导致 false positive。
|
||||
3. LayerGroup transient churn 继续导致 topology false positive。
|
||||
4. 最终改为 semantic content revision + stable topology snapshot,稳态复用恢复。
|
||||
|
||||
## 验证
|
||||
|
||||
- OpenGL:PASS。
|
||||
- Retained directed tests:12/12 PASS。
|
||||
- Metal/iOS mbgl-core:BUILD SUCCEEDED。
|
||||
- Android APK:BUILD SUCCESSFUL。
|
||||
- Android 稳态:
|
||||
- build 固定;
|
||||
- reuse 持续增长;
|
||||
- staticRevision == buildRevision 且保持稳定。
|
||||
- Camera 变化:
|
||||
- revision 前进;
|
||||
- cache rebuild 一次;
|
||||
- 随后恢复稳定 reuse。
|
||||
- git diff --check:PASS。
|
||||
|
||||
## Android 代表日志
|
||||
|
||||
```text
|
||||
frame=480 build=2 reuse=302 staticRevision=21 buildRevision=21
|
||||
frame=600 build=2 reuse=422 staticRevision=21 buildRevision=21
|
||||
frame=720 build=2 reuse=542 staticRevision=21 buildRevision=21
|
||||
```
|
||||
|
||||
相机变化后:
|
||||
|
||||
```text
|
||||
build=19 reuse=3519 staticRevision=44 buildRevision=44
|
||||
build=19 reuse=3639 staticRevision=44 buildRevision=44
|
||||
build=19 reuse=3759 staticRevision=44 buildRevision=44
|
||||
build=19 reuse=3879 staticRevision=44 buildRevision=44
|
||||
```
|
||||
|
||||
## Git
|
||||
|
||||
- Code commit: `bcec6ce5e0f4d1cfc35fd0b4dddadfaf6e37e0fb`
|
||||
- Code commit message: `feat: add retained underlay revision invalidation`
|
||||
- Remote branch: `origin/particle/main`
|
||||
- Push result / Final HEAD:见最终收尾提交。
|
||||
@@ -0,0 +1,133 @@
|
||||
# STEP-090 Particle-only Static CPU Fast Path Codex 完成报告 v1
|
||||
|
||||
## 最终结论
|
||||
|
||||
**CLOSED / PASS**
|
||||
|
||||
本阶段实现并验证了 MapLibre Native 内部自动 Particle-only CPU fast path。
|
||||
|
||||
## 完成项
|
||||
|
||||
- 已将 fast-path gate 放在 createRenderTree 之前。
|
||||
- 已实现 prepared RenderTree 跨帧复用。
|
||||
- 已实现 Particle-only 动态判定。
|
||||
- 已跳过 full static createRenderTree / prepare / static layer update。
|
||||
- 已保持 retained underlay GPU cache。
|
||||
- 已增加 Debug counters:
|
||||
- ParticleFastFrameHit
|
||||
- ParticleFastFrameMiss
|
||||
- ParticleFullFrame
|
||||
- 已增加周期性 fastReason 诊断。
|
||||
- 已处理 annotation pending mutation。
|
||||
- 已处理 non-particle repaint / transition。
|
||||
- 已处理 placement/fade / fading tile。
|
||||
- 已处理 camera / viewport / style / source / tile / topology / revision gate。
|
||||
- 已处理 context lost / clearData / reduceMemoryUse / style reload prepared tree 清理。
|
||||
- 未新增 Host/RN 手工 fast-frame API。
|
||||
- 未实现多动态层通用调度。
|
||||
- 未加入 Jest。
|
||||
|
||||
## 首轮实机问题与修复
|
||||
|
||||
首轮 Android 实机:
|
||||
- prepared tree 实际正常跨帧保存;
|
||||
- 但 fastHit=0;
|
||||
- 诊断 initially 显示 no-prepared-tree。
|
||||
|
||||
调查发现:
|
||||
1. clearPreparedRenderTree 覆盖了真实 miss reason,导致诊断误导;
|
||||
2. 实际阻塞为 placement-or-fade;
|
||||
3. 上一 full frame 的 placementChanged / symbolFadeChange 被当成永久 blocker。
|
||||
|
||||
最终修正:
|
||||
- clear 不再覆盖上一真实 miss reason;
|
||||
- placement gate 改为当前 `placementController.hasTransitions(timePoint)`;
|
||||
- 上一 full frame 的 placementChanged / symbolFadeChange 不再永久阻塞;
|
||||
- current active placement/fade 仍 fail closed。
|
||||
|
||||
## Android A/B 性能
|
||||
|
||||
### AFTER_CLIP
|
||||
|
||||
FastPath ON:
|
||||
- Process ≈ 27.7%
|
||||
- Main ≈ 2.9%
|
||||
- RenderThread ≈ 21.7%
|
||||
|
||||
FastPath OFF:
|
||||
- Process ≈ 111.4%
|
||||
- Main ≈ 2.8%
|
||||
- RenderThread ≈ 97.0%
|
||||
|
||||
RenderThread 下降约 75 个百分点。
|
||||
|
||||
### BEFORE_SYMBOL_CLUSTER
|
||||
|
||||
FastPath ON:
|
||||
- Process ≈ 26.3%
|
||||
- Main ≈ 2.7%
|
||||
- RenderThread ≈ 20.3%
|
||||
|
||||
## 运行时行为
|
||||
|
||||
稳定 fast path:
|
||||
- fastHit 持续增长;
|
||||
- particleFull 固定;
|
||||
- retained build 固定;
|
||||
- retained reuse 持续增长;
|
||||
- staticRevision == buildRevision;
|
||||
- fastReason=none。
|
||||
|
||||
Cache OFF:
|
||||
- fastReason=retained-disabled;
|
||||
- fastHit 停止;
|
||||
- particleFull 持续增长;
|
||||
- CPU 回到 full-path 基线。
|
||||
|
||||
Camera:
|
||||
- 变化时 full;
|
||||
- revision/build 更新;
|
||||
- 稳定后 fastHit 自动恢复。
|
||||
|
||||
TOP:
|
||||
- static-transition/static-placement;
|
||||
- fast path 不命中;
|
||||
- 保守 full path。
|
||||
|
||||
Viewport/orientation:
|
||||
- resize/context变化时 full/rebuild;
|
||||
- 稳定后自动恢复 fast path;
|
||||
- 设备旋转设置最终恢复原值。
|
||||
|
||||
## 长时间运行异常复测
|
||||
|
||||
2026-09-23 08:23:27,长时间运行后的 Test App 曾出现一次 `app died, no saved state`。当时 frame counter 已约 1,868,000,fast path 长时间稳定;日志中未检出 FATAL、signal、ANR、GL/EGL 或 OOM crash signature,原因未能确定。
|
||||
|
||||
随后按最终收尾要求重新启动 Test App,进入 `NAVSEA PBF / AFTER_CLIP / CACHE ON` 连续复测约 3 分钟:
|
||||
|
||||
- PID 始终为 29295,Activity 保持 top resumed;
|
||||
- `particleFull=218` 固定;
|
||||
- `fastHit` 约从 20662 增长到 33622;
|
||||
- `buildRevision == staticRevision == 18`;
|
||||
- `fastReason=none`;
|
||||
- 6 个连续 30 秒 RenderThread CPU 样本:21.2% / 21.3% / 21.5% / 21.7% / 21.8% / 22.0%;
|
||||
- 结束后扫描 FATAL EXCEPTION / Fatal signal / SIGSEGV / SIGABRT / ANR / GL_INVALID / EGL_BAD / OOM / app died,无命中。
|
||||
|
||||
结论:该 `app died` 作为**长时间单次异常、短时复测未复现**记录,不据此归因于 STEP-090 fast path;后续长时间 soak 继续观察。
|
||||
|
||||
## 测试与构建
|
||||
|
||||
- OpenGL:PASS。
|
||||
- Directed tests:21/21 PASS。
|
||||
- Android APK:BUILD SUCCESSFUL。
|
||||
- Android 安装/实机:PASS。
|
||||
- Metal/iOS mbgl-core:BUILD SUCCEEDED。
|
||||
- git diff --check:PASS。
|
||||
- 异常日志扫描:PASS。
|
||||
|
||||
## Git
|
||||
|
||||
- Final implementation commit: `4e3d27e1f3fb0b3ee6eea78cfec7db7fa78d2b3e`
|
||||
- Commit message: `feat: add particle static CPU fast path`
|
||||
- Push result: SUCCESS
|
||||
- Remote branch: `origin/particle/main`
|
||||
@@ -0,0 +1,96 @@
|
||||
# STEP-091 Zoom Out Viewport Particle Reseed Codex 完成报告 v1
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
**PASS(Codex 阶段)/ Android 实机验收待 RDC 执行**
|
||||
|
||||
根因已按当前正式 Renderer 修复:Zoom Out 的 camera gesture 稳定后,在新的有效 viewport respawn domain 已计算完成的同一帧,一次性把 State/Trail seed 状态置为未播种;下一次既有 State/Trail pass 完成全粒子新 viewport 播种与旧 Trail 清理。没有执行完整 simulation reset。
|
||||
|
||||
## 2. Git 基线
|
||||
|
||||
- 已完成:branch `step091-zoom-out-reseed`。
|
||||
- 已完成:Starting HEAD `297b9126200cea3da3bdef2faacedcd9845f6d08`,对应任务指定 `origin/particle/main` 基线。
|
||||
- 已完成:implementation commit `4286c16a53d4f49001f3b41ed4638d3c6edc95b3`。
|
||||
- 已完成:implementation commit message `fix: reseed particles after zoom out`。
|
||||
- 已完成:implementation push 至 `origin/particle/main` 成功,远端由 `297b9126200cea3da3bdef2faacedcd9845f6d08` 前进到 `4286c16a53d4f49001f3b41ed4638d3c6edc95b3`。
|
||||
- final documentation closeout push / Final HEAD / `origin/particle/main`:见 Git 收尾章节与最终交付回读。
|
||||
|
||||
## 3. 实际修改文件
|
||||
|
||||
- 已完成:`src/mbgl/renderer/layers/render_particle_validation_layer.cpp`。
|
||||
- 已完成:`test/CMakeLists.txt`。
|
||||
- 已完成:`粒子开发记录.md`。
|
||||
- 已完成:用户提供的 `docs/40_任务/STEP-091_ZoomOut_Viewport_Particle_Reseed_Codex任务_v1.md` 保留并纳入本 Step 文档提交。
|
||||
|
||||
## 4. 新增文件
|
||||
|
||||
- 已完成:`src/mbgl/renderer/particle_viewport_reseed.hpp`。
|
||||
- 已完成:`test/renderer/particle_viewport_reseed.test.cpp`。
|
||||
- 已完成:`docs/50_Step记录/STEP-091_ZoomOut_Viewport_Particle_Reseed_v1.md`。
|
||||
- 已完成:本报告。
|
||||
|
||||
## 5. 实际实现
|
||||
|
||||
- 已完成:`ParticleViewportReseedGesture` 只在一轮 invalidation 开始时保存 `startZoom`,连续帧不覆盖。
|
||||
- 已完成:沿用既有 150 ms debounce;stable 后先计算新 domain/bounds,再消费 gesture 判定。
|
||||
- 已完成:采用 `0.05` epsilon。只有 `endZoom < startZoom - 0.05` 才属于明确 Zoom Out。
|
||||
- 已完成:有效 viewport domain 下仅设置 `stateSeeded=false` 与 `trailSeeded=false`,复用现有 shader initial-seed、State/Trail target clear 与 pass 顺序。
|
||||
- 已完成:fallback/无效 domain 返回 `SkipInvalidDomain` 并清除 active gesture,不留延迟 pending。
|
||||
- 已完成:一次 gesture 只消费一次;下一轮重新捕获新的 startZoom;真实 `resetSequence()` 会取消内部 gesture。
|
||||
- 已完成:新增成功与 skip 单次诊断日志,包含任务要求字段。
|
||||
- 已完成:Zoom In、Pan、Rotate、Pitch 和 epsilon 抖动不触发 reseed。
|
||||
|
||||
## 6. 禁止项与接口边界
|
||||
|
||||
- 已完成:未为 Zoom Out 调用 `resetSequence()`。
|
||||
- 已完成:未重建 State/Trail GPU resource。
|
||||
- 已完成:未重新上传 Field;未 reset Timeline/T0/T1/MIX;未 reload style/source。
|
||||
- 已完成:未修改 OpenGL/Metal shader。
|
||||
- 已完成:未修改 UBO layout。
|
||||
- 已完成:未修改公共 Layer、Host、RN API。
|
||||
- 已完成:未引入 partial ratio、CPU readback、第二套粒子系统或 Host camera callback。
|
||||
|
||||
## 7. STEP-089 / STEP-090 保护
|
||||
|
||||
- 已完成:未修改 retained underlay、staticRevision/buildRevision、topology/invalidation 或 ParticleFastPath 文件。
|
||||
- 已完成:camera invalidation 与 stable debounce 主路径保持;本次只在动态粒子层 stable domain 判定后改变一次 seed flags。
|
||||
- 已完成:continuous simulation cadence、WeatherColor 分层、Runtime Field Window/T0/T1/MIX 保持。
|
||||
- 无法验证:Android 实机上的 `fastHit` 增长恢复由 RDC 后续串行确认;Codex 未运行 APK/ADB。
|
||||
|
||||
## 8. 构建记录
|
||||
|
||||
- 已完成:Mac OpenGL Debug 配置:`cmake -S . -B build-step091-opengl -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DMLN_WITH_OPENGL=ON -DMLN_WITH_GLFW=OFF -DMLN_WITH_WERROR=OFF`,PASS。sandbox module/ccache temp 指向 `/private/tmp`。
|
||||
- 已完成:`ninja -C build-step091-opengl -j3 CMakeFiles/mbgl-core.dir/src/mbgl/renderer/layers/render_particle_validation_layer.cpp.o test/CMakeFiles/mbgl-test.dir/renderer/particle_viewport_reseed.test.cpp.o`,2/2 PASS。
|
||||
- 未完成且不作为本 Step PASS 声明:新 build tree 的全量 `mbgl-test-runner` 冷链接;在受影响 objects 已独立 PASS 后,于 182/659 主动停止,避免把精确验证扩大成无关全仓冷构建。
|
||||
|
||||
## 9. 测试记录
|
||||
|
||||
- 已完成:仓库内 gtest source/object 编译 PASS。
|
||||
- 已完成:`clang++ -std=c++17 -Wall -Wextra -Werror -Isrc /private/tmp/step091_particle_viewport_reseed_directed.cpp ...` 后运行原生 directed executable,8/8 PASS。
|
||||
- 已完成:覆盖明确 Zoom Out、Zoom In、Pan、Rotate/Pitch、epsilon 边界、fallback 消费、单 gesture 单次、下一 gesture startZoom 刷新。
|
||||
- 已完成:`git diff --check` PASS。
|
||||
- 未运行:Jest。
|
||||
- 未运行:Android Gradle/APK/ADB/设备。
|
||||
|
||||
## 10. 生成产物
|
||||
|
||||
- 无:本任务不生成 AAR/APK/发布 artifact。
|
||||
|
||||
## 11. 已知问题与未完成事项
|
||||
|
||||
- 无已知 Codex 实现阻塞。
|
||||
- 无法验证:Android 正式 RasterParticleLayer 视觉效果、单次 `VIEWPORT_RESEED` 日志、GL/EGL、crash/ANR 与 ParticleFastPath stable 恢复,按任务边界交由 RDC。
|
||||
|
||||
## 12. 与任务书差异
|
||||
|
||||
- 无生产实现范围差异。
|
||||
- 验证方式说明:仓库内定向 gtest 已编译;因本 worktree 是全新 build tree,未完成非必要的 659-object 全 runner 冷链接,改以同一 production helper 的独立原生 C++ executable 实际运行 8/8,满足定向 native test 要求,未宣称全量 runner PASS。
|
||||
|
||||
## 13. Git 收尾
|
||||
|
||||
- implementation commit hash:`4286c16a53d4f49001f3b41ed4638d3c6edc95b3`。
|
||||
- commit message:`fix: reseed particles after zoom out`。
|
||||
- implementation push result:SUCCESS,`297b91262..4286c16a5 HEAD -> particle/main`。
|
||||
- remote branch:`origin/particle/main`。
|
||||
- documentation closeout push result:在本报告纳入 closeout commit 后执行,结果由最终交付回读记录。
|
||||
- Final HEAD / `origin/particle/main`:由最终 push 后的 `git rev-parse HEAD`、`git ls-remote` 回读记录在最终交付;Git commit 无法在其自身内容中自指尚未生成的 closeout hash。
|
||||
@@ -0,0 +1,157 @@
|
||||
# STEP-092 Continuous Range 可退化分段 Retained Cache Codex 完成报告 v1
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
**PARTIAL:实现完成,等待 RDC 独立编译、Native directed test、OpenGL/Metal 验证。**
|
||||
|
||||
未把静态审计写成编译通过,也未把 Android 项写成已执行;因此当前不满足任务的最终 PASS Gate。
|
||||
|
||||
## 2. Git 基线
|
||||
|
||||
- 机器:`Mac-mini-tei.lan`,Darwin x86_64。
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab`。
|
||||
- branch:`particle/main`。
|
||||
- Starting HEAD:`b29e564944197072a0effaec36ca95c48fe0649b`。
|
||||
- 起始 `origin/particle/main`:`b29e564944197072a0effaec36ca95c48fe0649b`。
|
||||
- 起始 dirty:仅用户提供的 untracked STEP-092 任务书;已保留并纳入本任务文档范围。
|
||||
- Implementation commit:`a9eaec6b65cc26aa70e03002bdc1326c46cad245`。
|
||||
- Implementation message:`feat: add continuous retained render ranges`。
|
||||
- Implementation push:SUCCESS,`b29e56494..a9eaec6b6 HEAD -> particle/main`。
|
||||
- 文档提交无法在自身内容中自指其最终 SHA;closeout SHA、最终本地/远端 HEAD 与最终 status 由提交后回读写入最终交付响应。
|
||||
|
||||
## 3. 修改文件
|
||||
|
||||
### C++ Renderer / Map
|
||||
|
||||
- `include/mbgl/map/map.hpp`
|
||||
- `include/mbgl/renderer/retained_underlay.hpp`
|
||||
- `src/mbgl/map/map.cpp`
|
||||
- `src/mbgl/renderer/continuous_layer_range.hpp`(新增)
|
||||
- `src/mbgl/renderer/render_orchestrator.cpp/.hpp`
|
||||
- `src/mbgl/renderer/renderer_impl.cpp/.hpp`
|
||||
- `src/mbgl/renderer/retained_underlay_cache.cpp/.hpp`
|
||||
- `src/mbgl/renderer/retained_underlay_runtime_state.hpp`
|
||||
|
||||
### Android API / bridge
|
||||
|
||||
- `platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMap.java`
|
||||
- `platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMap.java`
|
||||
- `platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapView.java`
|
||||
- `platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/RetainedUnderlayDiagnostics.java`
|
||||
- `platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp/.hpp`
|
||||
|
||||
### iOS API / bridge
|
||||
|
||||
- `platform/ios/src/MLNMapView.h/.mm`
|
||||
- `packages/maplibre-particle-rn/ios/WeatherRuntimeBridge.mm`
|
||||
|
||||
### RN API
|
||||
|
||||
- `packages/maplibre-particle-rn/src/NativeWeatherRuntimeBridge.ts`
|
||||
- `packages/maplibre-particle-rn/src/RendererRuntime.ts`
|
||||
- `packages/maplibre-particle-rn/src/types.ts`
|
||||
- `packages/maplibre-particle-rn/android/src/main/java/org/navsea/maplibre/particle/weather/WeatherRuntimeBridgeModule.kt`
|
||||
|
||||
### Tests / docs
|
||||
|
||||
- `test/renderer/retained_underlay_cache.test.cpp`
|
||||
- `docs/40_任务/STEP-092_ContinuousRange可退化分段RetainedCache_Codex任务_v1.md`
|
||||
- `docs/50_Step记录/STEP-092_ContinuousRange可退化分段RetainedCache_v1.md`
|
||||
- 本报告
|
||||
- `粒子开发记录.md`
|
||||
|
||||
## 4. Range config 所有者与解析
|
||||
|
||||
- map-scoped owner:`Map::Impl::retainedUnderlayState`。
|
||||
- thread safety:atomic control/counters + mutex-protected ID/config/resolution。
|
||||
- revision:每次实际 ID pair 变化单调递增;重复相同配置不前进。
|
||||
- ID→index:`RenderOrchestrator::createRenderTree()` 在完整 `orderedLayers` 上解析,visibility 不参与。
|
||||
- explicit valid 时只使用 `[startIndex, endIndex + 1)`;不读取 legacy boundary。
|
||||
- explicit invalid 时 index 置 `-1`、资源/prepared gate 失效并 full fallback;不按 Particle/Weather 类型纠正。
|
||||
|
||||
## 5. 1/2/3 Render Range 与 0/1/2 cache
|
||||
|
||||
- single-layer Continuous 正式支持。
|
||||
- Bottom+Continuous+Top / Bottom+Continuous / Continuous+Top / Continuous-only 全部由同一 plan 表达。
|
||||
- 两个 cache holder 分别代表 Bottom/Top capability;只有 range present 才建立 key 并 lazy 创建 RGBA texture/render target/compositor。
|
||||
- empty drawable content 不会把 Style 中实际存在的 Bottom/Top topology 偷偷消掉;可构建透明 retained texture。
|
||||
|
||||
## 6. Renderer、Z-order 与 Top 正确性
|
||||
|
||||
- Bottom build/reuse/composite → Continuous render → Top build/reuse/composite。
|
||||
- Opaque reverse、Translucent forward 保持;original ordinal、`currentLayer`、`depthRangeSize` 按全 Style 计算。
|
||||
- Top 使用透明 clear、RGBA offscreen、direct sample shader 与 alpha-blended premultiplied composite,可覆盖 Continuous Particle。
|
||||
- 未增加 LandMask RT、业务 Layer ID、Anchor/Marker 或第二套 metadata Z-order。
|
||||
|
||||
## 7. Invalidation / legacy / Fast Path
|
||||
|
||||
- STEP-089 content/topology/static revision 继续作为 conservative invalidation 基础;range config/end index/Top topology 已纳入。
|
||||
- 未配置 explicit range:保持 legacy retained-underlay。
|
||||
- explicit valid:legacy `startsRetainedDynamicRange()` 只可用于 fast-safety 类型证明,不参与 topology/boundary。
|
||||
- fast gate 对不存在 cache 不要求 valid;实际存在 Bottom/Top 都必须 valid/buildRevision 匹配。
|
||||
- unknown Continuous layer 不能证明 particle-only 安全时 full CPU fallback;正常 full range render/cache 不受影响。
|
||||
|
||||
## 8. Diagnostics
|
||||
|
||||
已增加并跨 C++/Android/iOS/RN 暴露:
|
||||
|
||||
- `rangeConfigured`, `rangeValid`
|
||||
- `startLayerId`, `endLayerId`, `startIndex`, `endIndex`
|
||||
- `bottomPresent`, `bottomBuild`, `bottomReuse`, `bottomInvalidation`
|
||||
- `topPresent`, `topBuild`, `topReuse`, `topInvalidation`
|
||||
- 保留 aggregate `fallback`, `lastReason` 与原有 enabled/build/reuse/invalidation。
|
||||
|
||||
Continuous-only 的 Bottom/Top present 均为 false,仍可为 rangeValid=true。
|
||||
|
||||
## 9. Native tests
|
||||
|
||||
测试源码新增覆盖:
|
||||
|
||||
- `start == end`、`start < end`、`start > end`
|
||||
- start/end missing
|
||||
- hidden endpoint ID 仍参与完整 ordered list
|
||||
- reorder、endpoint 删除后重新解析
|
||||
- 四种 topology 与 0/1/2 cache decision
|
||||
- Bottom/Top 不存在不造成 fast cache miss
|
||||
- config empty-ID/revision/same-ID
|
||||
- explicit valid/invalid 均不混用 legacy boundary
|
||||
|
||||
实际 test runner 尚未由 Codex 执行,等待 RDC。
|
||||
|
||||
## 10. OpenGL / Metal / Android
|
||||
|
||||
- OpenGL:源码实现完成;未执行 build/runtime,等待 RDC。
|
||||
- Metal:共用 range/cache topology 与既有 Metal compositor;源码实现完成;未执行 `ios-sdk-static`,等待 RDC。
|
||||
- Native directed tests:未执行,等待 RDC。
|
||||
- Android 项:**未执行 Android Gradle、AAR/APK、ADB、安装、设备视觉或生命周期验证**。
|
||||
- Jest:按任务要求未新增、未执行。
|
||||
|
||||
## 11. 静态检查与验证命令
|
||||
|
||||
Codex 已执行 `git diff --check`;最终提交前再次执行并回读。
|
||||
|
||||
RDC 精确命令:
|
||||
|
||||
```bash
|
||||
cmake --build build-step088-opengl --target mbgl-test-runner -j3
|
||||
build-step088-opengl/mbgl-test-runner \
|
||||
--gtest_filter='ContinuousLayerRange.*:RetainedDynamicBoundary.*:RetainedLayerGroupRange.*:RetainedUnderlayCache.*:RetainedUnderlayRevision.*:ParticleFastPath.*'
|
||||
cmake --build build-ios --target ios-sdk-static -- -quiet
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## 12. Known limitations / 未完成
|
||||
|
||||
- 尚无 RDC 编译结果,桥接/codegen/ObjC++/C++ 编译结论不得写 PASS。
|
||||
- 尚无 OpenGL/Metal frame capture 或资源计数运行证据。
|
||||
- 尚无 Android lifecycle/style mutation/视觉验收。
|
||||
- 本任务只支持一个 explicit Continuous Range;无 clear API、多个 Range 或 N Segment Planner。
|
||||
|
||||
## 13. Git 收尾
|
||||
|
||||
- scoped staging:只纳入本任务上列源码、测试与文档。
|
||||
- 禁止项:未使用 `git add -A`、`git add .`、reset、clean 或 force push。
|
||||
- Implementation commit SHA/message:`a9eaec6b65cc26aa70e03002bdc1326c46cad245` / `feat: add continuous retained render ranges`。
|
||||
- Implementation push result:SUCCESS。
|
||||
- Remote branch:`origin/particle/main`。
|
||||
- Final HEAD / `origin/particle/main` / `git status --short`:待提交与 push 后外部回读;在两者一致前不声明 Git 完成。
|
||||
@@ -0,0 +1,165 @@
|
||||
# STEP-092 Continuous Range 可退化分段 Retained Cache Codex 完成报告 v2
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
**PASS:Codex 实现 + RDC 独立 Mac Native 验证闭环完成。**
|
||||
|
||||
本报告是在 v1 PARTIAL 基础上的验证收尾版。RDC 已完成 OpenGL test-runner 编译、定向 Native tests、Metal-only iOS 静态 SDK 编译,并修复两处真实验证问题后重新通过。Android Gradle/APK/ADB/实体机与 Metal GPU frame capture 未执行,均明确保留为未执行项。
|
||||
|
||||
## 2. Git 基线与提交
|
||||
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab`
|
||||
- branch:`particle/main`
|
||||
- Starting HEAD:`b29e564944197072a0effaec36ca95c48fe0649b`
|
||||
- Codex implementation:`a9eaec6b65cc26aa70e03002bdc1326c46cad245`
|
||||
- Codex v1 docs:`b89c8c8cb4d941abdcd704b278284e209ba7399b`
|
||||
- RDC validation fix:`4908244d2`,message:`fix: close STEP-092 native validation issues`
|
||||
- 文档收尾提交在本报告写入后产生,最终 HEAD / remote HEAD 由收尾回读确认。
|
||||
|
||||
## 3. Codex 实现范围
|
||||
|
||||
C++ Renderer:
|
||||
- explicit Continuous Range config / resolution
|
||||
- 0/1/2 retained range cache
|
||||
- Bottom / Continuous / Top render slicing
|
||||
- Fast Path gate
|
||||
- diagnostics 与 invalidation
|
||||
|
||||
跨端 API:
|
||||
- C++ Map API
|
||||
- Android Java/JNI
|
||||
- iOS ObjC++
|
||||
- RN TypeScript / Android / iOS bridge
|
||||
核心语义:
|
||||
- `[startLayerId,endLayerId]` 为闭区间。
|
||||
- single-layer Continuous 合法。
|
||||
- hidden endpoint 仍决定 topology。
|
||||
- Style Layer 顺序是唯一 Z-order。
|
||||
- explicit valid 与 legacy boundary 排他。
|
||||
- explicit invalid fail closed。
|
||||
- Opaque reverse / Translucent forward 与 global ordinal 保持。
|
||||
|
||||
## 4. RDC 第一次 OpenGL 编译
|
||||
|
||||
命令:
|
||||
`cmake --build build-step088-opengl --target mbgl-test-runner -j3`
|
||||
|
||||
结果:FAIL。
|
||||
|
||||
首个真实错误:
|
||||
- `renderer_impl.cpp:873-874`
|
||||
- `RenderItem` 无 `index` 成员。
|
||||
|
||||
原因:
|
||||
- `layerRenderItems` 实际类型是 `std::set<LayerRenderItem>`;
|
||||
- 代码把当前元素绑定为 `const RenderItem&` 后又读取派生类 `index`。
|
||||
|
||||
## 5. RDC 第二次 OpenGL 编译
|
||||
|
||||
第一次局部修复改为 `LayerRenderItem&` 后继续编译,新的真实错误为:
|
||||
- `LayerRenderItem::hasRenderPass` private
|
||||
- `LayerRenderItem::render` private
|
||||
|
||||
最终修复:
|
||||
- `LayerRenderItem& layerItem` 专门读取 `index`;
|
||||
- 再绑定 `const RenderItem& item = layerItem` 调用公开虚接口。
|
||||
|
||||
第三轮 OpenGL build:
|
||||
- exit code:0
|
||||
- target:`mbgl-test-runner`
|
||||
- 结果:PASS。
|
||||
## 6. Native directed tests
|
||||
|
||||
过滤器:
|
||||
`ContinuousLayerRange.*:RetainedDynamicBoundary.*:RetainedLayerGroupRange.*:RetainedUnderlayCache.*:RetainedUnderlayRevision.*:ParticleFastPath.*`
|
||||
|
||||
第一轮:
|
||||
- 29 tests / 6 suites
|
||||
- PASS 28
|
||||
- FAIL 1
|
||||
|
||||
唯一失败:
|
||||
`RetainedUnderlayCache.RuntimeControlDisablesAndReenablesMapScopedCache`
|
||||
|
||||
实际值:
|
||||
- disabled.invalidation = 3
|
||||
- expected = 2
|
||||
|
||||
根因:
|
||||
- baseline 中 `setEnabled(false)` 已记录一次 aggregate `runtime-disabled` invalidation;
|
||||
- STEP-092 新增 range cache 同步路径再次调用 aggregate `invalidate()`;
|
||||
- 因而同一 disable 事件被重复计入 aggregate diagnostics。
|
||||
|
||||
RDC 修复:
|
||||
- disable 时不再重复调用 aggregate `recordInvalidation`;
|
||||
- 保持 legacy aggregate 计数语义;
|
||||
- 仍为实际 Bottom/Top cache 记录独立 `recordRangeInvalidation`。
|
||||
|
||||
第二轮:
|
||||
- 29/29 PASS
|
||||
- exit code:0
|
||||
- 全部 6 suites PASS。
|
||||
## 7. Metal / iOS 验证
|
||||
|
||||
后端真实性:
|
||||
- `MLN_WITH_METAL=ON`
|
||||
- `MLN_WITH_OPENGL=OFF`
|
||||
- Xcode project 定义 `MLN_RENDER_BACKEND_METAL=1`
|
||||
|
||||
命令:
|
||||
`cmake --build build-ios --target ios-sdk-static -- -quiet`
|
||||
|
||||
结果:
|
||||
- exit code:0
|
||||
- PASS
|
||||
- 仅有既有 EAGL deprecated、bundle identifier / script phase warning。
|
||||
|
||||
因此本轮不是“iOS target 名称推断”,而是实际 Metal-only 配置下完成原生编译与链接。
|
||||
|
||||
## 8. GPU resource lifecycle / composite 核验
|
||||
|
||||
RDC 审计的实际代码路径:
|
||||
- RendererImpl 常驻 Bottom/Top cache holder,但 GPU 资源不在构造时创建。
|
||||
- `prepareResources()` 只在对应 decision 为 Build/Reuse 时进入。
|
||||
- Range 不存在时 key/decision 为 Disabled,不执行 `prepareResources()`。
|
||||
- `createOffscreenTexture(..., UnsignedByte, true, true)` 仅在 texture 不存在或 viewport 变化时执行。
|
||||
- 同 viewport 复用已有 texture。
|
||||
- `compositeLayerGroup` 存在时不重建 compositor。
|
||||
- build render target clear color:透明 `0,0,0,0`。
|
||||
- composite:`gfx::ColorMode::alphaBlended()`。
|
||||
- `alphaBlended()` 使用 `src=One`、`dst=OneMinusSrcAlpha`,符合 premultiplied-alpha。
|
||||
- OpenGL 与 Metal Composite Shader 均直接采样 retained RGBA texture。
|
||||
- Renderer final 顺序:Bottom composite → Continuous render → Top composite。
|
||||
- Native test 已验证四种 topology、0/1/2 cache decision、reuse/invalidation 与 absent cache fast gate。
|
||||
|
||||
这些结果共同证明当前源码中不存在固定分配两个 retained GPU target,也不存在 stable frame 每帧重建同尺寸 texture/compositor 的路径。
|
||||
|
||||
## 9. RDC 实际修改
|
||||
|
||||
1. `src/mbgl/renderer/renderer_impl.cpp`
|
||||
- 修复 legacy `LayerRenderItem` index 访问与基类虚接口调用。
|
||||
|
||||
2. `src/mbgl/renderer/retained_underlay_cache.cpp`
|
||||
- 修复 runtime disable aggregate invalidation 重复计数;
|
||||
- 保留 Bottom/Top range invalidation diagnostics。
|
||||
|
||||
RDC 修复提交:
|
||||
- `4908244d2`
|
||||
- `fix: close STEP-092 native validation issues`
|
||||
- push:SUCCESS。
|
||||
|
||||
## 10. 未执行项目
|
||||
|
||||
- Android Gradle / AAR / APK:未执行。
|
||||
- ADB / 安装 / Android 实体机视觉:未执行。
|
||||
- iOS 实机 GPU runtime:未执行。
|
||||
- Metal frame capture / GPU resource counter:未执行。
|
||||
- Jest:未新增、未执行。
|
||||
|
||||
当前仓库未提供一个可直接运行的 STEP-092 Metal GPU runtime test target,因此未将 Metal compile PASS 描述成 frame-capture PASS。
|
||||
|
||||
## 11. 最终验收判断
|
||||
|
||||
STEP-092 的规定架构、四种 Range、0/1/2 retained cache、global layerIndex、Opaque/Translucent、Fast Path、diagnostics、RN/Android/iOS API、Native directed tests、OpenGL Native build、Metal-only iOS build、资源生命周期路径、文档和 Git 闭环均已完成。
|
||||
|
||||
结论:**CLOSED / PASS(Mac Native 范围)**。
|
||||
127
docs/报告/STEP-093_ContinuousRange运行态综合验收_Codex完成报告_v1.md
Normal file
127
docs/报告/STEP-093_ContinuousRange运行态综合验收_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# STEP-093 Continuous Range 运行态综合验收 Codex 完成报告 v1
|
||||
|
||||
## 1. 交付结论
|
||||
|
||||
**IMPLEMENTED / WAITING_RUNTIME_VALIDATION**
|
||||
|
||||
Codex 已完成 STEP-093 所需的 Android 可重复运行态测试入口、最小 diagnostics 可观察性、Fast Path fail-closed 原因、跨平台字段透传与定向测试源码,并完成静态一致性审查。Android/iOS runtime 均未实际执行,所以没有任何运行态场景被标记为 PASS。
|
||||
|
||||
## 2. Git 信息
|
||||
|
||||
- Repository:`/Users/tei/dev/MapLibreParticleLab`
|
||||
- Branch:`particle/main`
|
||||
- Starting HEAD / starting remote:`8a5d88008c142e2a80e0ff602d228e0e67de2d83`
|
||||
- Implementation commit:`b3ec8edc87ece404b5f7b3706f5acf2a70de6862`(`feat: add STEP-093 runtime validation tooling`)
|
||||
- Documentation closeout:本报告所在 scoped commit,在报告写入后生成;准确 hash、push 和最终 remote 回读以最终交付消息为准。
|
||||
|
||||
## 3. 修改文件
|
||||
|
||||
Core/Renderer:
|
||||
|
||||
- `include/mbgl/renderer/retained_underlay.hpp`
|
||||
- `src/mbgl/renderer/particle_fast_path.hpp`
|
||||
- `src/mbgl/renderer/renderer_impl.cpp/.hpp`
|
||||
- `src/mbgl/renderer/retained_underlay_cache.cpp/.hpp`
|
||||
- `src/mbgl/renderer/retained_underlay_runtime_state.hpp`
|
||||
- `test/renderer/retained_underlay_cache.test.cpp`
|
||||
|
||||
Android:
|
||||
|
||||
- `platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp`
|
||||
- `platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/RetainedUnderlayDiagnostics.java`
|
||||
- Test App Manifest、validation Activity 与 layout。
|
||||
|
||||
iOS / RN:
|
||||
|
||||
- `platform/ios/src/MLNMapView.h/.mm`
|
||||
- RN TypeScript types/spec、Android/iOS bridge、RN Test Host diagnostics display。
|
||||
|
||||
文档:
|
||||
|
||||
- STEP-093 任务书、Step 记录、Codex 完成报告;
|
||||
- `粒子开发记录.md` 追加记录。
|
||||
|
||||
## 4. 测试入口
|
||||
|
||||
Android:启动 `ParticleCoreLayerValidationActivity`,点击底部 `STEP 093`。菜单提供 A/B/C/D 四种 topology、E single-layer、mutation/cross-range、endpoint/visibility、invalid range、camera 与 Fast Path fail-closed;屏幕显示完整 pull diagnostics。
|
||||
|
||||
iOS:正式 diagnostics API 与 RN bridge 字段已补齐,但本轮没有建立/运行 iOS 专用 STEP-093 validation UI。状态为 `WAITING_IOS_RUNTIME_VALIDATION`。
|
||||
|
||||
完整 RDC 命令、逐项操作、判定标准和验收矩阵见同版本 Step 记录。
|
||||
|
||||
## 5. Diagnostics
|
||||
|
||||
新增并端到端暴露:
|
||||
|
||||
- Range configured/valid/IDs/index/continuous bounds/fallback/last reason;
|
||||
- Bottom/Top present/cache state/build/reuse/invalidation/last reason;
|
||||
- Bottom/Top texture create/composite create/resource release;
|
||||
- Fast Path eligible/active/fallback reason/hit/miss/full CPU frame;
|
||||
- unsupported Continuous layer 的 `unsupported-continuous-layer` 原因。
|
||||
|
||||
所有数据复用 STEP-092 map-scoped runtime state;没有增加 topology metadata 或 Z-order 真相源。counter 只代表事件累计,不代表 GPU memory bytes。
|
||||
|
||||
## 6. 静态审查与实际测试结果
|
||||
|
||||
实际执行并通过:
|
||||
|
||||
- Native → Android/iOS → RN diagnostics 字段与 Android 39 项 ABI 索引静态核对;
|
||||
- 场景 layer 顺序、endpoint 与菜单接线静态审查;
|
||||
- `xmllint --noout` 检查 Test App Manifest/layout:PASS;
|
||||
- `git diff --check`:PASS。
|
||||
|
||||
明确未执行:
|
||||
|
||||
- CMake/Ninja/Xcode/Gradle 编译:NOT RUN;
|
||||
- Native directed tests:NOT RUN;
|
||||
- TypeScript/codegen/Android JNI 实际编译:NOT RUN;
|
||||
- APK build/install、ADB、模拟器/真机:NOT RUN;
|
||||
- Android runtime/视觉/长时间运行:`WAITING_ANDROID_RUNTIME_VALIDATION`;
|
||||
- iOS/Metal runtime/视觉/长时间运行:`WAITING_IOS_RUNTIME_VALIDATION`;
|
||||
- Jest:未新增、未执行。
|
||||
|
||||
STEP-092 的 OpenGL/Metal build 与 29/29 tests 是起始历史证据,不是本轮新增代码的验证结果。
|
||||
|
||||
## 7. Android runtime 结果
|
||||
|
||||
**WAITING_ANDROID_RUNTIME_VALIDATION**
|
||||
|
||||
没有执行 Android Gradle、APK、ADB、安装、启动或画面操作。A–E、Z-order、stable reuse、invalidation、mutation、endpoint、visibility、invalid、Fast Path 与 alpha 全部为 NOT RUN。
|
||||
|
||||
## 8. iOS/Metal runtime 结果
|
||||
|
||||
**WAITING_IOS_RUNTIME_VALIDATION**
|
||||
|
||||
没有执行 iOS build/install/simulator/device/Metal runtime。Android 后续即使 PASS,也不能自动推导 iOS PASS;Metal Native build 历史结果也不能替代 runtime rendering。
|
||||
|
||||
## 9. 架构边界
|
||||
|
||||
本轮没有:
|
||||
|
||||
- 修改 `[startLayerId,endLayerId]` 闭区间或内部半开区间数学;
|
||||
- 增加第二套 Bottom/Continuous/Top metadata;
|
||||
- 增加 anchor/marker、特殊 layer type、多 range 或频率分类;
|
||||
- 在 Renderer 硬编码业务 Layer ID;
|
||||
- 重构 RenderGraph/PBF/Particle 算法;
|
||||
- 把 visibility 变成 topology 规则;
|
||||
- 为 Fast Path 移动未知 Continuous layer。
|
||||
|
||||
Style layer 顺序仍是唯一 Z-order 真相;App 只提供 endpoint ID,Native 解析 index。
|
||||
|
||||
## 10. 风险与已知限制
|
||||
|
||||
- 尚无任何运行态证据,不能确认真实 GPU 资源、画面合成或平台差异。
|
||||
- Android diagnostics 每秒 pull 一次但不逐秒 log;证据需手动 `READ DIAGNOSTICS`、截图或外部采样。
|
||||
- counter 在同一 Map 生命周期累计,RDC 应对比增量。
|
||||
- iOS 缺专用 STEP-093 validation 页面,需后续补齐实际可操作入口。
|
||||
- 本轮新增定向测试源码尚未编译/运行,任何预期 test 数都不能写成 PASS。
|
||||
|
||||
## 11. 最终状态
|
||||
|
||||
- 实现:完成;
|
||||
- 静态一致性审查:完成;
|
||||
- Android runtime:`WAITING_ANDROID_RUNTIME_VALIDATION`;
|
||||
- iOS/Metal runtime:`WAITING_IOS_RUNTIME_VALIDATION`;
|
||||
- 综合:**IMPLEMENTED / WAITING_RUNTIME_VALIDATION**。
|
||||
|
||||
Git push、`HEAD == origin/particle/main` 与 clean worktree 由本报告之后的 scoped 文档提交和最终回读确认;这些 Git 结果不改变 runtime 尚未执行的事实。
|
||||
126
docs/报告/STEP-094_通用RenderSegment分段渲染与Style分类诊断_Codex完成报告_v1.md
Normal file
126
docs/报告/STEP-094_通用RenderSegment分段渲染与Style分类诊断_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# STEP-094 通用 RenderSegment 分段渲染与 Style 分类诊断 Codex 完成报告 v1
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
**PASS / CLOSED(RDC 独立验证完成)**
|
||||
|
||||
任务书范围的 Core、Renderer、Android/iOS/RN bridge、定向测试、diagnostics pull 入口和中文文档均已完成。Codex 负责实现,RDC 按 AGENTS.md 独立完成 OpenGL、Metal、RN codegen/typecheck 与 Android Validation APK 验证。规定的代码与构建 Gate 均通过;Android/iOS 实体机 runtime/视觉未执行,未将其虚报为 PASS。
|
||||
|
||||
## 2. Git 基线
|
||||
|
||||
- Branch:`particle/main`
|
||||
- Starting HEAD:`8fbb892a1236a55329ed9ef5a612640c9363d3f1`
|
||||
- Starting remote:`origin/particle/main` 同上
|
||||
- Implementation commit:`016fd1fff`(完整 SHA `016fd1fff6a33cf795c10d4f5c5413d308383677`,如最终 Git 回读一致则以 Git 为准)
|
||||
- Commit message:`feat: add generic render segment model`
|
||||
- Implementation push:PASS,已推送 `origin/particle/main`
|
||||
- 文档收尾:本报告、Step 记录、设计文档与开发记录在实现 commit 后独立提交;包含本报告的 commit 无法在文件自身内自引用 SHA,最终远端 HEAD 由 RDC push 后回读确认。
|
||||
|
||||
## 3. 实际实现
|
||||
|
||||
- `RenderSegmentStrategy/Definition/Plan/Diagnostics` typed Core model。
|
||||
- schemaVersion=1 JSON parser/serializer,非法输入使 active plan invalid,不复用 stale topology。
|
||||
- boundary Layer ID → ordered Style layerIndex resolver,完整 coverage、严格递增、非空 Segment。
|
||||
- N Segment Renderer 遍历与 topology-sized retained cache ownership。
|
||||
- 每个 retained cache 独立 revision/build/reuse/invalidation/resource counters;Continuous 无 cache。
|
||||
- topology change 释放旧 cache;稳定 viewport lazy reuse。
|
||||
- LayerMutation 通过 immutable Impl fingerprint segment-local invalidation;无法可靠归属的 mutation 全 retained invalidation。
|
||||
- V12 `setContinuousLayerRange()` compatibility mapping 与旧 diagnostics API 保留。
|
||||
- 单 Continuous Particle Fast Path 保留;多 Continuous 明确 fallback。
|
||||
- diagnostics 返回 ordered styleLayers、ordered segments、每段 layers、sourceId/sourceLayer(可取得时)和 cache stats。
|
||||
- Android/iOS platform API、既有 RN TurboModule、typed TS API 和 RN Test Host pull 入口完成。
|
||||
|
||||
## 4. 修改文件类别
|
||||
|
||||
- Core public/private:Map API、RenderSegment model/resolver/JSON、runtime state、orchestrator、renderer、retained cache。
|
||||
- Build:CMake 与 Bazel core header/source lists。
|
||||
- Android:MapLibreMap/NativeMap/NativeMapView/JNI。
|
||||
- iOS:MLNMapView public API 与 ObjC++ bridge。
|
||||
- RN:types、Native Spec、RendererRuntime、Android/iOS bridge、Test Host。
|
||||
- Tests:`test/renderer/retained_underlay_cache.test.cpp`。
|
||||
- Docs:STEP-094 设计、Step 记录、本报告、开发记录。
|
||||
|
||||
准确逐文件列表以最终 `git status --short` 为准。
|
||||
|
||||
## 5. Diagnostics 与一致性
|
||||
|
||||
- valid 时 `segmentCount == segments.length`、`styleLayerCount == styleLayers.length`、Segment 连续覆盖 `[0,layerCount)`。
|
||||
- 每段 `layers` 从同一 ordered styleLayers slice 生成;flatten 后顺序与 styleLayers 完全一致。
|
||||
- 全局 layerIndex 不按 Segment 重编号。
|
||||
- Continuous `cache=null`;Retained cache object 包含 content revision 与生命周期 counters。
|
||||
- invalid plan 保留当前 ordered styleLayers,segments 为空且 `valid=false/fallback=true`。
|
||||
- snapshot 只在显式 pull 时 JSON stringify;Style 字符串 metadata 仅在 config/style layer metadata 变化时重建。
|
||||
|
||||
## 6. OpenGL / Metal / Tests
|
||||
|
||||
- OpenGL build:PASS。命令 `cmake --build build-step088-opengl --target mbgl-test-runner -j3`,exit 0;仅既有 Boost/macOS deployment warnings。
|
||||
- Native directed tests:PASS。过滤集共 8 suites / 43 tests,43/43 PASS;其中 `RenderSegmentPlan` 9、`RenderSegmentDiagnostics` 4,并包含 V12 Continuous/Retained/Fast Path 回归。
|
||||
- Metal build:PASS。预先确认 `MLN_WITH_METAL=ON`、`MLN_WITH_OPENGL=OFF`;`cmake --build build-ios --target ios-sdk-static -- -quiet` exit 0。存在既有 EAGL/statusBar deprecation 与 bundle-id warning,无编译/链接错误。
|
||||
- RN package TypeScript:PASS。
|
||||
- RN Test Host TypeScript:PASS,验证时临时将 node_modules package 指向本工作树,结束后恢复旧依赖。
|
||||
- RN codegen:PASS。工程的 `npx react-native codegen` 因缺少 `@react-native-community/cli` 入口返回 1;未升级/修改依赖,改用同一已安装 RN 0.85 `@react-native/codegen` 的 combine-js-to-schema + generate-all,exit 0,并在 Android Java/JNI/C++ JSI 与 iOS ObjC++ generated Spec 中确认两个新方法。
|
||||
- Android Validation APK:PASS。标准脚本 `build_validation_apk.sh`,Gradle `BUILD SUCCESSFUL in 4m 45s`;x86_64-only、strip=true;APK 60,294,298 bytes,`libmaplibre.so` 39,421,736 bytes,SHA-256 `848c3196c64cee6dfb1e9627f4af32b6e235fdd82afd1cfb8474c7fc5d591c71`。
|
||||
- Android/iOS runtime/视觉:NOT RUN。
|
||||
- Jest:未加入、未执行。
|
||||
|
||||
## 7. RDC 精确验证命令
|
||||
|
||||
### OpenGL Native runner
|
||||
|
||||
```bash
|
||||
cd /Users/tei/dev/MapLibreParticleLab
|
||||
cmake --build build-step088-opengl --target mbgl-test-runner -j3
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='RenderSegmentPlan.*:RenderSegmentDiagnostics.*:ContinuousLayerRange.*:RetainedDynamicBoundary.*:RetainedLayerGroupRange.*:RetainedUnderlayCache.*:RetainedUnderlayRevision.*:ParticleFastPath.*'
|
||||
```
|
||||
|
||||
### Metal / iPhoneOS arm64 compile
|
||||
|
||||
```bash
|
||||
cd /Users/tei/dev/MapLibreParticleLab
|
||||
cmake --build build-ios --target ios-sdk-static -- -quiet
|
||||
```
|
||||
|
||||
RDC 必须先确认 `build-ios/CMakeCache.txt` 的 `MLN_WITH_METAL=ON`、`MLN_WITH_OPENGL=OFF`,不能用 target 名称推断后端。
|
||||
|
||||
### RN TypeScript / Codegen
|
||||
|
||||
在现有 RN Test Host 已安装依赖环境中执行:
|
||||
|
||||
```bash
|
||||
cd /Users/tei/dev/MapLibreParticleLab/integration/react-native/MapLibreParticleRNTestHost
|
||||
npm run typecheck
|
||||
npx react-native codegen
|
||||
```
|
||||
|
||||
若当前工程的 RN 0.85 codegen 正式入口由 Expo prebuild/Pods 驱动,RDC 应使用现行 STEP-083 流程,并核对 generated Android/iOS Spec 确实含 `setRenderSegmentPlanJson`、`getRenderSegmentDiagnosticsJson`,不要只看 TS。
|
||||
|
||||
### Android compile / Validation APK
|
||||
|
||||
```bash
|
||||
cd /Users/tei/dev/MapLibreParticleLab
|
||||
./tools/particle_android_validation/build_validation_apk.sh
|
||||
```
|
||||
|
||||
若还需 RN Test Host Android compile,使用当前 STEP-080 v2 公网/本地 package handoff 流程生成包含本工作树 package 的测试依赖后再编译;不得误用远端 `.10` tgz 代替本轮源码。
|
||||
|
||||
## 8. 需要验证的 targets / tests
|
||||
|
||||
- `mbgl-test-runner`
|
||||
- `ios-sdk-static`(Metal-only iPhoneOS arm64)
|
||||
- Android `MapLibreAndroid` JNI/Java 与 `MapLibreAndroidTestApp` OpenGL Debug x86_64
|
||||
- RN `WeatherRuntimeBridgeSpec` Android/iOS codegen
|
||||
- RN Test Host TypeScript
|
||||
- Native suites:`RenderSegmentPlan`、`RenderSegmentDiagnostics` 及全部 V12 Continuous/Retained/Fast Path 回归
|
||||
- 运行态重点:R-C-R、C-R-R、R-R-C、R-C-R-C-R、相邻 R-R 独立 counters、invalid/recovery、hidden boundary、Cache OFF、单 Continuous Fast Path、多 Continuous fallback、Style/segment layer flatten 一致性
|
||||
|
||||
## 9. Known limitations / 未完成
|
||||
|
||||
- 多 Continuous Fast Path 未优化,按任务边界明确 fail closed。
|
||||
- Source/Tile mutation 仍为保守全 retained invalidation。
|
||||
- RN Test Host package.json 仍指向历史远端 `.10` tgz;本次通过临时工作树 package + 直接 RN 0.85 codegen 验证新接口,没有把旧包结果冒充新实现。
|
||||
- Android/iOS 实体机 runtime、视觉与性能观察 NOT RUN;本报告的 PASS 仅表示 STEP-094 规定的代码、编译、测试、ABI/codegen Gate 已通过。
|
||||
- Android Validation build-info 中 commit 字段为构建时尚未提交的起始 HEAD,且 `git_dirty=true`;这是预提交验证事实,产物 SHA 与验证结果有效,但不是发布 artifact。
|
||||
|
||||
## 10. 最终状态
|
||||
|
||||
代码/构建验收:`PASS / CLOSED`。实现 commit `016fd1fff` 已 push;文档收尾 commit 完成后由 RDC 再次确认 `HEAD == origin/particle/main` 与工作树 clean。
|
||||
90
docs/报告/STEP-095_V13独立严格代码与性能审查_Codex完成报告_v1.md
Normal file
90
docs/报告/STEP-095_V13独立严格代码与性能审查_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# STEP-095 V13 独立严格代码与性能审查 — Codex 完成报告 v1
|
||||
|
||||
日期:2026-09-25(Asia/Tokyo)。本轮为独立静态调查与中文文档交付,不是修复或产品验收。
|
||||
|
||||
## 1. 执行摘要与状态
|
||||
|
||||
已完成 V13 冻结源码审查、旧 F01~F10 独立复核、V12/V13 成本比较和 RDC 验证方案。新增最高优先问题为 target 切换后 stencil 记录未失效、Metal depth/stencil 契约未完整下传、绘制抛错后 cache 仍可能标成功、V4 PLAY 未接入生产时钟推进。另有 JSON、数值入库、listener、物化/UI 成本等明确源码链。
|
||||
|
||||
没有运行构建、测试、设备或 profiler;不宣称已发生黑屏、OOM、崩溃或达到某个性能收益。调查内容完成,Git 状态以[阶段记录第 4 节](../50_Step记录/STEP-095_V13独立严格代码与性能审查_v1.md)追加结果为准。**调查交付 PASS 与 V13 产品运行态 PASS 分离;后者本轮不成立,运行验证均 NOT RUN/NEEDS_RUNTIME。**
|
||||
|
||||
## 2. 基线与审查范围
|
||||
|
||||
环境为 `Mac-mini-tei.lan`、`/Users/tei/dev/MapLibreParticleLab`、`particle/main`。起始 HEAD/实际 origin 为 `05878d662308b9a92c20c433c3cd2943d74ae718`,工作树 clean。冻结源码 `ee0b89e711bf01335b3aa71d36def785cd97fec3`,功能提交 `016fd1fff6a33cf795c10d4f5c5413d308383677`。RN/Android `13.4.1-particle.13`、iOS `6.28.0-particle.13`;manifest/tag 已核对,manifest sourceCommit `ddf20dec…` 至冻结点无生产源码变化。
|
||||
|
||||
本次恢复指令由用户在同一会话明确指定 `gpt-6-astra high`,符合任务要求。该记录不冒充底层启动参数的独立核验。保留已有调查正文,仅做模型来源澄清及引用校正。
|
||||
|
||||
已读取 AGENTS、当前规范/总则/基线,先源码后旧报告,交叉检查 STEP-088~094;范围包括 Segment JSON/resolver、全局排序、runtime/cache、GL/Metal offscreen、State/Trail/时间场/QBIN、Android/iOS/RN、Host 版本锁及 Integration 只读状态。Vulkan、全部驱动/并发交错、业务 provider/HTTP、产物独立重建下载、真实 NavSea 接入未验证。
|
||||
|
||||
完整 manifest 坐标/散列、源码路径图及范围限制见[调查第 2 节](../20_调查/STEP-095_V13独立严格代码与性能调查_v1.md)。发布回读仅 HISTORY,文档提交不发布新软件。
|
||||
|
||||
## 3. Finding 总表与详细证据入口
|
||||
|
||||
以下为交付索引;每项的精确文件/行范围、最短可复核调用链、反例、修复方向和验收方案完整保存在[调查第 3~6 节](../20_调查/STEP-095_V13独立严格代码与性能调查_v1.md),作为本报告证据明细,不另复制一套可能漂移的行号。D=缺陷路径,C=确定成本,R=后果待运行,E=环境偏差;源码链置信度均高。
|
||||
|
||||
| 编号/级别/类别 | 问题及最短触发/影响 | 最小修复方向 | 验收 |
|
||||
| --- | --- | --- | --- |
|
||||
| N01 P1 D+R | 同 tiles 的 R-R/R-C 跨 target 复用 CPU stencil 映射 | target generation 隔离/重置 | OFF基线、stencil capture |
|
||||
| N02 P1 D+R | Metal clear/attachment 契约缺口 | 完整翻译 depth/stencil/combined attachment | 真机/模拟器 validation/capture |
|
||||
| N03 P1 D+R | group 抛错被 catch,残图仍 markBuildSucceeded | build 成功状态传递,失败不可发布 | 故障注入/恢复/no reuse |
|
||||
| N04 P1 D | 一次 set time 后 PLAY 无生产 advance | owner-thread 唯一时钟接线 | PLAY/PAUSE/速度/跨窗口 |
|
||||
| N05 P2 D | NUL 截断/重复 strategy 绕过严格语义 | 长度感知与唯一键校验 | JSON/两桥接错误回归 |
|
||||
| N06 P2 D+R | 坏数值先入库,物化失败丢可用投影 | 入库前约束/事务发布 | 好旧帧→坏高revision |
|
||||
| N07 P2 D | 同 view attach/detach listener递增 | 单listener/幂等/移除 | 100次/换view/销毁 |
|
||||
| C01 P1 C | full diagnostics 锁内深复制进热路 | 轻量一致快照,完整pull隔离 | alloc/锁/复制次数 |
|
||||
| C02 P1 C+R | retained及普通粒子Metal wait | 先profile,再有序提交 | wait/GPU/State-Trail顺序 |
|
||||
| C03 P1 C | dirty销毁同尺寸allocation | content/resource失效分离 | mutation/resize/context分别计数 |
|
||||
| C04 P1 C+R | GPU多R和CPU多field无总预算 | map预算/安全fallback | 阶梯压力/peak/长稳 |
|
||||
| C05 P2 C | ordinal线性与fast全items扫描 | generation索引/粒子列表 | S/G扩展和reorder回归 |
|
||||
| C06 P1 C+R | 同时间/mix也整网格物化、UI提交 | 早退/端点复用/安全异步发布 | 大网格/UI/alloc/rebuild |
|
||||
| C07 P2 C | tick drawable重建、端点复制 | 保留对象/不可变共享 | tick与窗口边界分测 |
|
||||
| O01 P2 D | 旧cache析构计数落新topology index | identity/generation计数 | 真cache切换/析构 |
|
||||
| E01 P1 E | Host `.10`/native `.13` | 对齐依赖和lock,独立后续任务 | 干净包/typecheck/codegen/设备 |
|
||||
| E02 P3 E | Integration旧协调状态 | 负责人按接入事实更新 | commit/包/设备交叉核对 |
|
||||
|
||||
## 4. 旧 F01~F10 独立复核
|
||||
|
||||
| 旧项 | 意见 | 修正依据 |
|
||||
| --- | --- | --- |
|
||||
| F01 | CONFIRMED | 深复制链成立,成功fast可两次而非所有帧必两次 |
|
||||
| F02 | CONFIRMED | retained仅build等待,补普通State/Trail等待 |
|
||||
| F03 | CONFIRMED | dirty释放成立,稳定reuse不分配 |
|
||||
| F04 | CONFIRMED | 无总预算成立,OOM/泄漏未测 |
|
||||
| F05 | QUALIFIED | 错配成立,旧TS2307为历史环境失败,不能仅归`.10` |
|
||||
| F06 | QUALIFIED | 显式设计限制建议P2;full CPU不等于关闭GPU reuse |
|
||||
| F07 | CONFIRMED | ordinal线性,但全局编号对正确性必要 |
|
||||
| F08 | QUALIFIED | 保守全局失效,裁窄前须证明依赖完整 |
|
||||
| F09 | CONFIRMED | 可具体复核先换entries后析构;不证明stale纹理 |
|
||||
| F10 | QUALIFIED | 文档偏差降P3;不能推定NavSea实际接入 |
|
||||
|
||||
不为凑数标 DISPROVED;没有证据的运行后果均留 NEEDS_RUNTIME。旧报告未作出的断言不反向归咎于旧作者。
|
||||
|
||||
## 5. V12/V13 与缺失验证
|
||||
|
||||
V12 固定最多两个 R,V13 任意独立 R/C,增加管理/资源上界;单 C fast 保留,多 C 回 full CPU。成本模型为完整快照 O(L+S)、多段 ordinal 最坏前缀 O(S×G)、retained 资源 O(R×W×H)、场物化 O(P)。普通粒子 wait、drawable/物化多为继承成本,不全归 V13。无 profiler 不给收益百分比,不外推 STEP-090 21.7%/97.0%历史值。
|
||||
|
||||
| 验证 | 本轮状态 | 不能替代它的历史证据 |
|
||||
| --- | --- | --- |
|
||||
| GL/Metal编译、Native/TS/codegen | NOT RUN | STEP-094构建/43 tests、旧审查69 tests/TS仅HISTORY |
|
||||
| 任意R/C像素、depth/stencil/alpha | NOT RUN/NEEDS_RUNTIME | resolver/diagnostics单测不覆盖GPU |
|
||||
| CPU/GPU时间、30/60FPS、单C/多C | NOT RUN/NEEDS_RUNTIME | 历史特定Style CPU不可外推 |
|
||||
| allocation/peak、context loss、长稳 | NOT RUN/NEEDS_RUNTIME | 逻辑release计数不等于物理显存 |
|
||||
| PLAY/数值/attach/destroy端到端 | NOT RUN | 手动clock advance不等于生产接线 |
|
||||
| 默认Host版本/ABI与设备调用 | NOT RUN | 临时工作树TS/codegen不等于默认安装 |
|
||||
| Android GL / iOS Metal / Vulkan运行 | 全部NOT RUN | 编译成功不证明运行支持 |
|
||||
|
||||
完整 topology/异常/生命周期矩阵、精确既有验证命令及日志要求见调查第 9 节。新增缺陷尚需增加对应测试;仅复跑现有过滤集不能关闭。所有运行由 RDC 后续独立执行。
|
||||
|
||||
## 6. 修复建议、交付边界与 Git
|
||||
|
||||
建议分批:A 正确性 gate N01~N04;B 输入/生命周期/观测/Host N05~N07/O01/E01;C 轻快照/ordinal/相同时间早退;D GPU预算、allocation/content分离和同步;E multi-C fast、source精确依赖、异步大场与正式mask独立设计。不得为性能绕过保守gate、合并相邻R或引入provider/HTTP/业务调度。只建议,不创建修复任务。
|
||||
|
||||
仅新增本报告、[调查](../20_调查/STEP-095_V13独立严格代码与性能调查_v1.md)、[阶段记录](../50_Step记录/STEP-095_V13独立严格代码与性能审查_v1.md),追加根[粒子开发记录](../../粒子开发记录.md)。没有生产源码、shader、API、package/lock、build、manifest、tag、产物或Integration变更。
|
||||
|
||||
起始SHA见第2节;实际主提交SHA/message、push、remote HEAD/status和最终收尾提交的自引用说明统一记录在阶段第4节。推送前交付暂记PARTIAL;完成并核对后以追加结果为准。最终由RDC独立审计本文证据和任务边界,Codex不宣布正式产品验收。
|
||||
|
||||
### 已完成的交付核对
|
||||
|
||||
主提交 `ad5f92c48ea353c18548c79d223e9a06888330d3`(`docs: audit V13 source and performance for STEP-095`)已push origin particle/main,exit 0;实际远端HEAD回读与本地一致,核对时工作树clean。四份文档、9处相对链接、63处带行号源码引用范围及cached diff空白检查通过,开发记录仅追加。调查主稿仍285行;无生产源码变化。
|
||||
|
||||
据此调查主交付为 **PASS**,产品运行态仍 **NOT RUN/未验收**。本追加结果由独立docs-only收尾提交保存,其最终SHA及二次push/clean核对按阶段记录自引用规则在最终回复提供;不虚构自身哈希或未执行测试。
|
||||
55
docs/报告/STEP-096_V13隔离盲审_Codex完成报告_v1.md
Normal file
55
docs/报告/STEP-096_V13隔离盲审_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# STEP-096 V13 隔离盲审 Codex 完成报告 v1
|
||||
|
||||
执行结果:**PASS(仅盲审文档交付)**。日期:2026-09-25。
|
||||
|
||||
本轮只使用隔离快照及其当前有效规范和[任务书](../40_任务/STEP-096_V13隔离盲审_Codex任务_v1.md),独立读取源码形成结论,未导入任何既有审查发现。PASS 不表示 V13 Android/iOS GPU 运行态、性能、构建或发布通过。
|
||||
|
||||
## 交付
|
||||
|
||||
1. [独立调查报告](../20_调查/STEP-096_V13隔离盲审调查_v1.md):架构、完整发现证据、反例、成本模型、覆盖缺口、后续验证矩阵及分批建议。
|
||||
2. [Step 记录](../50_Step记录/STEP-096_V13隔离盲审_v1.md):输入、只读过程、范围、结果和未运行事项。
|
||||
3. [本完成报告](STEP-096_V13隔离盲审_Codex完成报告_v1.md):交付结果、索引、证据边界与移交责任。
|
||||
|
||||
三份正文与相对链接、引用文件及行号、行尾空格已作轻量静态自检;这不属于构建或测试执行。
|
||||
|
||||
## Finding 索引
|
||||
|
||||
完整路径、行号、触发条件、反例和验证动作集中在调查报告对应 B 编号中。编号来自本轮源码证实顺序。
|
||||
|
||||
| 编号 | 优先级 / 类型 | 独立发现 |
|
||||
|---|---|---|
|
||||
| B01 | P1 / 确定缺陷 | 跨目标 tile stencil 覆盖记录未失效 |
|
||||
| B02 | P1 / 确定缺陷 | Metal 未落实 gfx depth/stencil 清屏请求 |
|
||||
| B03 | P2 / 源码确定成本 | Metal 离屏 present 同步等待 GPU |
|
||||
| B04 | P2 / 源码确定成本 | retained 内容失效释放同尺寸 GPU 资源 |
|
||||
| B05 | P2 / 源码确定成本 | 时间更新先全量物化后比较 |
|
||||
| B06 | P1 / 确定缺陷 | encoded PLAY 无生产时钟推进调用方 |
|
||||
| B07 | P2 / 确定缺陷 | 8-bit 场纹理零值编码偏差 |
|
||||
| B08 | P2 / 确定缺陷 | Mercator 东向速度未作纬度修正 |
|
||||
| B09 | P2 / 设计限制 | 缺正式共享地理 Ocean Mask |
|
||||
| B10 | P2 / 源码确定成本 | UI 派发链同步解码/物化 |
|
||||
| B11 | P2 / 设计限制 | store 无全局 key/字节预算 |
|
||||
| B12 | P2 / 确定缺陷 | retained 绘制异常后仍可提交成功缓存 |
|
||||
| B13 | P1 / 确定缺陷 | 粒子更新门槛遗漏合法窗口 V/有效性变化 |
|
||||
|
||||
共 13 条,P1 四条、P2 九条,无证据成立的 P0。源码确定缺陷七条、确定成本四条、设计限制两条。颜色投影候选因实际数组比较而排除,粒子强引用环、纯重排遗漏等候选也未成案;没有为凑数量将其写成问题。
|
||||
|
||||
## 证据质量和输入限制
|
||||
|
||||
- 每条保留隔离树内真实源码定位及可构造条件,区分源代码事实与运行后果。同步点、分配/遍历、缺失调用方、身份门槛、数值公式可静态分析;设备画面、CPU/GPU 时长、OOM、崩溃和收益全部 **NEEDS_RUNTIME**。
|
||||
- 目标 SHA `ee0b89e711bf01335b3aa71d36def785cd97fec3` 为任务书中的导出来源;隔离树不提供 Git 对象核验。本轮未运行 Git,未声称 branch、HEAD、commit、push、remote 或 clean。
|
||||
- 快照 manifest 声明 RN/Android `13.4.1-particle.13`、iOS `6.28.0-particle.13` 和产物 sourceCommit `ddf20dec9fe9cd05b77274827b6825829500dd5a`;未外部回读,不能当作安装环境或产物字节核验证据。
|
||||
- 实际模型/TUI/screen/effort 启动记录、历史构建日志、主仓库内容和其他审查材料均 **NOT AVAILABLE**;未向目录外寻找。当前规范中旧坐标仅作为设计/流程背景,不覆盖快照实现事实。
|
||||
- 这是选择性静态追踪,未穷尽所有源码、后端和竞态。测试源码被阅读,不等于通过测试。Vulkan 全链、ABI/codegen、透明/深度全组合、平台销毁竞态和设备表现仍待后续验证。
|
||||
|
||||
## 验证与改动边界
|
||||
|
||||
构建、链接、codegen、打包、C++/GL/Metal/Jest 测试、设备安装/运行、profiler 和 GPU capture:**NOT RUN**。发布、公网回读、正式仓库操作和 Git:**NOT RUN**。
|
||||
|
||||
本轮只写任务规定的三份中文 Markdown 报告;未改生产源码、测试、shader、manifest、package/lock 或工具链,未追加开发日志。这里的“源码未变”指本轮没有源码写操作,不代表已借助 Git diff 或导出对象对比核验整个快照。
|
||||
|
||||
## 停止与移交
|
||||
|
||||
Codex 文档交付后停止。RDC 负责随后核对隔离输出清单、计算三份原稿 SHA-256 并记录冻结时间,再独立移交正式仓库、追加开发记录、做四文件 scoped commit/push 及远端核对。本轮没有执行散列冻结或移交,没有最终提交 SHA。
|
||||
|
||||
冻结后不由本会话依据外部资料改写发现。后续修复、测试和解盲比较应有独立任务与记录;所需最小验证矩阵已在调查报告给出。
|
||||
38
docs/报告/STEP-097_A1_Android专项验收报告_v1.md
Normal file
38
docs/报告/STEP-097_A1_Android专项验收报告_v1.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# STEP-097 A1 Android 专项验收报告 v1
|
||||
|
||||
## 结论及范围
|
||||
|
||||
**A1 Android/Core 已完成本轮可执行检查点;iOS/Metal 运行测试按用户决定豁免。** 不将豁免写成实际 Metal PASS。STEP-097 总体仍进行中,A2/B/C 尚待实现和验证;无新软件发布。
|
||||
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab`,`particle/main`,源码提交 `8c006ac83d16c2683037f90484fd86fb917a9d78`。Integration `main` 核对 `318f8ccf911ac3136c3086a1d14e61f83e2d4bce`。
|
||||
- Android/FZG1-2:直接 ADB `192.168.200.139:5555`,非 Win11 虚拟机。APK OpenGL Debug/x86_64,60,311,571 bytes,SHA-256 `48df19cf5a4c4250934efa2596423d3ad9571fe49a65b9b704a81cdb41f58671`。标准脚本编译成功、ZIP/ABI 校验通过;`install -r` 返回 Success,设备安装文件哈希一致。
|
||||
- 构建记录 `git_dirty=true` 的原因是用户另行放在 `docs/40_任务/` 的未跟踪 STEP-098 MD;本次构建起始及源码提交无 tracked 修改,未将 STEP-098 纳入编译或提交。冻结 `.13` 未修改。
|
||||
|
||||
## 01:同源同 tile Stencil — Android 实测
|
||||
|
||||
新入口为 `Step097StencilFixture`,单一 `step097-shared-polygon` GeoJSON tiled source 的四层同一 Polygon,运行中仅改变 R/C 连续 Segment 排列,不改来源、几何及图层视觉参数。
|
||||
|
||||
- 实测 RR、RCR、CRR 各两轮,稳定帧、REBUILD CURRENT、CACHE OFF/ON 与再次拓扑切换,共 **18 个检查点**,18 张 1920×1200 PNG 与 18 份 UI XML。
|
||||
- 所有检查点均 `valid=true`、`fallback=false` 且实际策略序列匹配当前选项;缓存 OFF 时各 R 报告 fallback,ON 后重新构建,拓扑有效不变。代码回归脚本退出码 0;实时日志含 10 条 `STEP097_STENCIL_READY`,没有本次采集范围内的 FATAL/SIGSEGV/SIGABRT。
|
||||
- 使用 AppKit 对原始 PNG 采样:18 张中心像素全部为 RGB `[193,176,119]`,区域外像素全部为 `[27,49,75]`,中心与区域外有明显差异。两种采样均一致,不以截图文字代替像素核验。
|
||||
- 以上证明这组固定 same-source scene 在指定采样点和可见完整画面未出现异常;它不是对所有 tile/camera/pitch/zoom 组合的数学证明。
|
||||
|
||||
证据在忽略目录 `artifacts/maplibre-particle/step097-a1-fzg1/`:`run-stencil.log`、`pixel-check.log`、`after/*.png`、`after/*.xml`、`after/live-logcat.txt`、`after/step097-events.txt`、`after/fatal-events.txt`、`after/observations.json`、`install.log`、`installed.sha256`。
|
||||
|
||||
## 02:Metal 附件 — 源码与编译,不执行 iOS runtime
|
||||
|
||||
Metal 宿主测试二进制中包含 `MetalRetainedAttachments.*` 两个 GTest,Metal host CMake 增量编译退出码 0。当前 Mac 没有可用 `MTL::CreateSystemDefaultDevice()`,两个测试均按守卫 SKIP;没有将 0/2 误记为 PASS。针对 `metal-cpp` 对象指针解引用的最小编译修正已提交 `8c006ac83`。
|
||||
|
||||
用户于 2026-09-26 明确决定:本轮不执行 iOS 测试,Android 验收通过后 iOS 按其决定视作接受。记录为 `USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`,不宣称 iOS GPU/Metal 实际验证。
|
||||
|
||||
## 03:绘制异常恢复 — 原生测试
|
||||
|
||||
`RenderOrchestratorError.*` 两个新增原生测试实际执行 **2/2 PASS,exit 0**,覆盖 opaque/translucent 绘制异常、错误上报、失败 cache 不发布/不复用及干净重试恢复。日志:`artifacts/maplibre-particle/step097-a1-fzg1/render-exception.log`。本次不将 GTest 扩写为 Android GPU 真实异常注入 PASS。
|
||||
|
||||
## 19:资源释放归属 — 已有结果
|
||||
|
||||
`OldCacheReleaseCannotChargeReplacementAtSameIndex` 已在此前重新编译的 Mac Core 原生 runner 实测 **1/1 PASS**;诊断组 **5/5 PASS**。FZG1 资源辅助冒烟 20 个检查点已经单独报告。精确 revision 归属由原生 GTest 负责,不据设备摘要断言不存在 GPU 内存泄漏。
|
||||
|
||||
## A1 交接与限制
|
||||
|
||||
A1 在 Android/Core 验证和用户 iOS 豁免口径下可进入 A2;保留 B/D `topPresent=true` 的旧诊断疑点,不将其冒充已修复。下一阶段按原任务书 A2→B→C 严格串行;每阶段 Codex 实现/测试入口,RDC 负责实际构建、执行与独立审查;不加入 Jest、不覆盖 `.13`。
|
||||
53
docs/报告/STEP-097_A1_FZG1测试APK与实机验证报告_v1.md
Normal file
53
docs/报告/STEP-097_A1_FZG1测试APK与实机验证报告_v1.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# STEP-097 A1 FZG1 测试 APK 与实机验证报告 v1
|
||||
|
||||
**结论:Android FZG1 既有场景冒烟完成;STEP-097 A1 仍为 PARTIAL / BLOCKED。** 本次确实重新构建、安装并通过 ADB 操作 FZG1-2,获得 20 张截图与设备诊断。它不替代 01 stencil 同瓦片双目标注入、02 iOS Metal 附件验证、03 LayerGroup 绘制异常注入及 19 C++ GTest 的正式检查点;A2/B/C 未执行。
|
||||
|
||||
## 1. 源码、设备与边界
|
||||
|
||||
- 验证日期:2026-09-25;开发/构建/ADB 控制:Mac-mini-tei.lan;仓库:`/Users/tei/dev/MapLibreParticleLab`,`particle/main`,代码 HEAD `91c2491b0ef73fb9e01d7861b9c4ed7b55498197`,构建开始 `git_dirty=false`。
|
||||
- Integration 已通过 RDC 同步 `main` 至 `318f8ccf911ac3136c3086a1d14e61f83e2d4bce`;本轮未改公共协议、Integration 或 NavSea。
|
||||
- 设备:Panasonic `FZG1-2`,ADB `192.168.200.139:5555`,Android-x86/Bliss x86_64,1920×1200,ABI 列表以 `device-abi.txt` 为准。ADB 直接从 Mac 连接设备;**没有使用 Win11 虚拟机或模拟器**。
|
||||
- 包名:`org.maplibre.android.testapp`;Activity:`.activity.particle.ParticleCoreLayerValidationActivity`;测试时始终使用 `adb -s 192.168.200.139:5555`。
|
||||
|
||||
## 2. APK 构建和安装的实际结果
|
||||
|
||||
- 命令:`./tools/particle_android_validation/build_validation_apk.sh`;OpenGL Debug、x86_64、strip native debug symbols;Gradle `BUILD SUCCESSFUL in 15m 59s`,86 tasks(29 executed / 57 up-to-date)。
|
||||
- APK:`artifacts/maplibre-particle/step097-fzg1/MapLibreAndroidTestApp-STEP097-A1-91c2491b0-FZG1-x86_64.apk`,**60,295,184 bytes**;内含 `lib/x86_64/libmaplibre.so` 39,422,632 bytes;ZIP/ABI/标准 Pipeline 检查通过。
|
||||
- APK SHA-256:`8677197c5f83a22747cb51f0c261edffc5db4678fc25b3f366474f6ecb94dc99`;`build-info.txt` 记录同 commit、ABI、时间和 hash。冻结副本与标准 Pipeline 产物字节一致。
|
||||
- 已备份旧安装包至 `artifacts/maplibre-particle/step097-fzg1/before/original-installed-base.apk`。新包使用 `adb -s 192.168.200.139:5555 install -r <APK>` 覆盖安装,输出 `Success`;设备内 `base.apk` 的 SHA-256 与新包完全相同。未清理设备应用数据、未改 NavSea App。
|
||||
## 3. FZG1 ADB 操作与截图证据
|
||||
|
||||
脚本:`artifacts/maplibre-particle/step097-fzg1/run_smoke.py`(本地验证资料,不属于产品源码)。`smoke-run.log` 与 `mutation-run.log` 两次执行 RC=0;另有针对事件的 `live-capture-run.log` RC=0。原始截图、UI XML、日志统一保存在同目录的 `after/`。以下只表述真实场景和屏幕/诊断结果:
|
||||
|
||||
| 截图 | 场景 | 设备观察 |
|
||||
| --- | --- | --- |
|
||||
| 00–01 | 普通启动,A:R-C-R | Activity 可启动;A 范围 valid=true、fallback=false,B/T 各一缓存并有 build/reuse;蓝底、粉/灰图形及白线可见。 |
|
||||
| 02–05 | B:R-C、C:C-R、D:C only、E:single continuous | 场景可切换,截图显示对应图形可见/隐藏;状态均 valid=true、fallback=false。 |
|
||||
| 06–08 | 恢复 A,CACHE OFF→ON | OFF:`runtime-disabled`、fallback、Fast inactive;ON:`first-frame` 重新构建并恢复 reuse。 |
|
||||
| 09 | CAMERA NEXT | 相机变化后仍有有效范围与重建/复用,未观察到崩溃或整幅永久清空。 |
|
||||
| 10–11 | 缺失 START→RESTORE VALID | `valid=false/fallback=true/caches=0`,恢复后 `valid=true/fallback=false`、重建。 |
|
||||
| 12–15 | TOP REORDER、CROSS CONTINUOUS、FAST FAIL-CLOSED、RESET A | 层序变体可切换;带额外 Continuous 层时 Fast 关闭并保留全路径绘制,重置后恢复 Fast。 |
|
||||
| 16–19 | 实时抓日志时重置 A、相机、缺失 END、恢复 | 日志有 `STEP093_SCENE_READY` / `STEP093_RANGE`,缺失 END 后设备诊断为 `end-layer-missing`,恢复后有效。 |
|
||||
|
||||
- 设备全局 Logcat 缓冲区较小,单次事后 dump 可能覆盖早期 STEP093 事件,故补充**实时过滤采集** `after/live-particle-logcat.txt`。该文件记录 A 场景 READY、缺失 END 和恢复的实际事件;`after/logcat-final.txt` 保留事后日志。
|
||||
- 在本次保存的设备日志与实时采集内未发现 `FATAL EXCEPTION`、`SIGSEGV` 或 `SIGABRT`;最后确认该测试 Activity 仍为 topResumedActivity。这不是长稳/全设备无故障的证明。
|
||||
## 4. 异常观察及未关闭的检查点
|
||||
|
||||
**待复核诊断现象(不直接判定绘制缺陷):** B「BOTTOM+CONTINUOUS」与 D「CONTINUOUS ONLY」截图中没有上段粉色/白线对象,但诊断仍分别报告 `topPresent=true`、`caches=2/1`,且 T reuse 计数继续增长。可能涉及空段、旧 topology 统计或诊断刷新/对象身份;需用独立布局期望与源码追踪证实,不能仅凭截图认定旧上段污染像素。截图:`02_B_R-C.png`、`04_D_C.png`。
|
||||
|
||||
| 原 A1 问题 | 本次验证覆盖 | 状态 |
|
||||
| --- | --- | --- |
|
||||
| 01 N01/B01 stencil CPU/GPU 切换 | 只做 R-C-R 等既有不同 GeoJSON source 的视觉冒烟、相机/缓存切换;**没有同 vector source/same tiles 在 R-R/R-C-R 切换的像素 fixture** | 未闭合 |
|
||||
| 02 N02/B02 Metal depth/stencil | 本次 APK 是 Android OpenGL;不可能代替 Metal 附件、clear/load/store 和 combined attachment 验证 | 未执行 |
|
||||
| 03 N03/B12 render 抛异常后错误缓存成功 | 做了配置无效 fallback→恢复及 Fast fail-closed;**没有真正 LayerGroup render 抛异常注入与下帧恢复** | 未闭合 |
|
||||
| 19 F09/O01 旧 cache release 归属 | 设备保留了 topology/资源计数截图;新增 `OldCacheReleaseCannotChargeReplacementAtSameIndex` GTest **没有执行** | 未闭合 |
|
||||
|
||||
本次不是完整 STEP-097,也不意味着 24 项均修复;A2、B、C、正式 FZG1 同瓦片遮罩回归、iOS Metal、C++ GTest、性能 benchmark、发布与 NavSea 产品 Gate 均未完成。不存在新正式软件发布,原 `.13` tag/manifest/产物未动。
|
||||
|
||||
## 5. 后续准确步骤
|
||||
|
||||
1. 补齐 01 同 tile 的 R-R/R-C-R/C-R-R 设备 fixture,02 Metal 附件测试入口,03 render exception 注入及 recovery;解决/解释 B、D 的 topPresent 诊断现象。
|
||||
2. RDC 在同 commit 执行 19 定向 GTest,补齐上述源码测试;Android 再集中构建/安装一次而不是逐按钮重装,iOS Metal 由 Mac 单独做对应设备验收。
|
||||
3. 满足 A1 代码/测试入口检查点后,才进入 STEP-097 A2/B/C;本次不得修改旧三轮调查或将部分冒烟标成 A1 PASS。
|
||||
|
||||
证据目录与 APK 目前位于 Mac 仓库忽略的 `artifacts/maplibre-particle/step097-fzg1/`。已安装 APK 和冻存 APK SHA 相同,相关文本记录、截图、日志可复核。
|
||||
34
docs/报告/STEP-097_A1_第19项FZG1运行验证报告_v1.md
Normal file
34
docs/报告/STEP-097_A1_第19项FZG1运行验证报告_v1.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# STEP-097 A1 第 19 项 FZG1 运行验证报告 v1
|
||||
|
||||
## 1. 结论及验证边界
|
||||
|
||||
**FZG1-2 Android OpenGL 运行态冒烟 PASS;第 19 项原生 GTest 已于此前单独 1/1 PASS;A1 整体仍 PARTIAL。** 本次通过 Mac RDC 直接连接 Panasonic FZG1-2,不使用 Win11 虚拟机。20 个设备截图检查点、拓扑切换、缓存开关、无效范围回退及恢复完成;设备粗粒度资源计数符合被检查的不变量,未观察到本次采样范围内的应用崩溃。
|
||||
|
||||
本次 APK 未内嵌 Mac 原生 GTest,也不能从设备截图直接验证旧 topology revision 的内部归属。旧 revision 不计入新段 index 的精确断言由此前 `OldCacheReleaseCannotChargeReplacementAtSameIndex` C++ GTest(1/1 PASS)承担。设备测试仅对实际 Renderer 生命周期、画面与诊断提供辅助证据。
|
||||
|
||||
## 2. 基线、APK 和设备安装
|
||||
|
||||
- 时间:2026-09-26 08:10–08:19 JST;Mac `Mac-mini-tei.lan`;源码分支 `particle/main`,起始 HEAD `237a3e66083ede03f56b5e719086d3a1719b4790`,工作树 clean;Integration `main` HEAD `318f8ccf911ac3136c3086a1d14e61f83e2d4bce` 已核对同步。
|
||||
- APK 源码提交 `91c2491b0ef73fb9e01d7861b9c4ed7b55498197`。与当前 HEAD 比较只有文档、Step、测试记录变化,生产代码和现有测试源码没有变化,因此直接复用经标准构建的 `OpenGL Debug / x86_64` APK,不为文档提交重复 Gradle 编译。
|
||||
- 文件:`artifacts/maplibre-particle/step097-fzg1/MapLibreAndroidTestApp-STEP097-A1-91c2491b0-FZG1-x86_64.apk`,60,295,184 bytes;SHA-256 `8677197c5f83a22747cb51f0c261edffc5db4678fc25b3f366474f6ecb94dc99`。
|
||||
- 目标:`FZG1-2`,ADB 序列号 `192.168.200.139:5555`,1920×1200,x86_64;包名 `org.maplibre.android.testapp`。
|
||||
- 已执行 `adb -s 192.168.200.139:5555 install -r <APK>`,返回 `Success`;设备上 `base.apk` 的 SHA-256 与原始 APK 一致。覆盖安装未清 App 数据。
|
||||
## 3. ADB 场景、诊断和截图
|
||||
|
||||
- 从 `ParticleCoreLayerValidationActivity` 开始,连续执行两轮 A(R-C-R) → B(R-C) → C(C-R) → D(C only) → E(single continuous) → A 恢复;然后 TOP_REORDER、CROSS_CONTINUOUS、恢复默认、CACHE OFF/ON、MISSING START 无效配置和 RESTORE VALID。全程按界面真实控件通过 ADB 点击,不直接伪造内部计数。
|
||||
- 输出 20 张 1920×1200 PNG、20 份 UI XML,以及每次 UI STATUS/DIAG 的 `observations.json` 和实时 Logcat。除了启动首页和预期无效场景,另外 **18 个检查点均报告 `valid=true/fallback=false`**。
|
||||
- 缺失 START 后 `valid=false/fallback=true`、`reason=start-layer-missing`、`caches=0`;恢复后 `valid=true/fallback=false`、`reason=first-frame`、`caches=2`。
|
||||
- 对带 STEP-093 诊断的 19 个检查点,解析 B/T 两组 `res=textureCreate/compositeCreate/resourceRelease`;两组均满足 `textureCreate-resourceRelease ∈ {0,1}`。无效配置 B/T 差额均为 0,恢复后均为 1。**这是设备现有摘要计数检查,不能推论底层 GPU 没有泄漏,也不等同逐 revision 归属断言。**
|
||||
- 实时日志 `live-logcat.txt` 记录 34 条 STEP093 日志事件,筛选 `step093-events.txt` 共 38 行;`fatal-events.txt` 为空。本次脚本正常结束 `FZG1_DEVICE_SMOKE_OK`,退出码 0;最后确认测试 Activity 仍在前台。
|
||||
- 已查看 A、无效 START、恢复有效三张截图:主要图形、粒子与诊断区域仍可见,未观察到整幅永久清空。B/D 不显示上段对象但诊断仍为 `topPresent=true` 的历史待核现象仍存在;不据此直接判定图形错误,另需源码/拓扑语义核查。
|
||||
|
||||
## 4. 初次脚本重试与真实状态
|
||||
|
||||
第一次本地 ADB 测试脚本在普通首页截图后,因校验器错误地要求首页已有 STEP-093 专项诊断而触发 `Missing range diagnostic: 00_boot`;该失败属于测试脚本的启动页断言,不是 Renderer 测试失败。保留 `device-test-run-attempt1.log`;限定修正为启动首页允许 `n/a` 后完整重新执行,最终脚本退出码 0。没有跳过后续场景或修改 App。
|
||||
## 5. 证据、未完成项与下一步
|
||||
|
||||
证据根目录(Mac 本地忽略目录):`artifacts/maplibre-particle/step097-fzg1-gtest19-device/`。其中 `install.log`、`before/` 和 `after/installed_sha256.txt` 对应安装;`after/*.png` 与 `after/*.xml` 对应 20 个检查点;`after/observations.json`、`after/status-summary.txt`、`after/live-logcat.txt`、`after/step093-events.txt`、`after/fatal-events.txt` 保留原始诊断。单独压缩包:`STEP097_A1_第19项_FZG1设备证据.zip`,APK 不包含在证据 zip 内,仍使用上述冻结 APK。
|
||||
|
||||
第 19 项可以记录为**原生 GTest 已通过 + Android 设备辅助冒烟通过**。本次没有验证逐次 GPU 纹理实际释放/峰值内存,没有 iOS Metal 验证,也未补齐 A1 中 01 同源同 tile stencil、02 Metal depth/stencil 附件、03 LayerGroup 真正 render 异常注入专用入口;因此 STEP-097 A1 仍为 PARTIAL,不能进入 A2/B/C 或宣称 V13 发布/产品 Gate PASS。
|
||||
|
||||
本次只新增报告和追加阶段记录,未修改生产源码、旧调查、正式发布产物、公共契约或 NavSea。
|
||||
48
docs/报告/STEP-097_A1_第19项GTest运行验证报告_v1.md
Normal file
48
docs/报告/STEP-097_A1_第19项GTest运行验证报告_v1.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# STEP-097 A1 第 19 项 GTest 运行验证报告 v1
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
**第 19 项 F09/O01 的现有原生单元测试:PASS。** 专项 `RenderSegmentDiagnostics.OldCacheReleaseCannotChargeReplacementAtSameIndex` 实测 **1/1 通过、退出码 0**;相关 `RenderSegmentDiagnostics.*` **5/5 通过、退出码 0**。这是 Core 诊断计数验证,并非 GPU 内存泄漏测试或 A1 整体验收。
|
||||
|
||||
- 执行机器:`Mac-mini-tei.lan`,日期 2026-09-26(日本时间);仓库 `/Users/tei/dev/MapLibreParticleLab`;分支 `particle/main`。
|
||||
- 实测源码 HEAD:`0d13552bed84f427dd43cae9856391bd26622179`;其中第 19 项生产/测试源码提交 `e67e4961b5b1a1ff8fa7c1e3263799d63f89cadc`;开始时工作树 clean。
|
||||
- Integration `main` 已由 RDC 同步核实至 `318f8ccf911ac3136c3086a1d14e61f83e2d4bce`。本次无公共契约或生产源码修改。
|
||||
|
||||
## 2. 构建和运行
|
||||
|
||||
在已有、可追踪源码的 `build-step088-opengl` CMake/Ninja 配置中,针对 `mbgl-test-runner` 增量重编译:
|
||||
|
||||
```sh
|
||||
cmake --build build-step088-opengl --target mbgl-test-runner --parallel 3
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter=RenderSegmentDiagnostics.OldCacheReleaseCannotChargeReplacementAtSameIndex --gtest_color=no
|
||||
build-step088-opengl/mbgl-test-runner '--gtest_filter=RenderSegmentDiagnostics.*' --gtest_color=no
|
||||
```
|
||||
|
||||
- 构建:**exit 0**,95/95 Ninja steps,实际耗时 **261.74 秒**;测试程序确实重新链接且新增测试列在 `--gtest_list_tests` 输出中,未复用旧二进制代替新测试。
|
||||
- 编译有 Boost header 未使用形参与 vendor 静态库 macOS deployment target 链接警告;无构建失败,本次未更改工具链/警告规则。
|
||||
- 产物:`build-step088-opengl/mbgl-test-runner`;SHA-256 `9fbbc08c44d8445114040ef3a127f5efdad29d0e6d0d1437617421c8a392de52`。
|
||||
## 3. 测试和断言证据
|
||||
|
||||
专项用例源码:`test/renderer/retained_underlay_cache.test.cpp`,`RenderSegmentDiagnostics.OldCacheReleaseCannotChargeReplacementAtSameIndex`。
|
||||
|
||||
测试先配置旧 Segment 拓扑并取得 `oldRevision`,再将 Continuous 边界由 `weather` 改为 `land`,要求 `newRevision != oldRevision`。对相同 index=0 分别发送两种资源释放:
|
||||
|
||||
1. `recordSegmentResourceRelease(0, oldRevision)`:新 topology 第 0 段的 `resourceRelease` 必须仍为 **0**。
|
||||
2. `recordSegmentResourceRelease(0, newRevision)`:同一当前段的 `resourceRelease` 必须变为 **1**。
|
||||
|
||||
本轮真实输出:
|
||||
|
||||
```text
|
||||
[ RUN ] RenderSegmentDiagnostics.OldCacheReleaseCannotChargeReplacementAtSameIndex
|
||||
[ OK ] RenderSegmentDiagnostics.OldCacheReleaseCannotChargeReplacementAtSameIndex (0 ms)
|
||||
[==========] 1 test from 1 test suite ran.
|
||||
[ PASSED ] 1 test.
|
||||
```
|
||||
|
||||
相邻诊断组的 5 个用例亦全部通过:可见性元数据、旧缓存释放、缓存关闭保持拓扑、非法计划的 Style 顺序、快照序列化。全部无失败和跳过。
|
||||
|
||||
## 4. 事实边界、证据与后续
|
||||
|
||||
- 该测试仅验证**当前 topology 的按段释放计数不被旧 revision 污染**;不证明底层 GPU 纹理的实际释放、总分配平衡、没有显存泄漏或 FZG1/iOS 运行态表现。Aggregate release 仍由源码语义保证,本用例未单独断言 aggregate 数值。
|
||||
- 第 19 项的**已有 GTest 执行检查点可标为 PASS**;STEP-097 A1 **仍 PARTIAL**:01 同 vector source/same tiles stencil、02 Metal 附件、03 真正 LayerGroup 绘制异常注入均没有被此测试覆盖。
|
||||
- 保存日志:忽略目录 `artifacts/maplibre-particle/step097-gtest19/build.log`、`gtest19.log`、`diagnostics-suite.log`、`build-metadata.txt`。本轮没有执行 Jest、APK 构建/重装、性能或新版本发布。
|
||||
38
docs/报告/STEP-097_A2_阶段验证记录_v1.md
Normal file
38
docs/报告/STEP-097_A2_阶段验证记录_v1.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# STEP-097 A2 阶段验证记录 v1
|
||||
|
||||
状态:**PARTIAL / 代码待完成与审查**。本记录是 2026-09-26 Mac RDC 当前检查点,不是 A2 交付完成报告,不允许据此进入 B/C。
|
||||
|
||||
## 基线与执行事实
|
||||
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab`;分支 `particle/main`;起点 HEAD `72d93693842d0f98b1f26b4ec38d5b7389656184`。Integration `main` 同步 HEAD `318f8ccf911ac3136c3086a1d14e61f83e2d4bce`。
|
||||
- A1 的 Android/FZG1 检查点已在 `docs/报告/STEP-097_A1_Android专项验收报告_v1.md` 记载;iOS runtime 按用户决定豁免,不能把未运行写为 PASS。
|
||||
- GPT-6-Sol medium 可见 Codex 会话 `6215.7445.codex` 在 A2 修改 17 个已跟踪文件,并新增内部 GPU 数学头和定向测试;触及当期用量上限后停下,**源码和测试仍未形成 A2 完整提交**。未启动 B/C;不切换低能力模型冒充完成。
|
||||
- 用户另有未跟踪 `docs/40_任务/STEP-098_QBIN_V4单帧首显与八帧滚动缓存_Codex任务_v1.md`,原样保留;与本轮 A2 无关。
|
||||
|
||||
## 已进行的独立检查
|
||||
|
||||
| 检查 | 真实结果 |
|
||||
| --- | --- |
|
||||
| `git diff --check` | exit 0 |
|
||||
| OpenGL `cmake --build build-step088-opengl --target mbgl-test-runner --parallel 3` | exit 0,28 构建步骤,含新增测试目标 |
|
||||
| OpenGL 定向 `ParticleGpuField.*:Source.RasterArrayV4*:ParticleFieldRuntime.ForecastClock*` | **16/16 PASS,exit 0** |
|
||||
| `node shaders/generate_shader_code.mjs` 连续两次 | 两次 exit 0、输出一致,未产生额外工作树路径 |
|
||||
| Metal host `cmake --build build-step097-metal-host --target mbgl-test-runner --parallel 3` | exit 0,224 步,耗时约 519.65 秒;没有运行 iOS 设备 |
|
||||
| Metal host 同样的跨 Core 定向测试 | **16/16 PASS,exit 0** |
|
||||
|
||||
上述编译器提示 Boost vendor 未使用参数、macOS deployment target 链接警告,实际构建 exit 0。测试仅证明已有入口的行为,不等于整项 A2 运行态 PASS。
|
||||
|
||||
## 改动内容尚需复核
|
||||
|
||||
- 04 单一 map/source owner 基于 monotonic tick 推进 ForecastClock,Android/iOS Bridge 只作为唤醒调度;
|
||||
- 05 GPU 双端点身份包含 U/V、validity、空间网格、解释/版本信息;只变 mix 不应触发新上传;
|
||||
- 06 内部 RGBA8 signed zero 从 byte 128 精确重建,零/量化误差有数学测试;未改变公开 QBIN 格式;
|
||||
- 07 Mercator 东向 U 与北向 V 采用相同纬度度量及极区夹紧,GL/Metal 代码准备中。
|
||||
|
||||
这些是**在工作树内可见的实现方向,不是已批准的最终行为**。待 Codex 恢复后完成剩余审查、针对性修正和阶段文档,再由 RDC 独立编译/测试/设备验证,明确代码完成后才能进入 B。
|
||||
|
||||
## 原始证据及后续
|
||||
|
||||
忽略目录:`artifacts/maplibre-particle/step097-a2-rdc/`。包含未提交差异备份、新增测试文件备份、`build-opengl.log`、`a2-focused-tests.log`、`shader-codegen1.log`、`shader-codegen2.log`、`build-metal-host.log`、`a2-metalhost-core-tests.log`。本次没有 Android APK/ADB 验收、iOS runtime、Jest 或发布。
|
||||
|
||||
当前 Codex 模型用量预计 2026-09-26 13:54(日本时间)恢复;已安排 13:55 从现有会话与工作树核实后继续执行。继续时不得重复启动原有进程、不得先进入 B/C、不得覆盖未跟踪 STEP-098 或旧 `.13` 发布资产。
|
||||
44
docs/报告/STEP-097_A2_阶段验证记录_v2.md
Normal file
44
docs/报告/STEP-097_A2_阶段验证记录_v2.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# STEP-097 A2 阶段验证记录 v2
|
||||
|
||||
**状态:PARTIAL / 独立扩展回归发现既有断言与既有函数语义冲突。** 本报告接续 v1,不覆盖其原始验证事实;A2 生产代码仍为未提交的工作树改动,不可据此进入 B/C。
|
||||
|
||||
## 1. 本次实际动作
|
||||
|
||||
- 2026-09-26(JST),在 Mac `/Users/tei/dev/MapLibreParticleLab`,`particle/main` 当前远端与本地同为 `bd1cfe95b7595a95314b9083754823ae170c5c8e`;Integration `main` 已核对 `318f8ccf911ac3136c3086a1d14e61f83e2d4bce`。
|
||||
- 现有 A2 工作树修改仍保留,未修改生产文件、未提交 A2 源码;用户的未跟踪 STEP-098 任务 MD 原样保留。本次只扩大原生回归并核对失败的旧源码/测试。
|
||||
- v1 已记录:OpenGL 和 Metal host 原生测试程序各自编译 exit 0;A2 聚焦 16/16 PASS,两次 Shader 生成一致;Metal 仅主机源码/原生测试,不是 iOS 实机测试。
|
||||
|
||||
## 2. 扩大回归
|
||||
|
||||
使用 OpenGL `build-step088-opengl/mbgl-test-runner`,运行:
|
||||
|
||||
```sh
|
||||
--gtest_filter='ParticleFieldRuntime.*:ParticleFieldProfileValidator.*:ParticleGpuField.*:ParticleFieldBinding.*:Source.RasterArray*:Source.HostField*:Source.ParticleField*:Source.CanonicalField*'
|
||||
```
|
||||
|
||||
结果 **58 项中 57 PASS、1 FAIL,退出码 1**。失败项 `Source.RasterArrayGpuContentIdentityExcludesRuntimeGeneration` 位于 `test/style/source.test.cpp:288`,预期 `sameFieldGpuContent(east, changed)` 为 false,实测 true。
|
||||
|
||||
核查本次未修改的 HEAD 定义:`sameFieldGpuContent()` 明确只比较兼容性、解码 scalar/vector/valid,不比较 `generation`、`dataVersion` 等生产者身份;旧测试却在仅增加 `dataVersion` 时要求 false。两者存在**原有测试期望与既有函数契约不一致**。本轮未另建旧 HEAD 二进制作基线重演,因此只称静态可证的原有矛盾,不声称已做旧版运行复现。
|
||||
|
||||
A2 新增 `sameFieldGpuEndpoint()` 负责严格 GPU 端点身份,明确比较 generation/dataVersion/validTime、U/V、validity、grid、encoding 等;对应新 `ParticleGpuField.*` 四项已通过。旧内容等价与新端点等价不应被混为一谈。
|
||||
|
||||
排除上述旧测试后,同一组其余 **57/57 PASS,退出码 0**;这只是隔离验证,不是宣布全部回归 PASS。
|
||||
|
||||
## 3. 后续修复门槛
|
||||
|
||||
- Codex 恢复 GPT-6-Sol medium 后,先复核旧内容比较与新端点比较各自用途;修正旧测试断言,使其与实际函数语义一致,或发现更深的契约问题时写阻塞报告。禁止仅为让旧断言通过而更改新端点身份约束或放宽安全机制。
|
||||
- 修改后重新编译原生 runner,原 58 项完整回归必须退出 0,并复跑 A2 聚焦用例。随后核对 Android 端 A2 APK 与当前最终代码一致。
|
||||
- **Android Gradle 编译由用户手工执行。** 标准 Mac 仓库命令为 `cd /Users/tei/dev/MapLibreParticleLab && ./tools/particle_android_validation/build_validation_apk.sh`。在 A2 代码完成前执行只能算中途 smoke,不是最终 Android 验收。用户提供编译结果后,再通过 RDC 对 FZG1-2 `192.168.200.139:5555` 安装、Logcat、截图验证。
|
||||
- iOS runtime 按用户明确决定豁免,记 `USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`;不虚报 iOS 实测。
|
||||
- 未进入 B/C,未运行 STEP-098,未改冻结的 `.13` 正式发布。
|
||||
|
||||
## 4. 原始证据
|
||||
|
||||
Mac 忽略目录 `artifacts/maplibre-particle/step097-a2-rdc/`:
|
||||
|
||||
- `a2-broader-core.log`:58 项、1 FAIL 原始记录;
|
||||
- `a2-broader-excluding-legacy-test.log`:其余 57/57 PASS;
|
||||
- `a2-broader-run-metadata.txt` / `a2-broader-exclude-metadata.txt`:过滤器;
|
||||
- v1 中已有 `build-opengl.log`、`build-metal-host.log`、`a2-focused-tests.log`、`a2-metalhost-core-tests.log`、Shader 生成日志。
|
||||
|
||||
本报告不覆盖测试失败,也不将尚未提交的代码描述为正式完成。
|
||||
96
docs/报告/STEP-097_A2_阶段验证记录_v3.md
Normal file
96
docs/报告/STEP-097_A2_阶段验证记录_v3.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# STEP-097 A2 阶段验证记录 v3
|
||||
|
||||
## 1. 结论
|
||||
|
||||
**A2 Android/Core 验证完成;STEP-097 整体仍 PARTIAL。** A2 的 04–07 在 Core、OpenGL/Metal host 回归和 FZG1-2 设备闭环达到本阶段检查点。没有开始 B/C,没有执行 Jest、STEP-098 或 `.13` 发布。iOS runtime 依用户决定豁免,记为 `USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`,不是 iOS PASS。
|
||||
|
||||
Codex 会话配置:**GPT-6 Luna High**,用户可见会话。RDC 独立执行了 CMake/GTest、shader generator、标准 Android APK 构建、FZG1-2 安装和设备场景。Codex 本轮未执行这些命令。
|
||||
|
||||
## 2. Git 与版本基线
|
||||
|
||||
- 仓库:Mac `Mac-mini-tei.lan`,`/Users/tei/dev/MapLibreParticleLab`;分支 `particle/main`。
|
||||
- 验证源码基线 HEAD:`5bb1a0e6353c5dfe8db8a61bbeba9cef447280ef`;A2 源码工作树 dirty,尚未提交。结束 HEAD 未变化。
|
||||
- 本次记录前的 A2 source diff SHA-256:`18927a5f30a88f5c1c3a00e0017473dc33b04151c227e4baf52250dc4ad2e6a2`;与 `artifacts/maplibre-particle/step097-a2-final-validation/mix-reuse-build-source.diff` SHA-256 完全一致。标准 APK 的 build-info 也记录相同 commit/branch 和 `git_dirty=true`,证明设备验证所用 source snapshot 与本轮代码一致。
|
||||
- Integration `main`:`318f8ccf911ac3136c3086a1d14e61f83e2d4bce`;C-001 未修改。
|
||||
- 冻结的 `.13` 发布坐标、tag、manifest 与产物未修改;本轮不发布。
|
||||
- 用户 untracked `docs/40_任务/STEP-098_QBIN_V4单帧首显与八帧滚动缓存_Codex任务_v1.md` 保持原样、未 stage。
|
||||
|
||||
## 3. A2 实施内容
|
||||
|
||||
| 项 | 实施与不变量 |
|
||||
| --- | --- |
|
||||
| 04 N04/B06 Forecast PLAY | Map Continuous update 驱动 Source-owned monotonic forecast clock;Android/iOS timer 只负责唤醒。同步 SourceObserver reentry 由 `forecastTickInProgress` 防止重复推进。显式 setTime、PLAY/PAUSE、rate、clear 保持 Core clock 所有权。 |
|
||||
| 05 B13 GPU endpoint / mix-only reuse | `sameFieldGpuContent()` 继续只表示 version-agnostic decoded payload 等价;新的 `sameFieldGpuEndpoint()` 严格比较 revision/time、U/V、validity、grid、encoding 等 renderer endpoint 身份。Source 对相同 content identity、binding、immutable decoded frame 与 plane/validity storage pointer 复用已有 materialized T0/T1,只更新 `sampleState`。frame/revision 或任一输入 storage 改变则重新物化;invalid/clear fail-closed。移除每 tick 深比较完整 snapshot vectors。 |
|
||||
| 06 B07 RGBA8 signed zero | 增加内部 `particle_gpu_math.hpp` signed RGBA8 编码,byte 128 明确表示 0;shader 以 high precision 还原 byte。未改公开 QBIN/FieldProfile encoding。数学测试覆盖正负取消、零、NaN/invalid 与量化下限。 |
|
||||
| 07 B08 Mercator U/V | helper 与 GL/Metal shader 的东向 U、北向 V 都使用相同纬度度量,极区分母夹紧;保持 canonical m/s 和现有视觉时间倍率。测试覆盖 0°/45°/60° 四方向及极区有限值。 |
|
||||
|
||||
三项 A2 Source 测试还覆盖 50 次 mix-only clock tick 不重物化、真实 endpoint 和相同 valid-time revision 替换触发重物化、clear 后 projection 清空,以及 source notification 嵌套 tick 不双推进。Host-facing 新 API、C-001、QBIN 格式未改变。
|
||||
|
||||
## 4. 修改文件
|
||||
|
||||
### 源码、shader 与 A2 测试
|
||||
|
||||
- `include/mbgl/particle/field/field_data_adapter.hpp`
|
||||
- `include/mbgl/particle/field/particle_gpu_math.hpp`(新增)
|
||||
- `include/mbgl/shaders/gl/particle_gpu_validation.hpp`
|
||||
- `include/mbgl/shaders/mtl/particle_gpu_validation.hpp`
|
||||
- `include/mbgl/style/sources/raster_array_source.hpp`
|
||||
- `platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/WeatherRuntime.java`
|
||||
- `platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/ParticleCoreLayerValidationActivity.kt`
|
||||
- `platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/Step097V4GoldenFixture.kt`(新增)
|
||||
- `platform/android/MapLibreAndroidTestApp/src/main/assets/integration/step097-v4/wind/T0.qbin`、`T1.qbin`、`contract.json`(新增 test-only fixture)
|
||||
- `platform/ios/src/MLNParticleFieldRuntime.mm`
|
||||
- `shaders/particle_gpu_validation.fragment.glsl`、`shaders/particle_gpu_validation.vertex.glsl`
|
||||
- `src/mbgl/map/map_impl.cpp`
|
||||
- `src/mbgl/particle/field/field_data_adapter.cpp`
|
||||
- `src/mbgl/particle/field/field_projection_materialization.cpp`
|
||||
- `src/mbgl/particle/field/field_runtime.cpp`
|
||||
- `src/mbgl/renderer/layers/render_particle_validation_layer.cpp`
|
||||
- `src/mbgl/style/sources/raster_array_source.cpp`、`src/mbgl/style/sources/raster_array_source_impl.hpp`
|
||||
- `test/CMakeLists.txt`
|
||||
- `test/particle/field_runtime.test.cpp`
|
||||
- `test/particle/particle_gpu_math.test.cpp`(新增)
|
||||
- `test/source/raster_array_v4_host_api.test.cpp`
|
||||
- `test/style/source.test.cpp`(仅修正旧断言对 `sameFieldGpuContent()` 的预期;严格 endpoint identity 由新断言覆盖)
|
||||
|
||||
本报告之后的文档变更为 `docs/30_设计/STEP-097_V13三轮问题去重与修复矩阵_v1.md`、本报告、`docs/报告/STEP-097_V13三轮审查问题合并修复_Codex完成报告_v2.md`、`docs/50_Step记录/STEP-097_V13三轮审查问题合并修复_v1.md`、`docs/60_测试/STEP-097_V13修复后集中验证清单_v1.md` 及根目录 `粒子开发记录.md`。无其他步骤范围代码。
|
||||
|
||||
## 5. RDC 构建与测试
|
||||
|
||||
| 验证 | RDC 结果 |
|
||||
| --- | --- |
|
||||
| 原有完整 58 项回归(修正旧断言后) | OpenGL **58/58 PASS**。先前 57/58 的失败根因是旧断言把 `sameFieldGpuContent()` 当成 revision-sensitive;函数原有契约明确忽略 generation/dataVersion。测试现在分别验证 payload 等价与 strict endpoint identity,没有放宽 endpoint 比较。 |
|
||||
| 当前 broad Core filter `Particle*.*:Source.RasterArray*:Source.HostField*:Source.ParticleField*:Source.CanonicalField*` | OpenGL **121/121 PASS**;Metal host **121/121 PASS**。 |
|
||||
| A2 定向 Source 三项 | OpenGL **3/3 PASS**;Metal host **3/3 PASS**,包括 mix-only 50 ticks、endpoint/revision invalidation 与同步通知重入。 |
|
||||
| shader generator | `node shaders/generate_shader_code.mjs` 连续两次成功,输出幂等。 |
|
||||
|
||||
构建/测试日志:`artifacts/maplibre-particle/step097-a2-final-validation/mix-reuse-broad-opengl.log`、`mix-reuse-broad-metal-host.log`、`mix-reuse-focused-opengl.log`、`mix-reuse-focused-metal-host.log`、`build-mix-reuse-opengl.log`、`build-mix-reuse-metal-host.log`。Metal host 测试是 Mac Core runner,不是 iOS runtime 或设备 Metal 验证。
|
||||
|
||||
## 6. Android APK 与 FZG1-2
|
||||
|
||||
- 标准命令由 RDC 执行:`./tools/particle_android_validation/build_validation_apk.sh`。
|
||||
- 产物:`artifacts/maplibre-particle/android-validation/MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk`;OpenGL Debug、x86_64、61,753,803 bytes;SHA-256 `27bf4a6cc191bd86c4d1c10398478d81c2999edd61fe7242e40e5188516b0ca5`。步骤归档副本:`artifacts/maplibre-particle/step097-a2-final-validation/MapLibreAndroidTestApp-A2-mix-reuse-x86_64.apk`。
|
||||
- RDC 安装至 Panasonic FZG1-2(ADB `192.168.200.139:5555`)成功;设备内 APK SHA-256 与构建产物相同。
|
||||
- Test-App-only Golden Wind T0/T1 场景脚本 exit 0,17 PNG、24 steps、ANR=0:load、seek 25/75、speed 2x/1x、PLAY 30 秒与 PLAY 时 UI/TIME 响应、PAUSE/stable pause、replay、CLEAR、reload、再次 PLAY/PAUSE。截图 `after-v4-mix-reuse/05_play_15s.png` 可见粒子。
|
||||
- 上传/reset 计数按资源 epoch 记录:clear/reload 前 mix-only playback 稳定于 `fieldUploadCount=6/resetCount=2`;CLEAR/reload 后计数合法增加到 `fieldUploadCount=10/resetCount=4`,reload 后 playback 稳定。没有宣称跨 CLEAR/reload 零上传/零 reset。
|
||||
- APK 输入源码 diff 与保存快照 `mix-reuse-build-source.diff` 的 SHA-256 都为 `18927a5f30a88f5c1c3a00e0017473dc33b04151c227e4baf52250dc4ad2e6a2`。
|
||||
|
||||
## 7. 历史失败、性能与平台边界
|
||||
|
||||
- 2026-09-26 12:43 首次 FZG1 ANR 的 main stack 是 Forecast clock tick 的同步 SourceObserver reentry;`forecastTickInProgress` guard 与回归测试针对该缺陷。后续有一轮没有 ANR 但 UI/TIME 仍不可用、主线程高负载;debuggerd 定位到每 tick 对大 QBIN projection 重复物化 validity/vector 并深比较 snapshot。mix-only immutable snapshot reuse 后,最终 FZG1 闭环通过。
|
||||
- 历史 debuggerd、首次 ANR、第二次无响应试跑及最终验证证据均保存在 `artifacts/maplibre-particle/step097-a2-final-validation/`;报告保留失败经过,不把中间失败覆盖成 PASS。
|
||||
- PLAY 30 秒只是交互闭环,不是 long-run 稳定性、FPS、frame time、内存或 GPU 性能基准;本轮没有这类数字。
|
||||
- 用户豁免 iOS runtime:`USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`。Android 与 Metal host Core 结果不能替代 iOS device PASS。
|
||||
- Integration C-001 未改,Host 公开 API/QBIN schema 未改;没有进入 B/C、没有运行 STEP-098/Jest、没有更改 `.13` 发布物。
|
||||
|
||||
## 8. 阶段与 Git 收尾
|
||||
|
||||
- A2 状态:Android/Core 代码、原生回归和 FZG1 验证完成。
|
||||
- STEP-097 整体:**PARTIAL**;B/C 未开始。其余 24 项逐项状态见更新后的去重矩阵。
|
||||
- 本报告无 commit/push。RDC 后续做独立 QA 与 scoped Git 操作;用户未跟踪 STEP-098 不得纳入。
|
||||
|
||||
## 9. RDC Git 源码提交核验(报告编写后更新)
|
||||
|
||||
- A2 源码、标准 TestApp 内部 fixture 与定向测试已以 **`f49ffa68a17e0a505199bdbeccf07f8543f3a05f`** 独立 scoped commit 提交并推送 `origin/particle/main`;26 个代码/fixture/test 路径,未包含任何文档、STEP-098、Integration、NavSea 或发布资产。远端 HEAD 回读相同。
|
||||
- 该提交只是把已经通过 RDC 验证的源码固定进 Git;构建时仍如实为 `5bb1a0e` + dirty source diff。源码部分保存快照的 SHA-256 对应提交内容,不把后续文档更新误算进构建输入。
|
||||
- 文档与 Step/矩阵/测试清单将作为独立 scoped commit 收尾。
|
||||
36
docs/报告/STEP-097_B_独立验收与版本阻塞报告_v1.md
Normal file
36
docs/报告/STEP-097_B_独立验收与版本阻塞报告_v1.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# STEP-097 B 阶段独立验收与版本阻塞报告 v1
|
||||
|
||||
状态:**PARTIAL / BLOCKED_NATIVE_AAR_COMPATIBILITY**。B 不判 PASS,C 不启动。本报告按实际验证结果更新,不以 Codex 终态代替验收。
|
||||
|
||||
## 基线与执行
|
||||
- 日期:2026-09-26;Mac 仓库 `particle/main` HEAD `b1239ac3b1554943200e6fbf14321459ccc86527`;实施工作树 dirty,尚未 commit/push。
|
||||
- Integration `main` 已由 RDC fetch 核验,本地及远端均为 `318f8ccf911ac3136c3086a1d14e61f83e2d4bce`;Contract C-001 未改。
|
||||
- Codex 两次均使用 GPT-6 Luna High,`codex exec --json`;分别收到 `turn.completed`、退出码 0;完整 JSONL/status.json 位于 `artifacts/maplibre-particle/step097-b10-codex/` 和 `step097-b-fixture-codex/`。
|
||||
- 工作范围:08 JSON、09 数值 ingress、10 Android owner/map destroy、20 Test Host 版本配对;用户未跟踪 STEP-098 及正式 .13 发布资产未触碰。
|
||||
|
||||
## 代码和独立证据
|
||||
- 08:按真实字节长度解析,拒绝 NUL、重复键、非法 strategy/schema,失败清旧计划;已存在定向 GTest。
|
||||
- 09:有效 cell 数值及 scale/offset 有限性验证在提交 Store 前执行;合法 validity=0 保留,坏高 revision 不覆盖旧场。
|
||||
- 10:新 View attachment owner、generation guard 与 map destroy observer;附 Android owner/JUnit 和 MapLibreMap 真正 onDestroy 测试。
|
||||
- 20:Test Host package.json 与 lock 均精确为 RN `13.4.1-particle.13`;正式 tarball SHA-256 `3a726db06cb967fe56de77ca4a72feed98cd642a2d1b73fd51b1531d15ef7539` 与 release manifest 一致,lock integrity 的 SHA-512 与实包一致。
|
||||
- Mac Core 增量构建 PASS;B 四项定向 GTest **4/4 PASS**;原 broad 134 项中 2 项旧 UInt8→Int16/UInt16 4-byte fixture 不合法,修正为合法 8-byte 小端四样本后重新构建,broad **134/134 PASS**。未放宽生产数值校验或既有断言。
|
||||
- 原始日志:`artifacts/maplibre-particle/step097-b-rdc/core-*.log`。
|
||||
|
||||
## 阻塞门槛
|
||||
- B10 新增 `MapLibreMap.addOnDestroyListener()` 为 Android SDK 新方法;正式 .13 release source commit `ddf20dec9fe9cd05b77274827b6825829500dd5a` **没有此方法**。
|
||||
- Test Host 默认及 RN 模块 Gradle 仍消费已发布原生 AAR `13.4.1-particle.13`。所以新 RN Kotlin bridge 直接调用该方法时,现有默认正式消费组合不具备此符号;不能用本工作树 Native 源码 JUnit 冒充 .13 RN consumer 编译通过。
|
||||
- 需要选择不增加已发布 ABI 依赖的完整生命周期方案,或者另走正式新 Native/RN 同版发行与配对验证。未经上游决定不得改写 .13 tag、manifest/AAR,也不得以反射或私有绕道掩盖版本问题。
|
||||
|
||||
## 2026-09-26 RDC 补充独立实测(Mac,覆盖上方旧测试数字)
|
||||
- B10 Codex 的持久化 `status.json`:`gpt-6-luna/high`,75 条 JSONL 事件,`turn.completed`,exit 0;旧 fixture 修正任务同样 `turn.completed` / exit 0。两次交付本身不代表验收。
|
||||
- 本轮 Mac 原生测试 runner 增量编译 exit 0;六项 B 定向 GTest **6/6 PASS**,完整扩展 filter **139/139 PASS**;旧 fixture 仅修正 16-bit 四样本合法长度,未放宽生产校验或原断言。证据:`artifacts/maplibre-particle/step097-b-rdc/validation-{build-core,focused-gtest,broad-gtest}.log` 与 `validation-status.json`。
|
||||
- Mac 使用现有 OpenJDK 17 和 Android SDK:`:MapLibreAndroid:testOpenglDebugUnitTest --tests org.maplibre.android.maps.MapLibreMapDestroyListenerTest` **2/2 PASS**;`:MapLibreAndroid:compileOpenglDebugJavaWithJavac` exit 0。JUnit XML 位于 `platform/android/MapLibreAndroid/build/test-results/testOpenglDebugUnitTest/`;证据在 `android-validation-status.json`、`validation-native-{destroy-junit,compile}.log`。
|
||||
- Owner Kotlin 单测使用独立临时 JVM 项目,源码及测试副本 SHA-256 与本工作树对应文件分别一致:生产 `36b437a0bc1573bafcf1a99ee37d4c438b72ea9ad5a9847fb0021f8093a3296a`,测试 `4c27cc3e82c0570b78b27aec46d565359eff11e18c2ff292e4188c158e8a4407`;JUnit **4/4 PASS**,0 fail/skip。第一次 offline 插件 marker 缺失,第二次正常解析并通过;详见 `owner-jvm-status.json` 和 `validation-owner-jvm-retry.log`。这是 owner helper 的 JVM 回归,不冒充 RN Host 集成。
|
||||
- `git diff --check` exit 0,正式 .13 release commit `ddf20dec9` 的 `MapLibreMap.java` 无 `addOnDestroyListener()`,而当前 RN Bridge 调用该方法且 Gradle 精确锁定旧 .13;因此 **默认 RN consumer 编译仍未通过/不可宣称 PASS**,此阻塞不因上述单元测试通过而解除。
|
||||
- Linux 独立工作树的 Gradle 仍有原进程,状态未确认完成,不能以 Mac 测试代替 Linux 结果;须等待并单独记录最终退出状态。
|
||||
- 当前结论维持 `PARTIAL / BLOCKED_NATIVE_AAR_COMPATIBILITY`。STEP-097 整体未关闭;C、STEP-098、正式发布均 NOT RUN。未授权改写 .13 tag/manifest/产物,不把当前不兼容代码推入正式消费基线。
|
||||
|
||||
## Git 阻塞检查点(2026-09-26,后续事实)
|
||||
- 为避免将当前无法和正式 .13 AAR 配对编译的新 RN bridge 提交进 `particle/main`,RDC 以 `b1239ac3b1554943200e6fbf14321459ccc86527` 创建独立 worktree,逐文件 SHA-256 比对 21 个 B 源码/测试/文档,并 scoped commit/push 到 `step097-b-blocked-20260926`。
|
||||
- 首个 checkpoint commit:`e463ba04ce956a54eed02b77f981eeeedd9a5534`;独立远端回读相符。主线 `origin/particle/main=b1239ac3b1554943200e6fbf14321459ccc86527` 不变。原 Mac dirty B 工作树及用户 STEP-098 不变;发布资产无差异。
|
||||
- Git 保存成果不等于 B 验收通过;B 仍 `PARTIAL / BLOCKED_NATIVE_AAR_COMPATIBILITY`,待 Integration / 发行流程明确新 Native/RN 同版配对或实现不依赖新 ABI 的可靠替代方案。不得启动 C、STEP-098 或覆盖 .13。
|
||||
57
docs/报告/STEP-097_V13三轮审查问题合并修复_Codex完成报告_v1.md
Normal file
57
docs/报告/STEP-097_V13三轮审查问题合并修复_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# STEP-097 V13 三轮审查问题合并修复 — Codex 完成报告 v1
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
**PARTIAL,停在 A1。** 阶段 0 已静态复核并建 24 项矩阵;A1 的 01/02/03/19 已做部分源码修改。01/02/03 缺少任务要求的专用可执行像素、Metal 附件及异常注入测试入口,不能标 `CODE_IMPLEMENTED_STATIC_CHECKED`。19 有 GTest 源码且静态核对完成,测试 **NOT RUN**。按照任务书“前一阶段没有明确代码完成和静态检查结果,不推进下一阶段”,A2/B/C 未开始。此次提交不是 V13 运行态验收,也不是软件发布。
|
||||
|
||||
## 2. Git、机器与版本基线
|
||||
|
||||
- 机器:`Mac-mini-tei.lan`;目录:`/Users/tei/dev/MapLibreParticleLab`;分支:`particle/main`。
|
||||
- 起始 HEAD 与远端查询值:`720d309364628f85049cf9969d71c71e8dfacce9`;起始工作树 clean,子模块未见偏离。
|
||||
- A1 部分代码检查点:`e67e4961b5b1a1ff8fa7c1e3263799d63f89cadc`。本报告成稿时的代码 HEAD 为该提交;本报告及最终文档另作 scoped 交付提交,最终 HEAD 以交付后的 Git 记录为准。
|
||||
- `.13` tag 解引用:`ee0b89e711bf01335b3aa71d36def785cd97fec3`;manifest 记录 RN/Android `13.4.1-particle.13`、iOS `6.28.0-particle.13`。没有更改 tag、manifest 或已发布产物。
|
||||
- Integration 本地只读 HEAD `39ad8415fbdb76c1822460bb09f3a3f35fe141e0`,C-001/RFC-001 为既有合同;没有由 RDC 独立确认其远端同步。
|
||||
|
||||
## 3. 实际修改与新增文件
|
||||
|
||||
| 范围 | 文件与内容 |
|
||||
| --- | --- |
|
||||
| A1 代码 | `src/mbgl/renderer/paint_parameters.{hpp,cpp}`、`render_target.cpp`、`renderer_impl.cpp`、`render_orchestrator.hpp`、`retained_underlay_cache.{hpp,cpp}`、`retained_underlay_runtime_state.hpp`;`src/mbgl/mtl/offscreen_texture.cpp`、`render_pass.cpp`。 |
|
||||
| A1 测试源码 | `test/renderer/retained_underlay_cache.test.cpp` 增加旧 topology release 不计入新同 index 的 GTest。 |
|
||||
| 新增文档 | `docs/30_设计/STEP-097_V13三轮问题去重与修复矩阵_v1.md`、`docs/50_Step记录/STEP-097_V13三轮审查问题合并修复_v1.md`、`docs/60_测试/STEP-097_V13修复后集中验证清单_v1.md`、本报告。 |
|
||||
| 仅追加 | 根目录 `粒子开发记录.md`。 |
|
||||
|
||||
没有修改 Integration、NavSea、项目总则、旧调查、release manifest、公开 Field/QBIN 协议或已发布坐标。
|
||||
|
||||
## 4. 各问题状态
|
||||
|
||||
具体旧编号、源码定位、反例、测试入口和平台待测项见[24 项矩阵](../30_设计/STEP-097_V13三轮问题去重与修复矩阵_v1.md)。编号 01/02/03:源码已局部修改,仍为 `WAITING_RDC_BUILD_AND_RUNTIME`,且专用测试源码缺口阻止 A1 完成。19:`CODE_IMPLEMENTED_STATIC_CHECKED`,GTest **NOT RUN**。04/05/07/08/09/10/11/13/16/17/20:`WAITING_RDC_BUILD_AND_RUNTIME` 在矩阵中表示待实施,不表示已有代码。06/12/14/15/18/21/22/23/24:`DEFERRED_DESIGN`;其中 23/24 是 Integration 协调项。无项目被判产品级 PASS,无项因三轮重复编号被删除。
|
||||
|
||||
## 5. 已实施事实、差异与停止原因
|
||||
|
||||
1. 01:新 render target 绑定后重置 `nextStencilID` 与 tile clipping map;同一 target 内未清 stencil 时仍可复用。接入 retained、普通 offscreen 与主目标三处目标切换。
|
||||
2. 02:Metal 离屏 `isRenderable()` 检查 color/depth/stencil,simulator/x86 combined depth-stencil 同时绑定两个附件;RenderPass 转换 color/depth/stencil clear/load/store,创建失败显式抛出供 retained fallback。实际 Metal validation **NOT RUN**。
|
||||
3. 03:范围 visitor 可回报绘制异常;retained build 收到错误后标失败并转 full fallback,不发布成功缓存。真实 layer 故障注入 **NOT RUN**。
|
||||
4. 19:旧 cache 的 release 仅在同 topology revision 更新当前 segment 计数,aggregate release 仍记录。新增同 index 换边界的定向 GTest 源码。
|
||||
|
||||
与任务书差异:未完成 A1 关键测试入口,故没有进入 A2/B/C,未形成各阶段成功提交。停止依据是任务书第 2、4、7 节的不可跨越检查点,而非将未运行的构建当作失败。当前代码检查点仍需 RDC 独立审查,特别核对 Metal API/资源所有权和异常后的 GL/Metal fallback。
|
||||
|
||||
## 6. 静态检查、构建与测试记录
|
||||
|
||||
- 已执行:`git diff --check` 与 `git diff --cached --check`,均无输出、exit 0;`rg -n '[[:blank:]]+$'` 核对新文档,无匹配;`rg` 核对 `beginRenderTarget` 三处调用和 `recordSegmentResourceRelease` 的新签名及测试调用。已只读核对 metal-cpp 的 `StoreActionStore`、`setClearStencil` 声明。
|
||||
- CMake/Ninja/Xcode/Gradle、codegen、链接、APK/AAR/XCFramework、npm install/typecheck、GTest、Jest、GPU capture、设备与 NavSea:**NOT RUN**。未有本轮 build log、测试数、耗时或性能数。
|
||||
- 生成产物:无。大小/SHA-256/Renderer/Build Type/ABI:不适用。软件发布:**NOT RUN**。
|
||||
- 后续精确命令、fixtures 缺口、预期与责任见[集中验证清单](../60_测试/STEP-097_V13修复后集中验证清单_v1.md)。
|
||||
|
||||
## 7. 已知问题、未完成事项与下一步
|
||||
|
||||
- 先为 01 增加同 vector source/same tiles 的 R-R/R-C-R/C-R-R 可执行像素 fixture;为 02 增加 Metal descriptor/combined attachment 与失败注入入口;为 03 增加 layer render 异常注入及下一帧恢复入口。之后静态审查 A1,再由 RDC 在授权下集中编译和运行。
|
||||
- A2 的 PLAY/endpoint/零量化/Mercator,B 的 JSON/坏帧/Android listener/Host 版本,C 的 diagnostics/ordinal/物化/资源优化均未实施。不得将本报告用于关闭这些问题。
|
||||
- Integration C-001/RFC-001 未更新;23 的 Compatibility/Status 及 24 的共享地理 Ocean Mask 需粒子负责人协调。此次未触发需要改变公共合同的实现,因此没有伪造“公共接口阻塞报告”或声称决定已被接受。
|
||||
- 建议继续 STEP-097 A1 的测试入口与独立审查,在完成当前检查点后再按原任务书进入 A2;不自行创建下一 Step。
|
||||
|
||||
## 8. 2026-09-26 A1 测试入口续作(补充,历史结论保留)
|
||||
|
||||
从 `be5b9080ed926a2632c5126c3522e242a2a85967` 的 clean `particle/main` 继续,只处理 A1 01/02/03 测试入口。新增 `Step097StencilFixture.kt` 与 Validation Activity 菜单,提供一个 GeoJSON source 上的 R-R/R-C-R/C-R-R;新增 `render_orchestrator_error.test.cpp` 正反范围异常/observer/cache 重试 GTest;新增 `mtl_retained_attachments.test.cpp` 离屏附件和 clear/load/store GTest。`render_pass.{hpp,cpp}` 将生产 clear 逻辑抽为内部辅助函数供测试共用;`test/CMakeLists.txt` 登记两个测试源。更新矩阵、Step、集中验证清单和开发记录。
|
||||
|
||||
代码 scoped commit:`d0ced7235`。此续作的 `git diff --check` 静态检查无输出;构建、GTest、Jest、APK、Metal/iOS、设备截图及软件发布均 **NOT RUN**。RDC 已于本续作之前执行第 19 项 GTest 1/1 PASS 和 FZG1 设备辅助冒烟;不能把那些旧结果归到本次新测试源码。01/02/03 仍需 RDC 编译与 runtime 验证,特别是 Metal 失败注入和 GPU 层异常 fallback,A1 仍 PARTIAL;A2/B/C 未开始。文档 scoped commit 与远端状态以最终 Git 记录为准。
|
||||
96
docs/报告/STEP-097_V13三轮审查问题合并修复_Codex完成报告_v2.md
Normal file
96
docs/报告/STEP-097_V13三轮审查问题合并修复_Codex完成报告_v2.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# STEP-097 V13 三轮审查问题合并修复 — Codex 完成报告 v2
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
**PARTIAL:A1 与 A2 的 Android/Core 检查完成;B/C 未开始。** A2 04–07 已由 Codex GPT-6 Luna High 在用户可见会话实现,由 RDC 独立构建、测试和执行 FZG1-2 运行闭环。iOS runtime 依据用户决定豁免,状态为 `USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`,不是 iOS PASS。没有运行 Jest、STEP-098 或发布 `.13`。
|
||||
|
||||
## 2. Git、环境与发布基线
|
||||
|
||||
- 机器:`Mac-mini-tei.lan`;仓库:`/Users/tei/dev/MapLibreParticleLab`;branch:`particle/main`。
|
||||
- 源码验证基线 HEAD:`5bb1a0e6353c5dfe8db8a61bbeba9cef447280ef`;验证时 worktree dirty,未提交。报告编写只增加/更新本报告所列 A2 文档。
|
||||
- A2 source diff SHA-256 `18927a5f30a88f5c1c3a00e0017473dc33b04151c227e4baf52250dc4ad2e6a2`,与 APK build source snapshot 完全相同。
|
||||
- Integration `main`=`318f8ccf911ac3136c3086a1d14e61f83e2d4bce`;C-001 未修改。
|
||||
- 冻结 `.13` 坐标/tag/manifest/发布产物未改。没有提交或 push。
|
||||
- 用户 untracked STEP-098 任务文件保持未跟踪、未修改、未 stage。
|
||||
|
||||
## 3. 修改文件
|
||||
|
||||
源码、shader、测试及 Test-App-only V4 Golden fixture 的完整路径列于 [A2 阶段验证记录 v3](STEP-097_A2_阶段验证记录_v3.md) 第 4 节。核心范围包括 `RasterArraySource` 与 `Map::Impl` Forecast clock、严格 GPU endpoint identity 和 mix-only snapshot reuse、signed RGBA8 helper、GL/Metal shader 与 Mercator 数学、Android/iOS timer wake bridge、A2 Core 回归和 test-only Wind T0/T1 fixture。
|
||||
|
||||
本轮交付文档:
|
||||
|
||||
- `docs/报告/STEP-097_A2_阶段验证记录_v3.md`
|
||||
- 本文件 `docs/报告/STEP-097_V13三轮审查问题合并修复_Codex完成报告_v2.md`
|
||||
- 更新 `docs/30_设计/STEP-097_V13三轮问题去重与修复矩阵_v1.md`、`docs/50_Step记录/STEP-097_V13三轮审查问题合并修复_v1.md`、`docs/60_测试/STEP-097_V13修复后集中验证清单_v1.md`
|
||||
- 仅追加根目录 `粒子开发记录.md`
|
||||
|
||||
未修改 Integration、NavSea、项目总则、C-001、QBIN schema、`.13` 发布物、用户 STEP-098 或 Host public API。
|
||||
|
||||
## 4. 24 项状态
|
||||
|
||||
| # | 状态与实际结论 |
|
||||
| --- | --- |
|
||||
| 01 N01/B01 | A1 Android/Core 检查完成;同源 stencil fixture FZG1 检查已在 A1 报告记录。A1 iOS runtime 按用户决定豁免。 |
|
||||
| 02 N02/B02 | A1 Android/Core 阶段收口;Metal host 不是设备验证。无 iOS runtime,按用户豁免记录。 |
|
||||
| 03 N03/B12 | A1 Android/Core 阶段收口;异常恢复 GTest 已在 A1 报告记录。 |
|
||||
| 04 N04/B06 | A2 完成;唯一 Source forecast clock、wake-only timer 与同步回调重入保护;OpenGL/Metal host 定向测试和 FZG1 PLAY/PAUSE 实测通过。 |
|
||||
| 05 B13 | A2 完成;`sameFieldGpuContent` 仍为 payload 等价,strict endpoint identity 独立比较;mix-only snapshot reuse 与 revision invalidation 通过 Core/设备验证。 |
|
||||
| 06 B07 | A2 完成;内部 signed zero encoding/shader 路径和数学边界测试通过 OpenGL 与 Metal host broad 回归。 |
|
||||
| 07 B08 | A2 完成;U/V 纬度 metric 和极区夹紧,数学回归与 Android 场景通过;不等于 iOS runtime。 |
|
||||
| 08 N05 | `WAITING_RDC_BUILD_AND_RUNTIME`;B 未开始。 |
|
||||
| 09 N06 | `WAITING_RDC_BUILD_AND_RUNTIME`;B 未开始。 |
|
||||
| 10 N07 | `WAITING_RDC_BUILD_AND_RUNTIME`;B 未开始。 |
|
||||
| 11 F01/C01 | `WAITING_RDC_BUILD_AND_RUNTIME`;C 未开始。 |
|
||||
| 12 F02/C02/B03 | `DEFERRED_DESIGN`;同步等待依赖未在本阶段处理。 |
|
||||
| 13 F03/C03/B04 | `WAITING_RDC_BUILD_AND_RUNTIME`;C 未开始。 |
|
||||
| 14 F04/C04 | `DEFERRED_DESIGN`;无资源预算改动。 |
|
||||
| 15 C04/B11 | `DEFERRED_DESIGN`;无 store 总容量语义改动。 |
|
||||
| 16 F07/C05 | `WAITING_RDC_BUILD_AND_RUNTIME`;C 未开始。 |
|
||||
| 17 C06/B05/B10 | mix-only immutable materialization 子项随 A2 完成;其他 diagnostics、ordinal、UI owner 工作仍待后续,整体不得解读为 C 完成。 |
|
||||
| 18 C07 | `DEFERRED_DESIGN`;drawable/tweaker 生命周期未改。 |
|
||||
| 19 F09/O01 | A1 已有 GTest 与 FZG1 证据保留;不把资源诊断夸大为 GPU 无泄漏证明。 |
|
||||
| 20 F05/E01 | `WAITING_RDC_BUILD_AND_RUNTIME`;B/Test Host package lock 未改、未验证。 |
|
||||
| 21 F06 | `DEFERRED_DESIGN`;保留 multi-C 安全 fallback。 |
|
||||
| 22 F08 | `DEFERRED_DESIGN`;保留保守失效策略。 |
|
||||
| 23 F10/E02 | `DEFERRED_DESIGN`;需要跨项目协调,Integration status 未改。 |
|
||||
| 24 STEP-095 §7/B09 | `DEFERRED_DESIGN`;共享地理 Ocean Mask 需要后续跨项目契约。 |
|
||||
|
||||
矩阵保留全部 24 项、旧编号及平台边界。
|
||||
|
||||
## 5. 实现摘要与偏差
|
||||
|
||||
1. Forecast clock 每次由 Map Continuous update 调用一次;同步 SourceObserver 重入不再触发第二次 tick。Android/iOS timers 负责 triggerRepaint 唤醒,不独立推进时间。
|
||||
2. Strict endpoint identity 涵盖 frame/revision、grid、U/V、validity 与 encoding。mix-only 时复用当前 immutable materialized snapshots;新 frame/revision 或 storage identity 改变时重新 materialize;clear/invalid 仍 fail-closed。没有使用 generation/dataVersion 偷换 `sameFieldGpuContent()` 的已有语义。
|
||||
3. RGBA8 byte 128 表示 signed zero,GL/Metal shader 使用高精度 byte reconstruction。东/北向 Mercator 位移应用一致纬度 metric。
|
||||
4. 新增 A2 定向 source 测试、GPU math 测试及 test-only Golden Wind 两帧 fixture;没有新建 Host API、QBIN 格式或 C-001 语义。
|
||||
5. 首次 FZG1 ANR 是同步 clock notification 重入;guard 修复后仍有一轮因每 tick 重物化大场、深比较而 UI 不响应。最终 mix reuse 版本完成设备闭环。原始失败日志与 debuggerd 均保留。
|
||||
|
||||
## 6. RDC 构建、测试、设备及产物
|
||||
|
||||
- 原 58 项回归:旧断言修复后 **58/58 PASS**。失败断言与旧函数定义不符,修复测试语义;没有放宽新 strict endpoint 条件。
|
||||
- OpenGL broad Core filter:**121/121 PASS**。
|
||||
- Metal host 相同 broad Core filter:**121/121 PASS**。
|
||||
- 三项 A2 Source 定向测试:OpenGL 和 Metal host 各 **3/3 PASS**。
|
||||
- Shader generator 连续两次成功、输出幂等。
|
||||
- 标准 APK `./tools/particle_android_validation/build_validation_apk.sh`:OpenGL Debug、x86_64、61,753,803 bytes;SHA-256 `27bf4a6cc191bd86c4d1c10398478d81c2999edd61fe7242e40e5188516b0ca5`。FZG1-2 安装成功、设备 hash 相同。
|
||||
- Golden 闭环:17 PNG、24 steps、exit 0、ANR=0;PLAY 30 秒并确认 TIME/UI 操作响应,随后 PAUSE/stable pause/replay/CLEAR/reload/replay/PAUSE。15 秒截图有可见粒子。
|
||||
- 日志资源 epoch:首次 stable mix-only `upload=6/reset=2` 保持不变;CLEAR/reload 后合法到 `upload=10/reset=4`,随后 playback 稳定。没有宣称跨 CLEAR/reload 零上传或 reset。
|
||||
- Android 产物、测试日志、设备日志、截图、步骤清单、debuggerd 与历史失败证据在 `artifacts/maplibre-particle/step097-a2-final-validation/`。
|
||||
- 本轮无 FPS、p50/p95/p99、frame time、内存或 long-run 性能结论。iOS runtime 未运行,用户豁免。
|
||||
|
||||
## 7. 未完成与限制
|
||||
|
||||
- STEP-097 总体仍 `PARTIAL`:B/C 未进入。04–07 完成不代表 08–24 全完成;第 17 项只有 mix-only 子项完成。
|
||||
- iOS runtime:`USER_ACCEPTED_WITHOUT_IOS_RUNTIME_TEST`。Metal host GTest 只证明共享 Core host 路径,不能替代 iOS Metal device PASS。
|
||||
- FZG1 Golden 是 Test-App-only fixture,不是 NavSea、正式产品数据链或全量天气验收。
|
||||
- 30 秒 PLAY 交互不构成长时稳定性或正式 GPU 性能验收。
|
||||
- 无提交/推送;用户 STEP-098 未触碰;`.13` 未发布。
|
||||
|
||||
## 8. 后续交接
|
||||
|
||||
RDC 对当前 dirty A2 代码与本报告做独立 QA,按用户后续授权执行 scoped Git 操作。若后续继续 STEP-097,只能由用户明确允许进入下一个阶段后启动 B;本报告不自动开启 B/C。
|
||||
|
||||
## 9. RDC 源码 Git 后续事实(本报告编写后)
|
||||
|
||||
- 源码 scoped commit **`f49ffa68a17e0a505199bdbeccf07f8543f3a05f`** 已推送 `origin/particle/main`,远端核对一致。此事实覆盖前文“未提交/未推送”的报告编写时状态,保留原始验证 HEAD 以便追溯 dirty APK 的实际构建来源。
|
||||
- 文档另行提交;不将源代码 Git commit 当作额外编译/设备测试。用户 STEP-098 仍不提交,B/C 不启动。
|
||||
129
docs/报告/STEP-098_QBIN_V4单帧与八帧滚动缓存_Codex完成报告_v1.md
Normal file
129
docs/报告/STEP-098_QBIN_V4单帧与八帧滚动缓存_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# STEP-098 QBIN V4 单帧与八帧滚动缓存 Codex 完成报告 v1
|
||||
|
||||
日期:2026-09-26(JST)
|
||||
结论:**IMPLEMENTED / WAITING_RDC_VALIDATION_AND_PLATFORM_RUNTIME**。Codex 源码阶段已交付;未构建、未运行 T01–T17,不能标记功能 PASS。
|
||||
|
||||
## 1. 执行身份与契约
|
||||
|
||||
- 执行器指定实际模型:**GPT-6 Luna High**。
|
||||
- 仓库:Mac 原始正式源码目录 `/Users/tei/dev/MapLibreParticleLab`;branch `particle/main`。
|
||||
- 起始/结束 HEAD:`b1239ac3b1554943200e6fbf14321459ccc86527`(本轮无 commit)。
|
||||
- 起始时 `origin/particle/main` 同为 `b1239ac3b1554943200e6fbf14321459ccc86527`。
|
||||
- Integration `main`/`origin/main`:`1dd576d3304e267e53ed64d31f9711d2350ce10a`,工作树干净。
|
||||
- 本轮依据 RFC-004、ADR-001、C-002、COORD-005。新契约状态为已接受的设计决定,非代码/设备/V15 发布验收。旧 RFC-003/COORD-004 B 已按用户指示作废删除;此事实解除 `STEP-098_公共契约阻塞报告_v1.md` 的历史阻塞,本轮未修改旧报告或原任务 v1。
|
||||
- 本机 Android Native/RN 版本文件仍为 `13.4.1-particle.13`,iOS VERSION 为 `6.28.0`。未更改版本锁、`.13` release/tag/manifest/AAR、独立 checkpoint worktree 或远端。
|
||||
- `git submodule status` 与记录 SHA 一致。开工时工作树已有 STEP-097 B tracked/untracked 改动及 STEP-098 原任务等,共 29 条 status 项;均保留。没有执行 reset、clean、stage、commit 或 push。
|
||||
|
||||
## 2. 实施结果
|
||||
|
||||
### Core、Ingress、Resolver
|
||||
|
||||
- Store 按每个 `fieldKey` 最多 8 个帧升序存储。精确 validTime 返回真实单帧 T0=T1、mix=0;只有非精确请求且存在合法相邻 pair 时才做插值,未覆盖时间 fail-closed。
|
||||
- 分离可共存 schema 与插值身份:相同 grid/plane schema 的新 reference/profile/fieldID 批次可进入窗口并按精确时刻单帧首显;双帧插值必须匹配 `fieldID`、QBIN `referenceTimeMs`、resolved `profileID/version` 和 grid/plane schema。Ingress 验证后保留 profile 身份,避免同 schema 不同 profile 被混插。
|
||||
- 满额新时间插入使用临时候选有序集合:以新帧 validTime 为中心比较旧最早/最晚端点距离,远端先在候选集删除;等距按 C-002 删除原最早端。候选邻接 schema 验证成功后才 `swap`;失败不改 Store。满额正常有效插入仍返回既有 `AcceptedInserted`,`RejectedCapacity` 名称保留兼容。
|
||||
- Duplicate、同时间 revision/stale/identity-conflict 仍走既有分支,不触发其他时间淘汰;submit 不触碰 forecast clock。每 key 可独立达到 8 帧。
|
||||
- 增加原始 decoded 字节计数:每帧为有效性 bitset + 非 validity plane payload,Store/Series 汇总;不计 profile 元数据、Float32 物化快照、图像、GPU texture、particle/trail。
|
||||
|
||||
### Projection、Renderer、Bridge
|
||||
|
||||
- Binding 和 materialization 支持精确单帧,T0/T1 共享同一个物化 Snapshot。标量-only 帧仍不能成为向量 Particle projection。
|
||||
- Particle 与 WeatherColor 首次安装同一 endpoint 时复用 image/texture/scale;当前 bracket 不变时 Source projection 继续沿用 existing equality/materialization reuse;被淘汰的活动端点重新 resolve 后失效清除。
|
||||
- 实现位于 Shared Core/common Renderer,GL/Metal 共用语义;没有声明 Android 或 iOS runtime 通过。
|
||||
- `FieldRuntime.ts`、`NativeWeatherRuntimeBridge.ts`、Android Kotlin/JNI 与 iOS Objective-C++/Facade 静态核对:旧 `submitEncodedField(...)`、`setEncodedForecastTime(...)` 名称、签名、返回名称和调用顺序未修改;bridge 没有本轮改动。
|
||||
- **ABI 说明**:RN/平台 bridge 方法 ABI 未变;`DecodedFieldFrame` 新增内部 profile 字段,C++ 数据结构布局发生变化,需同源码重新构建 Native 消费端。未使用 `.13` AAR 冒充该 ABI 验证。
|
||||
|
||||
## 3. 本轮修改文件
|
||||
|
||||
### STEP-098 源码与测试
|
||||
|
||||
- `include/mbgl/particle/field/field_runtime.hpp`
|
||||
- `src/mbgl/particle/field/field_runtime.cpp`
|
||||
- `src/mbgl/particle/field/field_projection_materialization.cpp`
|
||||
- `src/mbgl/particle/ingress/qbin_v2_decoder.cpp`
|
||||
- `src/mbgl/renderer/layers/render_particle_validation_layer.cpp`
|
||||
- `src/mbgl/renderer/layers/render_weather_color_layer.cpp`
|
||||
- `test/particle/field_binding.test.cpp`
|
||||
- `test/particle/field_runtime.test.cpp`
|
||||
- `test/particle/qbin_v2_decoder.test.cpp`
|
||||
- `test/source/raster_array_v4_host_api.test.cpp`
|
||||
|
||||
### 新增/追加文档
|
||||
|
||||
- 新增 `docs/30_设计/STEP-098_QBIN_V4单帧与八帧滚动缓存设计_v1.md`
|
||||
- 新增 `docs/50_Step记录/STEP-098_QBIN_V4单帧与八帧滚动缓存_v1.md`
|
||||
- 新增本报告 `docs/报告/STEP-098_QBIN_V4单帧与八帧滚动缓存_Codex完成报告_v1.md`
|
||||
- 仅追加根目录 `粒子开发记录.md`
|
||||
|
||||
现存 dirty B 文件、STEP-098 原任务、旧阻塞报告与 B untracked helper/test 均保留。`test/particle/field_runtime.test.cpp` 和 `test/source/raster_array_v4_host_api.test.cpp` 开工前已有 B dirty 内容;其既有 fixture 修正未覆盖,本轮在其上增加 STEP-098 测试,并给 Source QBIN helper 加入 reference-time 参数。本轮没有新增 RN、Android/iOS bridge、Host/NavSea/Server 文件。
|
||||
|
||||
## 4. T01–T17 测试代码覆盖与真实运行状态
|
||||
|
||||
“代码覆盖”指对应源码测试场景已新增或由既有定向测试覆盖;所有行的**实际运行状态均为 NOT RUN**。没有将未运行测试写成 PASS。
|
||||
|
||||
| 项 | 代码覆盖 | 实际运行 |
|
||||
| --- | --- | --- |
|
||||
| T01 | 单帧精确 projection/materialization、标量-only 不产生向量、Source 色场/粒子单帧 | NOT RUN |
|
||||
| T02 | 第二帧加入后中点进入双帧插值 | NOT RUN |
|
||||
| T03 | 乱序升序;wind/wave 独立 key;批次边界不混插 | NOT RUN |
|
||||
| T04 | 满 8 后未来帧保留、最早端淘汰 | NOT RUN |
|
||||
| T05 | 满 8 后过去帧保留、最晚端淘汰 | NOT RUN |
|
||||
| T06 | 内部时间等距 tie 删除原最早端并维持 8 帧有序 | NOT RUN |
|
||||
| T07 | 新 reference/profile 批次精确首显、补帧升级、回到旧批次精确单帧 | NOT RUN |
|
||||
| T08 | Duplicate、identity conflict、revision、stale 有既有 Core/Source 用例;无单个覆盖所有分支的新满额综合用例 | NOT RUN |
|
||||
| T09 | 损坏/合同/数值/Grid schema 拒绝及旧投影/字节计数保持;既有 Binding 覆盖错误向量 | NOT RUN |
|
||||
| T10 | pair mix 0.25/0.5/0.99 与真实 validTime;精确端点使用单帧表示 | NOT RUN |
|
||||
| T11 | 非覆盖时间返回 WindowUnavailable | NOT RUN |
|
||||
| T12 | 外围滚动时活动 pair 与 requestedTime 保持 | NOT RUN |
|
||||
| T13 | 活动 endpoint 被淘汰后清除物化 projection | NOT RUN |
|
||||
| T14 | wind 与 wave 各持 8 帧、各自字节计数 | NOT RUN |
|
||||
| T15 | 同帧共用 Snapshot/首次 GPU endpoint 资源的源码路径;context loss/真实 GPU 未覆盖 | NOT RUN |
|
||||
| T16 | Core 120 次双向滚动、8 帧上限、clear/clearAll 与 decoded byte 回落;Runtime/GPU 长期泄漏未覆盖 | NOT RUN |
|
||||
| T17 | 现有 API/类型/方法顺序静态核对;自动化两步成功/失败 consumer 测试未运行 | NOT RUN |
|
||||
|
||||
## 5. 已执行与未执行验证
|
||||
|
||||
| 项目 | 实际结果 |
|
||||
| --- | --- |
|
||||
| `git diff --check` | **通过**,仅为空白与 patch 格式检查 |
|
||||
| 新增 C++ 编译 | **NOT RUN** |
|
||||
| Core/OpenGL GTest | **NOT RUN** |
|
||||
| Metal host GTest / iOS 编译 | **NOT RUN** |
|
||||
| Android Gradle、RN consumer、Test Host | **NOT RUN** |
|
||||
| iOS/Xcode、Metal 设备 | **NOT RUN** |
|
||||
| Jest/npm CI/ADB/模拟器/实体机 | **NOT RUN** |
|
||||
| release pipeline、artifact、commit/push | **NOT RUN** |
|
||||
|
||||
RDC 按顺序执行以下命令,阶段 B 通过后才进入 C:
|
||||
|
||||
### 阶段 B:Store / Ingress / Binding
|
||||
|
||||
```bash
|
||||
ninja -C build-step088-opengl mbgl-test-runner
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='ParticleFieldRuntime.*:ParticleFieldBinding.*:ParticleEncodedFieldIngress.*'
|
||||
```
|
||||
|
||||
### 阶段 C:Source / Projection / Renderer(OpenGL 与 Metal host)
|
||||
|
||||
```bash
|
||||
ninja -C build-step088-opengl mbgl-test-runner
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='Particle*.*:Source.RasterArray*'
|
||||
ninja -C build-step097-metal-host mbgl-test-runner
|
||||
build-step097-metal-host/mbgl-test-runner --gtest_filter='Particle*.*:Source.RasterArray*'
|
||||
```
|
||||
|
||||
OpenGL/Metal host 命令均未在本轮执行。Metal host 结果也不能替代 iOS device runtime。
|
||||
|
||||
## 6. 手工验收、内存与剩余事项
|
||||
|
||||
Windows 11 Android 手工命令和 T01–T17 一轮场景清单见 [STEP-098 记录 §D](../50_Step记录/STEP-098_QBIN_V4单帧与八帧滚动缓存_v1.md)。只可使用后续相同源码生成且 Native/RN 版本匹配的 V15 Host APK、RN 包和 Native AAR;命令显式使用 `adb -s 192.168.200.139`。本轮未启动 V15 pipeline。iOS 需由 Mac 用户在同一冻结源码手工构建并用 Metal 设备检查单帧、双帧、批次边界、双向滚动、clear/context 恢复和长时内存。
|
||||
|
||||
CPU decoded 原始存储字节测量 API 已加入,测试源码含 120 次循环和 clear 后归零断言,但断言未运行。Float32 物化占用、GL/Metal 纹理、trail/GPU 峰值、32 帧风险和长时间内存趋势均未测量;不能据此报告内存安全 PASS。当前没有证据显示必须增加全局 MiB 上限,后续由 RDC/设备数据判断是否另行设计预算。
|
||||
|
||||
## 7. 工作树、阻塞与停止点
|
||||
|
||||
- 旧 `BLOCKED_CONTRACT` 不再适用,RFC-004 等新契约已正式接受。
|
||||
- 本轮剩余门槛是 RDC 尚未构建/测试。测试失败、编译失败或状态未知时,停留该阶段并保留现场;不得跳过到平台/V15。
|
||||
- STEP-097 B 的既有源码仍在本地工作树,原 Mac 工作树与 checkpoint worktree 均未重置或清理。本轮没有将 B 改动归入 STEP-098 报告修改集。
|
||||
- 当前版本锁仍是 Native/RN `.13`;新增 `DecodedFieldFrame` C++ layout 不兼容证据尚未通过同版本新构建验证。不能用旧 `.13` AAR 替代。
|
||||
- 未完成:RDC Core/Renderer 构建与 GTest、Android/iOS 同源码配套编译与真实 runtime、T01–T17 逐项实际执行、长时间内存/资源验证、V15 pipeline、版本制品与 Git 收尾。
|
||||
- 本次停止点:**Codex 源码交付**。无 commit、push、release,也没有宣称 STEP-098 全部验收完成。
|
||||
33
docs/报告/STEP-098_RDC定向验收报告_v1.md
Normal file
33
docs/报告/STEP-098_RDC定向验收报告_v1.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# STEP-098 RDC 定向验收报告 v1
|
||||
|
||||
日期:2026-09-27(JST)
|
||||
状态:**CORE / OPENGL_HOST / METAL_HOST_SOURCE_VALIDATION_PASS;PLATFORM_AND_RELEASE_PENDING**
|
||||
仓库:Mac `/Users/tei/dev/MapLibreParticleLab`,执行前 `particle/main` HEAD `b1239ac3b1554943200e6fbf14321459ccc86527`,以此工作树叠加 STEP-097 B 及 STEP-098 实际源代码测试。
|
||||
|
||||
## 实际顺序和证据
|
||||
|
||||
1. Codex `GPT-6 Luna High` 源码运行 `step098-codex-20260926-224510` 的 JSONL 已有 `turn.completed`、退出码 0。设计/Step/完成报告存在;它自身没有编译,也不作为独立功能 PASS。
|
||||
2. RDC B 初次运行 `step098-b-core-20260927-0833` 因 launchd PATH 不含 ninja 退出 127;核实原进程已结束才补 PATH 重新运行。
|
||||
3. B 第一次真实编译 `step098-b-core-retry1-20260927-0834` 在 Source 测试引用不存在的 `Impl.encodedFieldStore` 处失败。按 public Source projection/seek 改测试断言,不改生产 API。下一次 `step098-b-core-retry2-20260927-0836` 编译 PASS,但 19 个定向测试中 2 个失败:测试 fixture 把 grid east=2 设成了小于 west=130 的非法 Grid,实际正确返回 `RejectedInvalidFrame`,而用例期待 `RejectedIncompatibleFrame`。将两处 east 改成 132(有效但与原 east=131 不兼容),没有降低生产校验。
|
||||
4. B 再次执行 `step098-b-core-retry3-20260927-0838`:OpenGL Host Native C++ 编译退出 0,Core/Binding/Ingress 19/19 PASS;原始日志 `artifacts/maplibre-particle/step098-rdc/b-core-build.log`、`b-core-gtest.log`。
|
||||
5. C1 `step098-c-opengl-20260927-0840`:Source/Projection/Renderer 扩展 OpenGL Host 119/119 PASS,日志 `c-opengl-gtest.log`。
|
||||
6. C2 `step098-c-metal-host-20260927-0841`:Metal Host C++ 编译退出 0,同 filter 119/119 PASS,日志 `c-metal-host-build.log`、`c-metal-host-gtest.log`。现有 maplibre-tile-spec 个别对象的 macOS deployment target 警告非失败。
|
||||
7. 全部新旧文件 `git diff --check` PASS;本记录时尚未执行正式 Android AAR、RN Test Host、iOS XCFramework、V15 Pipeline 或任何实体机验证。
|
||||
|
||||
## 未满足的门槛与边界
|
||||
|
||||
- `T01~T17` 已有对应源码/测试覆盖;上列 19/119/119 是实际运行的 Host GTest,不可将 T15 GPU/context loss、T16 长时 CPU/GPU 内存或 T17 RN 两步调用/真实 bridge 集成全部标 PASS。
|
||||
- `DecodedFieldFrame` 新增 profile 身份,需用**同一冻结源提交**构建 Android/RN/iOS;现有正式 `.13` AAR 与旧 `.14-dev` 不能替代。
|
||||
- V15 正式构建/发布需要先取得干净且确定的 release Git commit、检验本地空间、版本锁、脚本 prepare/build/publish/finalize 各自证据;公开版本不可覆盖。
|
||||
- STEP-097 B 用户原工作内容必须保留,禁止 reset/clean、混入无关 untracked。按已授权的统一 V15 发行目标进行 scoped Git 收尾,不能将 Host 源级 PASS 冒称设备验收或正式 RELEASE_PASS。
|
||||
|
||||
## 真实统计
|
||||
|
||||
| 阶段 | 构建 | GTest | 结论 |
|
||||
| --- | --- | --- | --- |
|
||||
| B OpenGL Core/Binding/Ingress | PASS | 19/19 | PASS |
|
||||
| C1 OpenGL Host | 已由 B 更新编译 | 119/119 | PASS |
|
||||
| C2 Metal Host | PASS | 119/119 | PASS |
|
||||
| Android/RN 同源配套 | NOT RUN | NOT RUN | PENDING |
|
||||
| iOS XCFramework/设备 | NOT RUN | NOT RUN | PENDING |
|
||||
| V15 Pipeline | NOT RUN | NOT RUN | PENDING |
|
||||
47
docs/报告/STEP-098_公共契约阻塞报告_v1.md
Normal file
47
docs/报告/STEP-098_公共契约阻塞报告_v1.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# STEP-098:公共契约闸门阻塞报告 v1
|
||||
|
||||
日期:2026-09-26(日本时间)
|
||||
状态:**BLOCKED_CONTRACT / 未进入功能实施和发行**。
|
||||
|
||||
## 1. 当前真实基线
|
||||
|
||||
- 用户本轮要求:RDC 严格串行执行 STEP-098,并使用统一 Pipeline 发布 V15;未要求将历史 .14-dev AAR 当作 V15 产物。
|
||||
- 源码仓库:Mac `/Users/tei/dev/MapLibreParticleLab`,分支 `particle/main`,本轮开工 HEAD 与公开 `origin/particle/main` 均为 `b1239ac3b1554943200e6fbf14321459ccc86527`。
|
||||
- 该正式工作树已有 22 项修改/未跟踪内容(含 STEP-097 B 和本任务 MD);这些既有内容全部受保护,不得 reset、clean、覆盖或整体暂存。
|
||||
- 本任务原始文件:`docs/40_任务/STEP-098_QBIN_V4单帧首显与八帧滚动缓存_Codex任务_v1.md`;其状态明确为“待契约闸门通过后执行”。
|
||||
- Integration:Mac `/Users/tei/dev/navsea-particle-integration` 已执行 `git fetch origin main`;本地 `main` 和 `origin/main` 一致,均为 `318f8ccf911ac3136c3086a1d14e61f83e2d4bce`,工作树干净。
|
||||
- 当前已安装正式版本:Android/RN `13.4.1-particle.13`,iOS 基础 VERSION `6.28.0`。先前 Native `13.4.1-particle.14-dev-d00d2ddc-bf4539890c6f` 仅为本地开发 AAR,不能替代 RN 联编、双平台测试或 V15 正式发行证据。
|
||||
|
||||
## 2. 契约冲突与停止原因
|
||||
|
||||
- `rfc/RFC-003_编码场两帧首显与增量补帧语义.md` 仍标为 `DRAFT/Shared Core 与 Android/iOS 评审待完成`,其目标语义第 1 项是至少两帧覆盖后才合法显示,第 4 项是超容量明确拒绝。
|
||||
- STEP-098 的目标不同:一个有效时间的单帧可按真实 validTime 首显并驱动合法向量粒子;后续每 fieldKey 维持按时间升序的最多 8 帧,合法第 9 帧按新帧时间选择远端淘汰,正常满额提交不返回容量拒绝。
|
||||
- `coordination/active/COORD-004/状态.md` 将 B 列为 `B_RFC_DRAFT`;同目录 `决议.md` 仅明确 A 的 React Style 方向,B 仍待调查。未发现正式接受 STEP-098 新语义的 RFC/ADR/COORD 决定或合同更新。现有 C-001 不可被当成这项新行为已经批准。
|
||||
- 按 STEP-098 任务 §1 第 3~4 项,必须先由粒子负责人/Integration 正式接受且消除上述冲突,记录准确文件、版本、commit;本执行窗口不能把聊天目标、自身判断或 DRAFT 冒充正式跨平台决定。
|
||||
|
||||
## 3. 需要的正式协调决定
|
||||
|
||||
请由粒子负责人在 Integration 的 COORD-004/RFC-003 或新 ADR/Contract 中明确并正式接受:
|
||||
1. 单帧真实时刻的首显和向量粒子行为;合法相邻两帧插值;不覆盖目标的旧帧必须 fail-closed,不能伪造数据或时间。
|
||||
2. 每 fieldKey 独立的 8 帧排序/容量;正常已满时先验证、再原子淘汰并保留新帧;无效帧零副作用、不同 key 不相互挤占。
|
||||
3. 未来/过去/内部插入的确定性端点淘汰,距离相同时的 tie-break,以及当前显示端点能否参与稳定优先级;禁止依赖尚未下发的新目标时间决定淘汰。
|
||||
4. 同 validTime duplicate/revision/stale/conflict、dataset/profile/grid 身份、请求时间不被 submit 静默修改、现有 RN 两步调用、Renderer/GPU 资源复用与释放、内存边界及 Android/iOS 同一 Shared Core 契约。
|
||||
5. 核对任务书中“13:00 ... 21:00 共 8 帧”的计数错误(若逐小时含两端实际为 9 个时间点),在正式合同及实施示例中写为确实 8 帧的区间。
|
||||
6. 指明是否因生命周期、错误/数据所有权语义变化需要升级 C-001,以及适用的正式版本与跨平台责任。
|
||||
|
||||
以上正式结果若与 STEP-098 MD 的行为不一致,应先修订任务书,不得由 Android/iOS 各自选择不同语义。
|
||||
|
||||
## 4. Git 和 V15 Pipeline 独立门槛
|
||||
|
||||
- 用户提出 V15:统一脚本 `tools/particle_release_pipeline/release.sh 15` 的目标将是 Android/RN `13.4.1-particle.15` 与 iOS `6.28.0-particle.15`。这是待发行目标,不是已经构建/发布的事实。
|
||||
- Pipeline 的 `prepare`、`build` 均要求工作树干净且版本锁正确;当前主工作树 dirty,不能直接运行。STEP-097 B 的未提交工作及正式版本锁需先按范围审计、受控收尾,不能 `git add -A` 或把 .14-dev 当正式 .15。
|
||||
- Pipeline 的 public target 不得覆盖现有版本;正式发布必须在同一冻结 source commit 上完成 Android Native 四 ABI、RN、iOS Metal/XCFramework,分别验证,再检查 SHA、manifest、SwiftPM tag 与公网实际回读。
|
||||
- Mac 只余约 11 GiB 可用空间,是完整构建前需要核实的容量风险;不能据此虚报构建失败或 PASS。
|
||||
|
||||
## 5. 本轮实际执行与未执行
|
||||
|
||||
- 已执行:RDC 只读 Git/任务/发布脚本审计、Integration fetch 和 SHA 对齐、RFC-003/COORD-004/Contract/职责核查。
|
||||
- 未执行:Codex 功能实现、Core/Bridge/Renderer 修改、T01~T17 测试、Android/iOS 编译或设备验收、V15 Pipeline 的 prepare/build/publish/finalize、Git commit/push。
|
||||
- 本报告是独立新增文件,不修改 STEP-098 MD、STEP-097 既有改动、Integration 文档、VERSION、历史制品或任何公网发行目录。
|
||||
|
||||
**当前结论:BLOCKED_CONTRACT。第一闸门未正式通过,不启动第二阶段,也不进行 V15 发行。**
|
||||
@@ -0,0 +1,536 @@
|
||||
# STEP-099 RenderSource 就绪与 RenderSegment 失效诊断 — Codex 完成报告 v1
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
**CLOSED / PASS(诊断阶段完成)**。
|
||||
|
||||
STEP-099 按任务书保持“纯诊断、fail-closed、不优化正式语义”的范围完成。Renderer/Source/RenderSegment diagnostics、Android Test App T1~T4 场景、定向 native 测试和 Android 实机运行证据均已取得,Q1~Q7 可以据实回答。
|
||||
|
||||
本 Step **没有**修改 retained cache 算法、fast path gate、`singleContinuousSegment`、multi-continuous 行为、segment-local invalidation、RenderSegment 执行语义、NavSea、Source `isLoaded()` 定义或 RN 业务 API 语义。
|
||||
|
||||
## 2. 基线与 Git
|
||||
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab-step099`
|
||||
- branch:`step099-render-source-diagnostics`
|
||||
- frozen release tag:`maplibre-particle/rn/13.4.1-particle.17`
|
||||
- frozen release commit / start base:`e8b451fe70054107773e82484c80e90df3889147`
|
||||
- implementation commit:`8a5214d92dd2f8899db76d2194da42999fa0c87d`
|
||||
- implementation message:`diagnostics: add STEP-099 render source readiness audit`
|
||||
- Integration/NavSea:未修改。
|
||||
- 文档 closeout commit、push result、`origin/particle/main` Final HEAD:Git 提交无法在自身内容中自指其最终 SHA,按项目既有规则由最终交付回读记录。
|
||||
|
||||
## 3. 实际修改范围
|
||||
|
||||
生产源码 / diagnostics:
|
||||
|
||||
- `include/mbgl/renderer/render_segment.hpp`
|
||||
- `src/mbgl/renderer/render_orchestrator.cpp`
|
||||
- `src/mbgl/renderer/render_orchestrator.hpp`
|
||||
- `src/mbgl/renderer/render_segment_json.hpp`
|
||||
- `src/mbgl/renderer/render_source.hpp`
|
||||
- `src/mbgl/renderer/retained_underlay_runtime_state.hpp`
|
||||
- `src/mbgl/renderer/sources/render_tile_source.cpp`
|
||||
- `src/mbgl/renderer/sources/render_tile_source.hpp`
|
||||
- `packages/maplibre-particle-rn/src/index.ts`
|
||||
- `packages/maplibre-particle-rn/src/types.ts`
|
||||
|
||||
测试 / Test App:
|
||||
|
||||
- `test/renderer/retained_underlay_cache.test.cpp`
|
||||
- `platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/ParticleCoreLayerValidationActivity.kt`
|
||||
- `platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/Step099ReadinessFixture.kt`
|
||||
|
||||
项目记录:
|
||||
|
||||
- `docs/50_Step记录/STEP-099_RenderSource就绪与RenderSegment失效诊断_v1.md`
|
||||
- `docs/报告/STEP-099_RenderSource就绪与RenderSegment失效诊断_Codex完成报告_v1.md`
|
||||
- `粒子开发记录.md`
|
||||
|
||||
## 4. 诊断能力
|
||||
|
||||
### 4.1 全局 readiness
|
||||
|
||||
新增只读 snapshot:
|
||||
|
||||
- `styleLoaded`
|
||||
- `imageManagerLoaded`
|
||||
- `renderOrchestratorLoaded`
|
||||
- `spriteLoaded`
|
||||
- `hasFadingTiles`
|
||||
- global retained `contentRevision`
|
||||
- RenderSegment plan/config/topology 信息
|
||||
- 最近 mutation sequence / reason / source / affected segment diagnostics
|
||||
|
||||
正式 loaded 路径保持原式:
|
||||
|
||||
```cpp
|
||||
renderTreeParameters->loaded = updateParameters->styleLoaded && isLoaded();
|
||||
```
|
||||
|
||||
diagnostics snapshot 在旁路采样,失败被 best-effort 隔离,不决定正式 loaded。
|
||||
|
||||
### 4.2 per-source readiness
|
||||
|
||||
每个 RenderSource additive 暴露:
|
||||
|
||||
- sourceId / sourceType
|
||||
- enabled / isLoaded
|
||||
- tileBacked
|
||||
- tileCount / incompleteTileCount / loadedTileCount
|
||||
- hasFadingTiles
|
||||
- last source/tile mutation sequence
|
||||
- last tile action / reason
|
||||
|
||||
默认非 tile source 返回完整显式初始化的空统计,Android NDK `-Werror` 下已验证通过;未改变任何 Source `isLoaded()` 实现。
|
||||
|
||||
### 4.3 RenderSegment 只读依赖
|
||||
|
||||
每个 segment additive 暴露:
|
||||
|
||||
- segmentIndex / strategy / beginIndex / endExclusive
|
||||
- `dependentSourceIds`
|
||||
- `unknownDependencies`
|
||||
|
||||
规则:
|
||||
|
||||
- 从实际 ordered layers 的 sourceId 推导;
|
||||
- shared source 可出现在多个 segment;
|
||||
- source-less background 不制造假依赖;
|
||||
- symbol/custom 等无法仅靠 sourceId 完整表达的依赖标记为 unknown/global;
|
||||
- 依赖图只用于诊断,不参与 cache/loaded/fast path/execution。
|
||||
|
||||
### 4.4 mutation 定位
|
||||
|
||||
Source/tile mutation 复用已有回调记录 sequence/source/reason/action。原 `retainedUnderlayContentRevision` 的增长次数与正式调用路径不变;diagnostics 不增加额外 revision。
|
||||
|
||||
query 路径不清理或修改 event history,重复 snapshot/JSON 查询保持无副作用。
|
||||
|
||||
## 5. Native 定向测试
|
||||
|
||||
### 5.1 Metal host 定向 GTest
|
||||
|
||||
实际执行:
|
||||
|
||||
```bash
|
||||
cmake --preset macos-metal
|
||||
cmake --build build-macos-metal --target mbgl-test-runner --parallel 3
|
||||
build-macos-metal/mbgl-test-runner --gtest_filter='RenderSegmentDiagnostics.*'
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
- `RenderSegmentDiagnostics.*`:**6/6 PASS**
|
||||
- 新增 `ReadinessSnapshotMapsSourcesToSegmentsWithoutSideEffects`:PASS
|
||||
- 覆盖 source loaded/unloaded、tile stats、source→segment、shared source、source-less、重复 query、topology/cache/content revision 无副作用。
|
||||
|
||||
证据:
|
||||
`artifacts/maplibre-particle/android-validation/step099-metal-gtest.log`
|
||||
|
||||
SHA-256:
|
||||
`f0618750ead03eae1b251126dd49488a9269a120373a66e3453fa9aaa1dc0f10`
|
||||
|
||||
说明:该 6/6 发生在 Android NDK 首轮暴露“默认 readiness struct designated initializer 未显式列完 optional 字段”之前;随后仅补全默认空字段初始化。最终源码由 Android 全量 native build 成功覆盖。为了在最终源码上重跑 Metal,重新 configure 时当前 Mac 缺 `libuv` pkg-config,configure fail;没有安装系统依赖来制造 PASS。
|
||||
|
||||
### 5.2 Vulkan
|
||||
|
||||
`macos-vulkan` configure:**BLOCKED / NOT RUN TESTS**。
|
||||
|
||||
原因:
|
||||
|
||||
```text
|
||||
The following required packages were not found:
|
||||
- libuv
|
||||
```
|
||||
|
||||
这是当前 Mac host 环境依赖,不是 STEP-099 源码编译错误。
|
||||
|
||||
### 5.3 Jest
|
||||
|
||||
**NOT RUN**。用户未要求 Jest,任务未加入 Jest。
|
||||
|
||||
## 6. Android 构建与设备
|
||||
|
||||
### 6.1 首轮 Android 编译问题
|
||||
|
||||
首轮 x86_64 OpenGL build 在 Android NDK `-Werror` 下发现:
|
||||
|
||||
```text
|
||||
render_source.hpp:84:51:
|
||||
error: missing field 'tileCount' initializer
|
||||
[-Werror,-Wmissing-designated-field-initializers]
|
||||
```
|
||||
|
||||
修复仅补全 `RenderSourceReadinessStats` 默认空字段初始化,不改变运行语义。
|
||||
|
||||
随后 native C++ 已越过原失败点并完整编译。
|
||||
|
||||
### 6.2 磁盘阻塞与恢复
|
||||
|
||||
第一次完整 native build 后,在:
|
||||
|
||||
`MapLibreAndroidTestApp:stripOpenglDebugDebugSymbols`
|
||||
|
||||
因 Mac 仅剩约 2 GiB 出现:
|
||||
|
||||
```text
|
||||
No space left on device
|
||||
```
|
||||
|
||||
RDC 只清理可再生 Gradle 9.5.1 cache / 本轮失败中间产物,保留 Android native object;可用空间恢复到约 12 GiB 后增量重跑。
|
||||
|
||||
### 6.3 最终 APK
|
||||
|
||||
命令:
|
||||
|
||||
```bash
|
||||
./tools/particle_android_validation/build_validation_apk.sh
|
||||
```
|
||||
|
||||
最终结果:
|
||||
|
||||
- Gradle:**BUILD SUCCESSFUL in 4m 54s**
|
||||
- actionable tasks:86(7 executed / 79 up-to-date)
|
||||
- variant:OpenGL Debug
|
||||
- ABI:x86_64
|
||||
- APK:61,923,024 bytes
|
||||
- `libmaplibre.so`:39,602,856 bytes
|
||||
- APK SHA-256:
|
||||
`5de73f0358b835a78d3ae7e2ac22f4efdb2a66775b8c63654a8bb7313957d90a`
|
||||
|
||||
构建时 metadata:
|
||||
|
||||
- base commit:`e8b451fe70054107773e82484c80e90df3889147`
|
||||
- `git_dirty=true`
|
||||
- 构建内容与随后形成的 implementation commit `8a5214d92` 源码一致;当前 source tree 对 implementation commit clean,剩余仅 docs closeout。
|
||||
|
||||
### 6.4 Android 设备
|
||||
|
||||
Mac ADB 直接连接:
|
||||
|
||||
- serial:`192.168.200.139:5555`
|
||||
- model:`FZG1-2`
|
||||
- Android:13
|
||||
- API:33
|
||||
- ABI:x86_64
|
||||
|
||||
APK 覆盖安装成功,`ParticleCoreLayerValidationActivity` 冷启动成功。
|
||||
|
||||
运行 log:
|
||||
`artifacts/maplibre-particle/android-validation/step099-logcat.txt`
|
||||
|
||||
log SHA-256:
|
||||
`0f15d75dde77e7dd2f4d98c46a4c1501a628205e8aae62130b0cb96db6b8482f`
|
||||
|
||||
## 7. T1~T4 实测
|
||||
|
||||
### T1 STATIC
|
||||
|
||||
场景:
|
||||
|
||||
- R0:静态 GeoJSON retained
|
||||
- C:本地静态 RasterArray field 驱动 WeatherColor + Particle
|
||||
- R1:静态 retained
|
||||
- camera fixed
|
||||
|
||||
结果:
|
||||
|
||||
- 启动阶段 1 个 false window:204 ms;
|
||||
- 随后 `renderOrchestratorLoaded=true` 稳定;
|
||||
- `particle-fixture` 与 `step099-static` 均 loaded;
|
||||
- retained cache 建立后持续 reuse;
|
||||
- fast path 进入 `active=true / eligible=true / reason=none`;
|
||||
- 没有持续 style-not-loaded。
|
||||
|
||||
aggregate:
|
||||
|
||||
```text
|
||||
falseWindowCount=1
|
||||
falseWindowAvgMs=204
|
||||
falseWindowMaxMs=204
|
||||
openFalseWindowMs=0
|
||||
```
|
||||
|
||||
结论:静态基线可稳定进入 loaded/cache reuse/fast path。
|
||||
|
||||
### T2 GEOJSON_1HZ
|
||||
|
||||
两个 GeoJSON source 每 500 ms 交错更新,因此每个约 1 Hz。
|
||||
|
||||
实际:
|
||||
|
||||
- 总 source update:122 次,约 61 次/源;
|
||||
- 60 秒量级观察中,出现 10 个 loaded=false window;
|
||||
- 平均 203 ms;
|
||||
- 最大 205 ms;
|
||||
- 最终 open window = 0;
|
||||
- 总 false 时间约 2.03 s,约占 60 s 的 3.4%,不是“几乎始终 false”。
|
||||
|
||||
aggregate:
|
||||
|
||||
```text
|
||||
falseWindowCount=10
|
||||
falseWindowAvgMs=203
|
||||
falseWindowMaxMs=205
|
||||
openFalseWindowMs=0
|
||||
```
|
||||
|
||||
同时 final sample:
|
||||
|
||||
- global contentRevision 已到 1679;
|
||||
- R0 仅依赖 `step099-static`,但 cache build=241、invalidation=362;
|
||||
- R1 依赖动态 GeoJSON,也同样 build=241;
|
||||
- mutation diagnostics 能正确指出动态 source 只 `affected=[2]`,但正式 cache invalidation 仍非局部。
|
||||
|
||||
### T3 PERMANENT_UNREADY
|
||||
|
||||
额外加入:
|
||||
|
||||
`step099-unready-raster-array`
|
||||
|
||||
它:
|
||||
|
||||
- 没有 layer;
|
||||
- 因此 `enabled=false`;
|
||||
- host-submission 无 field;
|
||||
- `isLoaded=false`。
|
||||
|
||||
其他主要 source 均 loaded。
|
||||
|
||||
实测:
|
||||
|
||||
- `renderOrchestratorLoaded=false` 持续;
|
||||
- retained segment cache 持续 fallback;
|
||||
- `lastInvalidationReason=style-not-loaded`;
|
||||
- 观察结束仍没有恢复。
|
||||
|
||||
aggregate:
|
||||
|
||||
```text
|
||||
falseWindowCount=0
|
||||
openFalseWindowMs=21459
|
||||
```
|
||||
|
||||
结论:即使 source 与当前 layer/segment 无关且 disabled,只要仍在 `renderSources` 且 `isLoaded=false`,V17 全局 `RenderOrchestrator::isLoaded()` 就保持 false。
|
||||
|
||||
### T4 R-C-R
|
||||
|
||||
结构:
|
||||
|
||||
- R0:`deps=[step099-static]`
|
||||
- C:`deps=[particle-fixture]`,WeatherColor + Particle
|
||||
- R1:`deps=[org.maplibre.annotations, step099-dynamic, step099-static]`
|
||||
- `step099-dynamic` 约 1 Hz 更新
|
||||
|
||||
实际:
|
||||
|
||||
- dynamic update:61 次;
|
||||
- loaded false window:4;
|
||||
- 平均 203 ms;
|
||||
- 最大 207 ms;
|
||||
- global contentRevision:1760 → 2540;
|
||||
- mutation diagnostics:`step099-dynamic` 对应 `affected=[2]`;
|
||||
- R0 并不依赖 `step099-dynamic`,但:
|
||||
- build:1 → 121
|
||||
- invalidation:20 → 200
|
||||
- R1 同步得到相同级别 build/invalidation 增长;
|
||||
- fast path 在稳定窗口可恢复 active,但 global revision / transient readiness 会反复制造 full CPU work。
|
||||
|
||||
aggregate:
|
||||
|
||||
```text
|
||||
falseWindowCount=4
|
||||
falseWindowAvgMs=203
|
||||
falseWindowMaxMs=207
|
||||
openFalseWindowMs=0
|
||||
```
|
||||
|
||||
结论:依赖诊断已经能准确表达“R1 source-local”,但 V17 正式 content revision/cache invalidation 仍是 global。
|
||||
|
||||
## 8. Q1~Q7
|
||||
|
||||
### Q1:单个 permanently unloaded source 会造成 V17 style-not-loaded 吗?
|
||||
|
||||
**YES / CONFIRMED。**
|
||||
|
||||
T3 中 `step099-unready-raster-array` 无 layer、`enabled=false`、`isLoaded=false`;其他主要 source loaded,但整体 `renderOrchestratorLoaded=false` 持续 21.459 s 未恢复,retained cache 持续 `style-not-loaded` fallback。
|
||||
|
||||
源码事实与运行事实一致:`RenderOrchestrator::isLoaded()` 遍历全部 `renderSources`,不按 enabled/segment dependency 排除 source。
|
||||
|
||||
### Q2:1 Hz GeoJSON 会周期性让 isLoaded=false 吗?平均/最大窗口?
|
||||
|
||||
**YES / CONFIRMED。**
|
||||
|
||||
T2 双 source 交错各约 1 Hz:
|
||||
|
||||
- 10 次 false window;
|
||||
- avg 203 ms;
|
||||
- max 205 ms。
|
||||
|
||||
T4 单动态 source 约 1 Hz:
|
||||
|
||||
- 4 次 false window;
|
||||
- avg 203 ms;
|
||||
- max 207 ms。
|
||||
|
||||
### Q3:错开的多个 1 Hz GeoJSON 会导致 global loaded 几乎无法稳定为 true 吗?
|
||||
|
||||
**NO / 本次观测不支持。**
|
||||
|
||||
T2 约 60 s 中总 false 时间约 2.03 s,约 3.4%;绝大多数时间 global loaded=true。它确实产生 readiness thrash,但不是“几乎永远 false”。
|
||||
|
||||
### Q4:GeoJSON tile mutation 会推进 global retainedUnderlayContentRevision 并影响 retained cache 吗?
|
||||
|
||||
**YES / CONFIRMED。**
|
||||
|
||||
T4 global contentRevision 1760 → 2540。仅依赖静态 source 的 R0 也从 build 1 → 121、invalidation 20 → 200。
|
||||
|
||||
### Q5:不引用 GeoJSON 的 retained segment 仍会受它的 loaded/fading/tile mutation 影响吗?
|
||||
|
||||
**YES。**
|
||||
|
||||
运行证据:
|
||||
|
||||
- T4 diagnostics 正确报告动态 source `affected=[2]`;
|
||||
- 但 R0 `deps=[step099-static]` 仍随动态 source mutation 重建/失效。
|
||||
|
||||
源码证据:
|
||||
|
||||
- `retainedRangeIsStable(begin,end)` 虽为 range API,但最后遍历 **全部 `renderSources`**,任一 source `hasFadingTiles()` 都让任意 retained range unstable。
|
||||
|
||||
本次 T4 稳态 `fade=false`,因此 R0 的实际反复失效主要直接证明 global content revision/tile mutation;GLOBAL_FADE 属源码确认、未在本轮场景主动触发的全局 gate。
|
||||
|
||||
### Q6:`tryRenderParticleFastFrame` 仍有哪些 global gate 会阻挡未来 localized cache CPU fast path?
|
||||
|
||||
**确认仍存在:**
|
||||
|
||||
1. **GLOBAL_UPDATE_SIGNATURE**
|
||||
- `particleFastPathNonCameraInputsMatch` 全局比较:
|
||||
- styleLoaded / spriteLoaded
|
||||
- light
|
||||
- images
|
||||
- **sources**
|
||||
- **layers**
|
||||
- fontFaces / fileSource / glyphURL
|
||||
- transition / placement / tile LOD 等全局输入。
|
||||
|
||||
2. **GLOBAL_CONTENT_REVISION**
|
||||
- `particlePreparedContentRevision == orchestrator.getRetainedUnderlayContentRevision()`。
|
||||
|
||||
3. **GLOBAL_FADE**
|
||||
- `particleFastPathDynamicState` 遍历全部 renderSources 汇总 fading;
|
||||
- `retainedRangeIsStable` 也遍历全部 renderSources。
|
||||
|
||||
4. **GLOBAL_PENDING / TRANSITION / REPAINT**
|
||||
- pendingChanges
|
||||
- renderLight transition
|
||||
- 任意 layer transition
|
||||
- 任意非粒子 layer repaint
|
||||
- placement transition。
|
||||
|
||||
5. **GLOBAL_ANNOTATION_MUTATION**
|
||||
- AnnotationManager `hasPendingChanges()`。
|
||||
|
||||
6. **RETAINED REVISION / CACHE / TOPOLOGY / BOUNDARY**
|
||||
- retained cache validity/static revision
|
||||
- global retained revision tracker
|
||||
- topology revision
|
||||
- boundary/config revision。
|
||||
|
||||
7. **singleContinuousSegment**
|
||||
- 仍要求 plan 恰好能由一个 Continuous segment 表达;
|
||||
- multi-continuous 继续 fail closed,本 Step 未修改。
|
||||
|
||||
因此即使未来 cache invalidation 变成 source/segment-local,fast path 仍有多组 global gate 需要后续 Step 单独设计,不能在 STEP-099 顺手放宽。
|
||||
|
||||
### Q7:blocker 分类
|
||||
|
||||
**运行态 CONFIRMED:**
|
||||
|
||||
- `PERMANENT_SOURCE_UNLOADED`
|
||||
- `DYNAMIC_SOURCE_READINESS_THRASH`
|
||||
- `GLOBAL_CONTENT_REVISION`
|
||||
|
||||
**源码 CONFIRMED,本轮未主动触发独立运行场景:**
|
||||
|
||||
- `GLOBAL_FADE`
|
||||
- `GLOBAL_UPDATE_SIGNATURE`
|
||||
|
||||
**OTHER:**
|
||||
|
||||
- global pendingChanges
|
||||
- placement/light/non-particle transition/repaint
|
||||
- annotation mutation
|
||||
- topology/boundary/revision/cache validity
|
||||
- singleContinuous fail-closed
|
||||
|
||||
## 9. 任务边界审计
|
||||
|
||||
确认未改:
|
||||
|
||||
- retained cache 核心算法;
|
||||
- fast path gate 逻辑;
|
||||
- `singleContinuousSegment`;
|
||||
- multi-continuous 策略;
|
||||
- segment-local invalidation 正式语义;
|
||||
- RenderSegment execution;
|
||||
- NavSea;
|
||||
- private NavSea Native API;
|
||||
- RN 业务 API;
|
||||
- Source `isLoaded()` 定义。
|
||||
|
||||
新增 dependency / affectedSegmentIndices 只是观察信息;它没有参与正式 invalidation。
|
||||
|
||||
## 10. 证据文件
|
||||
|
||||
忽略目录:
|
||||
|
||||
`artifacts/maplibre-particle/android-validation/`
|
||||
|
||||
主要文件:
|
||||
|
||||
- `build-info.txt`
|
||||
- `build.log`
|
||||
- `MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk`
|
||||
- `step099-logcat.txt`
|
||||
- `step099-device-info.txt`
|
||||
- `step099-metal-gtest.log`
|
||||
- `step099-cmake-metal.log`
|
||||
|
||||
APK SHA-256:
|
||||
|
||||
`5de73f0358b835a78d3ae7e2ac22f4efdb2a66775b8c63654a8bb7313957d90a`
|
||||
|
||||
STEP-099 log SHA-256:
|
||||
|
||||
`0f15d75dde77e7dd2f4d98c46a4c1501a628205e8aae62130b0cb96db6b8482f`
|
||||
|
||||
## 11. 下一阶段建议
|
||||
|
||||
STEP-099 只负责定位,不在本 Step 修复。
|
||||
|
||||
下一阶段应基于本轮证据拆开处理:
|
||||
|
||||
1. 先决定 global source readiness 是否要引入“影响当前 style/render segment 的 readiness 范围”,解决 permanently unready / transient unrelated source 对全局 loaded 的影响;
|
||||
2. 再设计 source/segment-local content revision 与 invalidation,避免 R1 GeoJSON mutation 重建 R0;
|
||||
3. 最后单独审查 fast-path global signature/fade/pending/placement 等 gate,不能把 cache locality 与 fast-path locality 混成一个修改;
|
||||
4. 保持 Style Z-order 唯一事实来源和当前通用 Render Segment 模型。
|
||||
|
||||
## 12. 最终状态
|
||||
|
||||
**STEP-099 CLOSED / PASS。**
|
||||
|
||||
Pass 含义仅为:
|
||||
|
||||
- diagnostics 能回答任务问题;
|
||||
- Native 定向测试已有 PASS 证据;
|
||||
- 最终 Android x86_64 OpenGL APK BUILD SUCCESS;
|
||||
- FZG1-2 T1~T4 实跑完成;
|
||||
- Q1~Q7 有源码/运行证据;
|
||||
- 未改变正式渲染语义。
|
||||
|
||||
不代表:
|
||||
|
||||
- Vulkan host 已通过;
|
||||
- iOS runtime 已测试;
|
||||
- 下一阶段 localized readiness/invalidation 已实现;
|
||||
- fast path global gates 已优化。
|
||||
454
docs/报告/STEP-100_SourceScoped_RenderSegment局部失效_Codex完成报告_v1.md
Normal file
454
docs/报告/STEP-100_SourceScoped_RenderSegment局部失效_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,454 @@
|
||||
# STEP-100 SourceScoped RenderSegment 局部失效 — 完成报告 v1
|
||||
|
||||
## 1. 最终结论
|
||||
|
||||
**CLOSED / PASS(功能与 Android 运行态目标确认)**。
|
||||
|
||||
Codex 实现使用:
|
||||
- model:`gpt-6-sol`
|
||||
- reasoning effort:`high`
|
||||
|
||||
RDC 独立完成:
|
||||
- 源码 diff 审计;
|
||||
- Android x86_64 OpenGL 全量 native build;
|
||||
- Validation APK;
|
||||
- FZG1-2 自动安装与 T1/T2/T3/T4;
|
||||
- STEP-100 60 秒 locality window;
|
||||
- validation build script 小型工程修复;
|
||||
- Git diff/status 审计。
|
||||
|
||||
本 Step 核心目标已确认:
|
||||
|
||||
> 动态 GeoJSON Source 更新只影响依赖它的 RETAINED Segment;无关 R0 cache 不再跟着 invalid/rebuild,并且在 global loaded=false 时仍可安全 reuse。
|
||||
|
||||
Native host GTest 因当前 Intel Mac 缺 `libuv` 未执行;曾尝试 Homebrew 安装,但 Homebrew 要连带升级 CMake/OpenSSL/Python 且 Intel 已无 bottle 支持,因此 RDC 主动终止,未把环境变化作为任务依赖。Android 最终 C++ native build 与设备运行态用于本轮功能验收。
|
||||
|
||||
Jest:**NOT RUN**,用户未要求。
|
||||
|
||||
## 2. Git / 基线
|
||||
|
||||
- worktree:`/Users/tei/dev/MapLibreParticleLab-step100`
|
||||
- branch:`step100-segment-local-source-invalidation`
|
||||
- start HEAD:`c4b481fd2c5b5da20d31f6ae54011f0552d7dbd0`
|
||||
- start remote:`origin/particle/main = c4b481fd2c5b5da20d31f6ae54011f0552d7dbd0`
|
||||
- frozen V17 tag:`maplibre-particle/rn/13.4.1-particle.17`
|
||||
- frozen V17 commit:`e8b451fe70054107773e82484c80e90df3889147`
|
||||
- V17 tag/release worktree:未修改。
|
||||
|
||||
最终 STEP-100 commit / push / remote HEAD 在 Git 收尾后由最终交付回读记录,避免文档自引用。
|
||||
|
||||
## 3. 实际修改
|
||||
|
||||
生产源码:
|
||||
|
||||
- `include/mbgl/renderer/render_segment.hpp`
|
||||
- `src/mbgl/renderer/render_orchestrator.hpp`
|
||||
- `src/mbgl/renderer/render_orchestrator.cpp`
|
||||
- `src/mbgl/renderer/renderer_impl.cpp`
|
||||
- `src/mbgl/renderer/retained_underlay_cache.cpp`
|
||||
- `src/mbgl/renderer/retained_underlay_runtime_state.hpp`
|
||||
- `src/mbgl/renderer/render_segment_json.hpp`
|
||||
- `packages/maplibre-particle-rn/src/types.ts`
|
||||
|
||||
Test App / tests:
|
||||
|
||||
- `platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/Step099ReadinessFixture.kt`
|
||||
- `platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/ParticleCoreLayerValidationActivity.kt`
|
||||
- `test/renderer/retained_underlay_cache.test.cpp`
|
||||
|
||||
工具:
|
||||
|
||||
- `tools/particle_android_validation/build_validation_apk.sh`
|
||||
- RDC 修复 `unzip | awk ... exit` 在 `pipefail` 下偶发 SIGPIPE/141;
|
||||
- 仅移除 awk 的提前 exit,不改变 APK 验证语义。
|
||||
|
||||
文档:
|
||||
|
||||
- `docs/40_任务/STEP-100_SourceScoped_RenderSegment局部失效_Codex任务_v2.md`
|
||||
- `docs/50_Step记录/STEP-100_SourceScoped_RenderSegment局部失效_v1.md`
|
||||
- 本报告;
|
||||
- `粒子开发记录.md`
|
||||
|
||||
## 4. 生产设计
|
||||
|
||||
### 4.1 Source-scoped revision
|
||||
|
||||
新增正式 source-scoped revision bookkeeping。
|
||||
|
||||
明确的 Source/Tile mutation:
|
||||
- source diff;
|
||||
- feature state;
|
||||
- tile changed;
|
||||
|
||||
推进对应 sourceId revision。
|
||||
|
||||
现有 global `retainedUnderlayContentRevision` 继续增长,供:
|
||||
- compatibility;
|
||||
- diagnostics;
|
||||
- Fast Path fail-closed。
|
||||
|
||||
RETAINED Segment cache 不再直接以 global Source/Tile revision 作为内容 revision。
|
||||
|
||||
### 4.2 Segment dependency
|
||||
|
||||
正式 renderer 与 diagnostics 共用 source→segment 依赖推导。
|
||||
|
||||
规则:
|
||||
- layer sourceId 决定 source dependency;
|
||||
- source-less background 不制造假 dependency;
|
||||
- shared source 可影响多个 segment;
|
||||
- symbol 的 glyph/image/placement 作为明确 global prerequisite;
|
||||
- 真正无法证明的 dependency fail closed。
|
||||
|
||||
### 4.3 Per-segment readiness
|
||||
|
||||
未修改:
|
||||
- `Source::isLoaded()`;
|
||||
- `renderTreeParameters->loaded = styleLoaded && isLoaded()`。
|
||||
|
||||
full CPU retained path 增加 per-segment source readiness/stability:
|
||||
|
||||
- dependent source unloaded → 仅该 Segment fallback;
|
||||
- dependent source fading → 仅该 Segment unstable;
|
||||
- unrelated source unloaded/fading → 不影响该 Segment;
|
||||
- style/image/sprite/glyph/placement/context 等 global prerequisite 仍 fail closed。
|
||||
|
||||
### 4.4 Mixed Build / Reuse / Fallback
|
||||
|
||||
移除 retained full-CPU path 的 all-or-none `useRetainedRanges` 依赖。
|
||||
|
||||
每个 RETAINED Segment独立决定:
|
||||
|
||||
- Build
|
||||
- Reuse
|
||||
- Fallback
|
||||
|
||||
主 buffer 按 Style Segment 顺序执行:
|
||||
|
||||
- RETAINED Build/Reuse → composite cache
|
||||
- RETAINED Fallback → live render only that range
|
||||
- CONTINUOUS → live render
|
||||
|
||||
因此允许:
|
||||
|
||||
```text
|
||||
R0 = Reuse
|
||||
C = Live
|
||||
R1 = Fallback
|
||||
```
|
||||
|
||||
不再因为 R1 fallback 把 R0 一起变成 live redraw。
|
||||
|
||||
Fast Path gate / `singleContinuousSegment` / multi-continuous Fast Path:**未修改**。
|
||||
|
||||
## 5. Android build
|
||||
|
||||
首次标准 validation script 的 Gradle 本体实际结果:
|
||||
|
||||
```text
|
||||
BUILD SUCCESSFUL in 12s
|
||||
86 actionable tasks: 6 executed, 80 up-to-date
|
||||
```
|
||||
|
||||
但脚本在 Gradle 成功后以 141 退出。RDC 用 `bash -x` 精确定位到:
|
||||
|
||||
```bash
|
||||
LIB_BYTES="$(unzip ... | awk '/.../ {print $1; exit}')"
|
||||
```
|
||||
|
||||
在 `set -o pipefail` 下触发 SIGPIPE。
|
||||
|
||||
RDC 仅去掉 awk 的 `exit`,重跑标准脚本:
|
||||
|
||||
```text
|
||||
RC=0
|
||||
[validation-apk] BUILD SUCCESS
|
||||
```
|
||||
|
||||
最终 APK:
|
||||
|
||||
- variant:OpenglDebug
|
||||
- ABI:x86_64
|
||||
- apk bytes:61,956,640
|
||||
- libmaplibre.so bytes:39,636,472
|
||||
- SHA-256:
|
||||
`86e22bf081bf7d3196f0bf0e7c56af51bde01e9f0b01e0bab238cb794273315a`
|
||||
|
||||
证据:
|
||||
`artifacts/maplibre-particle/android-validation/`
|
||||
|
||||
## 6. 设备
|
||||
|
||||
- serial:`192.168.200.139:5555`
|
||||
- model:FZG1-2
|
||||
- Android:13
|
||||
- API:33
|
||||
- ABI:x86_64
|
||||
|
||||
APK 覆盖安装成功,`ParticleCoreLayerValidationActivity` 冷启动成功。
|
||||
|
||||
STEP-100 log:
|
||||
`artifacts/maplibre-particle/android-validation/step100-logcat.txt`
|
||||
|
||||
log SHA-256:
|
||||
`e9d18ea987d12cb30e321d0d9c669b2588972aac89e5277920bf1fa9fee8146b`
|
||||
|
||||
## 7. T1 STATIC
|
||||
|
||||
运行约 20 秒。
|
||||
|
||||
aggregate:
|
||||
|
||||
```text
|
||||
falseWindowCount=1
|
||||
falseWindowAvgMs=203
|
||||
falseWindowMaxMs=203
|
||||
openFalseWindowMs=0
|
||||
```
|
||||
|
||||
最终:
|
||||
|
||||
- R0 build=1
|
||||
- R0 reuse=1217
|
||||
- R0 decision=reuse
|
||||
- R1 build=1
|
||||
- R1 reuse=1187
|
||||
- R1 decision=reuse
|
||||
|
||||
静态 R-C-R 正常。
|
||||
|
||||
## 8. T4 R-C-R 主验收
|
||||
|
||||
结构:
|
||||
|
||||
```text
|
||||
R0 -> step099-static
|
||||
C -> particle-fixture
|
||||
R1 -> step099-dynamic (+ framework annotation source if present)
|
||||
```
|
||||
|
||||
window marker:
|
||||
|
||||
```text
|
||||
baseline tick=8 17:05:21.642
|
||||
final tick=130 17:06:23.240
|
||||
```
|
||||
|
||||
窗口约 61.6 秒。
|
||||
|
||||
dynamic update 总计:68 次。
|
||||
|
||||
global revision:
|
||||
|
||||
```text
|
||||
128 -> 973
|
||||
```
|
||||
|
||||
说明 global compatibility revision **仍正常增长**,没有通过停掉 global revision“作弊”。
|
||||
|
||||
### R0
|
||||
|
||||
baseline:
|
||||
|
||||
```text
|
||||
build=1
|
||||
reuse=132
|
||||
invalidation=20
|
||||
srcRev=5127273714767523329
|
||||
decision=reuse
|
||||
```
|
||||
|
||||
final:
|
||||
|
||||
```text
|
||||
build=1
|
||||
reuse=4042
|
||||
invalidation=20
|
||||
srcRev=5127273714767523329
|
||||
decision=reuse
|
||||
```
|
||||
|
||||
Delta:
|
||||
|
||||
```text
|
||||
buildDelta = 0
|
||||
invalidationDelta = 0
|
||||
reuseDelta = +3910
|
||||
sourceRevision = unchanged
|
||||
```
|
||||
|
||||
**硬门槛 PASS。**
|
||||
|
||||
### R1
|
||||
|
||||
baseline:
|
||||
|
||||
```text
|
||||
build=5
|
||||
invalidation=26
|
||||
```
|
||||
|
||||
final:
|
||||
|
||||
```text
|
||||
build=135
|
||||
invalidation=211
|
||||
```
|
||||
|
||||
动态 Source mutation 正常驱动 R1。
|
||||
|
||||
mutation diagnostics 持续:
|
||||
|
||||
```text
|
||||
sourceId=step099-dynamic
|
||||
affected=[2]
|
||||
scope=source-local-diagnostic
|
||||
```
|
||||
|
||||
### loaded false windows
|
||||
|
||||
T4:
|
||||
|
||||
```text
|
||||
falseWindowCount=5
|
||||
falseWindowAvgMs=202
|
||||
falseWindowMaxMs=203
|
||||
openFalseWindowMs=0
|
||||
```
|
||||
|
||||
即 transient global loaded=false 仍存在,但不再让 R0 build/invalidation 增长。
|
||||
|
||||
## 9. T2 双 GeoJSON readiness 回归
|
||||
|
||||
约 35 秒。
|
||||
|
||||
aggregate:
|
||||
|
||||
```text
|
||||
falseWindowCount=6
|
||||
falseWindowAvgMs=203
|
||||
falseWindowMaxMs=205
|
||||
openFalseWindowMs=0
|
||||
```
|
||||
|
||||
R0 在动态窗口保持:
|
||||
|
||||
- build=1
|
||||
- invalidation=18
|
||||
- revision unchanged
|
||||
- reuse 持续增长。
|
||||
|
||||
R1 随两个动态 GeoJSON 持续 build/invalidate。
|
||||
|
||||
结论:
|
||||
**DYNAMIC_SOURCE_READINESS_THRASH 仍是 global loaded 事实,但不再误伤无关 retained cache。**
|
||||
|
||||
## 10. T3 permanently unready unrelated source
|
||||
|
||||
`step099-unready-raster-array`:
|
||||
|
||||
- no layer;
|
||||
- enabled=false;
|
||||
- isLoaded=false。
|
||||
|
||||
global:
|
||||
|
||||
```text
|
||||
renderOrchestratorLoaded=false
|
||||
openFalseWindowMs=21468
|
||||
```
|
||||
|
||||
约 21.468 秒持续 false。
|
||||
|
||||
与此同时:
|
||||
|
||||
R0:
|
||||
- build=1
|
||||
- invalidation=19
|
||||
- reuse:11 → 1200
|
||||
- decision=reuse
|
||||
|
||||
R1:
|
||||
- build=1
|
||||
- invalidation=19
|
||||
- reuse:11 → 1200
|
||||
- decision=reuse
|
||||
|
||||
因此:
|
||||
|
||||
> global loaded 仍保持旧兼容语义,但 unrelated permanently-unready source 已不能把无关 RETAINED Segment 拖成 style-not-loaded fallback。
|
||||
|
||||
**硬门槛 PASS。**
|
||||
|
||||
## 11. Mixed fallback
|
||||
|
||||
T4 中 R1 `fallbackCount` 从约 21 增长到 76,而 R0 fallback 计数保持不变,同时 R0 reuse 持续增长。
|
||||
|
||||
这证明运行期间真实发生了:
|
||||
|
||||
```text
|
||||
R0 = Reuse
|
||||
R1 = transient Fallback / rebuild
|
||||
```
|
||||
|
||||
而不是只有“两个段都 ready”的理想场景。
|
||||
|
||||
未观察到 crash / ANR。
|
||||
|
||||
## 12. Fast Path 边界
|
||||
|
||||
RDC diff 审计确认未修改:
|
||||
|
||||
- `tryRenderParticleFastFrame`
|
||||
- `particleFastPathNonCameraInputsMatch`
|
||||
- `singleContinuousSegment`
|
||||
- multi-continuous fail-closed
|
||||
- global Fast Path signature/fade/contentRevision/pending/placement 等 gate。
|
||||
|
||||
STEP-100 只修 full CPU retained locality。
|
||||
|
||||
## 13. Native host tests
|
||||
|
||||
Codex 已新增 `RenderSegmentLocality.*` C++ 测试源码。
|
||||
|
||||
RDC 尝试恢复 `macos-metal` test runner,但 host configure 需要 `libuv`,当前未安装。
|
||||
|
||||
尝试 `brew install libuv` 时发现 Intel Homebrew 会连带升级:
|
||||
- CMake
|
||||
- OpenSSL
|
||||
- Python
|
||||
- ca-certificates 等
|
||||
|
||||
且当前 Intel macOS 已无官方 bottle 支持。为避免污染开发环境,RDC 主动停止。
|
||||
|
||||
因此:
|
||||
|
||||
- Native host GTest:**ENV BLOCKED / NOT RUN**
|
||||
- Android C++ full native build:**PASS**
|
||||
- Android device runtime:**PASS**
|
||||
|
||||
## 14. Jest / iOS
|
||||
|
||||
- Jest:NOT RUN。
|
||||
- iOS runtime:本 Step 未运行。
|
||||
- V18 unified release:在 STEP-100 Git 收尾后单独执行。
|
||||
|
||||
## 15. 最终判定
|
||||
|
||||
STEP-100 的核心实现目标全部成立:
|
||||
|
||||
1. global revision 继续增长;
|
||||
2. R1 GeoJSON mutation 不再 invalid/rebuild R0;
|
||||
3. R0 buildDelta=0;
|
||||
4. R0 invalidationDelta=0;
|
||||
5. R0 reuseDelta>0;
|
||||
6. transient unrelated unloaded 不影响 R0 reuse;
|
||||
7. permanent unrelated unloaded 不影响 R0 reuse;
|
||||
8. mixed Reuse/Fallback 实际发生;
|
||||
9. Fast Path 语义未放宽。
|
||||
|
||||
因此本 Step 功能与 Android runtime 验收:
|
||||
|
||||
**PASS / CLOSED**。
|
||||
|
||||
下一步允许进入 V18 unified release。
|
||||
184
docs/报告/STEP-101_RenderOrchestrator销毁生命周期修复_Codex完成报告_v1.md
Normal file
184
docs/报告/STEP-101_RenderOrchestrator销毁生命周期修复_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# STEP-101 RenderOrchestrator 销毁生命周期修复 Codex 完成报告 v1
|
||||
|
||||
## 1. 执行结论
|
||||
|
||||
**PASS(RDC Native / Android runtime):V18 teardown destroyed-mutex 回归已修复并通过设备闭环。** Codex 阶段仅交付源码;随后 RDC 独立完成 Android full Native build、focused C++ TestRunner、STEP-101 T1~T4、20 次真实 renderer reset/recreate、STEP-100 locality 回归与 crash buffer 检查。V19 尚未在本报告阶段公开发布。
|
||||
|
||||
## 2. Git 基线
|
||||
|
||||
- 工作区:`/Users/tei/dev/MapLibreParticleLab-step101`
|
||||
- 分支:`step101-render-orchestrator-teardown`
|
||||
- start HEAD:`d29e84c1a90daeea104b389d28b000c4f017d08d`
|
||||
- implementation commit:`64cf8f44350eb8ec4b45360db4d3f9505ac4ac0b`
|
||||
- implementation message:`fix: make render orchestrator teardown safe`
|
||||
- push result:**PASS**,`origin/particle/main` 独立回读与 implementation commit 一致。
|
||||
- 文档 closeout commit / 最终远端 HEAD:Git 提交无法在自身内容中自指其最终 SHA,按项目既有规则由最终交付回读记录。
|
||||
- 子模块:`git submodule status` 列出的子模块均以 `-` 开头,表示当前工作区未初始化;本轮未修改或初始化子模块。
|
||||
- V18 Android/RN `13.4.1-particle.18`、iOS `6.28.0-particle.18` 保持不变;V19 **未发布**。
|
||||
|
||||
## 3. 根因与实现
|
||||
|
||||
V18 `readinessDiagnosticsMutex` 等成员声明在 `renderSources` 之后,按 C++ 反序析构会先于 sources 消失。RenderSource/Tile 销毁存在同步 `Cancelled` callback 到 Orchestrator 的可达路径,随后可能锁已销毁 mutex。这是已证实的生命周期漏洞,与 Android FORTIFY 报错高度吻合。虽未取得精确到最终 callback 的完整符号化现场栈,但同一设备上 V18 条件稳定复现,而 STEP-101 修复后 T1~T4、20 次 reset、动态 GeoJSON 与 diagnostics 条件均不再崩溃,因此将其作为本次 Android teardown 回归的已确认工程根因。
|
||||
|
||||
当前源码的析构顺序为:处理 contextLost layers → 第一次 `threadPool.waitForEmpty()` → detach 各 RenderSource、GlyphManager、ImageManager observer → readiness state 仍活着时 `renderSources.clear()` → 第二次 `threadPool.waitForEmpty()` → 常规成员析构。`RenderSource::setObserver(nullptr)` 恢复 NullObserver,tile 回调无需新增空指针分支。显式 detach 与清理建立 owner teardown 合同,不依赖今后成员顺序碰巧安全;没有增加 teardown flag。
|
||||
|
||||
render-job 队列独立于 tagged scheduler work。静态检查显示 tile/worker 销毁可投递捕获 render data/bucket 的 render jobs;`waitForEmpty()` 不处理它们。OpenGL 同步 Android reset 路径先 `renderer.reset()`、再 `backend.reset()`,其 Context 析构调用 `runRenderJobs(true)`;若启用 async renderer cleanup,backend 的清理时点另由后续生命周期决定。Metal Context 与 Vulkan backend 也有各自的 `runRenderJobs(true)` 清理入口,均未在本轮运行验证。测试显式 `runRenderJobs()`,但没有构建或设备证据证明所有 backend 模式均安全。这是 RDC 必须核对的剩余风险。
|
||||
|
||||
正常 source remove 使用 `renderSources.erase()`,`clearData()` 使用 `renderSources.clear()`;两者均在 Orchestrator 实体及 readiness state 存活时执行。`TileCache::~TileCache()` 会清理 pending releases 并等待先前 deferred deletion 结束。这个审计没有证成任意外部并发调用都安全,本轮不修改正常 erase/clearData 行为,也不声称所有 source replacement 异步问题已解决。
|
||||
|
||||
## 4. 实际修改与审计结论
|
||||
|
||||
源码/测试差异共七个文件:
|
||||
|
||||
1. `src/mbgl/renderer/render_orchestrator.cpp`:显式 detach、source clear 与双 drain。
|
||||
2. `src/mbgl/renderer/render_orchestrator.hpp`:仅为 focused test 开放 friend helper。
|
||||
3. `src/mbgl/renderer/render_source.cpp`:nullptr observer 回退 NullObserver。
|
||||
4. `test/renderer/render_orchestrator_error.test.cpp`:新增两个非 Jest teardown tests;helper 注册 Orchestrator observer,测试断言 source 销毁前 observer 已 detach、Cancelled 不再到旧 observer、第二次 drain 和独立 render-job 队列。
|
||||
5. `platform/android/MapLibreAndroid/src/cpp/map_renderer.cpp`:`nativeReset` 与 `resetRenderer` 入口/完成日志,仅用于配对观测,未改变 reset 调用顺序。
|
||||
6. `platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/surfaceview/SurfaceViewMapRenderer.java`:SurfaceView detach 与 Java `nativeReset()` 进入/返回日志。
|
||||
7. `platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/ParticleCoreLayerValidationActivity.kt`:T1~T4,tile ready/diagnostics、动态 GeoJSON、Activity recreate、重建与完成日志;保存状态里的空场景不会在配置变更后重新从 Intent 启动。
|
||||
|
||||
本轮新增/更新文档:本报告、`docs/50_Step记录/STEP-101_RenderOrchestrator销毁生命周期修复_v1.md`、`粒子开发记录.md`。既有未跟踪的 STEP-101 任务书 v1/v2 没有修改。
|
||||
|
||||
与 v2 合同静态核对:observer、双 drain、render-job 独立审计、四个 Android 场景均有对应代码;STEP-100 locality/fast path/source loaded 语义无本次差异。T2 代码计的是 reset **请求**,20 次 Native reset 必须由 log 中每轮 Java `nativeReset`、JNI `nativeReset`、`MapRenderer::resetRenderer entered/completed` 和新 renderer ready 独立确认,不能凭 UI DONE 计数认定。T3 使用已有 T4 R-C-R fixture 并持续更新动态 GeoJSON;T4 在 reset 前额外取 diagnostics。现有 C++ test 使用最小 tile-backed Source,并非实际 Android source;设备等价验收仍不可省略。
|
||||
|
||||
## 5. 实际检查与未执行项目
|
||||
|
||||
- `git diff --check`:**PASS**(已跟踪 diff 的 whitespace/error 静态检查;未跟踪的新文档不在此命令检查范围)。
|
||||
- 源码 diff/合同静态审计:完成;补了 observer detach 断言、Java/JNI reset 边界日志、T3 mutation 证据及场景恢复边界。
|
||||
- CMake/Ninja/Native compile、C++ GTest:**NOT RUN**。
|
||||
- Android Gradle、Validation APK、AAR/RN candidate:**NOT RUN**。
|
||||
- ADB、T1~T4、crash buffer、STEP-100 locality 运行态、NavSea 冷启动:**NOT RUN**。
|
||||
- Jest、iOS/Metal build 与设备、publish/tag/commit/push:**NOT RUN**。
|
||||
- 产物路径、大小、SHA-256、运行日志:本轮没有新产物,均无结果。
|
||||
|
||||
## 6. RDC 精确验证命令与验收证据
|
||||
|
||||
在本工作区执行 Mac Native 定向验证(若既有 `libuv` 环境问题仍在,保留首个错误并标为环境阻塞):
|
||||
|
||||
```bash
|
||||
cmake --preset macos-metal
|
||||
cmake --build build-macos-metal --target mbgl-test-runner --parallel 3
|
||||
build-macos-metal/mbgl-test-runner --gtest_filter='RenderOrchestratorTeardown.*:RenderSegmentLocality.*'
|
||||
```
|
||||
|
||||
Android Validation APK 使用固定入口,不手拼 Gradle:
|
||||
|
||||
```bash
|
||||
./tools/particle_android_validation/build_validation_apk.sh
|
||||
```
|
||||
|
||||
RDC 使用显式设备 serial。每个场景先清日志、停止旧 Activity,再单独启动;`T2` 等待 20 轮结束。安装前核对 APK 文件、SHA-256、ABI 与运行设备:
|
||||
|
||||
```bash
|
||||
adb -s 192.168.200.139:5555 install -r artifacts/maplibre-particle/android-validation/MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk
|
||||
adb -s 192.168.200.139:5555 logcat -c
|
||||
adb -s 192.168.200.139:5555 shell am force-stop org.maplibre.android.testapp
|
||||
adb -s 192.168.200.139:5555 shell am start -n org.maplibre.android.testapp/.activity.particle.ParticleCoreLayerValidationActivity --es step101_scenario T2_RESET_LOOP_20
|
||||
adb -s 192.168.200.139:5555 logcat -v threadtime -d > /tmp/step101-t2-logcat.txt
|
||||
rg -n 'STEP101_|SurfaceView detached; nativeReset|nativeReset returned|MapRenderer::nativeReset|MapRenderer::resetRenderer|pthread_mutex_lock called on a destroyed mutex|Fatal signal|ANR' /tmp/step101-t2-logcat.txt
|
||||
```
|
||||
|
||||
对 T1/T3/T4 分别重复相同 `logcat -c`、`force-stop`、`am start`、日志保存步骤,将 `step101_scenario` 依次替换为 `T1_RESET_ONCE`、`T3_RESET_WITH_DYNAMIC_GEOJSON`、`T4_RESET_WITH_DIAGNOSTICS`。逐轮配对 `RESET_REQUEST`、`MAPVIEW_DESTROY_ENTER/RETURN`、`SurfaceView detached; nativeReset entered`、JNI `MapRenderer::nativeReset entered/completed`、Native `resetRenderer entered/completed`、新 `STYLE_READY` 与 `NEW_RENDERER_READY`;T2 需至少 20 个完整 reset/recreate 配对,且无 SIGABRT/ANR。T3 核对 `STEP101_DYNAMIC_SOURCE_MUTATION` 在每轮 reset 前持续出现,T4 核对 `PRE_RESET_DIAGNOSTICS` 与 reset 后 diagnostics。STEP-100 T4 R-C-R 另按既有 Validation 菜单复跑,核对无关 R0 `buildDelta=0`、`invalidationDelta=0`、`reuseDelta>0`,动态 R1 正常 build/invalidation。
|
||||
|
||||
上述只是交接命令,**本轮均未执行**。V19 candidate 必须先于 NavSea 临时锁包和五次超过 30 秒的冷启动;crash buffer 与 locality 均通过后,才可进入不可变发布。NavSea 外部工作区/候选制品命令由 RDC 按其实际环境另行确定,本轮不伪造路径或结果。
|
||||
|
||||
## 7. 已知问题、未完成事项与下一步
|
||||
|
||||
静态审计无法证明 Android 每次 Activity recreate 都实际完成 native reset,也无法证明异步 backend 清理和真实 Source 并发场景无 late callback。Native 编译与定向测试、四个 Validation 场景、STEP-100 locality、NavSea 等价五次冷启动、crash buffer 均待 RDC;任何失败均应保留现场并按首个真实错误回传。无理由在本阶段修改 NavSea、发布 V19 或重写 STEP-100 语义。
|
||||
|
||||
|
||||
## 8. RDC 独立验收(2026-10-07)
|
||||
|
||||
### 8.1 Android full Native / Validation APK
|
||||
|
||||
标准入口:
|
||||
|
||||
```text
|
||||
./tools/particle_android_validation/build_validation_apk.sh
|
||||
```
|
||||
|
||||
结果:**PASS**。
|
||||
|
||||
```text
|
||||
variant=OpenglDebug
|
||||
abi=x86_64
|
||||
apk_bytes=61957678
|
||||
libmaplibre_bytes=39637512
|
||||
sha256=14cbd35ed0bc1366389ddea69925901013e3b209ed3575462421a7920c73c721
|
||||
```
|
||||
|
||||
该构建实际编译了本 Step 修改后的 C++ Core、`libmaplibre.so`、JNI/Java reset 日志和 Android Validation T1~T4。
|
||||
|
||||
### 8.2 STEP-101 Android teardown runtime
|
||||
|
||||
设备:FZG1-2 / Android 13 / API 33 / x86_64 / `192.168.200.139:5555`。
|
||||
|
||||
- T1 `RESET_ONCE`:真实 `MapRenderer::resetRenderer entered/completed`、`nativeReset entered/completed`、新 renderer `STYLE_READY / NEW_RENDERER_READY`;PASS。
|
||||
- T2 `RESET_LOOP_20`:完整运行至 iteration 20,最终 `STEP101_SCENARIO_DONE ... resetsRequested=20`;逐轮出现 destroy/reset/new renderer ready;PASS。
|
||||
- T3 `RESET_WITH_DYNAMIC_GEOJSON`:reset 前动态 GeoJSON mutation 持续产生,reset 完成后新 renderer 继续 ready;PASS。
|
||||
- T4 `RESET_WITH_DIAGNOSTICS`:reset 前 `getRenderSegmentDiagnostics()` 可读,随后真实 reset 完成并重建;PASS。
|
||||
- 四组验收 crash buffer 均为空;未出现 `pthread_mutex_lock called on a destroyed mutex`、SIGABRT 或 ANR。
|
||||
|
||||
### 8.3 STEP-100 locality 回归
|
||||
|
||||
R-C-R T4 重新运行 60+ 秒,稳定窗口:
|
||||
|
||||
```text
|
||||
global revision: 62 -> 894
|
||||
|
||||
R0 retained:
|
||||
build 1 -> 1 delta 0
|
||||
invalidation 19 -> 19 delta 0
|
||||
reuse 12 -> 3890 delta +3878
|
||||
|
||||
R1 retained:
|
||||
build / invalidation 持续增长
|
||||
```
|
||||
|
||||
动态 source mutation 仍只影响对应 segment;R0 source revision 保持不变。STEP-101 teardown 修复没有破坏 STEP-100 Source→Segment locality。
|
||||
|
||||
### 8.4 Focused Android C++ TestRunner
|
||||
|
||||
Mac host `mbgl-test-runner` 仍因本机缺失可用 `libuv` pkg-config 包而 ENV BLOCKED;没有安装或升级系统依赖来掩盖该环境事实。
|
||||
|
||||
改用仓库已有 Android Native C++ TestRunner,x86_64 build **PASS**。RDC 为 TestRunner 临时打包真实 `test/fixtures/` 到 `data.zip`,仅满足 Android runner 既有解压契约;该临时 asset 已删除,不进入 Git。
|
||||
|
||||
设备过滤执行:
|
||||
|
||||
```text
|
||||
RenderSegmentLocality.SourceRevisionsAreScopedAndRecreationUsesNewIdentity PASS
|
||||
RenderSegmentLocality.ReadinessAndFadeFollowOnlyDependencies PASS
|
||||
RenderSegmentLocality.MixedExecutionDrawsEachLiveRangeOnceInStyleOrder PASS
|
||||
RenderSegmentLocality.FailedBuildCannotReuseOldTexture PASS
|
||||
RenderOrchestratorTeardown.NullObserverAndDetachCoverEveryTileCallback PASS
|
||||
RenderOrchestratorTeardown.PendingTileCancellationAndSecondDrainRepeatSafely PASS
|
||||
TestRunner finished with status: '0'
|
||||
```
|
||||
|
||||
最终 TestRunner APK(含临时 fixtures,仅作验收证据)SHA-256:
|
||||
|
||||
```text
|
||||
e1b39b3eb035175b79dd60dbe39496ea982c2dd6e2f1593fd60dd9630f393712
|
||||
```
|
||||
|
||||
crash buffer 为空。
|
||||
|
||||
首次 TestRunner 编译暴露 6 个测试源码级 C++20 designated-initializer / protected-base conversion 问题;RDC 仅修正测试源码字段补齐和显式转换,未改变生产语义。修复后 Android C++ TestRunner build 与上述 filtered tests 均 PASS。
|
||||
|
||||
### 8.5 结论
|
||||
|
||||
STEP-101 的 Native 生命周期修复达到关闭条件:
|
||||
|
||||
1. RenderSource observer 在 owner teardown 前显式 detach;
|
||||
2. readiness state 仍存活时显式销毁 renderSources;
|
||||
3. source clear 前后 tagged work 双 drain;
|
||||
4. glyph/image observer 同步 detach;
|
||||
5. 20 次真实 Android renderer reset/recreate 无 destroyed-mutex;
|
||||
6. 动态 GeoJSON、diagnostics 条件下仍安全;
|
||||
7. STEP-100 locality 保持 PASS;
|
||||
8. focused C++ tests PASS。
|
||||
|
||||
Jest:**NOT RUN**。
|
||||
|
||||
V19 candidate、NavSea 五次冷启动与正式 publish/finalize 属于后续发行 Gate,不改变本 Step Native 修复的 PASS 结论。
|
||||
92
docs/报告/V15_iOS_WeatherColor纹理生命周期修复_Codex完成报告_v1.md
Normal file
92
docs/报告/V15_iOS_WeatherColor纹理生命周期修复_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# V15 iOS WeatherColor 纹理生命周期修复 Codex 完成报告 v1
|
||||
|
||||
日期:2026-09-27(JST)
|
||||
结论:**IMPLEMENTED / RDC HOST VALIDATION PASS / WAITING FOR MANUAL IPAD VALIDATION**。源码修复与测试代码已交付;Codex 未运行构建或测试,RDC 独立完成 OpenGL/Metal host 验证。不能据此标记 iPad PASS。
|
||||
|
||||
## 1. 执行配置与 Git 基线
|
||||
|
||||
- 实际执行模型与档位:**GPT-6 Luna High**(按本轮用户指定记录;未切换或升级)。
|
||||
- 仓库:`/Users/tei/dev/MapLibreParticleLab`;机器:Mac mini,Darwin 24.6.0,x86_64。
|
||||
- 分支:`particle/main`;开工/当前 HEAD:`cd71640a84901212ce4681e296f027befdff4ee0`。
|
||||
- V15 tag `maplibre-particle/rn/13.4.1-particle.15` peeled commit 与 HEAD 相同;`origin/particle/main` 与 HEAD 相同。
|
||||
- manifest 中的 V15 `sourceCommit` 是 `1fdc5df625d405233703136eb2dc12032d932da2`;后续 finalize release 生成的 V15 tag 指向 HEAD `cd71640a84901212ce4681e296f027befdff4ee0`。两者角色不同且本任务相关已跟踪源码相同,因此不存在基线冲突。任务书保留原样,tag/发行内容未修改。
|
||||
- 开工工作树只存在用户提供且未跟踪的 V15 任务书;本轮始终保留该文件且未纳入提交。源码阶段没有执行 reset/clean,也未更改版本/tag/发行包。
|
||||
- `platform/android/VERSION` 为 `13.4.1-particle.15`;`platform/ios/VERSION` 当前内容为 `6.28.0`。本轮未改版本坐标。
|
||||
|
||||
## 2. 根因链和修复
|
||||
|
||||
源码复核确认:`RenderWeatherColorLayer::update()` 上传/替换 Resources 中的纹理,但旧 Drawable 仅在 wrap 区间变化时重建;Metal `LayerGroup::upload()` 先于 `render()`;旧 Tweaker 在 `render()` 阶段才将新纹理写入 Drawable。`gfx::Drawable::setTexture()` 仅更换资源指针,不会补做前序 upload,因此新 Metal texture 可能在 draw 时仍为 dirty。IPS 不包含 Drawable/Texture ID,不能据此声称事故中的确切对象身份已由日志证明。
|
||||
|
||||
修复在 update 阶段比较 Drawable 原有与期望的 T0/T1 实际纹理指针。wrap 范围或任一 endpoint 指针变化时,当场重建该层 drawables,确保 builder 在本帧 upload 前安装纹理。Tweaker 不再绑定纹理,只更新 UBO 动态值。`assert(!textureDirty)` 和 Metal 通用绑定逻辑未改。
|
||||
|
||||
指针转移结果:
|
||||
|
||||
| 场景 | 指针比较 | 预期动作 |
|
||||
|---|---|---|
|
||||
| T0/T1 均不变 | 两指针相同 | 不重建、不重传 |
|
||||
| 仅 T0 或仅 T1 被替换 | 一指针不同 | update 阶段重建 |
|
||||
| 两端均替换 | 两指针不同 | update 阶段重建 |
|
||||
| 旧 T0/T1 交叉复用 | 顺序变化 | update 阶段重建 |
|
||||
| T0=T1 | 相同共享指针 | 保持时不重建;从双端切换/反向时重建 |
|
||||
| 仅 mix/opacity/stops 改变 | endpoint 不参与这些参数 | Tweaker 更新 UBO,不重建/重传 |
|
||||
| wrap 改变 | endpoint 可相同 | 重建 wrap drawables |
|
||||
| Resources 清空后恢复 | null → 新端点且 wrap 哨兵失配 | 新建资源与 drawables |
|
||||
|
||||
## 3. 实际修改文件
|
||||
|
||||
- `include/mbgl/renderer/layers/weather_color_texture_lifecycle.hpp`:新增 drawable endpoint/wrap 重建判定 helper。
|
||||
- `src/mbgl/renderer/layers/render_weather_color_layer.cpp`:update 阶段检测 endpoint 指针并重建;Tweaker 删除 draw 阶段纹理绑定。
|
||||
- `test/renderer/weather_color_texture_lifecycle.test.cpp`:新增六组指针/生命周期决策测试。
|
||||
- `test/CMakeLists.txt`:将新测试加入 CMake test target;Bazel 通过既有测试 glob 收录。
|
||||
- `docs/50_Step记录/V15_iOS_WeatherColor纹理生命周期修复_v1.md`:新增本阶段记录。
|
||||
- `粒子开发记录.md`:追加本轮事实。
|
||||
- 本 Codex 完成报告。
|
||||
|
||||
没有更改 V15 任务书、V12/V13、JS/RN、NavSea 宿主 API、Android、release pipeline、release tag/archive/manifest/version、粒子 advection/State/Trail/Retained、公共 API/ABI、Layer ID、Z-order、shader slot 或 Metal assert。
|
||||
|
||||
## 4. 检查和验证结果
|
||||
|
||||
| 检查 | 结果 |
|
||||
|---|---|
|
||||
| 源码调用链只读复核 | 已完成;事实和限制如上 |
|
||||
| `git diff --check` | **PASS**(已跟踪 diff 无 whitespace error;新增文件另行查看,未纳入该命令的 Git diff 范围) |
|
||||
| RDC 首次 OpenGL host 编译 | **FAIL**:`test/renderer/weather_color_texture_lifecycle.test.cpp:60:17` 的 `nullptr` 参数无法参与模板类型推导;RDC 同时反馈 production `render_weather_color_layer.cpp` object 已编译 |
|
||||
| OpenGL host 修正 | 已将该断言调用写为 `weatherColorDrawableBindingsChanged<int>(...)`,仅修正测试 helper 的模板类型推导 |
|
||||
| RDC OpenGL host build retry | **PASS**, exit 0 |
|
||||
| RDC OpenGL 定向 GTest | **PASS**, 6/6 |
|
||||
| RDC Metal host 编译 | **PASS**, exit 0 |
|
||||
| RDC Metal host 定向 GTest | **PASS**, 6/6 |
|
||||
| iOS/Xcode build、签名、安装 | 未执行,用户按项目边界手工完成 |
|
||||
| iPad Metal 运行、崩溃断言/内存/纹理计数 | 未执行,等待人工集中验收 |
|
||||
| Android/Gradle/APK/ADB/Jest/JS | 未执行 |
|
||||
| scoped commit/push | 已完成,详见 §7 |
|
||||
| release pipeline | 未执行 |
|
||||
|
||||
## 5. RDC Mac 精确命令
|
||||
|
||||
按 OpenGL → Metal host 顺序执行;每个 build 成功后运行该新增测试:
|
||||
|
||||
```bash
|
||||
cmake --build build-step088-opengl --target mbgl-test-runner --parallel 3
|
||||
build-step088-opengl/mbgl-test-runner --gtest_filter='WeatherColorTextureLifecycle.*'
|
||||
cmake --build build-step097-metal-host --target mbgl-test-runner --parallel 3
|
||||
build-step097-metal-host/mbgl-test-runner --gtest_filter='WeatherColorTextureLifecycle.*'
|
||||
```
|
||||
|
||||
RDC 日志目录:`/tmp/navsea_v15_weathercolor_validation/`。日志文件:`opengl_build.log`(首轮失败)、`opengl_build_retry.log`(重试成功)、`opengl_test.log`(6/6)、`metal_host_build.log`(成功)、`metal_host_test.log`(6/6)。未从回传摘要/日志记录到耗时,因此不补写耗时。Metal host 结果不是 iOS 真机通过。
|
||||
|
||||
## 6. 限制、剩余风险与人工验收
|
||||
|
||||
- 新测试覆盖 endpoint 判定矩阵,不是 drawable/Metal GPU 端到端模拟;`upload()` 到 draw 的实际时序仍需 RDC Mac host 验证和 iPad 验收。
|
||||
- 任务书引用的事故 IPS 与外部调查报告未在本机读取;没有推断其中未提供的内容。源代码链是可推导的候选根因,而非 IPS 已证明纹理 ID。
|
||||
- iPad 需使用后续正确版本 Native 手工编译;先核对二进制 UUID,再执行冷启动风场、风→浪→风、风→雨→潮→风、邻帧/第九帧淘汰后重入、快速 Seek、天气关闭重开、平移缩放、前后台和旋转。检查 assert/crash、WeatherColor/Particle 画面、上传/重建次数、内存及异常日志。本船放大、上层闪烁和 Android 场景独立验收。
|
||||
- 未发现 `STEP_RECORD.md`;本轮按根规则更新 Step 记录并追加 `粒子开发记录.md`。
|
||||
- 当前结论为源码已实施、RDC OpenGL/Metal host 定向验证 PASS,等待用户手工 iPad 验收;host PASS 不代表设备 PASS。iPad 实测通过前状态不得提升为 CLOSED/PASS。
|
||||
|
||||
## 7. Git 收尾
|
||||
|
||||
- scoped 实现提交:`5af91fe7a105f0ac047e18db1dc063c9ffd69d44`,message:`fix: 修复 V15 WeatherColor Metal 纹理生命周期`;仅含本任务源码、测试、Step、报告和开发记录共 7 个文件。
|
||||
- 已推送至 `origin/particle/main`。推送后执行 `git ls-remote origin refs/heads/particle/main`,读回 `5af91fe7a105f0ac047e18db1dc063c9ffd69d44`,与实现提交相同。
|
||||
- 验证结果补录提交:`4712e1752af2a8238def5839c8754270de4195c9`,message:`docs: 记录 V15 WeatherColor RDC 验证与推送`;仅包含 Step、完成报告、开发记录。推送后远端 HEAD 回读为该提交。
|
||||
- 用户提供的未跟踪 V15 任务书未加入提交。release tag、版本坐标和发行包未修改。
|
||||
- 本报告与 Step 后续补录此 Git 事实;设备状态仍为 **WAITING FOR MANUAL IPAD VALIDATION**。
|
||||
@@ -82,6 +82,10 @@ public:
|
||||
virtual void reduceMemoryUsage() = 0;
|
||||
|
||||
virtual std::unique_ptr<OffscreenTexture> createOffscreenTexture(Size, TextureChannelDataType) = 0;
|
||||
virtual std::unique_ptr<OffscreenTexture> createOffscreenTexture(Size,
|
||||
TextureChannelDataType,
|
||||
bool,
|
||||
bool) = 0;
|
||||
|
||||
template <RenderbufferPixelType pixelType>
|
||||
Renderbuffer<pixelType> createRenderbuffer(const Size size) {
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <mbgl/storage/resource_options.hpp>
|
||||
#include <mbgl/util/client_options.hpp>
|
||||
#include <mbgl/util/action_journal_options.hpp>
|
||||
#include <mbgl/renderer/retained_underlay.hpp>
|
||||
#include <mbgl/renderer/render_segment.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -55,6 +57,14 @@ public:
|
||||
/// Triggers a repaint.
|
||||
void triggerRepaint();
|
||||
|
||||
/** Enables or disables the map-scoped retained static underlay optimization. */
|
||||
void setRetainedUnderlayEnabled(bool);
|
||||
bool setContinuousLayerRange(std::string startLayerId, std::string endLayerId);
|
||||
bool setRenderSegmentPlan(RenderSegmentPlan);
|
||||
RenderSegmentDiagnostics getRenderSegmentDiagnostics() const;
|
||||
bool isRetainedUnderlayEnabled() const;
|
||||
RetainedUnderlayDiagnostics getRetainedUnderlayDiagnostics() const;
|
||||
|
||||
style::Style& getStyle();
|
||||
const style::Style& getStyle() const;
|
||||
|
||||
|
||||
@@ -106,7 +106,10 @@ public:
|
||||
|
||||
void setDirtyState() override;
|
||||
|
||||
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType, bool, bool);
|
||||
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size,
|
||||
gfx::TextureChannelDataType,
|
||||
bool,
|
||||
bool) override;
|
||||
|
||||
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType) override;
|
||||
|
||||
|
||||
@@ -17,6 +17,19 @@ class BufferResource;
|
||||
class CommandEncoder;
|
||||
class Context;
|
||||
|
||||
namespace detail {
|
||||
struct ClearedAttachments {
|
||||
bool color = false;
|
||||
bool depth = false;
|
||||
bool stencil = false;
|
||||
};
|
||||
|
||||
ClearedAttachments applyRenderPassClears(MTL::RenderPassDescriptor&,
|
||||
const std::optional<Color>&,
|
||||
const std::optional<float>&,
|
||||
const std::optional<int32_t>&);
|
||||
} // namespace detail
|
||||
|
||||
class RenderPass final : public gfx::RenderPass {
|
||||
public:
|
||||
RenderPass(CommandEncoder&, const char* name, const gfx::RenderPassDescriptor&);
|
||||
|
||||
@@ -101,6 +101,8 @@ struct ParticleFieldSnapshot {
|
||||
std::vector<float> vector;
|
||||
std::vector<uint8_t> valid;
|
||||
std::optional<VectorSemantics> vectorSemantics;
|
||||
// Internal interpretation identity. Never part of the public field ABI.
|
||||
std::string gpuEncodingIdentity;
|
||||
|
||||
bool isValid() const;
|
||||
bool isTimeInterpolationCompatibleWith(const ParticleFieldSnapshot&) const;
|
||||
@@ -120,6 +122,28 @@ inline bool sameFieldGpuContent(const ParticleFieldSnapshot& current, const Part
|
||||
current.vector == next.vector && current.valid == next.valid;
|
||||
}
|
||||
|
||||
// A GPU slot may be reused only for the same immutable endpoint. A matching
|
||||
// producer revision alone is insufficient, and equal bytes at another valid
|
||||
// time must not silently inherit the previous endpoint's provenance.
|
||||
inline bool sameFieldGpuEndpoint(const ParticleFieldSnapshot& a, const ParticleFieldSnapshot& b) {
|
||||
return a.fieldID == b.fieldID && a.generation == b.generation && a.dataVersion == b.dataVersion &&
|
||||
a.validTime == b.validTime && a.fieldType == b.fieldType && a.displayDomain == b.displayDomain &&
|
||||
a.vectorSemantics == b.vectorSemantics && a.noData == b.noData &&
|
||||
a.fieldSha256 == b.fieldSha256 && a.gpuEncodingIdentity == b.gpuEncodingIdentity &&
|
||||
a.width == b.width && a.height == b.height && a.west == b.west && a.south == b.south &&
|
||||
a.east == b.east && a.north == b.north && a.vector == b.vector && a.valid == b.valid;
|
||||
}
|
||||
|
||||
enum class GpuEndpointSource : uint8_t { Upload, OldT0, OldT1 };
|
||||
|
||||
inline GpuEndpointSource gpuEndpointSource(const ParticleFieldSnapshot& oldT0,
|
||||
const ParticleFieldSnapshot& oldT1,
|
||||
const ParticleFieldSnapshot& wanted) {
|
||||
if (sameFieldGpuEndpoint(oldT0, wanted)) return GpuEndpointSource::OldT0;
|
||||
if (sameFieldGpuEndpoint(oldT1, wanted)) return GpuEndpointSource::OldT1;
|
||||
return GpuEndpointSource::Upload;
|
||||
}
|
||||
|
||||
struct ParticleFieldTimeWindow {
|
||||
ParticleFieldSnapshot t0;
|
||||
ParticleFieldSnapshot t1;
|
||||
|
||||
@@ -78,6 +78,10 @@ struct DecodedFieldGrid {
|
||||
struct DecodedFieldFrame {
|
||||
FrameContentIdentity frameContentIdentity;
|
||||
uint64_t referenceTimeMs = 0;
|
||||
// Validation provenance retained so a stored frame cannot be interpolated
|
||||
// across two otherwise byte-compatible but semantically different profiles.
|
||||
std::string resolvedProfileID;
|
||||
uint32_t resolvedProfileVersion = 0;
|
||||
DecodedFieldGrid grid;
|
||||
// One frame-wide validity domain. The descriptor and payload share storage;
|
||||
// projections can retain the same payload without copying it.
|
||||
@@ -87,8 +91,12 @@ struct DecodedFieldFrame {
|
||||
std::vector<DecodedFieldPlane> planes;
|
||||
|
||||
bool isStructurallyValid() const;
|
||||
bool isSeriesCompatibleWith(const DecodedFieldFrame&) const;
|
||||
bool isInterpolationCompatibleWith(const DecodedFieldFrame&) const;
|
||||
bool hasSameContentAs(const DecodedFieldFrame&) const;
|
||||
// Raw decoded plane payload plus the shared validity bitset; excludes
|
||||
// separately materialized CPU snapshots and renderer/GPU resources.
|
||||
uint64_t decodedByteSize() const;
|
||||
};
|
||||
|
||||
enum class FieldSeriesSubmitResult : uint8_t {
|
||||
@@ -118,6 +126,7 @@ public:
|
||||
std::optional<FieldSeriesWindow> resolve(uint64_t requestedForecastTimeMs) const;
|
||||
size_t size() const { return frames.size(); }
|
||||
size_t capacity() const { return capacity_; }
|
||||
uint64_t decodedByteSize() const;
|
||||
const std::vector<std::shared_ptr<const DecodedFieldFrame>>& entries() const { return frames; }
|
||||
|
||||
private:
|
||||
@@ -158,6 +167,7 @@ public:
|
||||
bool clear(const std::string& fieldKey);
|
||||
void clearAll() { series.clear(); }
|
||||
size_t fieldCount() const { return series.size(); }
|
||||
uint64_t decodedByteSize() const;
|
||||
|
||||
private:
|
||||
size_t perSeriesCapacity_;
|
||||
|
||||
33
include/mbgl/particle/field/particle_gpu_math.hpp
Normal file
33
include/mbgl/particle/field/particle_gpu_math.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
namespace mbgl::particle {
|
||||
|
||||
// Internal RGBA8 UNORM field representation. Nearest sampling and highp
|
||||
// shader arithmetic reconstruct the byte with floor(sample*255 + 0.5).
|
||||
inline uint8_t encodeSignedVelocity8(const float value, const float scale, const bool valid) {
|
||||
if (!valid || !std::isfinite(value) || !std::isfinite(scale) || scale <= 0.0f) return 128;
|
||||
return static_cast<uint8_t>(128 + static_cast<int>(
|
||||
std::lround(std::clamp(value / scale, -1.0f, 1.0f) * 127.0f)));
|
||||
}
|
||||
|
||||
inline float decodeSignedVelocity8(const uint8_t byte, const float scale) {
|
||||
return static_cast<float>(static_cast<int>(byte) - 128) * scale / 127.0f;
|
||||
}
|
||||
|
||||
inline std::array<double, 2> mercatorVelocityDelta(const double eastMps,
|
||||
const double northMps,
|
||||
const double latitudeDegrees,
|
||||
const double visualSeconds) {
|
||||
constexpr double circumference = 40075016.68557849;
|
||||
constexpr double radiansPerDegree = 3.14159265358979323846 / 180.0;
|
||||
const double cosine = std::max(std::cos(latitudeDegrees * radiansPerDegree), 0.01);
|
||||
return {{eastMps * visualSeconds / (circumference * cosine),
|
||||
-northMps * visualSeconds / (circumference * cosine)}};
|
||||
}
|
||||
|
||||
} // namespace mbgl::particle
|
||||
@@ -22,6 +22,16 @@ enum class RendererDebugCounter : uint8_t {
|
||||
TrailTargetCreateOrResize,
|
||||
V4ProjectionMaterialize,
|
||||
FieldWindowRevision,
|
||||
FullRendererFrame,
|
||||
RetainedUnderlayBuild,
|
||||
RetainedUnderlayReuse,
|
||||
RetainedUnderlayFallback,
|
||||
RetainedUnderlayInvalidation,
|
||||
RetainedUnderlayStaticGroupDraw,
|
||||
RetainedDynamicGroupDraw,
|
||||
ParticleFastFrameHit,
|
||||
ParticleFastFrameMiss,
|
||||
ParticleFullFrame,
|
||||
Count,
|
||||
};
|
||||
|
||||
@@ -40,6 +50,16 @@ struct RendererDebugCounterSnapshot {
|
||||
uint64_t trailTargetCreateOrResize = 0;
|
||||
uint64_t v4ProjectionMaterialize = 0;
|
||||
uint64_t fieldWindowRevision = 0;
|
||||
uint64_t fullRendererFrame = 0;
|
||||
uint64_t retainedUnderlayBuild = 0;
|
||||
uint64_t retainedUnderlayReuse = 0;
|
||||
uint64_t retainedUnderlayFallback = 0;
|
||||
uint64_t retainedUnderlayInvalidation = 0;
|
||||
uint64_t retainedUnderlayStaticGroupDraw = 0;
|
||||
uint64_t retainedDynamicGroupDraw = 0;
|
||||
uint64_t particleFastFrameHit = 0;
|
||||
uint64_t particleFastFrameMiss = 0;
|
||||
uint64_t particleFullFrame = 0;
|
||||
};
|
||||
|
||||
inline std::atomic_uint64_t* rendererDebugCounterStorage() {
|
||||
@@ -53,13 +73,34 @@ inline void incrementRendererDebugCounter(const RendererDebugCounter counter) {
|
||||
|
||||
inline RendererDebugCounterSnapshot rendererDebugCounterSnapshot() {
|
||||
const auto* values = rendererDebugCounterStorage();
|
||||
return {values[0].load(std::memory_order_relaxed), values[1].load(std::memory_order_relaxed),
|
||||
values[2].load(std::memory_order_relaxed), values[3].load(std::memory_order_relaxed),
|
||||
values[4].load(std::memory_order_relaxed), values[5].load(std::memory_order_relaxed),
|
||||
values[6].load(std::memory_order_relaxed), values[7].load(std::memory_order_relaxed),
|
||||
values[8].load(std::memory_order_relaxed), values[9].load(std::memory_order_relaxed),
|
||||
values[10].load(std::memory_order_relaxed), values[11].load(std::memory_order_relaxed),
|
||||
values[12].load(std::memory_order_relaxed)};
|
||||
RendererDebugCounterSnapshot result;
|
||||
#define MLN_LOAD_RENDERER_COUNTER(field, counter) \
|
||||
result.field = values[static_cast<size_t>(RendererDebugCounter::counter)].load(std::memory_order_relaxed)
|
||||
MLN_LOAD_RENDERER_COUNTER(weatherColorNeedsRepaint, WeatherColorNeedsRepaint);
|
||||
MLN_LOAD_RENDERER_COUNTER(particleNeedsRepaint, ParticleNeedsRepaint);
|
||||
MLN_LOAD_RENDERER_COUNTER(weatherColorUpdate, WeatherColorUpdate);
|
||||
MLN_LOAD_RENDERER_COUNTER(particleUpdate, ParticleUpdate);
|
||||
MLN_LOAD_RENDERER_COUNTER(particleSimulationAdvance, ParticleSimulationAdvance);
|
||||
MLN_LOAD_RENDERER_COUNTER(weatherColorDrawableRebuild, WeatherColorDrawableRebuild);
|
||||
MLN_LOAD_RENDERER_COUNTER(particleDrawableRebuild, ParticleDrawableRebuild);
|
||||
MLN_LOAD_RENDERER_COUNTER(fieldTextureUpload, FieldTextureUpload);
|
||||
MLN_LOAD_RENDERER_COUNTER(gpuResourceCreate, GpuResourceCreate);
|
||||
MLN_LOAD_RENDERER_COUNTER(gpuResourceRelease, GpuResourceRelease);
|
||||
MLN_LOAD_RENDERER_COUNTER(trailTargetCreateOrResize, TrailTargetCreateOrResize);
|
||||
MLN_LOAD_RENDERER_COUNTER(v4ProjectionMaterialize, V4ProjectionMaterialize);
|
||||
MLN_LOAD_RENDERER_COUNTER(fieldWindowRevision, FieldWindowRevision);
|
||||
MLN_LOAD_RENDERER_COUNTER(fullRendererFrame, FullRendererFrame);
|
||||
MLN_LOAD_RENDERER_COUNTER(retainedUnderlayBuild, RetainedUnderlayBuild);
|
||||
MLN_LOAD_RENDERER_COUNTER(retainedUnderlayReuse, RetainedUnderlayReuse);
|
||||
MLN_LOAD_RENDERER_COUNTER(retainedUnderlayFallback, RetainedUnderlayFallback);
|
||||
MLN_LOAD_RENDERER_COUNTER(retainedUnderlayInvalidation, RetainedUnderlayInvalidation);
|
||||
MLN_LOAD_RENDERER_COUNTER(retainedUnderlayStaticGroupDraw, RetainedUnderlayStaticGroupDraw);
|
||||
MLN_LOAD_RENDERER_COUNTER(retainedDynamicGroupDraw, RetainedDynamicGroupDraw);
|
||||
MLN_LOAD_RENDERER_COUNTER(particleFastFrameHit, ParticleFastFrameHit);
|
||||
MLN_LOAD_RENDERER_COUNTER(particleFastFrameMiss, ParticleFastFrameMiss);
|
||||
MLN_LOAD_RENDERER_COUNTER(particleFullFrame, ParticleFullFrame);
|
||||
#undef MLN_LOAD_RENDERER_COUNTER
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
inline void incrementRendererDebugCounter(RendererDebugCounter) {}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
template <class Texture>
|
||||
constexpr bool weatherColorDrawableBindingsChanged(const Texture* previous0,
|
||||
const Texture* previous1,
|
||||
const Texture* next0,
|
||||
const Texture* next1,
|
||||
bool wrapsChanged) noexcept {
|
||||
return wrapsChanged || previous0 != next0 || previous1 != next1;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
271
include/mbgl/renderer/render_segment.hpp
Normal file
271
include/mbgl/renderer/render_segment.hpp
Normal file
@@ -0,0 +1,271 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
enum class RenderSegmentStrategy : uint8_t { Retained, Continuous };
|
||||
|
||||
inline const char* renderSegmentStrategyText(RenderSegmentStrategy strategy) noexcept {
|
||||
return strategy == RenderSegmentStrategy::Retained ? "RETAINED" : "CONTINUOUS";
|
||||
}
|
||||
|
||||
struct RenderSegmentDefinition {
|
||||
RenderSegmentStrategy strategy = RenderSegmentStrategy::Continuous;
|
||||
std::optional<std::string> startLayerId;
|
||||
|
||||
bool operator==(const RenderSegmentDefinition&) const = default;
|
||||
};
|
||||
|
||||
struct RenderSegmentPlan {
|
||||
uint32_t schemaVersion = 1;
|
||||
std::vector<RenderSegmentDefinition> segments;
|
||||
|
||||
bool operator==(const RenderSegmentPlan&) const = default;
|
||||
};
|
||||
|
||||
struct StyleLayerDiagnostics {
|
||||
int32_t layerIndex = -1;
|
||||
std::string layerId;
|
||||
std::string layerType;
|
||||
bool visible = false;
|
||||
std::optional<std::string> sourceId;
|
||||
std::optional<std::string> sourceLayer;
|
||||
|
||||
bool operator==(const StyleLayerDiagnostics&) const = default;
|
||||
};
|
||||
|
||||
// The style layer list is the single dependency source for diagnostics and rendering.
|
||||
struct RenderSegmentDependencies {
|
||||
std::vector<std::string> sourceIds;
|
||||
std::vector<std::string> globalPrerequisites;
|
||||
bool unknown = false;
|
||||
};
|
||||
|
||||
inline RenderSegmentDependencies renderSegmentDependencies(const std::vector<StyleLayerDiagnostics>& layers,
|
||||
int32_t begin,
|
||||
int32_t endExclusive) {
|
||||
RenderSegmentDependencies result;
|
||||
if (begin < 0 || endExclusive < begin || endExclusive > static_cast<int32_t>(layers.size())) {
|
||||
result.unknown = true;
|
||||
return result;
|
||||
}
|
||||
std::set<std::string> sources;
|
||||
std::set<std::string> prerequisites;
|
||||
for (int32_t i = begin; i < endExclusive; ++i) {
|
||||
const auto& layer = layers[static_cast<std::size_t>(i)];
|
||||
if (layer.sourceId) sources.insert(*layer.sourceId);
|
||||
if (layer.layerType == "symbol") prerequisites.insert("symbol/glyph/image/placement");
|
||||
if (layer.layerType == "custom" || layer.layerType == "custom-drawable") result.unknown = true;
|
||||
if (layer.layerType != "background" && layer.layerType != "custom" &&
|
||||
layer.layerType != "custom-drawable" && !layer.sourceId) result.unknown = true;
|
||||
}
|
||||
result.sourceIds.assign(sources.begin(), sources.end());
|
||||
result.globalPrerequisites.assign(prerequisites.begin(), prerequisites.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
class RenderSegmentRevisionBook {
|
||||
public:
|
||||
void advanceGlobal() noexcept { ++globalEpoch; }
|
||||
void advanceSource(const std::string& sourceId) { sourceRevisions[sourceId] = ++sequence; }
|
||||
std::optional<uint64_t> revisionFor(const RenderSegmentDependencies& dependencies) const {
|
||||
if (dependencies.unknown) return std::nullopt;
|
||||
uint64_t revision = globalEpoch;
|
||||
for (const auto& id : dependencies.sourceIds) {
|
||||
const auto found = sourceRevisions.find(id);
|
||||
if (found == sourceRevisions.end()) return std::nullopt;
|
||||
for (const unsigned char ch : id) revision = (revision ^ ch) * 1099511628211ULL;
|
||||
revision = (revision ^ found->second) * 1099511628211ULL;
|
||||
}
|
||||
return revision;
|
||||
}
|
||||
private:
|
||||
uint64_t globalEpoch = 1;
|
||||
uint64_t sequence = 1;
|
||||
// Retain tombstones: remove + recreate of the same ID always receives a newer identity.
|
||||
std::unordered_map<std::string, uint64_t> sourceRevisions;
|
||||
};
|
||||
|
||||
struct RenderSegmentSourceStatus { bool loaded = false; bool fading = false; };
|
||||
struct RenderSegmentSourceAssessment {
|
||||
bool ready = false;
|
||||
bool stable = false;
|
||||
const char* reason = "unknown-dependency";
|
||||
};
|
||||
|
||||
template <typename Lookup>
|
||||
RenderSegmentSourceAssessment assessRenderSegmentSources(const RenderSegmentDependencies& dependencies,
|
||||
Lookup lookup) {
|
||||
RenderSegmentSourceAssessment result;
|
||||
if (dependencies.unknown) return result;
|
||||
for (const auto& id : dependencies.sourceIds) {
|
||||
const std::optional<RenderSegmentSourceStatus> source = lookup(id);
|
||||
if (!source) { result.reason = "missing-source"; return result; }
|
||||
if (!source->loaded) { result.reason = "source-not-loaded"; return result; }
|
||||
if (source->fading) { result.ready = true; result.reason = "source-fading"; return result; }
|
||||
}
|
||||
result.ready = true;
|
||||
result.stable = true;
|
||||
result.reason = "none";
|
||||
return result;
|
||||
}
|
||||
|
||||
struct RenderSourceReadinessDiagnostics {
|
||||
std::string sourceId;
|
||||
std::string sourceType;
|
||||
bool enabled = false;
|
||||
bool isLoaded = false;
|
||||
bool tileBacked = false;
|
||||
std::optional<uint32_t> tileCount;
|
||||
std::optional<uint32_t> incompleteTileCount;
|
||||
std::optional<uint32_t> loadedTileCount;
|
||||
bool hasFadingTiles = false;
|
||||
uint64_t lastSourceMutationSequence = 0;
|
||||
uint64_t lastTileChangedSequence = 0;
|
||||
uint64_t lastTileActionSequence = 0;
|
||||
std::string lastTileAction{"none"};
|
||||
std::string lastMutationReason{"none"};
|
||||
|
||||
bool operator==(const RenderSourceReadinessDiagnostics&) const = default;
|
||||
};
|
||||
|
||||
struct RenderMutationDiagnostics {
|
||||
uint64_t sequence = 0;
|
||||
uint64_t contentRevision = 0;
|
||||
std::string eventType{"none"};
|
||||
std::string reason{"none"};
|
||||
std::optional<std::string> sourceId;
|
||||
std::vector<uint32_t> affectedSegmentIndices;
|
||||
std::string dependencyScope{"none"};
|
||||
|
||||
bool operator==(const RenderMutationDiagnostics&) const = default;
|
||||
};
|
||||
|
||||
struct RendererReadinessDiagnostics {
|
||||
bool styleLoaded = false;
|
||||
bool imageManagerLoaded = false;
|
||||
bool renderOrchestratorLoaded = false;
|
||||
bool spriteLoaded = false;
|
||||
bool hasFadingTiles = false;
|
||||
uint64_t contentRevision = 0;
|
||||
std::vector<RenderSourceReadinessDiagnostics> sources;
|
||||
RenderMutationDiagnostics lastMutation;
|
||||
|
||||
bool operator==(const RendererReadinessDiagnostics&) const = default;
|
||||
};
|
||||
|
||||
struct RenderSegmentCacheDiagnostics {
|
||||
std::string cacheState{"disabled"};
|
||||
uint64_t contentRevision = 0;
|
||||
uint64_t build = 0;
|
||||
uint64_t reuse = 0;
|
||||
uint64_t invalidation = 0;
|
||||
uint64_t fallbackCount = 0;
|
||||
std::string finalDecision{"disabled"};
|
||||
std::string lastFallbackReason{"none"};
|
||||
bool sourceReady = false;
|
||||
bool sourceStable = false;
|
||||
bool supported = false;
|
||||
uint64_t sourceRevision = 0;
|
||||
std::string lastInvalidationReason{"none"};
|
||||
uint64_t textureCreate = 0;
|
||||
uint64_t compositeCreate = 0;
|
||||
uint64_t resourceRelease = 0;
|
||||
};
|
||||
|
||||
struct RenderSegmentDiagnosticsEntry {
|
||||
uint32_t segmentIndex = 0;
|
||||
RenderSegmentStrategy strategy = RenderSegmentStrategy::Continuous;
|
||||
std::optional<std::string> configuredStartLayerId;
|
||||
int32_t beginIndex = -1;
|
||||
int32_t endExclusive = -1;
|
||||
std::optional<std::string> resolvedStartLayerId;
|
||||
std::optional<std::string> resolvedEndLayerId;
|
||||
std::vector<StyleLayerDiagnostics> layers;
|
||||
std::vector<std::string> dependentSourceIds;
|
||||
std::vector<std::string> unknownDependencies;
|
||||
std::optional<RenderSegmentCacheDiagnostics> cache;
|
||||
};
|
||||
|
||||
struct RenderSegmentDiagnostics {
|
||||
uint32_t schemaVersion = 1;
|
||||
bool enabled = true;
|
||||
bool configured = false;
|
||||
bool valid = false;
|
||||
std::string configSource{"unconfigured/legacy"};
|
||||
uint64_t configRevision = 0;
|
||||
uint64_t topologyRevision = 0;
|
||||
uint32_t styleLayerCount = 0;
|
||||
uint32_t segmentCount = 0;
|
||||
uint32_t retainedSegmentCount = 0;
|
||||
uint32_t continuousSegmentCount = 0;
|
||||
bool coverageComplete = false;
|
||||
bool fallback = false;
|
||||
std::string lastReason{"not-configured"};
|
||||
bool fastPathEligible = false;
|
||||
bool fastPathActive = false;
|
||||
std::string fastPathFallbackReason{"no-prepared-tree"};
|
||||
uint64_t fastPathHit = 0;
|
||||
uint64_t fastPathMiss = 0;
|
||||
uint64_t fullCpuFrame = 0;
|
||||
bool styleLoaded = false;
|
||||
bool imageManagerLoaded = false;
|
||||
bool renderOrchestratorLoaded = false;
|
||||
bool spriteLoaded = false;
|
||||
bool hasFadingTiles = false;
|
||||
uint64_t contentRevision = 0;
|
||||
std::vector<RenderSourceReadinessDiagnostics> sources;
|
||||
RenderMutationDiagnostics lastMutation;
|
||||
std::vector<StyleLayerDiagnostics> styleLayers;
|
||||
std::vector<RenderSegmentDiagnosticsEntry> segments;
|
||||
};
|
||||
|
||||
struct ResolvedRenderSegment {
|
||||
uint32_t segmentIndex = 0;
|
||||
RenderSegmentStrategy strategy = RenderSegmentStrategy::Continuous;
|
||||
std::optional<std::string> configuredStartLayerId;
|
||||
int32_t beginIndex = 0;
|
||||
int32_t endExclusive = 0;
|
||||
|
||||
bool operator==(const ResolvedRenderSegment&) const = default;
|
||||
};
|
||||
|
||||
struct ResolvedRenderSegmentPlan {
|
||||
bool configured = false;
|
||||
bool valid = false;
|
||||
std::string configSource{"unconfigured/legacy"};
|
||||
uint64_t configRevision = 0;
|
||||
std::string reason{"not-configured"};
|
||||
int32_t layerCount = 0;
|
||||
std::vector<ResolvedRenderSegment> segments;
|
||||
|
||||
bool operator==(const ResolvedRenderSegmentPlan&) const = default;
|
||||
};
|
||||
|
||||
// This is the main-buffer traversal used by the renderer. A failed retained range
|
||||
// is drawn live exactly once; neighboring cached ranges remain composites.
|
||||
template <typename Cached, typename Composite, typename Live>
|
||||
void visitRenderSegmentExecution(const ResolvedRenderSegmentPlan& plan,
|
||||
Cached cached,
|
||||
Composite composite,
|
||||
Live live) {
|
||||
if (!plan.valid) {
|
||||
live(0, plan.layerCount);
|
||||
return;
|
||||
}
|
||||
for (const auto& segment : plan.segments) {
|
||||
if (segment.strategy == RenderSegmentStrategy::Retained && cached(segment.segmentIndex)) {
|
||||
composite(segment.segmentIndex);
|
||||
} else {
|
||||
live(segment.beginIndex, segment.endExclusive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
74
include/mbgl/renderer/retained_underlay.hpp
Normal file
74
include/mbgl/renderer/retained_underlay.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
struct RetainedUnderlayDiagnostics {
|
||||
bool enabled = true;
|
||||
uint64_t build = 0;
|
||||
uint64_t reuse = 0;
|
||||
uint64_t fallback = 0;
|
||||
uint64_t invalidation = 0;
|
||||
std::string lastReason;
|
||||
bool rangeConfigured = false;
|
||||
bool rangeValid = false;
|
||||
std::string startLayerId;
|
||||
std::string endLayerId;
|
||||
int32_t startIndex = -1;
|
||||
int32_t endIndex = -1;
|
||||
int32_t continuousBegin = -1;
|
||||
int32_t continuousEndExclusive = -1;
|
||||
bool bottomPresent = false;
|
||||
std::string bottomCacheState{"disabled"};
|
||||
uint64_t bottomBuild = 0;
|
||||
uint64_t bottomReuse = 0;
|
||||
uint64_t bottomInvalidation = 0;
|
||||
std::string bottomLastInvalidationReason{"none"};
|
||||
uint64_t bottomTextureCreate = 0;
|
||||
uint64_t bottomCompositeCreate = 0;
|
||||
uint64_t bottomResourceRelease = 0;
|
||||
bool topPresent = false;
|
||||
std::string topCacheState{"disabled"};
|
||||
uint64_t topBuild = 0;
|
||||
uint64_t topReuse = 0;
|
||||
uint64_t topInvalidation = 0;
|
||||
std::string topLastInvalidationReason{"none"};
|
||||
uint64_t topTextureCreate = 0;
|
||||
uint64_t topCompositeCreate = 0;
|
||||
uint64_t topResourceRelease = 0;
|
||||
bool rangeFallback = false;
|
||||
bool fastPathEligible = false;
|
||||
bool fastPathActive = false;
|
||||
std::string fastPathFallbackReason{"no-prepared-tree"};
|
||||
uint64_t fastPathHit = 0;
|
||||
uint64_t fastPathMiss = 0;
|
||||
uint64_t fullCpuFrame = 0;
|
||||
};
|
||||
|
||||
struct ContinuousLayerRangeConfig {
|
||||
bool configured = false;
|
||||
std::string startLayerId;
|
||||
std::string endLayerId;
|
||||
uint64_t revision = 0;
|
||||
};
|
||||
|
||||
struct ContinuousLayerRangeResolution {
|
||||
bool configured = false;
|
||||
bool valid = false;
|
||||
std::string startLayerId;
|
||||
std::string endLayerId;
|
||||
int32_t startIndex = -1;
|
||||
int32_t endIndex = -1;
|
||||
uint64_t configRevision = 0;
|
||||
std::string reason{"not-configured"};
|
||||
|
||||
int32_t begin() const noexcept { return startIndex; }
|
||||
int32_t endExclusive() const noexcept { return valid ? endIndex + 1 : -1; }
|
||||
bool bottomPresent() const noexcept { return valid && startIndex > 0; }
|
||||
bool topPresent(int32_t layerCount) const noexcept { return valid && endExclusive() < layerCount; }
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
@@ -34,8 +34,8 @@ layout (std140) uniform ParticleGpuValidationUBO {
|
||||
|
||||
uniform sampler2D u_state_texture;
|
||||
uniform sampler2D u_previous;
|
||||
uniform sampler2D u_field_texture;
|
||||
uniform sampler2D u_next_field_texture;
|
||||
uniform highp sampler2D u_field_texture;
|
||||
uniform highp sampler2D u_next_field_texture;
|
||||
|
||||
vec2 fieldUv(vec2 world) {
|
||||
return vec2((world.x - u_field_bounds.x) / (u_field_bounds.z - u_field_bounds.x),
|
||||
@@ -47,8 +47,8 @@ bool validFieldUv(vec2 uv) {
|
||||
}
|
||||
|
||||
bool resolveAdvection(vec4 field0, vec4 field1, out vec2 advection) {
|
||||
vec2 mixed = mix((field0.rg * 2.0 - 1.0) * u_field_scale.x,
|
||||
(field1.rg * 2.0 - 1.0) * u_field_scale.y,
|
||||
vec2 mixed = mix(((floor(field0.rg * 255.0 + 0.5) - 128.0) / 127.0) * u_field_scale.x,
|
||||
((floor(field1.rg * 255.0 + 0.5) - 128.0) / 127.0) * u_field_scale.y,
|
||||
u_marker.x);
|
||||
if (any(isnan(mixed)) || any(greaterThan(abs(mixed), vec2(3.4e38)))) return false;
|
||||
if (u_advection.x > 0.5) {
|
||||
@@ -116,7 +116,11 @@ void main() {
|
||||
}
|
||||
}
|
||||
)";
|
||||
static constexpr const char* fragment = R"(layout (std140) uniform ParticleGpuValidationUBO {
|
||||
static constexpr const char* fragment = R"(// The shared GL prelude defaults fragment float to mediump. Byte reconstruction
|
||||
// from RGBA8 UNORM and Mercator state require highp on ES 3.x.
|
||||
precision highp float;
|
||||
|
||||
layout (std140) uniform ParticleGpuValidationUBO {
|
||||
highp vec4 u_pass;
|
||||
highp vec4 u_marker;
|
||||
highp vec4 u_mask;
|
||||
@@ -135,8 +139,8 @@ void main() {
|
||||
|
||||
uniform sampler2D u_previous;
|
||||
uniform sampler2D u_mask_texture;
|
||||
uniform sampler2D u_field_texture;
|
||||
uniform sampler2D u_next_field_texture;
|
||||
uniform highp sampler2D u_field_texture;
|
||||
uniform highp sampler2D u_next_field_texture;
|
||||
uniform sampler2D u_state_texture;
|
||||
in vec2 v_uv;
|
||||
in vec2 v_world;
|
||||
@@ -195,8 +199,8 @@ bool fieldAdvectionAt(vec2 position, out vec2 advection) {
|
||||
vec2 uv = fieldUv(position);
|
||||
vec4 field0 = texture(u_field_texture, uv);
|
||||
vec4 field1 = texture(u_next_field_texture, uv);
|
||||
vec2 mixed = mix((field0.rg * 2.0 - 1.0) * u_field_scale.x,
|
||||
(field1.rg * 2.0 - 1.0) * u_field_scale.y,
|
||||
vec2 mixed = mix(((floor(field0.rg * 255.0 + 0.5) - 128.0) / 127.0) * u_field_scale.x,
|
||||
((floor(field1.rg * 255.0 + 0.5) - 128.0) / 127.0) * u_field_scale.y,
|
||||
u_marker.x);
|
||||
if (!validFieldUv(uv) || field0.a < 0.5 || field1.a < 0.5 || any(isnan(mixed)) ||
|
||||
any(greaterThan(abs(mixed), vec2(3.4e38)))) return false;
|
||||
@@ -236,14 +240,14 @@ void main() {
|
||||
if (field0.a < 0.5 || field1.a < 0.5) discard;
|
||||
float fieldValue;
|
||||
if (u_weather_ramp.y > 0.5) {
|
||||
vec2 vectorValue = mix((field0.rg * 2.0 - 1.0) * u_field_scale.x,
|
||||
(field1.rg * 2.0 - 1.0) * u_field_scale.y,
|
||||
vec2 vectorValue = mix(((floor(field0.rg * 255.0 + 0.5) - 128.0) / 127.0) * u_field_scale.x,
|
||||
((floor(field1.rg * 255.0 + 0.5) - 128.0) / 127.0) * u_field_scale.y,
|
||||
u_marker.x);
|
||||
if (any(isnan(vectorValue))) discard;
|
||||
fieldValue = length(vectorValue);
|
||||
} else {
|
||||
fieldValue = mix((field0.r * 2.0 - 1.0) * u_field_scale.x,
|
||||
(field1.r * 2.0 - 1.0) * u_field_scale.y,
|
||||
fieldValue = mix(((floor(field0.r * 255.0 + 0.5) - 128.0) / 127.0) * u_field_scale.x,
|
||||
((floor(field1.r * 255.0 + 0.5) - 128.0) / 127.0) * u_field_scale.y,
|
||||
u_marker.x);
|
||||
if (isnan(fieldValue)) discard;
|
||||
}
|
||||
@@ -286,7 +290,7 @@ void main() {
|
||||
|
||||
if (!respawn) {
|
||||
float latitude = mercatorLatitudeRadians(position.y);
|
||||
vec2 displacement = vec2(advection.x * u_pass.y / EARTH_CIRCUMFERENCE_METERS,
|
||||
vec2 displacement = vec2(advection.x * u_pass.y / (EARTH_CIRCUMFERENCE_METERS * max(cos(latitude), 0.01)),
|
||||
-advection.y * u_pass.y /
|
||||
(EARTH_CIRCUMFERENCE_METERS * max(cos(latitude), 0.01)));
|
||||
vec2 nextPosition = position + displacement;
|
||||
@@ -337,10 +341,10 @@ void main() {
|
||||
// U/V remain canonical metres per second. u_pass.y is the explicit
|
||||
// visual simulation delta in seconds; the validation clock may run
|
||||
// faster than wall time, but it never changes the field contract.
|
||||
// Web Mercator x spans Earth's circumference; y additionally scales
|
||||
// with sec(latitude). V is north-positive while world y is southward.
|
||||
// Both east and north metres scale with sec(latitude) in Web Mercator.
|
||||
// V is north-positive while world y is southward.
|
||||
float latitude = mercatorLatitudeRadians(position.y);
|
||||
float dx = advection.x * u_pass.y / EARTH_CIRCUMFERENCE_METERS;
|
||||
float dx = advection.x * u_pass.y / (EARTH_CIRCUMFERENCE_METERS * max(cos(latitude), 0.01));
|
||||
float dy = -advection.y * u_pass.y /
|
||||
(EARTH_CIRCUMFERENCE_METERS * max(cos(latitude), 0.01));
|
||||
position = position + vec2(dx, dy);
|
||||
|
||||
29
include/mbgl/shaders/gl/retained_underlay_composite.hpp
Normal file
29
include/mbgl/shaders/gl/retained_underlay_composite.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Generated code, do not modify this file!
|
||||
#pragma once
|
||||
#include <mbgl/shaders/shader_source.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
template <>
|
||||
struct ShaderSource<BuiltIn::RetainedUnderlayCompositeShader, gfx::Backend::Type::OpenGL> {
|
||||
static constexpr const char* name = "RetainedUnderlayCompositeShader";
|
||||
static constexpr const char* vertex = R"(layout (location = 0) in vec2 a_pos;
|
||||
out vec2 v_uv;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(a_pos.x * 2.0 - 1.0, 1.0 - a_pos.y * 2.0, 0.0, 1.0);
|
||||
v_uv = vec2(a_pos.x, 1.0 - a_pos.y);
|
||||
}
|
||||
)";
|
||||
static constexpr const char* fragment = R"(in vec2 v_uv;
|
||||
uniform sampler2D u_image;
|
||||
|
||||
void main() {
|
||||
fragColor = texture(u_image, v_uv);
|
||||
}
|
||||
)";
|
||||
};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
@@ -156,6 +156,13 @@ struct ShaderInfo<BuiltIn::ParticleGpuValidationShader, gfx::Backend::Type::Open
|
||||
static const std::vector<TextureInfo> textures;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ShaderInfo<BuiltIn::RetainedUnderlayCompositeShader, gfx::Backend::Type::OpenGL> {
|
||||
static const std::vector<AttributeInfo> attributes;
|
||||
static const std::vector<UniformBlockInfo> uniformBlocks;
|
||||
static const std::vector<TextureInfo> textures;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ShaderInfo<BuiltIn::HillshadePrepareShader, gfx::Backend::Type::OpenGL> {
|
||||
static const std::vector<AttributeInfo> attributes;
|
||||
|
||||
@@ -46,7 +46,7 @@ float2 fieldUv(float2 world, device const ParticleGpuValidationUBO& u) {
|
||||
bool validFieldUv(float2 uv) { return all(uv >= float2(0.0)) && all(uv <= float2(1.0)); }
|
||||
bool finite2(float2 value) { return all(isfinite(value)) && all(abs(value) <= float2(3.4e38)); }
|
||||
bool resolveAdvection(float4 a, float4 b, device const ParticleGpuValidationUBO& u, thread float2& out) {
|
||||
float2 mixed=mix((a.rg*2.0-1.0)*u.fieldScale.x,(b.rg*2.0-1.0)*u.fieldScale.y,u.marker.x);
|
||||
float2 mixed=mix(((floor(a.rg*255.0+0.5)-128.0)/127.0)*u.fieldScale.x,((floor(b.rg*255.0+0.5)-128.0)/127.0)*u.fieldScale.y,u.marker.x);
|
||||
if (!finite2(mixed)) return false;
|
||||
if (u.advection.x > .5) { float l=length(mixed); if (!isfinite(l)||l>3.4e38||l<=u.advection.z) return false; out=mixed/l*u.advection.y; }
|
||||
else { out=mixed; return length(out)>0.00001; }
|
||||
@@ -126,12 +126,12 @@ float4 fragment fragmentMain(FragmentStage in [[stage_in]], device const Particl
|
||||
sampler previousSampler [[sampler(0)]], sampler maskSampler [[sampler(1)]], sampler fieldSampler [[sampler(2)]], sampler nextFieldSampler [[sampler(3)]], sampler stateSampler [[sampler(4)]]) {
|
||||
float mode=u.pass.x;
|
||||
if(mode>5.5){if(in.valid<.5)discard_fragment();float speed=length(in.velocity);return float4(1,1,clamp(speed/30.,0.,1.),1);}
|
||||
if(mode>4.5){float2 uv=fieldUv(in.world,u); if(!validFieldUv(uv)) discard_fragment(); float4 a=field.sample(fieldSampler,uv),b=nextField.sample(nextFieldSampler,uv);if(a.a<.5||b.a<.5)discard_fragment();float value=u.weatherRamp.y>.5?length(mix((a.rg*2.-1.)*u.fieldScale.x,(b.rg*2.-1.)*u.fieldScale.y,u.marker.x)):mix((a.r*2.-1.)*u.fieldScale.x,(b.r*2.-1.)*u.fieldScale.y,u.marker.x);if(!isfinite(value))discard_fragment();float4 c=ramp(value,u);float alpha=c.a*u.visual.x;return float4(c.rgb*alpha,alpha);}
|
||||
if(mode>4.5){float2 uv=fieldUv(in.world,u); if(!validFieldUv(uv)) discard_fragment(); float4 a=field.sample(fieldSampler,uv),b=nextField.sample(nextFieldSampler,uv);if(a.a<.5||b.a<.5)discard_fragment();float value=u.weatherRamp.y>.5?length(mix(((floor(a.rg*255.+.5)-128.)/127.)*u.fieldScale.x,((floor(b.rg*255.+.5)-128.)/127.)*u.fieldScale.y,u.marker.x)):mix(((floor(a.r*255.+.5)-128.)/127.)*u.fieldScale.x,((floor(b.r*255.+.5)-128.)/127.)*u.fieldScale.y,u.marker.x);if(!isfinite(value))discard_fragment();float4 c=ramp(value,u);float alpha=c.a*u.visual.x;return float4(c.rgb*alpha,alpha);}
|
||||
float4 old=previous.sample(previousSampler,in.uv);
|
||||
if(mode<.5){
|
||||
float2 position=u.mask.w>.5?mix(u.fieldBounds.xy,u.fieldBounds.zw,in.uv):old.rg; float age=old.b; bool initial=u.mask.w>.5; bool respawn=initial; float2 advection; float maxAge=u.marker.z*mix(.75,1.25,particleHash(in.uv+float2(17.31,83.17)));
|
||||
if(u.mask.z>.5){age=initial?particleHash(in.uv+float2(71.19,29.47))*maxAge:old.b+u.pass.w;float2 uv=fieldUv(position,u);float4 a=field.sample(fieldSampler,uv),b=nextField.sample(nextFieldSampler,uv);bool ok=validFieldUv(uv)&&a.a>=.5&&b.a>=.5&&resolveAdvection(a,b,u,advection);respawn=initial||!ok||age>=maxAge||(u.fieldScale.z>.5&&!initial&&(position.x<u.respawnBounds.x||position.x>u.respawnBounds.z||position.y<u.respawnBounds.y||position.y>u.respawnBounds.w));if(!respawn){float lat=mercatorLatitudeRadians(position.y);float2 n=position+float2(advection.x*u.pass.y/40075016.68557849,-advection.y*u.pass.y/(40075016.68557849*max(cos(lat),.01)));float2 nuv=fieldUv(n,u);float4 na=field.sample(fieldSampler,nuv),nb=nextField.sample(nextFieldSampler,nuv);if(validFieldUv(nuv)&&na.a>=.5&&nb.a>=.5&&resolveAdvection(na,nb,u,advection))position=n;else respawn=true;}if(respawn){bool found=false;for(int i=0;i<3;i++){float2 c=respawnSeed(in.uv,old.rg,float(i),u);float2 cuv=fieldUv(c,u);float4 ca=field.sample(fieldSampler,cuv),cb=nextField.sample(nextFieldSampler,cuv);if(validFieldUv(cuv)&&ca.a>=.5&&cb.a>=.5&&resolveAdvection(ca,cb,u,advection)){position=c;found=true;break;}}if(!found)position=respawnSeed(in.uv,old.rg,3.,u);if(!initial)age=0.;}return float4(position.x,position.y,age,respawn?0.:1.);}
|
||||
float2 uv=fieldUv(position,u);float4 a=field.sample(fieldSampler,uv),b=nextField.sample(nextFieldSampler,uv);if(!validFieldUv(uv)||a.a<.5||b.a<.5||!resolveAdvection(a,b,u,advection)){position=mix(u.fieldBounds.xy,u.fieldBounds.zw,in.uv);advection=float2(0);}float lat=mercatorLatitudeRadians(position.y);position+=float2(advection.x*u.pass.y/40075016.68557849,-advection.y*u.pass.y/(40075016.68557849*max(cos(lat),.01)));if(!validFieldUv(fieldUv(position,u))||!finite2(advection))position=mix(u.fieldBounds.xy,u.fieldBounds.zw,in.uv);return float4(position.x,position.y,fract(old.b+.03125*u.pass.y),1.);
|
||||
if(u.mask.z>.5){age=initial?particleHash(in.uv+float2(71.19,29.47))*maxAge:old.b+u.pass.w;float2 uv=fieldUv(position,u);float4 a=field.sample(fieldSampler,uv),b=nextField.sample(nextFieldSampler,uv);bool ok=validFieldUv(uv)&&a.a>=.5&&b.a>=.5&&resolveAdvection(a,b,u,advection);respawn=initial||!ok||age>=maxAge||(u.fieldScale.z>.5&&!initial&&(position.x<u.respawnBounds.x||position.x>u.respawnBounds.z||position.y<u.respawnBounds.y||position.y>u.respawnBounds.w));if(!respawn){float lat=mercatorLatitudeRadians(position.y);float2 n=position+float2(advection.x*u.pass.y/(40075016.68557849*max(cos(lat),.01)),-advection.y*u.pass.y/(40075016.68557849*max(cos(lat),.01)));float2 nuv=fieldUv(n,u);float4 na=field.sample(fieldSampler,nuv),nb=nextField.sample(nextFieldSampler,nuv);if(validFieldUv(nuv)&&na.a>=.5&&nb.a>=.5&&resolveAdvection(na,nb,u,advection))position=n;else respawn=true;}if(respawn){bool found=false;for(int i=0;i<3;i++){float2 c=respawnSeed(in.uv,old.rg,float(i),u);float2 cuv=fieldUv(c,u);float4 ca=field.sample(fieldSampler,cuv),cb=nextField.sample(nextFieldSampler,cuv);if(validFieldUv(cuv)&&ca.a>=.5&&cb.a>=.5&&resolveAdvection(ca,cb,u,advection)){position=c;found=true;break;}}if(!found)position=respawnSeed(in.uv,old.rg,3.,u);if(!initial)age=0.;}return float4(position.x,position.y,age,respawn?0.:1.);}
|
||||
float2 uv=fieldUv(position,u);float4 a=field.sample(fieldSampler,uv),b=nextField.sample(nextFieldSampler,uv);if(!validFieldUv(uv)||a.a<.5||b.a<.5||!resolveAdvection(a,b,u,advection)){position=mix(u.fieldBounds.xy,u.fieldBounds.zw,in.uv);advection=float2(0);}float lat=mercatorLatitudeRadians(position.y);position+=float2(advection.x*u.pass.y/(40075016.68557849*max(cos(lat),.01)),-advection.y*u.pass.y/(40075016.68557849*max(cos(lat),.01)));if(!validFieldUv(fieldUv(position,u))||!finite2(advection))position=mix(u.fieldBounds.xy,u.fieldBounds.zw,in.uv);return float4(position.x,position.y,fract(old.b+.03125*u.pass.y),1.);
|
||||
}
|
||||
if(mode<1.5){if(u.mask.z>.5){float f=pow(clamp(u.visual.w,.0001,1.),u.pass.y*u.marker.w);return float4(old.r*f,0,0,1);}float marker=0.;float2 samples[4]={float2(.125),float2(.375),float2(.625),float2(.875)};for(int i=0;i<4;i++)marker=max(marker,1.-smoothstep(0.,u.marker.z,distance(in.uv,state.sample(stateSampler,samples[i]).rg)));return float4(min(old.rgb*u.marker.w+marker*float3(.08,.55,1),float3(1)),1);}
|
||||
if(mode>3.5){if(in.valid<.5)discard_fragment();float speed=length(in.velocity);return float4(1,1,clamp(speed/30.,0.,1.),1);}
|
||||
|
||||
34
include/mbgl/shaders/mtl/retained_underlay_composite.hpp
Normal file
34
include/mbgl/shaders/mtl/retained_underlay_composite.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/shaders/shader_source.hpp>
|
||||
#include <mbgl/shaders/mtl/shader_program.hpp>
|
||||
|
||||
namespace mbgl::shaders {
|
||||
|
||||
template <>
|
||||
struct ShaderSource<BuiltIn::RetainedUnderlayCompositeShader, gfx::Backend::Type::Metal> {
|
||||
static constexpr auto name = "RetainedUnderlayCompositeShader";
|
||||
static constexpr auto vertexMainFunction = "vertexMain";
|
||||
static constexpr auto fragmentMainFunction = "fragmentMain";
|
||||
static const std::array<AttributeInfo, 1> attributes;
|
||||
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
|
||||
static const std::array<TextureInfo, 1> textures;
|
||||
static constexpr auto prelude = R"()";
|
||||
static constexpr auto source = R"(
|
||||
struct VertexStage { short2 pos [[attribute(0)]]; };
|
||||
struct FragmentStage { float4 position [[position, invariant]]; float2 uv; };
|
||||
|
||||
FragmentStage vertex vertexMain(thread const VertexStage in [[stage_in]]) {
|
||||
const float2 p = float2(in.pos);
|
||||
return { .position = float4(p.x * 2.0 - 1.0, 1.0 - p.y * 2.0, 0.0, 1.0), .uv = p };
|
||||
}
|
||||
|
||||
half4 fragment fragmentMain(FragmentStage in [[stage_in]],
|
||||
texture2d<float, access::sample> image [[texture(0)]],
|
||||
sampler imageSampler [[sampler(0)]]) {
|
||||
return half4(image.sample(imageSampler, in.uv));
|
||||
}
|
||||
)";
|
||||
};
|
||||
|
||||
} // namespace mbgl::shaders
|
||||
@@ -347,6 +347,11 @@ enum {
|
||||
particleGpuValidationTextureCount
|
||||
};
|
||||
|
||||
enum {
|
||||
idRetainedUnderlayCompositeTexture,
|
||||
retainedUnderlayCompositeTextureCount
|
||||
};
|
||||
|
||||
enum {
|
||||
idHillshadeImageTexture,
|
||||
hillshadeTextureCount
|
||||
@@ -392,6 +397,7 @@ static constexpr uint32_t maxTextureCountPerShader = std::max({static_cast<uint3
|
||||
static_cast<uint32_t>(fillExtrusionTextureCount),
|
||||
static_cast<uint32_t>(heatmapTextureCount),
|
||||
static_cast<uint32_t>(particleGpuValidationTextureCount),
|
||||
static_cast<uint32_t>(retainedUnderlayCompositeTextureCount),
|
||||
static_cast<uint32_t>(hillshadeTextureCount),
|
||||
static_cast<uint32_t>(colorReliefTextureCount),
|
||||
static_cast<uint32_t>(lineTextureCount),
|
||||
@@ -410,6 +416,11 @@ enum {
|
||||
particleGpuValidationVertexAttributeCount
|
||||
};
|
||||
|
||||
enum {
|
||||
idRetainedUnderlayCompositePosVertexAttribute,
|
||||
retainedUnderlayCompositeVertexAttributeCount
|
||||
};
|
||||
|
||||
enum {
|
||||
idCirclePosVertexAttribute,
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <mbgl/shaders/gl/heatmap.hpp>
|
||||
#include <mbgl/shaders/gl/heatmap_texture.hpp>
|
||||
#include <mbgl/shaders/gl/particle_gpu_validation.hpp>
|
||||
#include <mbgl/shaders/gl/retained_underlay_composite.hpp>
|
||||
#include <mbgl/shaders/gl/hillshade_prepare.hpp>
|
||||
#include <mbgl/shaders/gl/color_relief.hpp>
|
||||
#include <mbgl/shaders/gl/hillshade.hpp>
|
||||
|
||||
@@ -31,6 +31,7 @@ enum class BuiltIn {
|
||||
HeatmapShader,
|
||||
HeatmapTextureShader,
|
||||
ParticleGpuValidationShader,
|
||||
RetainedUnderlayCompositeShader,
|
||||
HillshadePrepareShader,
|
||||
ColorReliefShader,
|
||||
HillshadeShader,
|
||||
|
||||
@@ -238,10 +238,17 @@ public:
|
||||
void setForecastPlaybackState(particle::ForecastPlaybackState);
|
||||
bool setForecastPlaybackSpeed(float);
|
||||
bool advanceForecastClock(uint64_t elapsedMs);
|
||||
// Called once per Map update on the owner thread; render layers only read
|
||||
// the resulting immutable projection and never advance this clock.
|
||||
bool tickForecastClock(std::chrono::steady_clock::time_point now);
|
||||
particle::ColorProjectionResult resolveColorProjection() const;
|
||||
particle::ParticleProjectionResult resolveParticleProjection() const;
|
||||
bool clearEncodedField(const std::string& fieldKey);
|
||||
void clearAllEncodedFields();
|
||||
// Explicit terminal cleanup for an attached WeatherRuntime. Unlike an ordinary
|
||||
// V4 window clear, this also drops bindings, forecast clock and V3 host ingress.
|
||||
// Source and Style layers remain installed for the next runtime generation.
|
||||
bool resetWeatherRuntimeState();
|
||||
// Producer-facing timestamp seek. Core alone selects T0/T1 and MIX.
|
||||
bool seekRuntimeTimelineToTimestamp(uint64_t timestamp);
|
||||
// Low-frequency JNI commands for the Source-owned validation timeline.
|
||||
@@ -302,6 +309,10 @@ private:
|
||||
// across clear operations and resolve their own field-keyed windows.
|
||||
particle::EncodedFieldStore encodedFieldStore;
|
||||
particle::ForecastClock forecastClock;
|
||||
std::chrono::steady_clock::time_point lastForecastTick{};
|
||||
// A Source change synchronously invokes Style::onUpdate, which re-enters
|
||||
// Map::onUpdate. Prevent a second forecast tick during the first notification.
|
||||
bool forecastTickInProgress = false;
|
||||
std::optional<particle::ColorBinding> colorBinding;
|
||||
std::optional<particle::ParticleBinding> particleBinding;
|
||||
mapbox::base::WeakPtrFactory<Source> weakFactory{this};
|
||||
|
||||
@@ -106,7 +106,10 @@ public:
|
||||
|
||||
void setDirtyState() override {}
|
||||
|
||||
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType, bool, bool);
|
||||
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size,
|
||||
gfx::TextureChannelDataType,
|
||||
bool,
|
||||
bool) override;
|
||||
|
||||
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType) override;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user