Files
MapLibreParticleLab/docs/20_调查/STEP-056_RN天气渲染桥接现状调查_v1.md
OpenAI Codex f37f6eea1f
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
docs: investigate React Native weather rendering bridge
2026-09-05 19:01:59 +08:00

16 KiB
Raw Blame History

STEP-056 React Native 天气渲染桥接现状调查

状态:调查完成(只读);STEP-055 仍为 IMPLEMENTED / WAITING_ANDROID_VALIDATION。

Starting HEAD:44a50e6e4a4e79fae7ddf36af007b5e35e969a92(particle/main,origin/particle/main 同步确认)。本报告没有修改功能代码、没有执行 Android Gradle/APK/ADB/设备测试或 Jest。

A. 当前真实调用架构

flowchart LR
    A[ParticleCoreLayerValidationActivity.kt\nsubmitRealWeatherDataset] --> B[MapLibreMap.submitRasterArraySourceHostField]
    B --> C[NativeMapView.java\nnativeSubmitRasterArraySourceHostField]
    C --> D[native_map_view.cpp\nNativeMapView::submitRasterArraySourceHostField]
    D --> E[RasterArraySource::submitHostField]
    E --> F[LocalParticleFieldFeed::load]
    F --> G[ParticleFieldDataAdapter::decode]
    G --> H[immutable ParticleFieldSnapshot]
    H --> I[RasterArrayTimelineRuntime\nT0/T1 + cursor/mix]
    I --> J[RenderRasterParticleLayer]
    I --> K[RenderWeatherColorLayer]
    J --> L[State/Trail/particle GPU passes]
    K --> M[WeatherColor GPU pass]

当前真实 WeatherV2 Test App 资源位于 platform/android/MapLibreAndroidTestApp/src/main/assets/integration/particle-weatherv2/ecmwf-hres-wind-20260807/;ParticleCoreLayerValidationActivity.kt::submitRealWeatherDataset() 读取每个 metadata.json 与 field.f32,依次提交 2026080700/06/12/18。

仓库没有被跟踪的 React Native package、TurboModule、Fabric、JSI 或 RN NativeModule。现有可复用入口是 Android Test App 的 Java/JNI/C++ Host Field Submission,不是 RN binding。

B. 文件与职责清单

文件 类/函数 当前职责 RN v1 可复用 备注
include/mbgl/particle/field/field_data_adapter.hpp ParticleFieldDescriptor / ParticleFieldSnapshot / ParticleFieldDataAdapter canonical descriptor、immutable snapshot、校验/解码 contract 是 U=eastward、V=northward、float32-le、interleaved U/V;不含 provider/URL
src/mbgl/particle/field/field_data_adapter.cpp decode() 校验 identity/generation/time/dimension/bytes,复制并解码 payload 是 Adapter 不补 generation,也不理解 JS/provider
include/mbgl/style/sources/raster_array_source.hpp RasterArraySource::submitHostField Host ingress、submit result、Timeline holder 是 当前缺少 clearWindFields()
src/mbgl/style/sources/raster_array_source.cpp submitHostField / RasterArrayTimelineRuntime T0/T1 创建、后续 append、Timeline/Window state 是 有 bounded timeline(8 entries)及 log,不向 Host 发事件
platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMap.java submitRasterArraySourceHostField / controlRasterArraySourceTimeline Android public facade 是 RN Android module 可调用或包裹它
platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/NativeMapView.java native declarations Java 到 JNI 是 payload 目前仅 byte[]
platform/android/MapLibreAndroid/src/cpp/native_map_view.cpp NativeMapView::submitRasterArraySourceHostField byte[] 拷贝进 C++、调用 Source 是 返回 jint result;没有 callback
platform/android/MapLibreAndroidTestApp/.../ParticleCoreLayerValidationActivity.kt submitRealWeatherDataset / controlTimeline Test App asset 模拟 Host 仅作参考 不是正式 RN 入口
src/mbgl/style/layers/raster_particle_layer.cpp RasterParticle properties particle style property parser/model 部分 无 arbitrary particle/trail color
src/mbgl/renderer/layers/render_particle_validation_layer.cpp RenderRasterParticleLayer field upload、Timeline tick、GPU simulation/trail 不直接 需保持 C++ ownership
src/mbgl/style/layers/weather_color_layer.cpp WeatherColor property parser 仅 opacity 部分 无 dynamic speed stops
src/mbgl/renderer/layers/render_weather_color_layer.cpp RenderWeatherColorLayer 同一 Timeline frame 的色场绘制 不直接 重用 T0/T1 GPU content
shaders/particle_gpu_validation.fragment.glsl weatherSpeedColor / particleSpeedColor 当前固定色表/粒子 speed-color 将来需扩展 色带硬编码在 shader

