Files
MapLibreParticleLab/docs/20_调查/10_架构调查报告_v1.md
OpenAI Codex 74be2a5052
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
docs: freeze particle architecture investigation and design
2026-08-29 12:33:09 +08:00

392 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MapLibreParticleLab 架构调查报告 v1
调查日期:2026-08-29
调查分支:`particle/main`
调查性质:只读架构调查,不是粒子方案设计
## 1. 调查环境
- 仓库:`/mnt/sda1/MapLibreParticleLab`,根目录包含 `PARTICLE_BASELINE.md`、`include/`、`src/`、`platform/android/`、`platform/ios/`、`shaders/`、`particle-fixtures/` 和 `docs/`,确认是 MapLibreParticleLab。
- 主机基线:Ubuntu 24.04 x86_64;项目基线记录的 Java 17、Android API 34、NDK 28.2.13676358、构建并行度 3 已读取自 `PARTICLE_BASELINE.md` 与 `docs/00_项目规范/06_官方基线来源_v1.md`。
- 本次只执行文件搜索、源码阅读、配置阅读和 Git 查询;未运行构建、测试、依赖安装或会生成产物的命令。
## 2. Git、基线和规则读取
- 分支:`particle/main`。
- HEAD:`127de2271480000fcc8f390d81cf446e98757800`,最近提交主题为 `docs: freeze particle project governance v1`,时间为 2026-08-29 11:13:49 +08:00。
- 远端:`origin` 为本地 Gitea SSH 地址;`upstream` 为 MapLibre Native GitHub,只启用 fetch,push 显式为 `DISABLED`。
- 工作区:调查开始时无已跟踪修改;有一个未跟踪的当前任务书 `docs/40_任务/10_架构调查指令书_v1.md`。因此“工作区干净”检查结果为:`未完成`,原因是任务书本身在当前工作区未跟踪,非本次生成。
- 当前基线:Android `android-v13.4.1`、iOS `ios-v6.28.0`、共用 Core `18545ca10d296704b2cba675b182ff2c8da24588`。实际版本文件为 `platform/android/VERSION` 和 `platform/ios/VERSION`。
- 已完整读取:根目录 `AGENTS.md`;`PARTICLE_BASELINE.md`;`docs/00_项目总则/` 下当前四份总则;`docs/00_项目规范/` 下当前六份规范及官方基线来源;当前 `STEP-000` 记录和报告;`docs/40_任务/README.md`;本任务书;调查、Step、报告 README;现有 `docs/10_架构/MapLibre粒子总体架构_v1.md`;`粒子开发记录.md`。
- 本次未修改 `docs/00_项目总则/`、`docs/00_项目规范/`、生产代码、实验源码、构建配置或依赖。
## 3. 当前项目整体架构
### 3.1 工程形态
当前仓库不是 React Native、Expo 或 Bare RN 产品工程。根 `package.json` 的名称是 `maplibre-native`,脚本只有样式规范复制;`tsconfig.json` 只包含 `.github/scripts/*.ts`、`scripts/**/*.mjs` 和声明文件,并设置 `noEmit`。仓库没有业务页面、React 组件、`react-native`/Expo 依赖、Metro 配置或 Babel 配置。
因此任务书中的“业务/页面 → RN Map 组件 → RN 封装”在当前仓库不存在,不能据此补画一条虚构调用链。这里的实际入口是各平台 MapLibre SDK 示例、测试应用或直接使用 Native C++ API 的宿主。
### 3.2 主要目录
```text
MapLibreParticleLab/
├── include/mbgl/ Core 对外 C++ API
├── src/mbgl/ Core 实现:Map、Style、Source、Render、gfx、线程
├── platform/android/ Android SDK、JNI、Java/Kotlin View、C++ backend、测试 App
├── platform/ios/ iOS SDK、MLNMapView、OpenGL/Metal backend、示例和测试
├── platform/darwin/ Darwin 公共绑定、CustomStyleLayer 和 CustomDrawableLayer
├── platform/node/ Node binding 与 Node 测试,不是 RN UI 宿主
├── shaders/ MapLibre 内置 GLSL/生成清单;无粒子 shader
├── particle-fixtures/ 第一阶段离线粒子场/Mask fixture 占位目录
├── render-test/ test/ 渲染测试、API 测试、fixture 和服务器测试
├── scripts/ 样式代码、设备农场和工程脚本
├── docs/ 总则、架构、任务、调查、Step、报告
├── vendor/ 第三方源码子模块
└── CMakeLists.txt、Makefile、bazel/、platform 构建文件
```
### 3.3 入口和边界
- Core 入口是 `mbgl::Map`(`include/mbgl/map/map.hpp`),通过构造时注入 `RendererFrontend` 和 `MapObserver`。
- Android 入口是 `MapView`(`platform/android/MapLibreAndroid/src/main/java/.../MapView.java`),内部创建 `MapRenderer`、`NativeMapView` 和 Java/C++ JNI peer。
- iOS 入口是 `MLNMapView : UIView`(`platform/ios/src/MLNMapView.h`),内部由 `MLNMapViewImpl` 连接 Core `Map` 和 OpenGL/Metal View。
- 没有项目自有 `assets/` 业务资源目录;`test/fixtures` 是 MapLibre 测试资源,`particle-fixtures` 目前仅包含 README 和五个 `.gitkeep`。
- 现有 `build-linux-opengl`、`build-particle` 等是工作区中的构建目录,不视为源码模块。
## 4. 依赖与版本
| 类别 | 当前可确认内容 | 证据/结论 |
|---|---|---|
| JavaScript 直接依赖 | `@maplibre/maplibre-gl-style-spec`、`simple-git`、`minimatch`、`npm-run-all`、GitHub Actions 等 | 根 `package.json`;不是地图 UI 依赖 |
| JavaScript 开发依赖 | TypeScript、EJS、Express、AWS SDK、MVT/style fixtures 等 | 用于脚本、测试、设备农场和代码生成 |
| RN/React/Expo | 未找到 `react`、`react-native`、Expo、RN MapLibre 包 | 当前仓库没有 RN 产品层 |
| Skia/Reanimated/WebGL JS | 未找到项目直接依赖 | 不能假定存在 |
| Android SDK | `platform/android/VERSION=13.4.1`;`compileSdk=34`、`minSdk=23`;OpenGL/Vulkan/WebGPU flavor;Java SDK 直接依赖 GeoJSON、gestures 等 | `platform/android/MapLibreAndroid/build.gradle.kts` |
| Android Core | `libmaplibre` 由本仓库 C++ Core 构建并通过 JNI/Prefab 接入,不是另一个 RN 包 | Android CMake、JNI 和 Prefab 配置 |
| iOS SDK | `platform/ios/VERSION=6.28.0`;Podspec 通过 `ios-v#{version}` release xcframework;iOS deployment target 12 | `platform/ios/MapLibre.podspec` |
| iOS backend | 当前源码含 OpenGL、Metal、WebGPU 条件路径;当前项目基线将 OpenGL 作为主线、Metal 作为后续验证方向 | `MLNMapView+Impl.mm` 和各 backend 文件 |
| lock 文件 | 根 `package-lock.json` lockfile v3、`pnpm-lock.yaml` lockfile v9;锁定 TypeScript 5.9.3 等脚本依赖 | 未安装/更新 |
需要区分:`package.json` 的版本 `5.4.0` 是 Native 仓库/脚本包版本;Android/iOS SDK 版本分别由各平台 `VERSION` 决定,不能把三者合并为一个 RN 版本。
## 5. MapLibre 实际接入链
当前无 RN 绑定,因此实际链路分为 Android SDK、iOS SDK 和直接 Core 三条:
```text
Android Activity/Test App
→ MapView(FrameLayout)
→ MapRenderer + NativeMapView(Java JNI peer)
→ AndroidRendererFrontend
→ mbgl::Map / mbgl::Style
→ RendererFrontend::update(UpdateParameters)
→ Android MapRenderer render thread
→ mbgl::Renderer
→ RenderOrchestrator / RenderTree / RenderLayer / gfx::Drawable
→ Android OpenGL 或 Vulkan RendererBackend
→ Surface/TextureView → GPU
```
```text
iOS App/Example
→ MLNMapView(UIView)
→ MLNMapViewImpl(MapObserver adapter)
→ mbgl::Map + RendererFrontend
→ RenderOrchestrator / RenderTree / RenderLayer / gfx
→ MLNMapViewOpenGLImpl(GLKView) 或 MLNMapViewMetalImpl(MTKView)
→ EAGL/CAMetalLayer → GPU
```
```text
直接 Core 宿主
→ mbgl::Map(RendererFrontend, MapObserver)
→ Map::Impl::onUpdate()
→ Immutable Style/Source/Layer snapshot
→ Renderer::render()
```
当前能够确认的对象:MapView、Camera、Style、RasterSource、ImageSource、Vector/GeoJSON/CustomGeometrySource、Layer、MapObserver 事件和 CustomLayer。当前仓库没有 RN `ShapeSource` 组件层,也没有 RN `onPress` API;Android/iOS Native SDK 的点击、相机和样式 API 需分别通过各自 SDK 查询,不能移植成当前 RN 事实。
## 6. MapLibre 生命周期
### 6.1 Core 生命周期
`Map::Impl::onUpdate()` 在连续模式下读取当前时间、更新 transition,构造包含 `TransformState`、style/source/layer immutable snapshot、图片、字体和 FileSource 的 `UpdateParameters`,再交给 `RendererFrontend::update()`。`Map::triggerRepaint()` 也只是触发同一更新路径。
`Renderer::render()` 创建并准备 `RenderTree`,`Renderer::Impl::render()` 依次执行:开始 frame、上传 source/layer 资源、更新 layer group、运行 tweaker、上传 drawable/render target、可选 3D pass、render target、清屏、opaque pass、translucent pass、debug pass、postRender、present、结束 frame,并回调 `onDidFinishRenderingFrame`。
### 6.2 Android
- `MapView.initializeDrawingSurface()` 创建 `MapRenderer` 和 `NativeMapView`;Surface 创建后回到 UI 线程初始化 `MapLibreMap` 并调用 `onStart()`。
- `MapView.onStart/onResume/onPause/onStop/onDestroy` 分别驱动 Java map/renderer;SurfaceView detach 会触发 native reset,Surface 销毁/重建可能导致 backend 和 `Renderer` 重建。
- Java `MapRenderer` 的 `onDrawFrame()` 调用 JNI `nativeRender()`;SurfaceView 由 `MapLibreSurfaceView` 的 render thread 请求 dirty/continuous frame,TextureView 由 `TextureViewRenderThread` 的事件队列和 request flag 驱动。
- `AndroidRendererFrontend::update()` 保存最新 `UpdateParameters`,异步任务把它交给 `MapRenderer` 并调用 Java `requestRender()`。C++ render thread 只持有一次 render 所需的参数快照。
- Native MapObserver 事件由 JNI 转回 Java `MapChangeReceiver`,包括 camera will/is/did、地图/样式加载、render frame start/end、idle、source 和 shader 事件。
### 6.3 iOS
- `MLNMapView` 创建 Core `Map` 和 `MLNMapViewImpl`;实现通过编译宏选择 OpenGL、Metal 或 WebGPU backend。
- 显示刷新由 `CADisplayLink` 调用 `updateFromDisplayLink:`;当 `needsDisplayRefresh` 或 pending completion 存在时,调用 backend View 的 `setNeedsDisplay`。
- `renderSync` 清除刷新标记后调用 `_rendererFrontend->render()`;渲染 View delegate 的 `drawInRect:`/`drawInMTKView:` 再调用 `MLNMapViewImpl::render()`,形成地图自己的渲染帧。
- View 不可见、进入后台、销毁或恢复时,DisplayLink、drawable、backend 资源和 dormant 状态分别处理;具体资源重建依赖 backend 实现。
- iOS delegate 可获得相机、地图/样式加载、render frame start/end、idle 等事件;CustomStyleLayer 的 `drawInMapView:withContext:` 在 MapLibre 的自定义 layer 绘制时调用。
### 6.4 TS/RN 层与内部事件
当前仓库没有 TS/RN 层,所以“TS/RN 直接获得的生命周期事件”:`未确认/不适用`,原因是该层不存在。Native SDK 可观察的事件如上;只存在于 Core/Renderer 的 `UpdateParameters`、RenderTree、LayerGroup、GPU command encoder 的内部状态,当前没有 JS 层封装。
## 7. 自定义渲染扩展点分类
### A:当前项目已经直接使用
- Core `style::CustomLayer` + `CustomLayerHost` + `RenderCustomLayer`:现有 Android Test App 的 `CustomLayerActivity` 和 C++ `ExampleCustomLayer`/`ExampleVulkanCustomLayer` 实际使用。
- Darwin `MLNCustomStyleLayer`:iOS/Swift 示例实现 `drawInMapView:withContext:`,获得同一 frame 的 projection matrix;Metal 示例使用当前 render encoder。
- Darwin `MLNCustomDrawableStyleLayer`/Core `CustomDrawableLayer`:示例 `ExampleCustomDrawableStyleLayer` 和 UI 测试实际使用,属于 Drawable/Builder 路径而不是 raw GL callback。
- 内置 `RenderLayer`、`LayerGroup`、`DrawableBuilder`、`DrawableTweaker`、shader manifest、texture/buffer/render target:是当前地图本身的生产渲染链,粒子尚未接入。
### B:当前依赖已暴露,但目标粒子业务尚未使用
- Android Java `CustomLayer(String id, long host)` 暴露了 native host 指针入口,但 Java API 明确标注 experimental/do not use;当前只在 SDK test app 示例使用,粒子业务未使用。
- Android `CustomGeometrySource` 和 iOS `MLNComputedShapeSource` 已暴露,能提供按 tile 的 GeoJSON/几何数据;当前没有粒子/格点数据实现。
- Core `gfx::Context` 已有 `createTexture2D`、`createDynamicTexture`、`createRenderTarget`、uniform buffer、`createDrawableBuilder`、generic shader 和 upload pass 接口;这些是现有通用基础设施,不等于粒子专用 API。
### C:Native Core 支持,但当前高层封装未统一暴露
- `CustomLayerHost::render()` 可拿到 width/height、center、zoom、bearing、pitch、FOV、projection matrix 和 near-clipped matrix;Vulkan/Metal 还有 backend-specific render parameters。Android 高层 Java host 没有一个类型安全的 `CustomLayerRenderParameters` API,应用级可直接使用范围未确认。
- Core `RenderLayer`、`RenderOrchestrator`、`LayerManager`、`RendererObserver` 和 gfx backend 内部 hook 可承载 native 粒子实现,但它们是 Core 内部边界,不是当前 RN/业务 API。
- iOS `MLNBackendResource` 可暴露 Metal view/device/render pass/command buffer;这是 iOS Native API,当前仓库没有 RN 统一桥接。
### D:当前代码无法确认
- React Native UI/Main Thread、JS bridge、TurboModule/Fabric、RN `requestAnimationFrame` 或 Reanimated frame callback:当前项目没有 RN host,无法确认。
- 任何外部 Overlay 与 MapLibre View 的同步质量:当前正式边界禁止外部 Overlay,仓库也没有粒子 Overlay 实现,因此没有实测数据。
- 未实现的粒子 shader、GPU ping-pong state、trail render target、velocity texture 的具体格式和生命周期:当前只有架构草图,没有实现。
## 8. Android 渲染架构
- 原生 View:`MapView` 是 `FrameLayout`,将 SurfaceView 或 TextureView renderer 放在 child index 0;不是 RN `View`。
- renderer:Java `MapRenderer` 仅负责生命周期、JNI 和调度;C++ `MapRenderer` 持有 Android backend、Core `Renderer`、`ActorRef`、update mutex 和 renderer observer。
- backend:Gradle flavor 通过 `-DMLN_WITH_OPENGL=ON` 或 `-DMLN_WITH_VULKAN=ON` 构建;C++ `AndroidGLRendererBackend` 和 `AndroidVulkanRendererBackend` 分别管理 EGL/GL 或 Vulkan surface/swapchain/device。
- 帧驱动:SurfaceView 默认可以 WHEN_DIRTY,CONTINUOUS 用于连续渲染/benchmark;TextureView 自有 render thread。Core update 通过 `AndroidRendererFrontend` 调用 `requestRender`。
- 相机:Java `MapLibreMap`/`Transform` 调 JNI,NativeMapView 调 Core `jumpTo/easeTo/flyTo`、坐标投影和反投影;完整 matrix 仍在 Core Transform/Paint/CustomLayer 参数中。
- 自定义入口:Core CustomLayer 和 Android wrapper/test example 明确存在;但 Java host 是原始 `long`,生命周期、指针归属、context loss 和参数传递要求高,不能当作稳定业务接口。
- Native Module:未找到 RN NativeModule/TurboModule/Fabric。Android 的 `NativeMapView` 是 MapLibre Java/C++ peer/JNI,不是 RN module;当前不存在“Native Module 接触 RN MapView 实例”的事实。
## 9. iOS 渲染架构
- 原生 View:`MLNMapView` 继承 `UIView`;内部由 `MLNMapViewImpl` 将 Core MapObserver 与 ObjC delegate 接起来。
- OpenGL:`MLNMapViewOpenGLImpl` 持有 `gl::RendererBackend`、Renderable 和 `GLKView`/EAGL context;GLKView delegate 的绘制回调进入 Core render。
- Metal:`MLNMapViewMetalImpl` 持有 Metal backend、`MTKView`、command queue/buffer 和 render pass descriptor;`drawInMTKView:` 调 `impl->render()`,Core 负责 command encoding/present。
- Camera/transform:Core `TransformState` 生成 `TransformParameters` 的 projection/aligned/near-clipped matrices;`MLNCustomStyleLayer` 将这些和中心、zoom、方向、pitch、FOV 传给 ObjC drawing context。
- 自定义接口:`MLNCustomStyleLayer` 是可确认的 Native custom layer;Metal 下可以使用当前 encoder,OpenGL 下存在 deprecated EAGL context API。`MLNCustomDrawableStyleLayer` 是另一条 Drawable/Builder 接口。
- Native 对象范围:iOS adapter 能访问 Core Map、RendererFrontend 和 backend resource;公开的业务层主要通过 `MLNMapView`、`MLNStyle`、`MLNStyleLayer` 及 delegate 访问。没有 RN 连接层,RN 侧范围为 `未确认`。
- Android/iOS 差异:Android 有明确 OpenGL/Vulkan flavor 和实验性 raw host;iOS 示例有成熟的 ObjC/Swift CustomStyleLayer 与 Metal encoder 上下文。两者虽共享 Core CustomLayer 接口,但高层资源和生命周期 API 不一致。
## 10. JS/UI/Native/Worker/Render/GPU 边界
```text
[不存在的 JS/RN 业务层]
│ 当前仓库无桥接事实
▼
Android UI thread / iOS main thread
├─ Native MapView、Camera、Style API
├─ Core Map 所在线程(由宿主创建线程决定)
└─ MapObserver 回调
▼ immutable UpdateParameters / mailbox / async task
MapLibre Worker/FileSource
├─ Style/tile 解析、布局、bucket/buffer 计算
└─ FileSource 网络/缓存/离线 I/O
▼
MapLibre Render thread(Android 明确独立;iOS 当前 render delegate 路径需遵循宿主主线程/渲染上下文约束)
→ Renderer → RenderTree → LayerGroup/Drawable → gfx Context/CommandEncoder
→ OpenGL/Vulkan/Metal backend → GPU
```
- 高频数据若走不存在的 JS bridge,当前没有事实可测;按现有边界,不能将每帧大数组交给 Java/ObjC API 作为已支持能力。
- Android 的 `UpdateParameters` 在 frontend 与 render peer 之间异步保存并加锁,渲染使用最新快照;Java `requestRender` 只触发帧,不传粒子数据。
- iOS `setNeedsRerender` 只设置刷新标记,`CADisplayLink`/UIView backend 再进入 render;这不是 JS 每帧回调。
- Native 每帧回调存在:Core `MapObserver`/`RendererObserver` 的 render frame 事件,以及 CustomLayerHost render callback;它们位于地图渲染链内部。
- GPU 资源直接更新位置存在于 Core `gfx::Context`、UploadPass、Drawable、Texture、RenderTarget 和 backend command encoder;当前没有对业务开放的跨平台粒子资源接口。
## 11. Camera、Matrix 与坐标转换能力
| 信息 | JS/RN | Native SDK | Core 内部/CustomLayer | 当前结论 |
|---|---|---|---|---|
| center/zoom/bearing/pitch | 当前无 RN host,未确认 | Android `MapLibreMap`、iOS `MLNMapView`/delegate 可访问部分状态 | `TransformState` 全部存在,CustomLayer 参数传出 | 可由 Native/Core 获取 |
| bounds/viewport | 无 RN host | SDK 有尺寸和相机/投影 API | `TransformState::getSize`、frustum offset、tile cover | Core 信息完整 |
| screen ↔ geographic | 无 RN host | Android NativeMapView 和 iOS 公共 API 有相应能力 | `latLngToScreenCoordinate`、`screenCoordinateToLatLng` | 已确认 Core 能力 |
| projection/unprojection | 未确认 | 高层不统一暴露 raw matrix | Core `getProjMatrix`、projection/inverse matrices、camera utility | 内部可用 |
| view/transform matrix | 未确认 | iOS CustomStyleLayer 以 projection context 形式收到;Android Java wrapper 未提供同等类型 API | `TransformState::matrixFor`、`TransformParameters`、`PaintParameters` | 跨平台高层不一致 |
证据:`TransformParameters` 保存 `projMatrix`、`alignedProjMatrix`、`nearClippedProjMatrix`;`CustomLayerRenderParameters` 从同一 `PaintParameters.state` 读取中心、zoom、bearing、pitch、FOV 和 projection matrix;`PaintParameters::matrixForTile` 负责 tile matrix。
严格同步结论:在 Core 的同一渲染帧内,CustomLayer callback 可以获得地图当前 transform,因此“Core 内部同帧使用地图矩阵”已确认;但“Android/iOS 业务高层均能以统一公开 API 获取并接入粒子”未确认,当前不能宣称跨平台正式实现已具备严格同步能力。
## 12. 帧驱动能力
已确认:Android SurfaceView/TextureView render loop、Android `requestRender`/CONTINUOUS、iOS CADisplayLink、GLKView/MTKView draw callback、MapLibre `triggerRepaint`、Core CustomLayer render callback。
未确认/不适用:当前没有 RN `requestAnimationFrame`、RN animation frame、Reanimated frame callback 或 JS 每帧回调。
地图 Renderer 的帧驱动不是独立于地图的粒子动画时钟:Core `Map::Impl::onDidFinishRenderingFrame` 在 `needsRepaint` 或 transition 时重新 `onUpdate()`;平台再请求下一帧。若未来另建独立动画循环,现有代码无法保证它与相机快照、drawable context、present 时刻一致,可能产生一帧延迟、不同步或重复渲染;这里只记录风险,不作方案选择。
## 13. 当前数据与粒子相关实验代码
- `particle-fixtures/README.md` 明确第一阶段禁止真实天气服务器,具体数据格式在设计冻结后确定;`coastal-mask/`、`shear/`、`uniform-east/`、`uniform-north/`、`vortex/` 目前均只有 `.gitkeep`。
- 全仓库排除 vendor、node_modules、build 后搜索 `particle/wind/current/wave/vector field/rasterarray/velocity`,没有发现粒子运行时、速度场解析、粒子 shader、GPU ping-pong 或 trail 实现。
- 现有 `raster` 文件是 MapLibre 常规 RasterSource/RasterLayer 测试和内置 raster shader;`interpolate` 多数是 Style Spec/通用工具,不是向量场插值实现。
- `shaders/` 有 raster、heatmap、hillshade、fill、line、symbol、custom geometry 等现有地图 shader,没有 `particle`、`velocity` 或 trail shader。
- Android/iOS custom layer 示例是图形 API 示例/测试用途,不是粒子基础代码;应分类为“仅实验/示例用途”,不能直接判定为最终可复用粒子实现。
## 14. 海洋/气象格点数据可经过的现有层
| 阶段 | 当前仓库事实 |
|---|---|
| Fetch | Core `FileSource` 支持样式、tile、资源和缓存;没有粒子气象数据协议或 fixture loader |
| ArrayBuffer/TypedArray | JS 侧只有 Node/脚本和测试用途;未发现粒子 ArrayBuffer/TypedArray 模型 |
| 二进制解码 | Core 有 tile、PBF、图片等既有解码;没有速度场/海洋格点解码器 |
| Grid 数据模型 | 没有项目粒子 Grid/经纬度格点类型;`grid_index` 等是通用地图工具或测试 |
| 插值 | 有通用数值/Style 插值工具;没有已确认的向量场双线性/球面插值模块 |
| 内存结构 | Core 可用 immutable/vector/CPU buffer;没有粒子数据生命周期模型 |
| GPU upload | gfx Context、UploadPass、Texture2D、UniformBuffer、RenderTarget、Drawable 已存在 |
| Shader uniform | 内置 shader 与 global/layer uniform 已存在;没有粒子 uniform contract |
| 缓存/生命周期 | FileSource、tile cache、style snapshot 和 gfx resource 生命周期已存在;粒子数据生命周期未实现 |
可复用基础设施只能确认到通用地图层:FileSource/离线 fixture 测试体系、immutable snapshot、thread pool、gfx Context/Texture/Buffer/RenderTarget/Drawable、shader registry 和 frame observer。未来粒子数据格式、解析、插值、上传策略均未确定。
## 15. 性能与线程风险
| 风险 | 等级 | 当前证据与边界 |
|---|---|---|
| JS 高频循环/每帧 JS→Native | 尚无法判断 | 当前无 JS/RN 宿主;若未来新增,仓库没有现成桥接基准 |
| 大数组/JSON/ArrayBuffer 跨桥复制 | 高 | 当前 MapLibre update 是 immutable snapshot/异步传递,未提供粒子大数组桥接;跨平台高层接口若采用逐帧对象传输会形成风险 |
| Native→GPU 重复复制 | 中 | gfx upload/update 能力存在,但粒子资源尚未定义,复制次数无法实测 |
| Texture 高频重建 | 中 | Context 有 Texture2D/DynamicTexture;RenderCustomLayer 示例未提供粒子资源复用策略 |
| Camera 变化触发大规模重算 | 中 | Map update 会重新计算 RenderTree、tile/layer evaluation;未来粒子若绑定 camera 需避免把每帧状态误当 style/source 变更 |
| React re-render | 尚无法判断 | 没有 React/RN 工程 |
| 外部 Overlay 不同步 | 低(当前)/架构禁止 | 当前没有外部粒子 Overlay;正式规则也禁止该路径 |
| Android/iOS 实现分叉 | 高 | 同 Core 但高层 CustomLayer、context/encoder、线程和 surface 生命周期不同 |
| GPU context/surface loss | 高 | Android `onSurfaceCreated` 会 mark context lost 并重建 backend/Renderer;CustomLayer 有 contextLost;iOS 后台/视图重建也涉及资源生命周期 |
| MapView 销毁后资源泄漏 | 中 | Core/RenderCustomLayer 有 deinitialize/contextLost 路径,raw `long` host 和应用归属仍需后续设计验证 |
| Render pass 顺序/状态污染 | 高 | CustomLayer 在 translucent pass;raw backend callback 需要遵守当前 render pass 和状态恢复约束 |
## 16. 当前架构图
### 16.1 App / MapLibre 总体调用图
```text
Android 示例或 iOS 示例
└→ 平台 MapView
└→ 平台 RendererFrontend / MapObserver adapter
└→ mbgl::Map
├→ Style / Source / Layer snapshot
├→ Worker + FileSource
└→ Renderer → RenderTree → gfx → 平台 GPU backend
```
### 16.2 JS → Native → MapLibre 调用链
```text
JS/RN 业务入口:不存在于当前仓库
└→ RN MapLibre binding:不存在于当前仓库
└→ Android Java MapView 或 iOS MLNMapView:由外部宿主提供
└→ JNI/ObjC++ adapter
└→ mbgl::Map → RendererFrontend → Renderer → GPU
```
### 16.3 Android
```text
UI thread: MapView / MapLibreMap / NativeMapView JNI
└→ AndroidRendererFrontend + AsyncTask/mailbox
Render thread: MapRenderer → Renderer → RenderOrchestrator
└→ OpenGL EGL 或 Vulkan device/swapchain
└→ SurfaceView/TextureView → GPU
Workers/FileSource: tile/style parsing、layout、I/O
```
### 16.4 iOS
```text
main/UI: MLNMapView(UIView) + CADisplayLink
└→ MLNMapViewImpl(MapObserver) + RendererFrontend
draw callback: GLKView 或 MTKView
└→ mbgl::Renderer → RenderTree → gfx command encoder
└→ EAGL/OpenGL 或 Metal command buffer → GPU
```
### 16.5 数据与渲染边界
```text
FileSource / tile / offline fixture
→ immutable Source/Layer/UpdateParameters
→ RenderTree / RenderSource / RenderLayer
→ gfx Texture/Buffer/Drawable/RenderTarget
→ backend command encoding
→ GPU
粒子 field / particle state / trail:当前不存在;未来位置为 [未确认]
```
## 17. 关键结论分类
### 17.1 已确认事实
1. 当前仓库是 MapLibre Native monorepo,不是 RN app。
2. Android 13.4.1、iOS 6.28.0 和 Core commit 已从版本文件/基线确认。
3. Core 的真实主链是 Map → RendererFrontend → UpdateParameters → Renderer → RenderTree/Orchestrator → Layer/Drawable → gfx backend。
4. Android 有 OpenGL/Vulkan flavor、独立 renderer thread、JNI Map peer 和实验性 CustomLayer。
5. iOS 有 `MLNMapView`、CADisplayLink、OpenGL/Metal backend、Native CustomStyleLayer 和 Drawable 示例。
6. Core CustomLayer render 参数包含相机和 projection 信息;CustomLayer callback 在地图 render pass 内执行。
7. 现有 fixture 只有目录占位;没有粒子运行时、格点模型、粒子 shader 或速度场解析器。
8. Core 已有可复用的 immutable snapshot、worker/FileSource、gfx texture/buffer/render target/drawable/upload/shader registry 基础设施。
### 17.2 当前限制
- 没有 RN 页面、RN binding、Metro/Babel、NativeModule/TurboModule/Fabric,因此不能从本仓库确认 RN 到 MapView 的调用和生命周期。
- Android CustomLayer 的 Java API 使用 raw native host 指针且标记 experimental;与 iOS CustomStyleLayer 的上下文能力不对称。
- 正式粒子数据格式、内存模型、插值、GPU state、trail 和跨平台资源生命周期尚不存在。
- 不可把现有 RasterSource 直接解释为 Raster/Vector Field source;它当前只处理常规 raster tile/image。
- 本次未进行设备运行时验证,不能把静态可见接口写成已验证的帧率、延迟或 context-loss 行为。
### 17.3 候选扩展点(不作最终选择)
1. Core `CustomLayerHost`/`RenderCustomLayer` 的同帧 callback。
2. Core `CustomDrawableLayer` + DrawableBuilder/Tweaker/gfx resource。
3. 新的隔离 Core particle render layer,与现有 RasterLayer/WeatherColorLayer 分离。
4. 现有 Source/Tile/FileSource/fixture 管线的薄接入,用于离线数据而非直接改造 RasterSource。
5. 平台薄接入:Android 复用 renderer surface/context 生命周期,iOS 复用 CustomStyleLayer/Metal 或 Core adapter 的同帧 transform。
这些只是调查候选;本报告不决定 CPU/GPU、CustomLayer 最终是否采用、OpenGL/Vulkan/Metal 路线、数据纹理格式、shader 架构或线程模型。
### 17.4 未确认问题
| 问题 | 当前无法确认原因 | 是否阻塞 `20_架构设计` | 最小确认方式 |
|---|---|---|---|
| 实际目标产品的 RN 宿主和 binding 版本 | 不在当前仓库 | 否;当前阶段可先按 Native Core 事实设计边界 | 后续由产品宿主提供 package/native 工程清单 |
| Android 业务侧能否稳定创建并持有 CustomLayerHost | 当前只有 raw `long` 示例和 JNI peer | 否,但影响平台薄接入细节 | 单独最小 Android 示例运行验证,不改本阶段代码 |
| iOS 当前正式发布构建究竟启用 OpenGL 还是 Metal | 源码同时支持,构建宏由外部工程决定 | 否;设计需保留 backend 分支 | 读取目标 Pod/Xcode 构建设置或做最小设备验证 |
| Core render thread 与 iOS app main thread 的确切调度策略 | 静态源码能确认 DisplayLink/MTKView callback,不能确认目标宿主调度 | 否 | 目标 iOS scheme 的运行时线程标记/最小 sample |
| 粒子目标场的具体文件格式、范围、时间步长 | fixture 只有占位目录且设计尚未冻结 | 否;属于后续数据设计输入 | 由设计阶段冻结离线 fixture contract |
| 目标设备的 GPU 特性、纹理格式和可用 compute 能力 | 当前没有设备矩阵或运行时探测结果 | 否;可在设计中标注 capability gate | Android/iOS 设备专项最小验证 |
| 旋转、pitch、wrap、frustum 下粒子坐标的最终视觉约束 | Core matrix 可用,但粒子规则尚未定义 | 否 | 进入 `20_架构设计` 后以 fixture 和截图测试确认 |
## 18. 是否追加调查、是否进入 20_架构设计
选择:**A,可以直接进入 `20_架构设计`。**
理由:本次已确认项目实际形态、Core 调用链、Android/iOS render lifecycle、CustomLayer/gfx 位置、相机矩阵来源、frame driver、现有数据基础设施和主要风险。未确认项主要是外部目标宿主、运行时设备和未来数据 contract,不满足“必须先追加一个调查任务”的必要条件;它们可作为 `20_架构设计` 的约束和验证清单。
下一阶段必须继续遵守:不复制 Mapbox 新源码或 Windy APK 代码;粒子模块高度隔离;WeatherColorLayer 与 RasterParticleLayer 分离;Wind=GLOBAL;Wave/Tide/Current=OCEAN_ONLY 并共用 Land/Ocean Mask;第一阶段只使用离线 fixture;平台只做薄接入。
## 19. 本次未做和边界声明
- 未创建粒子实现、shader、CustomLayer 实现、Native Module 或架构重构。
- 未升级/安装 npm、Gradle、Pod 或其他依赖。
- 未修改 Android/iOS 原生工程、Gradle/Podfile/Metro/Babel/TypeScript 配置。
- 未运行构建和测试,因为任务明确为只读调查,且构建可能写入产物;历史基线构建 PASS 仅引用已有基线记录,不作为本次重新验证结果。
- 按用户前置要求,未自行创建或改写 Step 文件;现有 `docs/50_Step记录/STEP-000_完整环境初始化_v1.md` 的文件内容仍为“进行中”,本报告不改动其历史状态。