diff --git a/docs/50_Step记录/STEP-041_Android双时间片GPU时间插值_v1.md b/docs/50_Step记录/STEP-041_Android双时间片GPU时间插值_v1.md new file mode 100644 index 000000000..0907b4298 --- /dev/null +++ b/docs/50_Step记录/STEP-041_Android双时间片GPU时间插值_v1.md @@ -0,0 +1,24 @@ +# STEP-041 Android 双时间片 GPU 时间插值 + +- 状态:`IMPLEMENTED / WAITING FOR ANDROID VALIDATION` +- 前置:STEP-040(40-01)已在 Android OpenGL 手工验收为 PASS。 +- 目标:在唯一正式链路 `RasterArraySource -> RasterParticleLayer -> GPU Simulation -> Trail -> Composite` 中,把单 Field 扩展为 T0/T1 两个独立 GPU Field,并在 State pass 对速度作 GPU `mix`。 +- 实现提交:`4675a8a29dd6fc7b684bfb64913a8c9075ad8583`(`feat: add GPU particle time interpolation`)。 + +## 本轮实现 + +- `RasterArraySource` 支持可选 `next-fixture`、`next-metadata`、`next-field`,以同一 FileSource byte boundary 加载 T0/T1;两份快照必须具有相同 grid、bounds、NoData、Field 类型及 display domain。 +- `RasterParticleLayer` 新增常量 paint property `raster-particle-time-mix`(有限值,内部 clamp 至 `[0,1]`)。它仅更新既有 UBO,不能触发 Field upload 或 State/Trail 重建。 +- `RenderRasterParticleLayer` 维持一套既有 State A/B、Trail A/B、Mask 与 Composite,只将 Field GPU resource 扩为 T0/T1 两个 texture slot。窗口推进时会按 generation + fixtureID + dataVersion 匹配旧 slot;A/B -> B/C 复用 B、只上传 C,并不 reset simulation/trail。完全无重叠的新窗口仍走既有安全 reset/reseed。 +- State shader 分别 decode T0/T1 RG velocity 后使用 `mix(velocity0, velocity1, timeMix)`;没有 CPU 生成中间完整 Field。NoData alpha 在任一端无效时保持现有确定性 respawn。 +- Android Test App 新增 T0 EAST/T1 NORTH、MIX 0/50/100 与 ADVANCE B->C 入口;保留既有 ACTIVE/STATIC、mask、Camera、world-wrap 与 antimeridian 入口。 + +## 验证边界 + +- Linux OpenGL 受影响 Core 与 source test object:通过。 +- shader generator、二次生成稳定性、C++ format、`git diff --check`:通过。 +- 未运行 Android Gradle、ADB、APK 安装或设备测试;T1--T10 必须由用户在 Android OpenGL 上验证。 + +## 不在本 Step 范围 + +iOS/Metal、Vulkan、网络/真实气象数据、完整 forecast cache、Jest、最终性能基线均不在本任务范围。40-02 在 T1--T10 通过前不得标记 PASS。 diff --git a/docs/报告/40-02_Android双时间片GPU时间插值_Codex完成报告_v1.md b/docs/报告/40-02_Android双时间片GPU时间插值_Codex完成报告_v1.md new file mode 100644 index 000000000..b00915b1c --- /dev/null +++ b/docs/报告/40-02_Android双时间片GPU时间插值_Codex完成报告_v1.md @@ -0,0 +1,60 @@ +# 40-02 Android 双时间片 GPU 时间插值 Codex 完成报告 v1 + +## 结论 + +代码阶段完成,状态为 `IMPLEMENTED / WAITING FOR ANDROID VALIDATION`,不是 PASS。 + +- 实际开始 HEAD:`b0f855eb094c21eda9471088537a79f493f12dca`(`docs: record 40-01 closeout push`) +- 实际完成实现 HEAD:`4675a8a29dd6fc7b684bfb64913a8c9075ad8583`(`feat: add GPU particle time interpolation`);本报告作为紧随其后的文档收尾提交。 + +## 实现内容 + +1. 双时间片数据结构:`ParticleFieldTimeWindow` 包含 immutable T0/T1 `ParticleFieldSnapshot`。兼容性检查比较 grid width/height、bounds、NoData、fieldType 和 displayDomain;fixture identity/dataVersion 不作为兼容性条件,但仍作为 GPU slot identity。 +2. Source:`RasterArraySource` 保持原 `fixture/metadata/field`,并可接收 `next-fixture/next-metadata/next-field`。T0/T1 均经 FileSource byte boundary 与 adapter 校验;不兼容窗口报 source error。 +3. GPU T0/T1:同一 `ParticleGpuResources` 现在持有 `fieldTexture` 与 `nextFieldTexture`;未创建第二套 Layer、State、Trail、Mask 或 simulation。 +4. Shader:唯一源 `shaders/particle_gpu_validation.fragment.glsl` 采样两个 field texture,分别 decode 后执行 `mix(velocity0, velocity1, u_marker.x)`。`u_marker.x` 来自 `raster-particle-time-mix`,不是 CPU 中间 Field。 +5. 生命周期:初次 resource create 上传 T0、T1。仅 timeMix 更新时只写 UBO;Camera/transform 不走 Field source-change 分支。A/B -> B/C 如能匹配 B,则移动/复用其 GPU texture,只上传 C,且保留 simulation/trail;没有重叠的真实新窗口保留 40-01 的安全 reset/reseed 行为。 +6. Mask/ACTIVE/STATIC:共享 mask 未复制且 timeMix 不参与 mask upload。ACTIVE 仍是既有连续 simulation;STATIC 不因双 Field 请求连续 repaint。World wrap、antimeridian、rotation/viewport 与既有 State/Trail ping-pong 路径未改。 + +## 修改文件及目的 + +- `src/mbgl/particle/field/offline_fixture_particle_adapter.*`:time-window 兼容性模型。 +- `include/mbgl/style/sources/raster_array_source.hpp`、`src/mbgl/style/sources/raster_array_source*`、`src/mbgl/style/conversion/source.cpp`:T0/T1 source contract 与加载。 +- `include/mbgl/style/layers/raster_particle_layer.hpp`、`src/mbgl/style/layers/raster_particle_layer*`、`particle_render_layer_properties.hpp`:timeMix 常量 Layer property。 +- `render_raster_particle_layer.hpp`、`render_particle_validation_layer.cpp`:双 Field texture slot、复用/advance 与低频资源日志。 +- `shaders/particle_gpu_validation.fragment.glsl`、生成的 OpenGL header、shader defines/info:实际 GPU dual-field sampling。 +- Android validation Activity/layout/strings:T0/T1、mix、advance 手工入口。 +- `test/style/source.test.cpp`:compatible/incompatible time-window snapshot 定向测试。 + +## 日志与资源证明 + +`ParticleGPU` 日志记录 T0/T1 fixture/dataVersion、upload count、timeMix、窗口 advance、reuse、simulationRebuild 和 trailCleared。正常 timeMix 更新输出 `simulationRebuild=0 trailRebuild=0`;不在每帧输出。资源销毁沿用 `ParticleGpuResources` 既有析构路径。 + +## 静态/本地检查 + +- `node shaders/generate_shader_code.mjs`:通过;随后再次生成应无语义改动。 +- Linux OpenGL 精确 object compile:`raster_array_source.cpp`、`raster_particle_layer.cpp`、`render_particle_validation_layer.cpp`、`shader_info.cpp`、adapter 及 `test/style/source.test.cpp`:通过。 +- `clang-format --dry-run --Werror`:通过。 +- `git diff --check`:通过。 +- 未运行完整 test binary、Android Gradle、ADB 或设备;遵守任务限制。 + +## 用户手工 Android 验证 + +在仓库根目录执行: + +```bash +./gradlew :MapLibreAndroidTestApp:assembleOpenglDebug --console=plain +adb -s 192.168.1.146 install -r platform/android/MapLibreAndroidTestApp/build/outputs/apk/opengl/debug/MapLibreAndroidTestApp-opengl-debug.apk +adb -s 192.168.1.146 shell am start -n org.maplibre.android.testapp/.activity.particle.ParticleCoreLayerValidationActivity +adb -s 192.168.1.146 logcat -v time -s ParticleValidation mbgl +``` + +APK 输出路径为 `platform/android/MapLibreAndroidTestApp/build/outputs/apk/opengl/debug/MapLibreAndroidTestApp-opengl-debug.apk`。先点 **T0 EAST / T1 NORTH**,再依次点 **MIX 0% / MIX 50% / MIX 100%**;随后在 ACTIVE 下反复调 mix、点 **ADVANCE B→C**,并回归 Camera、GLOBAL/OCEAN、STATIC、Rotation/Viewport、World Wrap 与 Antimeridian。 + +## T1--T10 验收状态 + +T1 east、T2 50% northeast、T3 north、T4 连续 mix 无 upload、T5 B/C 复用、T6 Camera 无 upload、T7 ACTIVE/STATIC、T8 mask、T9 Rotation/Viewport、T10 World Wrap/Antimeridian:均为**用户手工执行**,尚未验证。 + +## 未完成、风险与下一步 + +本轮不包含 Android 设备证据,因此不能确认实际 OpenGL sampler binding、视觉方向或 APK UI 资源。时间窗口 advance 的 GPU slot 复用已由 identity 实现与日志覆盖,但必须以 T5 Android 日志确认。若 T1--T10 无阻塞失败,建议进入 40-03 Android OpenGL 性能与资源压力基线;否则先按失败日志做最小修复。 diff --git a/粒子开发记录.md b/粒子开发记录.md index fbee37589..e3a2a4bd4 100644 --- a/粒子开发记录.md +++ b/粒子开发记录.md @@ -222,3 +222,12 @@ - 同一 RenderThread/resource generation 内 GLOBAL/OCEAN_ONLY_A/OCEAN_ONLY_B 复用同一 `maskGeneration`,日志为 `reuse=true`;graphics resource 重建后的 generation/maskGeneration 递增属于新 GPU resource,不属于同 generation 内重复创建。 - 40-01 历史调试链完整保留:OfflineDownload exhaustive switch、RasterArray no-op RenderItem SIGSEGV、Android APK/.cxx 旧 native 缓存、lifecycle reset loop、formal Composite Trail 显示、shader source/generated header 同步、opaque clear State seed、field identity generation collision、Rotation/Viewport STATIC restore 均已解决并由最终手工回归覆盖。 - 本次仅回填 Stage B 文档,不重新运行 Android Gradle、ADB 或实体机;iOS/Metal、Vulkan、真实数据、生产 mask、性能和独立 EGL context-loss 设备证据仍为范围外,不被 40-01 Android OpenGL PASS 隐含覆盖。 + +## 2026-09-02 — 40-02 Android 双时间片 GPU 时间插值(代码阶段) + +- 在 40-01 唯一正式 `RasterArraySource -> RasterParticleLayer -> GPU Simulation -> Trail -> Composite` 链路上扩展为 T0/T1 两份 immutable Field snapshot;source 通过可选 `next-fixture/next-metadata/next-field` 从同一 FileSource byte boundary 加载,拒绝 grid/bounds/NoData/Field 语义不兼容的窗口。 +- `RasterParticleLayer` 新增 `raster-particle-time-mix` 常量属性;其只更新既有 UBO。State shader 分别从两张 GPU Field texture 解码速度后 GPU `mix`,没有 CPU 完整中间 Field、第二 Layer 或第二 simulation。 +- Renderer 保持一套 State A/B、Trail A/B、shared Mask 和 Composite;T0/T1 使用两个可复用 GPU Field slot。窗口 A/B -> B/C 通过 generation + fixtureID + dataVersion 识别 B,复用其 texture、只上传 C,保留 simulation/trail;无重叠的新窗口沿用 40-01 的安全 reset/reseed。 +- Android Test App 增加 T0 EAST/T1 NORTH、MIX 0/50/100 与 ADVANCE B→C 入口,原 ACTIVE/STATIC、GLOBAL/OCEAN、Camera、wrap/antimeridian 入口保留。 +- Linux OpenGL 受影响 Core/source-test object、shader generator、C++ format 和 diff check 已通过;未执行 Android Gradle、ADB 或设备操作。40-02 当前只能标记 `IMPLEMENTED / WAITING FOR ANDROID VALIDATION`,T1–T10 手工 Android OpenGL 验收后才能判 PASS。 +- 实现提交为 `4675a8a29dd6fc7b684bfb64913a8c9075ad8583`(`feat: add GPU particle time interpolation`);文档收尾提交另行记录,均只面向 40-02。