C. 当前 Host Field Submission 路径

NativeMapView::submitRasterArraySourceHostField(JNIEnv&, String, String, byte[]) 用 jni::GetArrayRegion 把 Java byte[] 复制到 std::vector<char>,再调用 RasterArraySource::submitHostField(metadataText, payload)。

  1. LocalParticleFieldFeed::load() 解析 provider-neutral metadata,并调用 ParticleFieldDataAdapter::decode()。
  2. Adapter 严格检查非空 fieldId、合法 generation/validTime、width/height、float32-le + interleaved-uv、精确 payload bytes;成功后生成拥有 velocity 与 valid 的 immutable Snapshot。
  3. 第一个合法 Field 成为 pending T0,结果为 AcceptedPendingInitialWindow。
  4. 第二个严格较新的 compatible Field 通过既有 submitInitialRuntimeFieldWindow(T0,T1),建立 RasterArrayTimelineRuntime,结果为 InitialWindowReady。
  5. 后续 Field 按 producer validTime 追加同一个 bounded Timeline,结果为 AcceptedNextSnapshot。
  6. 相同 GPU content 返回 Duplicate;非法、非单调、capacity 等返回结构化负 result,最后有效 Field 保留(fail-closed)。

当前 513×769 单帧为 513 × 769 × 2 × 4 = 3,155,976 bytes,约 3.01 MiB。Android Test App 路径至少有:asset 到 Java ByteArray、ByteArray → vector<char>、vector<char> → Snapshot::velocity 三段 materialization/copy;随后 renderer 还有 GPU image upload。它不适合转换成巨大 JSON 数组。

D. Timeline 与时间插值

RasterArrayTimelineRuntime(raster_array_source.hpp/.cpp)持有按 timestamp 排序的 Snapshot entries、T0 index、mix、speed 与 state。tick() 使用 steady_clock wall elapsed 推进 mix;相邻有效 entries 决定 T0/T1,跨界时 moveToLocked() 递增 window revision 并记录 OLD_T1_TO_NEW_T0。

RenderRasterParticleLayer::prepare/update 读取 timeline->frame(),在 revision 变化时让 RasterArraySource 的 authoritative window 前进;renderer 用同一 GPU content 判定复用,因而正常 A/B→B/C 只上传 new T1,不 rebuild simulation/clear trail。RenderWeatherColorLayer 读取同一 frame/mix,在 shader 内先插值 U/V 再计算 speed。

Android 当前低频命令路径是:ParticleCoreLayerValidationActivity::controlTimeline → MapLibreMap::controlRasterArraySourceTimeline → NativeMapView::nativeControlRasterArraySourceTimeline → RasterArraySource::controlRuntimeTimeline。现有命令为 play/pause、0.5/1/2 speed、step、按 index seek、Gap/Invalid/Recover;没有 setForecastTime(timestamp) 的公开 Host API,也没有向 JS 返回 MIX/T0/T1 的 callback。

E. WeatherColorLayer 当前颜色实现

WeatherColorLayer 当前唯一公开 paint property 是 weather-color-opacity(weather_color_layer.cpp)。RenderWeatherColorLayer 以 UBO mode 5 运行 particle_gpu_validation.fragment.glsl::weatherSpeedColor(speed);该函数把 0–30 m/s 映射到硬编码蓝/青/红色,并在 shader 用 smoothstep/mix 插值。

结论:当前没有 dynamic stops/ramp 数据结构,也没有 Android/JNI setter。React dynamic color stops 最小新增点应是 WeatherColorLayer 的 style property/model(有序 speedMps,color contract)及紧凑 shader-visible ramp(uniform/texture),由 Native 插值、绘制;React 只提交 stops。不能把 provider 色表放入 Renderer,也不能继续把固定 preset 作为唯一 API。

