392 lines
31 KiB
Markdown
392 lines
31 KiB
Markdown
# 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` 的文件内容仍为“进行中”,本报告不改动其历史状态。
|