31 KiB
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、iOSios-v6.28.0、共用 Core18545ca10d296704b2cba675b182ff2c8da24588。实际版本文件为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 主要目录
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连接 CoreMap和 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 三条:
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
iOS App/Example
→ MLNMapView(UIView)
→ MLNMapViewImpl(MapObserver adapter)
→ mbgl::Map + RendererFrontend
→ RenderOrchestrator / RenderTree / RenderLayer / gfx
→ MLNMapViewOpenGLImpl(GLKView) 或 MLNMapViewMetalImpl(MTKView)
→ EAGL/CAMetalLayer → GPU
直接 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()调用 JNInativeRender();SurfaceView 由MapLibreSurfaceView的 render thread 请求 dirty/continuous frame,TextureView 由TextureViewRenderThread的事件队列和 request flag 驱动。 AndroidRendererFrontend::update()保存最新UpdateParameters,异步任务把它交给MapRenderer并调用 JavarequestRender()。C++ render thread 只持有一次 render 所需的参数快照。- Native MapObserver 事件由 JNI 转回 Java
MapChangeReceiver,包括 camera will/is/did、地图/样式加载、render frame start/end、idle、source 和 shader 事件。
6.3 iOS
MLNMapView创建 CoreMap和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/CoreCustomDrawableLayer:示例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和 iOSMLNComputedShapeSource已暴露,能提供按 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 没有一个类型安全的CustomLayerRenderParametersAPI,应用级可直接使用范围未确认。- 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;不是 RNView。 - renderer:Java
MapRenderer仅负责生命周期、JNI 和调度;C++MapRenderer持有 Android backend、CoreRenderer、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 调 CorejumpTo/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 边界
[不存在的 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 之间异步保存并加锁,渲染使用最新快照;JavarequestRender只触发帧,不传粒子数据。 - 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 总体调用图
Android 示例或 iOS 示例
└→ 平台 MapView
└→ 平台 RendererFrontend / MapObserver adapter
└→ mbgl::Map
├→ Style / Source / Layer snapshot
├→ Worker + FileSource
└→ Renderer → RenderTree → gfx → 平台 GPU backend
16.2 JS → Native → MapLibre 调用链
JS/RN 业务入口:不存在于当前仓库
└→ RN MapLibre binding:不存在于当前仓库
└→ Android Java MapView 或 iOS MLNMapView:由外部宿主提供
└→ JNI/ObjC++ adapter
└→ mbgl::Map → RendererFrontend → Renderer → GPU
16.3 Android
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
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 数据与渲染边界
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 已确认事实
- 当前仓库是 MapLibre Native monorepo,不是 RN app。
- Android 13.4.1、iOS 6.28.0 和 Core commit 已从版本文件/基线确认。
- Core 的真实主链是 Map → RendererFrontend → UpdateParameters → Renderer → RenderTree/Orchestrator → Layer/Drawable → gfx backend。
- Android 有 OpenGL/Vulkan flavor、独立 renderer thread、JNI Map peer 和实验性 CustomLayer。
- iOS 有
MLNMapView、CADisplayLink、OpenGL/Metal backend、Native CustomStyleLayer 和 Drawable 示例。 - Core CustomLayer render 参数包含相机和 projection 信息;CustomLayer callback 在地图 render pass 内执行。
- 现有 fixture 只有目录占位;没有粒子运行时、格点模型、粒子 shader 或速度场解析器。
- 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 候选扩展点(不作最终选择)
- Core
CustomLayerHost/RenderCustomLayer的同帧 callback。 - Core
CustomDrawableLayer+ DrawableBuilder/Tweaker/gfx resource。 - 新的隔离 Core particle render layer,与现有 RasterLayer/WeatherColorLayer 分离。
- 现有 Source/Tile/FileSource/fixture 管线的薄接入,用于离线数据而非直接改造 RasterSource。
- 平台薄接入: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的文件内容仍为“进行中”,本报告不改动其历史状态。