F. ParticleStyle 当前实现

已有 RasterParticleLayer paint entry:raster-particle-active、mask、time-mix、particle-count、line-width、particle-length、particle-opacity、trail-opacity、trail-fade、speed-color-enabled。Android 低频 NativeMapView::setStyleLayerFloatProperty 已转发上述大部分 float/bool property。

目标项 当前入口/行为 副作用
particleCount raster-particle-count State/draw resources 必要重建;不应 field resubmit
lineWidth、particleLength、particleOpacity、trailOpacity、trailFade RasterParticle paint property visual/uniform 更新;当前路径不应 style reload、field upload 或 Timeline reset
speedColor raster-particle-speed-color-enabled shader 在固定 speed palette 与固定蓝色之间切换
particleColor 不存在 需新增明确 style property
trailColor 不存在 当前 trail RGB 与 particle segment 路径耦合,需独立 style/shader contract

因此未来应将 speedColor 保留为可选模式;当它为 false 时,用 React 的 particleColor;trailColor 必须独立提交,不能让 WeatherColorLayer speed ramp 隐式控制粒子颜色。

G. Runtime Enable / Disable / Release

当前 generic Layer visibility 可以隐藏 layer;RenderRasterParticleLayer::needsRepaint() 仅在 active && visibility != None 时为真,故 particle hidden 时停止该层持续 repaint。它不等价于 release:layer/source 未 remove 时 State/Trail/field GPU resources 与 Snapshot/Timeline 仍被持有。Weather color 的 needsRepaint() 返回 isLayerRenderable(),visibility none 是否不再 render/repaint 由通用 Layer renderability 决定;本调查未用设备测试验证。

真实释放的现有路径是 layer removal:RenderRasterParticleLayer::layerRemoved() 移除 State/Trail targets 与 draw buffers;RenderWeatherColorLayer::layerRemoved() reset resources。source destruction 释放 RasterArraySource 的 timeline/pending Snapshot 共享引用;Activity 当前 enabled=false 是重载 validation style 并省略 layers,不是对 RN 暴露的一次性 runtime unload API。

setWeatherEnabled(false) 可实现真正释放,但当前没有一条 API 完整完成它。最小高层语义应在 map/style 安全线程中:停止低频 playback → remove Particle/WeatherColor layers(GPU release)→ clear timeline/pending host fields 或 remove RasterArraySource(Snapshot release)。需要新增 clearWindFields()/runtime lifecycle bridge;不能以 visibility 伪装 unload,也不应向 React 暴露 State/Trail/T0/T1。

H. RN Binary Bridge 建议

当前没有 RN binary bridge。建议 v1 使用 ArrayBuffer/Uint8Array 在 RN Android module 侧稳定 materialize 为 byte[],复用现有 MapLibreMap.submitRasterArraySourceHostField(..., byte[]) 与 JNI;metadata 继续是小型 String/JSON。约 3.01 MiB/帧和四帧队列的峰值复制需要纳入内存预算,但在先验证正确性阶段不必承诺 zero-copy。

后续 profiling 若证明 JS→Java→JNI copies 是瓶颈,再增加 新的 direct ByteBuffer JNI overload(或宿主 JSI ArrayBuffer native ownership seam)。不能假称现有 byte[] 通路可 zero-copy;也不应把文件/mmap 作为 RN v1 的必需前提。

I. RN API 与 Native 能力映射

RN 目标功能 当前 Native 能力 已有入口 缺口 / 建议接入点
setWeatherEnabled 层 removal/资源释放存在 generic Style remove;layerRemoved 新增高层 lifecycle/unload + clear fields,不用 visibility 代替
setWeatherColorVisible generic visibility Style Layer visibility 可包装;隐藏不释放
setParticleVisible generic visibility;active 控制 simulation Style property 可包装;明确 hide/suspend/unload 三语义
submitWindField 完整 Host canonical ingress submitRasterArraySourceHostField RN module/facade 与 binary adapter 缺失
clearWindFields 无 无 Source clear/lifecycle seam
setForecastTime 仅 index seek timeline command 5 增 timestamp→entry selection API,JS 不设 T0/T1/MIX
setPlaybackState play/pause timeline commands 0/1 可包装
setPlaybackSpeed 0.5/1/2 command 2 枚举/范围需正式 Host contract
setWindColorStyle opacity only WeatherColor paint property 增 dynamic stops style model + shader ramp
setParticleStyle 多数 float/bool 属性 setStyleLayerFloatProperty 增 particle/trail color typed properties;不要靠 speedColor
setParticlePerformance 无 weather-local FPS gate global MapView#setMaximumFps 独立 render scheduling 阶段
runtime/error callbacks submit jint + logcat JNI result 增低频 structured event/callback/state query;不回调 per-frame MIX

J. targetFps 调查

当前持续帧由 RenderRasterParticleLayer::needsRepaint()(active/visible)及 RenderWeatherColorLayer::needsRepaint()(renderable)驱动。Map::Impl::onDidFinishRenderingFrame() 在 needsRepaint 时再次 onUpdate();Android frontend 最终调用 MapRenderer::requestRender()。因此接近 60 FPS 是 MapLibre continuous render request 链的结果,不是 Test App Timer。

现有 MapView#setMaximumFps() / MapRenderer#setMaximumFps() 是整张地图的 global cap,不应直接实现天气专属 targetFps。最安全的天气 30 FPS 节流点是 future RenderRasterParticleLayer/WeatherColor coordination 的 render scheduling gate:只在 deadline 到达时执行 State/Trail 与 dependent color redraw,使用累计 monotonic wall delta 驱动 lifecycle、trail fade 和 Timeline tick。这样不能把固定 1/30 秒塞给 shader;需保留真实 elapsed,并审查 simulation dt clamp/substep。WeatherColor 也必须参与 gate,否则它单独 needsRepaint() 仍保持全图约 60 FPS。建议该项独立于 RN Bridge v1,以免把 render scheduling 风险混入接口接线。

K. 建议实施拆分与结论

推荐下一实现阶段:STEP-057 Android Host Weather Runtime Bridge Foundation。

目标是完成可被未来 RN module 包装的 Android/Native typed facade,而不是在本仓库凭空建立 RN package(当前没有 RN dependency、package 或 host app topology)。建议一个较大 Batch:

  1. 低频 runtime lifecycle(enable/suspend/hide/unload)与 clearWindFields;安全 layer/source teardown。
  2. canonical submitWindField(metadata, binary)、timestamp seek、play/pause/speed 的 typed Android/JNI facade,复用 Source/Timeline。
  3. 可见性、现有 particle visual properties、WeatherColor opacity 的 typed API;增加 structured submission/runtime/error event,避免 per-frame callback。
  4. 定义并接入 dynamic WeatherColor stops 与独立 particle/trail colors 的 style property contract;所有 provider/network/forecast decision 仍留 JS。
  5. 以 Test App/Native tests 走完整 facade,最后一次 Android build/install 验收;真正 RN module 留给具备 RN host/build dependency 的应用仓库或后续明确任务。

后续 Batch(STEP-058,建议独立): weather-local targetFps、repaint gate、validation logging 降频与性能测量。不要把 MapView#setMaximumFps() 当作默认实现。

直接进入“RN package 实现”目前的阻塞不是 Core 数据 contract,而是仓库不存在 RN binding 与 RN Android build ownership。可立即开发的是上述 Host-native facade;等宿主提供 RN 依赖和 module placement 后,RN 层可薄包裹它。

最大三项风险:

  1. 3.01 MiB 级 payload 在 JS/Java/JNI/Snapshot 的多次 copy 造成峰值内存、GC 与提交抖动。
  2. visibility、suspend 与 unload 被混同,导致 Native/GPU/Snapshot 资源未真正释放,或在 style/render thread 上错误 teardown。
  3. Timeline tick、particle State pass 与持续 repaint 当前耦合;错误 targetFps gate 会改变 motion、wall-time lifecycle 或 Timeline 行为。

冻结边界继续有效:Descriptor/Snapshot、T0/T1 window/reuse、Core-owned Timeline/Playback、canonical m/s U/V、WeatherColor 与 RasterParticle 分离、Renderer 不含 provider/network/vendor 逻辑。