docs: freeze particle architecture investigation and design
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
This commit is contained in:
773
docs/10_架构/20_粒子系统总体架构设计_v1.md
Normal file
773
docs/10_架构/20_粒子系统总体架构设计_v1.md
Normal file
@@ -0,0 +1,773 @@
|
||||
# MapLibreParticleLab 粒子系统总体架构设计 v1
|
||||
|
||||
设计日期:2026-08-29
|
||||
|
||||
基线分支:`particle/main`
|
||||
|
||||
基线 Core:`18545ca10d296704b2cba675b182ff2c8da24588`
|
||||
|
||||
性质:正式总体架构设计,不包含实现
|
||||
|
||||
## 1. 设计输入与适用范围
|
||||
|
||||
本设计以项目总则、项目规范、`PARTICLE_BASELINE.md`、`docs/20_调查/10_架构调查报告_v1.md` 和当前 MapLibre Native 源码为事实基础。当前仓库不是 React Native 工程,因此架构从 Native Core、Android/iOS 平台薄接入和 MapLibre gfx 开始,不定义不存在的 JS 每帧运行时。
|
||||
|
||||
现有 `docs/10_架构/MapLibre粒子总体架构_v1.md` 只给出了 Layer 顺序和 GPU 组件候选,与本设计没有冲突。本文件把这些候选收敛为正式模块、接口和生命周期;后续以本文件作为 `30_最小技术验证` 和实现任务的主要架构输入。
|
||||
|
||||
固定长期边界继续有效:clean-room 独立实现;正式实现不用 React Native/WebView/外部 Overlay;Android/iOS 共用 C++ Core;OpenGL 为 Android 主开发 backend;WeatherColorLayer 与 RasterParticleLayer 分离;Wind=`GLOBAL`;Wave/Tide/Current=`OCEAN_ONLY` 并共用 Land/Ocean Mask;第一阶段只用离线 fixture。
|
||||
|
||||
## 2. 核心决策摘要
|
||||
|
||||
1. **唯一正式主路线**:新增独立 Core `RasterParticleLayer`、`RenderRasterParticleLayer` 和隔离的 `mbgl::particle` 模块,进入 Style/RenderTree,通过 MapLibre gfx 执行 GPU simulation、trail 和最终合成。
|
||||
2. `CustomLayerHost` 与 `CustomDrawableLayer` 不作为正式粒子引擎宿主;需要时只可用于一次性技术探针,不能进入公共 API、产品依赖或长期双轨。
|
||||
3. 新增 `RasterArraySource` 作为 Field 数据边界;WeatherColorLayer 与 RasterParticleLayer 可引用同一 Field snapshot 和 Mask,但拥有不同 RenderLayer、GPU 状态和生命周期。
|
||||
4. 每帧由 MapLibre render loop 驱动;活动粒子通过 Core render state 持续声明 `needsRepaint`,不建立第二套 UI/JS 动画循环。
|
||||
5. Camera/Matrix 只从当前 render frame 的 `PaintParameters/TransformParameters/TransformState` 读取。
|
||||
6. 第一版 GPU simulation 使用跨 backend 的 render-pass ping-pong,不以 compute 为前置;compute 仅作为后续 capability-gated 优化。
|
||||
7. 第一阶段正式视觉范围包含粒子、颜色映射和屏幕空间 trail;最小验证可先无 trail 验证状态更新,但在第一阶段冻结前必须通过 trail 验证。
|
||||
8. 第一阶段实现/日常验证范围是 Android OpenGL;Android Vulkan 在第一阶段冻结前做 gfx 兼容 gate;iOS OpenGL/Metal 保留相同 Core contract,在独立后续阶段验证。
|
||||
9. 下一阶段选择 **B:进入 `30_最小技术验证`**,不直接进入正式实现。
|
||||
|
||||
## 3. 候选路线比较与选择
|
||||
|
||||
| 候选 | 优点 | 不作为正式主路线的原因 | 定位 |
|
||||
|---|---|---|---|
|
||||
| `CustomLayerHost/RenderCustomLayer` | 已有同帧 transform 和 backend callback;示例可运行 | Android 使用 raw host 指针且 API 标记 experimental;平台 context/encoder 不对称;公共生命周期容易平台化 | 只允许一次性验证,不进入正式 API |
|
||||
| `CustomDrawableLayer` | 已使用 gfx DrawableBuilder,跨 backend 程度较高 | 当前接口偏向线、面、符号、几何 drawable;不天然表达 velocity、state ping-pong、trail target 和 source contract | 作为 gfx/Drawable 设计参考 |
|
||||
| 独立 Core particle layer | 可进入 Style、RenderTree、layer order、repaint、context lifecycle;可隔离数据与 GPU 资源;平台只需薄 wrapper | 需要新增 Layer/Source/RenderLayer、shader 注册和少量 RenderTarget 调度薄接入 | **正式唯一主路线** |
|
||||
| MapView Overlay/独立 renderer | 开发表面上独立 | 与地图 frame、matrix、depth/layer order、context 和生命周期不同步;违反固定规则 | 拒绝 |
|
||||
|
||||
正式路线的退出条件不是“以后再决定”:从第一个可保留实现开始,产品 API 和生产资源均必须归属独立 Core layer。若技术验证临时使用 CustomLayer,验证结束后仅保留测试结论;正式 `RasterParticleLayer` 可渲染第一张 fixture 且通过 lifecycle gate 时,任何 CustomLayer 粒子探针不得继续作为产品路径。
|
||||
|
||||
主路线风险是现有通用 RenderTarget/LayerGroup 是否足以表达多 offscreen pass、跨 backend 的资源状态转换,以及新增 Layer 对 `needsRepaint` 的最小接入是否能够保持 thin-hook。风险不通过平台分叉规避,而由第 26 节的独立 RenderLayer、State/Trail ping-pong 和 context recreation 验证决定;若验证失败,只允许调整粒子专用 gfx 抽象或必要薄 hook,不回退到 Overlay、JS loop 或两套平台引擎。
|
||||
|
||||
## 4. 粒子系统总体架构图
|
||||
|
||||
```text
|
||||
Host Native API
|
||||
├─ RasterArraySource ───────────────┐
|
||||
├─ WeatherColorLayer ───────┐ │
|
||||
└─ RasterParticleLayer ─────┼───────┘
|
||||
▼
|
||||
Style immutable snapshot
|
||||
▼
|
||||
RenderTree / RenderOrchestrator / layer order
|
||||
┌────────────┴────────────┐
|
||||
▼ ▼
|
||||
RenderWeatherColorLayer RenderRasterParticleLayer
|
||||
│
|
||||
mbgl::particle Core
|
||||
Field / Simulation / Mask / GPU / Controller
|
||||
│
|
||||
gfx Texture/Buffer/RenderTarget/Drawable/Shader
|
||||
│
|
||||
OpenGL / Vulkan / Metal backend → GPU
|
||||
```
|
||||
|
||||
正式 Layer 顺序由宿主 Style 控制,推荐:
|
||||
|
||||
```text
|
||||
Base Map
|
||||
→ WeatherColorLayer
|
||||
→ RasterParticleLayer
|
||||
→ Navigation Reference / coast / labels
|
||||
→ AIS / Route / Ownship / Alarm
|
||||
```
|
||||
|
||||
WeatherColor 保持定量色场;导航信息通过更高 Style Layer 显示,不以降低天气层透明度作为默认策略。
|
||||
|
||||
## 5. Core 模块边界与建议目录
|
||||
|
||||
```text
|
||||
include/mbgl/style/sources/
|
||||
└─ raster_array_source.hpp 公共 Field Source API
|
||||
include/mbgl/style/layers/
|
||||
├─ weather_color_layer.hpp 定量色场 Layer API
|
||||
└─ raster_particle_layer.hpp 粒子 Layer API
|
||||
|
||||
src/mbgl/particle/
|
||||
├─ field/ Field descriptor/snapshot/validation
|
||||
├─ data_adapter/ fixture 与未来数据适配
|
||||
├─ simulation/ 状态语义、时间步、respawn
|
||||
├─ render/ pass 描述、frame parameters
|
||||
├─ gpu/ velocity/state/trail/mask resources
|
||||
├─ mask/ Land/Ocean Mask snapshot/cache/contract
|
||||
└─ controller/ 状态机、revision、repaint、错误
|
||||
|
||||
src/mbgl/style/sources/ RasterArraySource Impl
|
||||
src/mbgl/style/layers/ 两个 Layer Impl/properties
|
||||
src/mbgl/renderer/sources/ RenderRasterArraySource
|
||||
src/mbgl/renderer/layers/
|
||||
├─ render_weather_color_layer.*
|
||||
└─ render_raster_particle_layer.*
|
||||
src/mbgl/layermanager/ 两个 LayerFactory 的薄注册
|
||||
shaders/particle/ update/draw/trail/composite shader contract
|
||||
platform/android/ 类型安全 Java/Kotlin + JNI 薄 wrapper
|
||||
platform/darwin/ ObjC/Swift Source/Layer peer 薄 wrapper
|
||||
```
|
||||
|
||||
目标仍是约 95% 粒子代码位于新增文件。允许修改的官方既有文件只限 Layer/Source factory 注册、shader/build 清单、RenderTarget/repaint 所需的最小 hook;不得把算法写入 Map、RasterLayer、Camera、MapView 或 backend 主文件。
|
||||
|
||||
### 5.1 Particle Field
|
||||
|
||||
`ParticleFieldDescriptor` 描述:field id/type、revision、语义单位、bounds、CRS、grid registration、尺寸、U/V、NoData、时间片、display domain 和数据版本。`ParticleFieldSnapshot` 是 descriptor 与只读数据的不可变组合。
|
||||
|
||||
Field 数据进入 Core 后统一为:U 为正东、V 为正北、canonical simulation unit 为米/秒。原始数据单位转换属于 Data Adapter,不进入每帧 shader 的业务分支。
|
||||
|
||||
### 5.2 Particle Data Adapter
|
||||
|
||||
第一阶段 `OfflineFixtureParticleAdapter` 负责读取、校验、字节序转换并产生 immutable snapshot。未来真实天气协议必须通过新的 adapter 转成同一 snapshot,Renderer 不认识 URL、HTTP、GRIB、厂商字段名或 Server 协议。
|
||||
|
||||
### 5.3 Particle Simulation
|
||||
|
||||
Core 定义状态语义、固定/受限 timestep、field sample、NoData、mask、respawn 和 deterministic seed。正式每粒子更新在 GPU;CPU 只负责 fixture 解码、验证、测试参考值和资源描述,不允许每帧 CPU 粒子循环冒充正式实现。
|
||||
|
||||
### 5.4 Particle Render
|
||||
|
||||
负责 simulation pass、particle draw、trail fade/accumulation、最终 translucent composite、viewport 和当前 frame transform。它不拥有天气色场 RenderLayer。
|
||||
|
||||
### 5.5 Particle GPU Resources
|
||||
|
||||
负责 velocity texture、state A/B、trail A/B、mask texture、uniform、shader set、context generation、创建/释放/重建。公共逻辑只使用 gfx 抽象;backend-specific 格式映射、barrier/encoder 细节留在 gfx/backend。
|
||||
|
||||
### 5.6 Particle Mask
|
||||
|
||||
Mask 是可共享的 immutable snapshot 与 GPU resource,不属于某一个 RasterParticleLayer。Wave/Tide/Current 和未来 OCEAN_ONLY field 通过 mask id/revision 引用同一资源。
|
||||
|
||||
### 5.7 Particle Controller / Layer State
|
||||
|
||||
Controller 只管理 enable、source/mask revision、map/data/GPU readiness、状态机、repaint 和错误;UI/宿主不得持有或销毁 GPU texture。
|
||||
|
||||
## 6. 与 Style、RenderTree 和 gfx 的关系
|
||||
|
||||
- `RasterArraySource` 进入 Style Source 系统,负责提供 immutable Field snapshot,不直接绘制。
|
||||
- `WeatherColorLayer` 和 `RasterParticleLayer` 分别引用 source id,进入 Style Layer、LayerFactory、RenderLayer 和 RenderTree。
|
||||
- `RenderRasterParticleLayer` 最终合成属于 `Translucent` pass,按 Style layer index 与其他地图内容排序。
|
||||
- simulation 与 trail 使用独立 offscreen render targets,在主 buffer 合成前完成;最终 composite drawable 仍在该 Layer 的 translucent 位置绘制。
|
||||
- Layer 活动且可见时参与 `needsRepaint`;首次 enable、数据/选项变化和恢复时调用一次现有 invalidate/`triggerRepaint` 路径唤醒渲染。
|
||||
- 每帧 transform 直接来自当前 `PaintParameters`,不缓存 UI 线程推测值。
|
||||
|
||||
### 6.1 复用的 gfx 能力
|
||||
|
||||
| 现有能力 | 用途 |
|
||||
|---|---|
|
||||
| `Texture2D` | velocity、state、mask、trail texture |
|
||||
| `DynamicTexture` | 仅在技术验证证明适合局部 field 更新时使用;不是默认 state 资源 |
|
||||
| Buffer/UniformBuffer | quad/point geometry、frame/field/options uniform |
|
||||
| `RenderTarget` | simulation output、trail accumulation |
|
||||
| `DrawableBuilder` | 最终 composite 与可由通用 drawable 表达的 draw geometry |
|
||||
| `DrawableTweaker` | 同帧 transform/options uniform 更新 |
|
||||
| Shader registry | 后端 shader 注册与查询 |
|
||||
| UploadPass | field/mask/静态资源低频上传 |
|
||||
| CommandEncoder | pass encoding/present,由 render thread 和 gfx 管理 |
|
||||
|
||||
新增粒子专用抽象仅描述语义:`ParticleVelocityResource`、`ParticleStateResources`、`ParticleTrailResources`、`ParticleMaskResource`、`ParticleShaderSet`、`ParticleFrameParameters`。这些类型不能暴露 GL/Vulkan/Metal handle。
|
||||
|
||||
## 7. WeatherColorLayer 与 RasterParticleLayer
|
||||
|
||||
| 边界 | WeatherColorLayer | RasterParticleLayer |
|
||||
|---|---|---|
|
||||
| 目的 | 定量连续色场 | 方向、运动、动态流线 |
|
||||
| RenderLayer | 独立 `RenderWeatherColorLayer` | 独立 `RenderRasterParticleLayer` |
|
||||
| GPU 状态 | color ramp/field sampling 资源 | state A/B、simulation、trail、particle draw |
|
||||
| 动画 | 不承担粒子动画 | 由 MapLibre frame loop 驱动 |
|
||||
| 可共享 | RasterArraySource snapshot、grid metadata、time slice、Mask snapshot/cache | 同左 |
|
||||
| 不共享 | RenderLayer、粒子 state、trail target、动画状态 | 同左 |
|
||||
|
||||
二者不是同一 Layer 的模式开关。删除或禁用粒子 Layer 不影响 WeatherColor;更换天气颜色样式不重置 Particle State,除非 Field revision、Mask 或影响 simulation 的参数同时改变。
|
||||
|
||||
## 8. Field 与显示域模型
|
||||
|
||||
`FieldType` 第一版至少包含 `WIND`、`WAVE`、`TIDE`、`CURRENT`。`DisplayDomain` 只有 `GLOBAL` 和 `OCEAN_ONLY`。
|
||||
|
||||
规则由 Core validation 强制执行:
|
||||
|
||||
- WIND → `GLOBAL`,陆地与海洋均可显示,不依赖 Land/Ocean Mask。
|
||||
- WAVE/TIDE/CURRENT → `OCEAN_ONLY`,必须有有效 Mask;mask 缺失时 fail closed,不在陆地或未知区继续显示。
|
||||
- 未来新增 OCEAN_ONLY field 复用同一 Mask contract,不创建 field-specific mask engine。
|
||||
|
||||
Field snapshot 的 revision 单调变化。只改变 UI 可视属性不生成 Field revision;U/V、bounds、grid、time slice、NoData 或数据版本变化必须生成新 revision。
|
||||
|
||||
## 9. 第一阶段离线 fixture contract v1
|
||||
|
||||
### 9.1 目录与文件
|
||||
|
||||
每个 fixture 目录固定包含:
|
||||
|
||||
```text
|
||||
particle-fixtures/<fixture-id>/
|
||||
├─ metadata.json
|
||||
├─ field.f32
|
||||
└─ mask.u8 仅 OCEAN_ONLY/Mask 场景必需
|
||||
```
|
||||
|
||||
`metadata.json` 最小结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "maplibre-particle-fixture/v1",
|
||||
"fixtureId": "uniform-east",
|
||||
"fieldType": "WIND",
|
||||
"displayDomain": "GLOBAL",
|
||||
"dataVersion": 1,
|
||||
"timeIndex": 0,
|
||||
"timestamp": null,
|
||||
"crs": "EPSG:4326",
|
||||
"bounds": { "west": -180.0, "south": -85.0, "east": 180.0, "north": 85.0 },
|
||||
"grid": {
|
||||
"width": 64,
|
||||
"height": 32,
|
||||
"registration": "node",
|
||||
"origin": "north-west",
|
||||
"rowOrder": "north-to-south",
|
||||
"columnOrder": "west-to-east"
|
||||
},
|
||||
"vector": {
|
||||
"uPositive": "east",
|
||||
"vPositive": "north",
|
||||
"unit": "m/s",
|
||||
"layout": "interleaved-uv",
|
||||
"encoding": "float32-le",
|
||||
"noData": -9999.0,
|
||||
"file": "field.f32"
|
||||
},
|
||||
"mask": { "mode": "none" },
|
||||
"integrity": {
|
||||
"fieldByteLength": 16384,
|
||||
"fieldSha256": "<sha256>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
实际 width/height 和 checksum 由 fixture 生成任务填写,上例数值只展示 contract,不冻结具体场景分辨率。
|
||||
|
||||
### 9.2 Binary 规则
|
||||
|
||||
- `field.f32`:little-endian IEEE-754 float32,row-major,按 `u0,v0,u1,v1...` 交错;文件长度必须等于 `width × height × 2 × 4`。
|
||||
- 任一 cell 的 U 或 V 等于 `noData`,整个 cell 无效;不依赖 NaN payload 表达 NoData。
|
||||
- adapter 校验有限值、长度、checksum、bounds、grid 和 domain 后,转换为 canonical immutable Field。
|
||||
- `mask.u8`:row-major、与 metadata 指定 grid 对齐;`0=LAND`、`255=OCEAN`,`1..254` 为未来 coverage/shore antialias 保留,v1 validator 不接受未声明的中间值。
|
||||
- Mask 文件长度必须等于 mask width × height,并记录 byte length/SHA256。
|
||||
- v1 fixture 的 `bounds.west < bounds.east`,单文件不跨反经线;全球使用 `[-180,180]`。运行时 Field/Mask 模型仍支持以 unwrapped longitude interval 表达未来反经线数据。
|
||||
- `timeIndex` 必须存在;有真实时间语义时同时填写 UTC ISO-8601 `timestamp`,确定性 synthetic fixture 可令其为 `null`。
|
||||
|
||||
### 9.3 Mask metadata
|
||||
|
||||
OCEAN_ONLY 场景把 `mask` 写为:
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "embedded",
|
||||
"maskId": "coast-v1",
|
||||
"dataVersion": 1,
|
||||
"file": "mask.u8",
|
||||
"encoding": "uint8-land-ocean",
|
||||
"width": 64,
|
||||
"height": 32,
|
||||
"registration": "node",
|
||||
"origin": "north-west",
|
||||
"noDataPolicy": "land"
|
||||
}
|
||||
```
|
||||
|
||||
### 9.4 预留 fixture 覆盖
|
||||
|
||||
| fixture | 合同目标 |
|
||||
|---|---|
|
||||
| `uniform-east` | WIND/GLOBAL,常量 +U,验证东西向和平移/wrap |
|
||||
| `uniform-north` | WIND/GLOBAL,常量 +V,验证纬向和 projection |
|
||||
| `vortex` | WIND/GLOBAL 或明确区域 bounds,验证旋转方向和采样连续性 |
|
||||
| `shear` | WIND/GLOBAL 或区域 bounds,验证速度梯度和 timestep |
|
||||
| `coastal-mask` | CURRENT/OCEAN_ONLY,必须含 mask,验证海岸裁剪和 fail-closed |
|
||||
|
||||
后续 fixture 可增加 west/south、双向 vortex、divergence/convergence,但不改变 v1 contract。
|
||||
|
||||
## 10. Offline Fixture 到 GPU 数据流
|
||||
|
||||
```text
|
||||
Offline Fixture files
|
||||
→ Worker: read / checksum / decode / validate
|
||||
→ ParticleFieldSnapshot(shared immutable, revision N)
|
||||
→ Map/Style thread: RasterArraySource Impl snapshot
|
||||
→ RendererFrontend immutable UpdateParameters
|
||||
→ Render thread: compare revision
|
||||
→ UploadPass: velocity texture + optional mask texture
|
||||
→ ParticleState A/B initialization
|
||||
→ Simulation pass
|
||||
→ Particle draw + Trail A/B
|
||||
→ RasterParticleLayer translucent composite
|
||||
→ MapLibre main render pass → GPU
|
||||
```
|
||||
|
||||
### 10.1 低频步骤
|
||||
|
||||
fixture I/O、checksum、decode、unit normalization、Field validation、CPU snapshot 创建、velocity/mask upload、shader/resource 创建。仅在 source revision、mask revision、context generation、viewport target size或影响资源布局的选项变化时执行。
|
||||
|
||||
### 10.2 每帧步骤
|
||||
|
||||
读取当前 frame transform;计算受限 timestep;state A→B 或 B→A simulation;particle draw;trail fade/accumulate;final composite;更新 rendering stats;在仍活动时声明下一帧 repaint。
|
||||
|
||||
### 10.3 Camera 变化时
|
||||
|
||||
更新 transform、pixel ratio、viewport、visible field intersection、world-wrap selection 等 uniform。v1 不重建 velocity texture,不重新解析 Field,不重建 Particle State;viewport resize 只重建/清空 trail target。任何 pan/zoom/bearing/pitch/resize 的有效变化清空旧 screen-space trail,避免错误残影;后续可验证 trail reprojection,但不属于 v1。
|
||||
|
||||
## 11. 线程模型
|
||||
|
||||
```text
|
||||
Map/UI thread
|
||||
├─ 创建/删除 Source、Layer
|
||||
├─ 修改业务 options、enable、source/mask reference
|
||||
└─ 发布 immutable Impl/snapshot revision
|
||||
│
|
||||
▼
|
||||
Worker thread
|
||||
├─ fixture I/O、decode、validate、unit normalize
|
||||
└─ 产出 shared_ptr<const ParticleFieldSnapshot>
|
||||
│ ownership/immutable snapshot
|
||||
▼
|
||||
Render thread
|
||||
├─ snapshot revision diff
|
||||
├─ GPU resource create/upload/release/recreate
|
||||
├─ 当前 frame Camera/Matrix
|
||||
└─ encode simulation/trail/draw/composite
|
||||
▼
|
||||
GPU
|
||||
└─ velocity sample、particle update、trail、rasterization
|
||||
```
|
||||
|
||||
- Field 解码:Style 所属 worker/thread pool 或独立受控 worker;不得在 render frame 内 decode。
|
||||
- CPU 插值:只用于 validator、测试参考或查询 API;正式每帧 field sampling 在 GPU。
|
||||
- GPU resource 创建/释放:持有有效 `gfx::BackendScope` 的 render thread。
|
||||
- 每帧 simulation:render thread 编码,GPU 执行。
|
||||
- Camera Matrix:当前 `PaintParameters.transformParams/state`。
|
||||
- Data 交接:`shared_ptr<const ParticleFieldSnapshot>`/MapLibre Immutable snapshot + monotonic revision;render thread 原子替换当前引用后再上传。
|
||||
- 避免复制:decode 只产生一次 owning buffer;source/render snapshot 共享 const ownership;只在 GPU revision 变化时上传,不通过 Java/ObjC/JS 每帧传大数组。
|
||||
- context loss:平台/backend 通知 Core;render thread 丢弃 GPU generation,CPU snapshot 保留;新 context 下 render thread 重建。
|
||||
|
||||
## 12. Camera / Matrix 同帧同步
|
||||
|
||||
```text
|
||||
Map camera mutation
|
||||
→ TransformState snapshot in UpdateParameters
|
||||
→ RenderTree TransformParameters
|
||||
→ PaintParameters for current frame
|
||||
├─ state: center/zoom/bearing/pitch/size/frustum
|
||||
├─ proj/aligned/near-clipped matrix
|
||||
└─ pixel ratio/frame count/time
|
||||
→ ParticleFrameParameters
|
||||
→ simulation visible domain + particle draw/composite
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- pan/zoom/bearing/pitch 全部使用当前 render frame snapshot;不接受 UI/JS 另行推算的 Camera。
|
||||
- position state 存储与 Camera 无关的 Field-normalized/geographic 语义位置;绘制时转为 canonical Web Mercator/world coordinate,再使用当前 frame matrix。
|
||||
- resize/pixel ratio 改变更新 viewport uniform,并重建 trail target;state 和 velocity texture保持。
|
||||
- GLOBAL field 的 longitude 在 canonical interval 内 wrap;绘制时选择距离当前 map center 最近的 unwrapped world copy。
|
||||
- 区域 Field 以 descriptor 的 unwrapped longitude interval 判断范围;v1 fixture 不跨反经线,运行时 contract 保留跨反经线能力。
|
||||
- projection/unprojection 只复用 MapLibre Core utility/transform,不在平台层复制公式。
|
||||
- v1 Camera 任意有效变化清空 screen-space trail;粒子位置继续保留并以新 transform 绘制。
|
||||
|
||||
## 13. 帧驱动与动画时钟
|
||||
|
||||
唯一 frame driver 是 MapLibre render loop。
|
||||
|
||||
1. enable 或资源 ready 时,Controller 通过现有 Core invalidate/`triggerRepaint` 路径唤醒第一帧。
|
||||
2. `RENDERING` 且可见、Field/GPU ready 时,Particle Render 在当前 RenderTree 中声明 `needsRepaint=true`,使 MapLibre 请求下一帧。
|
||||
3. 地图静止但粒子活动时,以上机制持续;不依赖 camera transition。
|
||||
4. disable、Layer 不可见、App background、View 不可见、Field 不可用、ERROR 或 layer removal 时停止额外 repaint。
|
||||
5. App foreground/context restore 后,只有资源重新 READY 且 layer enabled/visible 才恢复。
|
||||
6. simulation time 使用 monotonic render time;采用固定逻辑步长加有界 accumulator,过长 pause 后不追赶所有丢失步,避免恢复瞬间爆炸。
|
||||
|
||||
平台 `requestRender`/CADisplayLink 只是现有 MapLibre frontend 的落地机制,不形成第二套粒子时钟。
|
||||
|
||||
## 14. GPU 粒子状态模型
|
||||
|
||||
### 14.1 冻结的逻辑状态
|
||||
|
||||
每个 particle 逻辑上至少包含:
|
||||
|
||||
- `position`:Field-normalized 2D position,可无损映射到 descriptor bounds;与屏幕 Camera 无关。
|
||||
- `age`:秒或归一化寿命语义。
|
||||
- `seed`:稳定随机/respawn 输入。
|
||||
- `alive/valid`:可由 age/特殊值派生,不要求独立 channel。
|
||||
|
||||
Exact texture format、channel packing 和最终最大 particle count **不在本阶段冻结**;它们必须在 Camera correctness 和 backend capability 验证后选择。
|
||||
|
||||
### 14.2 Ping-pong 与 simulation
|
||||
|
||||
- `Particle State A/B` 同尺寸,当前帧只读 current、只写 next,帧末交换。
|
||||
- texture texel count 至少覆盖 particle count;多余 texel 标记 inactive。
|
||||
- velocity texture 保存 canonical U/V 与 validity;shader 按 descriptor 将物理速度转换为 Field-normalized displacement。
|
||||
- timestep 使用受限固定步长;speed factor 是业务参数,不能改变原始 Field snapshot。
|
||||
- Field bounds 外、NoData、age 到期或 OCEAN_ONLY mask 非 ocean 时执行 deterministic respawn。
|
||||
- respawn domain 优先取 Field bounds 与当前可见 geographic bounds 的有效交集;交集为空时不绘制且不忙循环重建资源。
|
||||
- viewport clipping 只影响 draw,不以离开屏幕立即销毁 state;region/domain 无效时才 respawn。
|
||||
|
||||
### 14.3 为什么第一阶段不用 compute 作为前置
|
||||
|
||||
当前目标同时覆盖 Android OpenGL/Vulkan 与后续 iOS Metal,而现有跨 backend 最稳定的共同语义是 render pass、texture、render target 和 shader registry。第一阶段以 full-screen/texture render-pass simulation 验证主架构,避免先冻结 Vulkan/Metal compute pipeline、workgroup 和 barrier contract。
|
||||
|
||||
后续 compute 只能作为 `ParticleSimulationBackend` 的 capability-gated 实现;逻辑 state、Field、Mask、Controller 和公共 API 不变。无 compute 时继续使用 render-pass simulation,不回退到正式 CPU 每粒子循环。
|
||||
|
||||
## 15. Trail 架构
|
||||
|
||||
第一阶段正式视觉范围包含 trail,但最小验证顺序允许先验证无 trail 粒子点/短线,再增加 trail gate。
|
||||
|
||||
```text
|
||||
Trail Current texture
|
||||
→ Fade pass → Trail Next
|
||||
→ Particle draw blend into Trail Next
|
||||
→ Composite Trail Next at RasterParticleLayer position
|
||||
→ swap Current/Next
|
||||
```
|
||||
|
||||
- 使用 screen-space Trail A/B,避免依赖所有 backend 对同一 attachment 同时读写的行为。
|
||||
- accumulation/fade 与 particle draw 是独立 pass;最终 composite 使用 premultiplied alpha contract。
|
||||
- resize/pixel ratio/render target scale 变化:重建并 clear A/B。
|
||||
- pan/zoom/bearing/pitch/frustum 有效变化:v1 clear A/B,不错误复用旧屏幕轨迹。
|
||||
- Field/mask revision、simulation reset、disable、style reload、context loss、App background、ERROR、layer removal:clear 或释放。
|
||||
- opacity/trail fade 的纯样式变化可从下一帧生效;是否清空由 property 语义明确,改变 fade 不强制清空,改变坐标/particle size 可清空。
|
||||
- context loss 后不恢复旧 trail 内容,只重建空 target。
|
||||
|
||||
## 16. Land/Ocean Mask 设计
|
||||
|
||||
### 16.1 数据与资源
|
||||
|
||||
Mask 数据属于独立 `ParticleMask` 模块。CPU 层是 `LandOceanMaskSnapshot`:mask id/revision、CRS、bounds、grid、registration、coverage policy 和 immutable byte data。GPU 层是按 `(maskId, revision, backend context generation)` 缓存的 texture,可被多个 OCEAN_ONLY Layer 共享。
|
||||
|
||||
第一阶段使用 fixture `mask.u8` GPU texture;CPU geometry 只允许作为未来 mask 生产/测试输入,不在每帧做 point-in-polygon。未来可扩展 tiled/pyramid mask,但保持同一 snapshot/sample contract。
|
||||
|
||||
### 16.2 坐标、分辨率和采样
|
||||
|
||||
- CRS 为 EPSG:4326,bounds/grid registration 与 Field contract 相同。
|
||||
- fixture v1 mask 默认与对应 Field 同 bounds/width/height;架构允许独立分辨率,shader 使用各自 transform。
|
||||
- binary 只接受 LAND/OCEAN;未知/NoData 对 OCEAN_ONLY 按 LAND 处理。
|
||||
- v1 采用 nearest/明确阈值采样,避免线性过滤把海值渗入陆地。未来 coverage mask 可显式声明 coverage encoding 后再允许平滑。
|
||||
- zoom 不触发 mask 重建;未来 pyramid 只在 LOD 变化时切换 resource revision。
|
||||
- bearing/pitch 不改变 mask sample:particle 在 geographic/Field space 采样,再与地图使用同帧 transform 绘制。
|
||||
- GLOBAL Wind 完全绕过 mask branch/resource requirement。
|
||||
|
||||
### 16.3 wrap、反经线与海岸 artifact
|
||||
|
||||
- 全球 mask longitude 周期 repeat;区域 mask 使用 unwrapped interval。
|
||||
- 反经线数据拆分或使用 unwrapped bounds,不能用 `west > east` 的模糊单文件语义。
|
||||
- 海岸 artifact 风险包括 mask 分辨率不足、Field 与 Mask registration 不同、nearest 锯齿、trail 残留和海图海岸语义差异。
|
||||
- v1 通过严格 metadata validation、nearest sample、未知即陆地、Camera 变化清 trail 和 coastal fixture 控制;最终生产 Mask 来源/分辨率留待后续数据设计与 NavSea 语义 Gate。
|
||||
|
||||
### 16.4 更新、缓存和恢复
|
||||
|
||||
- 仅 mask id/revision/context generation 改变时重新 upload;Camera 和每帧 animation 不重建。
|
||||
- CPU snapshot 可跨 context 保留;GPU texture 由 render thread 在新 context 重建。
|
||||
- mask 创建失败时 OCEAN_ONLY layer 进入 ERROR/不绘制,不能降级为 GLOBAL。
|
||||
|
||||
## 17. Land/Ocean Mask 共用图
|
||||
|
||||
```text
|
||||
LandOceanMaskSnapshot(maskId, revision)
|
||||
│
|
||||
▼
|
||||
Shared ParticleMaskCache
|
||||
├─ CPU immutable bitmap
|
||||
└─ GPU mask texture per context generation
|
||||
│
|
||||
┌──────┼─────────┬──────────────┐
|
||||
▼ ▼ ▼ ▼
|
||||
Wave Tide Current Future OCEAN_ONLY
|
||||
|
||||
Wind/GLOBAL ─────────────────────→ bypass Mask
|
||||
```
|
||||
|
||||
## 18. 生命周期状态机
|
||||
|
||||
```text
|
||||
DISABLED
|
||||
└─ enable → WAITING_MAP
|
||||
WAITING_MAP
|
||||
├─ map/render context ready, no field → WAITING_FIELD
|
||||
└─ disable/remove/destroy → DISABLED
|
||||
WAITING_FIELD
|
||||
├─ valid field snapshot → PREPARING
|
||||
└─ invalid field → ERROR
|
||||
PREPARING
|
||||
├─ GPU resources ready → READY
|
||||
├─ context lost → CONTEXT_LOST
|
||||
└─ resource/shader failure → ERROR
|
||||
READY
|
||||
├─ enabled + visible + foreground → RENDERING
|
||||
├─ background/invisible → PAUSED
|
||||
└─ new field/mask revision → PREPARING
|
||||
RENDERING
|
||||
├─ background/invisible → PAUSED
|
||||
├─ context lost → CONTEXT_LOST
|
||||
├─ new resource revision → PREPARING
|
||||
├─ disable/remove → DISABLED
|
||||
└─ failure → ERROR
|
||||
PAUSED
|
||||
├─ foreground/visible, resources valid → READY → RENDERING
|
||||
├─ context lost → CONTEXT_LOST
|
||||
└─ disable/remove/destroy → DISABLED
|
||||
CONTEXT_LOST
|
||||
├─ new context + CPU snapshot valid → PREPARING
|
||||
└─ disable/remove/destroy → DISABLED
|
||||
ERROR
|
||||
├─ valid new field/options/manual retry/context generation → PREPARING
|
||||
└─ disable/remove/destroy → DISABLED
|
||||
```
|
||||
|
||||
责任:
|
||||
|
||||
- Map/Style thread 管理 Layer/Source 存在性、enable 和 immutable revision。
|
||||
- Worker 管理 decode job 的取消和 CPU snapshot。
|
||||
- Render thread 管理所有 GPU resource 和 context generation。
|
||||
- GPU 不拥有跨 Map 生命周期的资源责任。
|
||||
- Style reload 删除旧 RenderLayer 时先停止 repaint,再释放 GPU;同 id 新 layer 按新实例从 WAITING_FIELD/PREPARING 开始。
|
||||
- Map destroy/layer removal 是终止边界:取消 worker callback、断开 observer、释放 render resources,不自动复活。
|
||||
|
||||
## 19. 每帧渲染链图
|
||||
|
||||
```text
|
||||
MapLibre requests frame
|
||||
→ RenderTree prepared with current TransformState
|
||||
→ RenderRasterParticleLayer checks state/revisions
|
||||
→ Upload only if Field/Mask/resource revision changed
|
||||
→ Simulation pass: State Current → State Next
|
||||
→ Trail fade: Trail Current → Trail Next
|
||||
→ Draw particles into Trail Next (Mask/domain applied)
|
||||
→ Main translucent pass: composite Trail Next by layer index
|
||||
→ present
|
||||
→ active? needsRepaint=true : stop extra repaint
|
||||
```
|
||||
|
||||
## 20. Android 薄接入
|
||||
|
||||
```text
|
||||
Kotlin/Java Source + Layer API
|
||||
→ typed JNI peer (no raw host pointer)
|
||||
→ Core RasterArraySource / RasterParticleLayer
|
||||
→ RenderRasterParticleLayer / mbgl::particle / gfx
|
||||
→ Android OpenGL or Vulkan backend
|
||||
→ existing MapRenderer requestRender + Surface lifecycle
|
||||
```
|
||||
|
||||
- Java/Kotlin 只表达 source/layer/options/status;不接收 texture id、不执行每帧 simulation、不持有 GPU resources。
|
||||
- 正式接口需要类型安全 JNI wrapper;禁止把 `CustomLayer(String,long)` 的 raw `long CustomLayerHost` 作为粒子 API。
|
||||
- SurfaceView/TextureView 的 surface/context recreation 统一转为 Core context generation/lifecycle;不在两种 View 内分别写 particle engine。
|
||||
- OpenGL/Vulkan 差异留在 gfx shader/resource implementation;Core logical resources 和 pass graph 相同。
|
||||
- repaint 复用 `Map::triggerRepaint` → `AndroidRendererFrontend` → `MapRenderer.requestRender`,平台不增加粒子 timer。
|
||||
- 第一阶段 Android OpenGL 完成日常开发;Vulkan 在冻结前验证 state/trail/mask/context-loss,不另写业务逻辑。
|
||||
|
||||
## 21. iOS 薄接入
|
||||
|
||||
```text
|
||||
ObjC/Swift Source + Layer peer
|
||||
→ Core RasterArraySource / RasterParticleLayer
|
||||
→ RenderRasterParticleLayer / mbgl::particle / gfx
|
||||
→ OpenGL or Metal backend
|
||||
→ existing MLNMapView RendererFrontend / CADisplayLink / draw callback
|
||||
```
|
||||
|
||||
- `MLNCustomStyleLayer`、`MLNCustomDrawableStyleLayer` 和 `MLNBackendResource` 是已确认能力与验证参考,不是正式粒子引擎所有者。
|
||||
- 正式 iOS API 应是 Core Source/Layer 的 ObjC peer;ObjC/Swift 不保存 particle state、不编写第二套 simulation。
|
||||
- CADisplayLink 继续驱动 MapLibre view;粒子通过 Core `needsRepaint` 使 MapLibre 保持活动,不添加独立 display link。
|
||||
- Metal/OpenGL resource 与 encoder 只在 gfx/backend 内可见;平台 wrapper 不暴露给业务 API。
|
||||
- iOS 最终 backend 和设备能力在后续专项验证;不能据当前源码宣称 Metal/OpenGL 已通过粒子 gate。
|
||||
|
||||
## 22. 最小公共 API 设计
|
||||
|
||||
以下为语义 API,不冻结具体 C++/Java/ObjC 方法签名:
|
||||
|
||||
```text
|
||||
RasterArraySource(id)
|
||||
setField(ParticleFieldSnapshot)
|
||||
clearField()
|
||||
getRevision()/getDescriptor()
|
||||
|
||||
RasterParticleLayer(id, sourceId)
|
||||
setEnabled(bool)
|
||||
setDisplayDomain(DisplayDomain) 受 FieldType 固定规则校验
|
||||
setParticleOptions(ParticleOptions)
|
||||
setMaskId(optional maskId) OCEAN_ONLY 必需
|
||||
getParticleOptions()
|
||||
getStatus()/getLastError()
|
||||
|
||||
Style
|
||||
add/remove RasterArraySource
|
||||
add/remove RasterParticleLayer
|
||||
observe ParticleStatus changes 低频状态,不是每帧 telemetry
|
||||
```
|
||||
|
||||
数据更新通过 Source revision,而不是把 field 数组设为 Layer paint property。公共 API 禁止 GL texture id、VkImage、MTLTexture、command encoder、backend workgroup、raw native pointer。
|
||||
|
||||
Layer display domain 必须符合业务规则;例如尝试把 CURRENT 改为 GLOBAL 返回 validation error,不静默放宽。
|
||||
|
||||
## 23. 配置分层
|
||||
|
||||
### 23.1 稳定业务参数
|
||||
|
||||
- particle count(目标值,受 capability/memory clamp)
|
||||
- speed factor
|
||||
- lifetime
|
||||
- opacity
|
||||
- particle size/line width
|
||||
- trail enabled
|
||||
- trail fade
|
||||
- color ramp/reference
|
||||
- enabled/visibility
|
||||
|
||||
业务参数具有 backend-independent 范围、默认值和 validation;具体默认值在最小验证后冻结。
|
||||
|
||||
### 23.2 内部 backend 参数
|
||||
|
||||
- state/velocity/trail texture format
|
||||
- buffer alignment、binding index
|
||||
- render target channel type/scale
|
||||
- shader variant/defines
|
||||
- compute workgroup/barrier(未来)
|
||||
- upload staging、sampler/filter 细节
|
||||
|
||||
这些由 capability profile 与 gfx implementation 决定,不能出现在产品业务 API 或 fixture contract。
|
||||
|
||||
## 24. 错误与降级策略
|
||||
|
||||
| 情况 | 行为 | 重试 |
|
||||
|---|---|---|
|
||||
| metadata/binary/checksum/Field 无效 | Source 拒绝新 revision;Layer 保留最后一个有效 snapshot或进入 WAITING_FIELD/ERROR | 仅新数据 revision |
|
||||
| Field 与 viewport 无交集 | READY 但不绘制、不持续空忙 repaint | Camera/Field 改变时恢复 |
|
||||
| OCEAN_ONLY Mask 缺失/无效 | fail closed,Layer ERROR;地图继续 | 有效 mask revision |
|
||||
| GPU resource 创建失败 | 释放本次部分资源,Layer ERROR;其他 Layer 不受影响 | 一次受控低质量 profile 可选,否则人工/新配置 |
|
||||
| backend 不支持必要 render target/format | Layer ERROR/UNSUPPORTED;不回退 CPU 正式粒子 | backend capability 变化或实现更新 |
|
||||
| context loss | 停止 repaint,丢弃 GPU generation,保留 CPU snapshot,进入 CONTEXT_LOST | 新 context 自动 PREPARING |
|
||||
| shader 创建失败 | 释放 particle shader/resource,Layer ERROR | 新 context/shader revision 时一次 |
|
||||
| memory pressure | 先释放 trail、闲置 cache;允许按内部 profile 降低 trail scale/particle count并记录状态 | 有界一次;仍失败则 ERROR |
|
||||
| Layer removal/Map destroy | 取消任务、停止 callback/repaint、释放 GPU/CPU ownership | 不自动重试 |
|
||||
|
||||
任何错误不得使 MapLibre Map、其他 Style Layer 或平台 View 崩溃。自动重试必须按 revision/context generation 去重,禁止每帧重试形成死循环。
|
||||
|
||||
## 25. 性能预算与后续测量
|
||||
|
||||
以下是设计目标,不是已验证结论。正式 PASS 必须绑定具体设备、分辨率、backend、build type、fixture 和 particle count。
|
||||
|
||||
### 25.1 第一阶段 Gate 指标
|
||||
|
||||
| 指标 | 设计目标/测量档位 |
|
||||
|---|---|
|
||||
| particle count | 16K correctness、32K target、64K stress;最终默认/最大值待验证 |
|
||||
| FPS/frame time | 声明设备上 32K 的目标下限 30 FPS/p95 ≤33.3ms;不得写成已达到 |
|
||||
| Particle CPU time | 32K target p95 ≤3ms/frame,不含 MapLibre 基础渲染 |
|
||||
| Particle GPU time | 32K target p95 ≤12ms/frame,分别记录 simulation/trail/composite |
|
||||
| Particle GPU memory | 1080p、32K target ≤64MiB,含 state/trail/velocity/mask,不含 base map |
|
||||
| Field upload | 1024×1024 U/V target ≤200ms,且只在 revision 变化时发生 |
|
||||
| Context recreation | target ≤1s 恢复可绘制;旧 trail 不要求恢复 |
|
||||
| correctness | pan/zoom/bearing/pitch/resize/wrap/mask/context loss 无崩溃、无明显错位 |
|
||||
|
||||
若没有声明的 baseline 设备,只能报告测量值,不能判定性能 PASS。第一阶段必须提供 CPU/GPU time、资源字节数、upload/recreate time 的 instrumentation;60 FPS、最终最大粒子数、最佳格式和厂商优化属于后续优化。
|
||||
|
||||
## 26. 后续最小技术验证清单
|
||||
|
||||
| 优先级 | 验证 | 是否阻塞正式实现 | 可能改变什么 | 最小方式 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 独立 Core RenderLayer 在 Android OpenGL 中进入正确 Style order、读取同帧 transform并持续/停止 repaint | 是 | 主入口与 frame driver | 仅一层、单 fixture、固定少量 state 的 RenderTree probe;不做完整视觉 |
|
||||
| 2 | gfx render-pass State A/B + Trail A/B 在 Android OpenGL/Vulkan 的创建、采样、交换、barrier和 context recreation | 是 | render-pass simulation 是否可作为共同基线、资源抽象边界 | 最小 ping-pong pattern + trail target,Surface recreate 后重建;记录两 backend |
|
||||
| 3 | Camera/wrap/Mask contract | 是 | position semantic、trail clear 和 Mask sampling | uniform-east/north + coastal-mask,覆盖 pan/zoom/bearing/pitch/resize/反经线邻近视图 |
|
||||
| 4 | iOS Core layer 薄 peer 与 Metal/OpenGL 同帧 gfx 资源可达性 | 不阻塞 Android 第一阶段;阻塞 iOS 实现 | iOS wrapper/backend adapter 细节,不得改变 Core engine | Mac/iOS 同 commit 的最小 Source/Layer/render-target probe |
|
||||
|
||||
Android raw `CustomLayerHost` 不再是正式路线的阻塞项,不专门扩展产品 API;若独立 Layer probe 需要对照,可运行现有 CustomLayer 示例,但其结果只作诊断。
|
||||
|
||||
## 27. 明确不采用的架构
|
||||
|
||||
| 不采用方案 | 原因 |
|
||||
|---|---|
|
||||
| React Native/JS 每帧粒子 | 当前仓库无 RN runtime;高频大数组/Camera/frame 跨桥;无法与 render frame 严格同步 |
|
||||
| MapView 外部 Overlay | 独立 surface/frame/matrix/layer order;Camera/pitch/context lifecycle 易错;违反固定边界 |
|
||||
| 每种海洋场各自一套 Mask | 数据重复、海岸语义分叉、缓存和 artifact 不一致;违反统一 Mask 决策 |
|
||||
| 直接改造 RasterLayer 承担粒子 | 普通 RasterLayer 是静态 raster semantics;会污染官方层并混合数据、simulation、trail |
|
||||
| Android/iOS 各写一套引擎 | 状态、Field、Mask、生命周期和算法会分叉;违反共享 C++ Core |
|
||||
| CustomLayer 作为长期产品主宿主 | Android raw host 与 iOS context API 不对称,生命周期/API 难以稳定;不利于 Source/Layer/RenderTree 正式集成 |
|
||||
| 每帧重建 Field texture | 造成 upload、allocation、driver 和内存抖动;Field 应按 revision 缓存 |
|
||||
| 每帧大数组跨 Java/ObjC/JS | 复制、序列化和 GC/ARC 压力;只允许低频 immutable snapshot/revision |
|
||||
| backend-specific GPU API 暴露给业务 | 破坏跨平台、测试和升级边界;handle 只能在 gfx/backend 内部 |
|
||||
| 正式 CPU 每粒子循环 | 不满足 GPU Particle Engine 目标;CPU 只做 decode/validation/reference test |
|
||||
| compute-only 第一版 | 当前跨 backend capability 未验证,会过早冻结 workgroup/barrier;先用 render-pass simulation |
|
||||
|
||||
## 28. Camera / Matrix 同步图
|
||||
|
||||
```text
|
||||
UI/Native camera API
|
||||
→ mbgl::Map / TransformState
|
||||
→ immutable UpdateParameters
|
||||
→ RenderTree TransformParameters
|
||||
→ current PaintParameters
|
||||
└→ ParticleFrameParameters
|
||||
├→ visible Field intersection / world copy
|
||||
├→ GPU simulation uniforms
|
||||
└→ draw/composite matrix
|
||||
|
||||
Independent UI/JS camera guess ─X→ Particle Renderer
|
||||
```
|
||||
|
||||
## 29. Core 模块边界图
|
||||
|
||||
```text
|
||||
Style API
|
||||
├─ RasterArraySource ─→ ParticleFieldSnapshot
|
||||
├─ WeatherColorLayer ─→ RenderWeatherColorLayer
|
||||
└─ RasterParticleLayer
|
||||
│
|
||||
▼
|
||||
RenderRasterParticleLayer ← current PaintParameters
|
||||
│
|
||||
▼
|
||||
mbgl::particle
|
||||
├─ DataAdapter → Field
|
||||
├─ Controller → lifecycle/repaint/revision
|
||||
├─ Simulation → logical state/timestep/respawn
|
||||
├─ Mask → shared domain filter
|
||||
├─ GPUResources → velocity/state/trail/uniform
|
||||
└─ Render → offscreen passes/composite
|
||||
│
|
||||
▼
|
||||
gfx/backend only
|
||||
```
|
||||
|
||||
## 30. 架构冻结项
|
||||
|
||||
以下内容从本设计起冻结,后续普通实现 Step 不得自行改变:
|
||||
|
||||
1. 主路线是独立 Core `RasterParticleLayer/RenderRasterParticleLayer`,进入 Style/RenderTree/gfx;CustomLayer/CustomDrawable 不是正式宿主。
|
||||
2. Android/iOS 共用 C++ `mbgl::particle` Field、Simulation、Render、GPU resource contract、Mask、Controller 和状态机;平台只有类型 peer、backend lifecycle 与 capability 薄接入。
|
||||
3. `RasterArraySource` 是 Field 数据边界;Field snapshot immutable、revisioned、canonical U-east/V-north/SI unit。
|
||||
4. WeatherColorLayer 与 RasterParticleLayer 是不同 Layer/RenderLayer/GPU 状态/动画生命周期,只共享数据 snapshot、grid/time metadata 和 Mask。
|
||||
5. Wind=`GLOBAL`;Wave/Tide/Current=`OCEAN_ONLY`;OCEAN_ONLY 统一共享 Land/Ocean Mask,缺失时 fail closed。
|
||||
6. fixture contract v1 使用 `metadata.json + field.f32 + optional/required mask.u8`、float32 little-endian interleaved UV、uint8 mask、checksum 和明确 grid/bounds/NoData。
|
||||
7. frame driver 是 MapLibre render loop;active layer 贡献 `needsRepaint`,没有独立 UI/JS 主时钟。
|
||||
8. Camera/Matrix 来自同一 render frame 的 Paint/Transform parameters;position state 与 Camera 无关。
|
||||
9. GPU simulation 的第一共同基线是 render-pass State A/B ping-pong;正式 CPU 每粒子循环和 compute-only 第一版均拒绝。
|
||||
10. 第一阶段正式视觉包含粒子、颜色映射和 screen-space Trail A/B;Camera 变化清 trail,不做 v1 trail reprojection。
|
||||
11. GPU resource 只由 render thread/gfx 创建;context loss 保留 CPU snapshot、丢弃 GPU generation并重建空 trail。
|
||||
12. Android 第一阶段主验证 OpenGL,冻结前必须做 Vulkan gfx gate;iOS OpenGL/Metal 使用同一 Core contract,后续 Mac/iOS 专项验证。
|
||||
13. 公共 API 不暴露 backend handle、raw host pointer或每帧大数组;数据更新通过 Source immutable revision。
|
||||
14. 下一阶段只执行第 26 节四项最小技术验证,不重新讨论总体架构。
|
||||
|
||||
## 31. 本阶段不得冻结的内容
|
||||
|
||||
- 已达到的 FPS、最终最大/default particle count。
|
||||
- 最佳 state/velocity/trail texture format 和 channel packing。
|
||||
- Vulkan/Metal compute workgroup、barrier 和厂商优化。
|
||||
- 最终生产 Mask 来源、分辨率与 NavSea 海岸语义。
|
||||
- 真实天气 Server/GRIB/厂商数据格式。
|
||||
- NavSea/RN 最终 Host API 和产品桥接。
|
||||
- 最终生产设备能力矩阵、iOS 最终 backend。
|
||||
- Camera 验证前的最终 Particle State 物理 packing。
|
||||
|
||||
这些内容只能由后续最小技术验证、数据设计和产品 Gate 冻结。
|
||||
|
||||
## 32. 下一阶段判定
|
||||
|
||||
选择:**B,进入 `30_最小技术验证`。**
|
||||
|
||||
原因:独立 Core layer、render-pass ping-pong/trail 在 OpenGL/Vulkan 的实际资源行为、context recreation、同帧 Camera/wrap/Mask correctness 和 iOS backend 薄接入仍缺运行时证据。这些未知项会影响实现细节或架构可行性,不能直接进入正式实现;但它们已被压缩为四项最小验证,不需要重新开展总体架构讨论。
|
||||
|
||||
## 33. 本次设计边界
|
||||
|
||||
本阶段未创建 Source、Layer、Renderer、Shader、GPU resource、CustomLayerHost、JNI/ObjC 类或 fixture 数据;未修改生产源码、构建配置、依赖、lock、Step 或项目总则;未运行会写工程状态的构建/测试。
|
||||
391
docs/20_调查/10_架构调查报告_v1.md
Normal file
391
docs/20_调查/10_架构调查报告_v1.md
Normal file
@@ -0,0 +1,391 @@
|
||||
# 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` 的文件内容仍为“进行中”,本报告不改动其历史状态。
|
||||
754
docs/40_任务/10_架构调查指令书_v1.md
Normal file
754
docs/40_任务/10_架构调查指令书_v1.md
Normal file
@@ -0,0 +1,754 @@
|
||||
# MapLibreParticleLab 10_架构调查指令书 v1
|
||||
|
||||
## 1. 任务基本信息
|
||||
|
||||
- 项目:MapLibreParticleLab
|
||||
- 大阶段:`10_架构调查`
|
||||
- 当前基线分支:`particle/main`
|
||||
- 当前前置状态:总则 v1 已提交并推送
|
||||
- 任务性质:只读架构调查
|
||||
- 是否允许修改业务代码:否
|
||||
- 是否允许升级或安装依赖:否
|
||||
- 是否允许顺手修复发现的问题:否
|
||||
|
||||
---
|
||||
|
||||
## 2. 任务目标
|
||||
|
||||
对 MapLibreParticleLab 当前真实代码、依赖、原生工程、MapLibre 接入方式和渲染架构进行一次完整调查。
|
||||
|
||||
本阶段的目的不是设计最终粒子系统,而是回答:
|
||||
|
||||
> 当前项目实际上是怎样运行的,MapLibre 实际经过哪些层完成地图渲染,未来粒子系统有哪些真实可用的接入位置,以及当前仍有哪些关键问题无法仅通过现有代码确认。
|
||||
|
||||
最终必须形成一份基于实际仓库源码和配置的中文架构调查报告,为下一阶段 `20_架构设计` 提供事实基础。
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心原则
|
||||
|
||||
### 3.1 以实际代码为唯一事实来源
|
||||
|
||||
不得根据:
|
||||
|
||||
- 记忆
|
||||
- MapLibre 一般架构
|
||||
- React Native 常见做法
|
||||
- 网上其他项目的实现
|
||||
- 对新版 MapLibre 的印象
|
||||
|
||||
推断当前项目架构。
|
||||
|
||||
所有结论必须优先来自当前 `particle/main` 实际源码、配置、依赖锁定文件和原生工程。
|
||||
|
||||
如果某项无法通过仓库确认,必须明确标记为:
|
||||
|
||||
`未确认`
|
||||
|
||||
不得自行补全。
|
||||
|
||||
### 3.2 本任务只调查,不设计
|
||||
|
||||
本任务不得直接决定:
|
||||
|
||||
- 最终粒子渲染技术路线
|
||||
- CPU 粒子还是 GPU 粒子
|
||||
- CustomLayer 是否作为最终方案
|
||||
- 是否修改 MapLibre Native
|
||||
- 是否自行创建原生 Renderer
|
||||
- 是否使用 Metal / OpenGL / Vulkan
|
||||
- 最终数据纹理格式
|
||||
- 最终 Shader 架构
|
||||
- 最终线程模型
|
||||
|
||||
可以列出候选扩展位置和现有限制,但不得提前作最终技术选型。
|
||||
|
||||
### 3.3 不预设多个调查任务
|
||||
|
||||
本次应尽可能一次完成整个 `10_架构调查`。
|
||||
|
||||
只有在调查后发现某个关键问题同时满足以下条件时,才建议追加新的调查任务:
|
||||
|
||||
1. 无法依靠当前项目源码确认;
|
||||
2. 该问题会直接影响下一阶段技术路线;
|
||||
3. 需要额外读取第三方源码、编译验证、最小实验或平台专项验证。
|
||||
|
||||
不得为了形式完整主动拆出大量 `10-01 / 10-02 / 10-03` 子任务。
|
||||
|
||||
---
|
||||
|
||||
## 4. 开始前检查
|
||||
|
||||
开始调查后先确认以下信息:
|
||||
|
||||
- [ ] 当前仓库确实为 MapLibreParticleLab
|
||||
- [ ] 当前分支为 `particle/main`
|
||||
- [ ] 读取当前 Git 状态
|
||||
- [ ] 确认工作区是否干净
|
||||
- [ ] 找到并完整阅读当前“总则 v1”
|
||||
- [ ] 确认项目根目录结构
|
||||
- [ ] 确认主要源码目录
|
||||
- [ ] 确认 Android / iOS 原生目录是否存在
|
||||
- [ ] 确认依赖清单及 lock 文件
|
||||
|
||||
如果当前分支或代码状态与上述基线明显不符:
|
||||
|
||||
不要修改仓库状态。
|
||||
|
||||
继续进行能够完成的只读调查,并在报告中明确记录实际状态。
|
||||
|
||||
---
|
||||
|
||||
## 5. 必做调查内容
|
||||
|
||||
### 5.1 项目整体架构
|
||||
|
||||
调查当前项目的真实目录与模块组织。
|
||||
|
||||
至少确认:
|
||||
|
||||
- 项目入口
|
||||
- React Native / Expo / Bare RN 等实际工程形态
|
||||
- TypeScript / JavaScript 使用情况
|
||||
- Android 工程
|
||||
- iOS 工程
|
||||
- 公共源码目录
|
||||
- 地图相关源码目录
|
||||
- 实验代码目录
|
||||
- 数据处理目录
|
||||
- Shader 或图形相关资源目录
|
||||
- assets
|
||||
- scripts
|
||||
- docs
|
||||
- 测试目录
|
||||
- 构建配置
|
||||
- Metro 配置
|
||||
- Babel 配置
|
||||
- TypeScript 配置
|
||||
|
||||
报告中需要给出简化的当前项目结构图。
|
||||
|
||||
只列架构相关目录,不需要机械复制整个文件树。
|
||||
|
||||
### 5.2 当前依赖与版本
|
||||
|
||||
读取实际:
|
||||
|
||||
- `package.json`
|
||||
- lock 文件
|
||||
- Android Gradle 相关配置
|
||||
- iOS Pod 相关配置
|
||||
- 与地图和图形渲染相关的依赖配置
|
||||
|
||||
重点确认:
|
||||
|
||||
- React Native 版本
|
||||
- React 版本
|
||||
- MapLibre React Native 包及实际版本
|
||||
- MapLibre Android / iOS Native 版本,如能够从当前工程确认
|
||||
- Skia 或其他图形库是否存在
|
||||
- Reanimated 等与渲染相关的辅助依赖
|
||||
- OpenGL / Metal / WebGL / GPU 相关第三方库
|
||||
- Native Module / TurboModule / Fabric 相关配置
|
||||
|
||||
必须区分:
|
||||
|
||||
1. 直接依赖
|
||||
2. 间接依赖
|
||||
3. 原生 MapLibre 底层实际版本
|
||||
|
||||
如果底层版本无法确认,明确写“未确认”。
|
||||
|
||||
---
|
||||
|
||||
## 6. MapLibre 当前接入链调查
|
||||
|
||||
这是本任务重点。
|
||||
|
||||
必须从项目实际地图页面或地图 Host 开始向下追踪。
|
||||
|
||||
调查:
|
||||
|
||||
`业务/页面`
|
||||
↓
|
||||
`React Native Map 组件`
|
||||
↓
|
||||
`MapLibre React Native 封装`
|
||||
↓
|
||||
`Android / iOS Native View`
|
||||
↓
|
||||
`MapLibre Native`
|
||||
|
||||
需要确认当前代码实际使用的:
|
||||
|
||||
- MapView
|
||||
- Camera
|
||||
- Style
|
||||
- Source
|
||||
- Layer
|
||||
- ShapeSource
|
||||
- RasterSource
|
||||
- ImageSource
|
||||
- CustomLayer,如存在
|
||||
- 地图事件
|
||||
- 地图生命周期事件
|
||||
- onPress
|
||||
- camera change
|
||||
- style load
|
||||
- render/frame 相关事件
|
||||
|
||||
报告中必须给出一张文字形式的实际调用关系图。
|
||||
|
||||
例如:
|
||||
|
||||
`Screen → MapHost → MapView → RN MapLibre Binding → Native Map View`
|
||||
|
||||
但最终结构必须根据当前代码得出,不得照抄该例子。
|
||||
|
||||
---
|
||||
|
||||
## 7. 地图生命周期调查
|
||||
|
||||
调查当前代码能够观察或控制哪些生命周期阶段。
|
||||
|
||||
至少关注:
|
||||
|
||||
- 地图组件创建
|
||||
- Native Map 创建
|
||||
- Style 加载开始
|
||||
- Style Ready
|
||||
- 地图 Ready
|
||||
- Camera 变化
|
||||
- 地图移动
|
||||
- 地图缩放
|
||||
- 地图旋转
|
||||
- 每帧刷新能力
|
||||
- App 前后台
|
||||
- MapView unmount
|
||||
- Native 资源释放
|
||||
|
||||
明确:
|
||||
|
||||
### 当前 TS/RN 层已经直接获得哪些生命周期事件
|
||||
|
||||
以及:
|
||||
|
||||
### 哪些生命周期只存在 Native 或 MapLibre 内部,目前 JS 层拿不到
|
||||
|
||||
---
|
||||
|
||||
## 8. 自定义渲染扩展点调查
|
||||
|
||||
调查当前工程和当前依赖版本实际存在的自定义渲染能力。
|
||||
|
||||
重点寻找:
|
||||
|
||||
- CustomLayer
|
||||
- CustomLayerHost
|
||||
- Native Layer
|
||||
- Render Layer
|
||||
- MapLibre 自定义 layer 接口
|
||||
- 自定义 View Overlay
|
||||
- Surface
|
||||
- Texture
|
||||
- GL Context
|
||||
- Metal Context
|
||||
- Renderer hook
|
||||
- Render pass
|
||||
- Native callback
|
||||
- Frame callback
|
||||
|
||||
每个发现必须分类。
|
||||
|
||||
### A. 当前项目已经直接使用
|
||||
|
||||
### B. 当前依赖已经暴露,但项目尚未使用
|
||||
|
||||
### C. MapLibre Native 可能支持,但 RN 封装当前未暴露
|
||||
|
||||
### D. 当前代码无法确认
|
||||
|
||||
不要把 B/C/D 写成“当前已经支持”。
|
||||
|
||||
---
|
||||
|
||||
## 9. Android 渲染架构调查
|
||||
|
||||
单独调查 Android。
|
||||
|
||||
至少确认:
|
||||
|
||||
- MapLibre Android Native 如何被当前 RN 包引入
|
||||
- MapView 对应原生 View
|
||||
- renderer 所处代码边界
|
||||
- 当前工程是否显式涉及 OpenGL
|
||||
- 是否能够找到 render thread 相关实现或接口
|
||||
- 地图帧刷新由谁驱动
|
||||
- Camera / transform 数据当前在哪一层
|
||||
- Native Module 是否能够接触 MapView 实例
|
||||
- 是否存在可插入自定义渲染的明确入口
|
||||
- JS 与 Native 的当前桥接方式
|
||||
|
||||
如果当前项目使用 New Architecture,也需要说明其实际启用状态。
|
||||
|
||||
---
|
||||
|
||||
## 10. iOS 渲染架构调查
|
||||
|
||||
Android 与 iOS 不得合并推断。
|
||||
|
||||
单独确认:
|
||||
|
||||
- iOS MapLibre Native 依赖
|
||||
- MapView 原生类型
|
||||
- Metal / OpenGL 实际情况
|
||||
- renderer 生命周期
|
||||
- Camera / transform 数据
|
||||
- RN 到 Native 的连接方式
|
||||
- 是否能够确认自定义渲染接口
|
||||
- 当前 Native 层能够访问的 MapLibre 对象范围
|
||||
|
||||
如果 Android 与 iOS 能力不一致,必须明确列出。
|
||||
|
||||
---
|
||||
|
||||
## 11. JS / UI / Native / Render / GPU 边界
|
||||
|
||||
基于当前工程实际情况,整理线程和执行层级。
|
||||
|
||||
至少区分:
|
||||
|
||||
- JavaScript Thread
|
||||
- React Native UI/Main Thread
|
||||
- Android/iOS Native 层
|
||||
- MapLibre 内部 Worker
|
||||
- MapLibre Render Thread
|
||||
- GPU
|
||||
|
||||
如果某个平台实际名称有所不同,使用项目和 MapLibre 当前版本对应的真实名称。
|
||||
|
||||
重点调查:
|
||||
|
||||
- 当前高频数据从 JS 传 Native 会经过什么链路
|
||||
- 地图 Camera 高频变化如何传播
|
||||
- 是否存在 JS 每帧回调
|
||||
- 是否存在 Native 每帧回调
|
||||
- 是否存在直接 GPU 资源更新的位置
|
||||
- 哪些数据如果每帧跨 JS Bridge 会形成明显风险
|
||||
|
||||
这里只描述架构事实和风险,不提前设计最终优化方案。
|
||||
|
||||
---
|
||||
|
||||
## 12. Camera 与坐标变换能力
|
||||
|
||||
粒子系统未来必须与地图同步,因此重点调查当前代码能够获得哪些:
|
||||
|
||||
- center
|
||||
- zoom
|
||||
- bearing
|
||||
- pitch
|
||||
- bounds
|
||||
- viewport size
|
||||
- screen coordinate
|
||||
- geographic coordinate
|
||||
- projection / unprojection
|
||||
- camera matrix
|
||||
- projection matrix
|
||||
- view matrix
|
||||
- transform matrix
|
||||
|
||||
区分:
|
||||
|
||||
1. JS API 可以直接获取
|
||||
2. Native API 可以获取
|
||||
3. MapLibre 内部存在但当前封装未暴露
|
||||
4. 无法确认
|
||||
|
||||
尤其要明确:
|
||||
|
||||
> 当前是否可以获得足够信息,使自定义粒子渲染与 MapLibre 地图的平移、缩放、旋转、pitch 保持严格同步。
|
||||
|
||||
如果不能确认,不得猜测。
|
||||
|
||||
---
|
||||
|
||||
## 13. 帧驱动能力
|
||||
|
||||
调查未来粒子动画最关键的问题:
|
||||
|
||||
> 谁负责下一帧?
|
||||
|
||||
确认当前可利用的:
|
||||
|
||||
- requestAnimationFrame
|
||||
- RN animation frame
|
||||
- Reanimated frame callback
|
||||
- Native Choreographer / DisplayLink
|
||||
- MapLibre render loop
|
||||
- CustomLayer render callback
|
||||
- GPU render callback
|
||||
|
||||
需要说明:
|
||||
|
||||
- 哪些帧驱动来自地图 Renderer 本身
|
||||
- 哪些与地图 Renderer 独立
|
||||
- 如果使用独立动画循环,理论上可能出现什么同步问题
|
||||
|
||||
这里只进行架构分析。
|
||||
|
||||
---
|
||||
|
||||
## 14. 当前数据与实验代码调查
|
||||
|
||||
搜索当前仓库内已经存在的:
|
||||
|
||||
- particle
|
||||
- wind
|
||||
- current
|
||||
- wave
|
||||
- grid
|
||||
- vector field
|
||||
- raster
|
||||
- interpolation
|
||||
- texture
|
||||
- shader
|
||||
- velocity
|
||||
- animation
|
||||
- GPU
|
||||
- GL
|
||||
- Metal
|
||||
- MapLibre custom rendering
|
||||
|
||||
以及相关实验代码。
|
||||
|
||||
对发现的代码进行分类:
|
||||
|
||||
- 可复用基础代码
|
||||
- 仅实验用途
|
||||
- 与最终架构可能无关
|
||||
- 已弃用
|
||||
- 状态不明确
|
||||
|
||||
不得因为文件名相似就判定“可复用”。
|
||||
|
||||
需要查看真实实现。
|
||||
|
||||
---
|
||||
|
||||
## 15. 海洋/气象格点数据未来可能经过的现有层
|
||||
|
||||
本任务不设计数据系统,只调查当前项目有哪些已经存在的基础设施。
|
||||
|
||||
关注未来可能涉及:
|
||||
|
||||
`原始格点数据`
|
||||
→ `解析`
|
||||
→ `内存结构`
|
||||
→ `插值`
|
||||
→ `渲染资源`
|
||||
→ `地图显示`
|
||||
|
||||
调查当前是否已经存在:
|
||||
|
||||
- 数据 Fetch 层
|
||||
- ArrayBuffer
|
||||
- TypedArray
|
||||
- 二进制解码
|
||||
- Grid 数据模型
|
||||
- 经纬度格点模型
|
||||
- 缓存
|
||||
- Worker
|
||||
- Native Buffer
|
||||
- Texture Upload
|
||||
- Shader Uniform
|
||||
- 数据生命周期管理
|
||||
|
||||
报告只描述现状。
|
||||
|
||||
---
|
||||
|
||||
## 16. 性能风险调查
|
||||
|
||||
不得运行大型性能优化工作。
|
||||
|
||||
仅根据真实架构识别潜在风险。
|
||||
|
||||
至少关注:
|
||||
|
||||
- JS 高频循环
|
||||
- 每帧 JS → Native 调用
|
||||
- 大数组 Bridge 传输
|
||||
- JSON 序列化
|
||||
- ArrayBuffer 复制
|
||||
- Native → GPU 重复复制
|
||||
- Texture 高频重建
|
||||
- 地图 Camera 变化导致的大规模重新计算
|
||||
- React re-render
|
||||
- MapView Overlay 与地图不同步
|
||||
- Android/iOS 实现分叉
|
||||
- GPU context 共享风险
|
||||
- 地图销毁后的资源泄漏
|
||||
|
||||
风险需要标记等级:
|
||||
|
||||
- 高
|
||||
- 中
|
||||
- 低
|
||||
- 尚无法判断
|
||||
|
||||
---
|
||||
|
||||
## 17. 当前架构图
|
||||
|
||||
最终调查报告必须至少包含以下几个简化架构图。
|
||||
|
||||
全部使用 Markdown 文本图即可。
|
||||
|
||||
### 17.1 当前 App / MapLibre 总体调用图
|
||||
|
||||
### 17.2 JS → Native → MapLibre 调用链
|
||||
|
||||
### 17.3 Android 地图渲染相关层级
|
||||
|
||||
### 17.4 iOS 地图渲染相关层级
|
||||
|
||||
### 17.5 当前能够确认的数据与渲染边界
|
||||
|
||||
不得为了图看起来完整而补画未经确认的组件。
|
||||
|
||||
未知位置使用:
|
||||
|
||||
`[未确认]`
|
||||
|
||||
---
|
||||
|
||||
## 18. 关键结论分类
|
||||
|
||||
调查报告结尾必须将全部关键结论整理成四类。
|
||||
|
||||
### 18.1 已确认事实
|
||||
|
||||
只能放已有源码或配置证据的事实。
|
||||
|
||||
### 18.2 当前限制
|
||||
|
||||
当前架构真实存在的限制。
|
||||
|
||||
### 18.3 候选扩展点
|
||||
|
||||
当前看来可能承载未来粒子系统的位置。
|
||||
|
||||
这里只列候选,不做最终选择。
|
||||
|
||||
### 18.4 未确认问题
|
||||
|
||||
列出不能通过本次仓库静态调查确认,但会影响后续架构设计的问题。
|
||||
|
||||
每个“未确认问题”注明:
|
||||
|
||||
- 为什么当前无法确认
|
||||
- 是否会阻塞 `20_架构设计`
|
||||
- 如果需要继续调查,建议采用什么最小方式确认
|
||||
|
||||
---
|
||||
|
||||
## 19. 是否需要追加调查任务的判断
|
||||
|
||||
本次调查结束时必须明确给出:
|
||||
|
||||
### A. 可以直接进入 `20_架构设计`
|
||||
|
||||
或:
|
||||
|
||||
### B. 必须追加一个新的 `10_架构调查` 子任务
|
||||
|
||||
只有关键技术事实无法确认并且会直接影响架构选择时才能选择 B。
|
||||
|
||||
如果选择 B,只提出真正需要的最少调查任务。
|
||||
|
||||
不得为了保险而把所有未知问题拆成新任务。
|
||||
|
||||
---
|
||||
|
||||
## 20. 禁止事项
|
||||
|
||||
本任务禁止:
|
||||
|
||||
- 修改生产源码
|
||||
- 修改实验源码
|
||||
- 新建粒子实现
|
||||
- 修改 MapLibre 接入
|
||||
- 升级 MapLibre
|
||||
- 升级 React Native
|
||||
- 安装任何新 npm 包
|
||||
- 安装任何新 Pod
|
||||
- 修改 Gradle 依赖
|
||||
- 修改 Podfile
|
||||
- 修改 Metro
|
||||
- 修改 Babel
|
||||
- 修改 TypeScript 配置
|
||||
- 修改 Android 原生代码
|
||||
- 修改 iOS 原生代码
|
||||
- 创建 Shader 实现
|
||||
- 创建 CustomLayer 实现
|
||||
- 创建 Native Module
|
||||
- 进行架构重构
|
||||
- 顺手修复发现的问题
|
||||
|
||||
发现问题只记录。
|
||||
|
||||
---
|
||||
|
||||
## 21. 允许执行的验证
|
||||
|
||||
允许执行不会改变项目状态的只读命令,例如:
|
||||
|
||||
- Git 状态查询
|
||||
- Git 分支查询
|
||||
- 文件搜索
|
||||
- 文本搜索
|
||||
- 查看 package 配置
|
||||
- 查看 Gradle 配置
|
||||
- 查看 Pod 配置
|
||||
- 查看源码
|
||||
- 查看当前项目已有脚本定义
|
||||
|
||||
如果某个命令可能:
|
||||
|
||||
- 自动安装依赖
|
||||
- 修改 lock 文件
|
||||
- 修改构建产物
|
||||
- 修改 Native 工程
|
||||
- 写入项目配置
|
||||
|
||||
则不要执行。
|
||||
|
||||
本任务主要依赖静态调查。
|
||||
|
||||
---
|
||||
|
||||
## 22. 必做输出
|
||||
|
||||
### 22.1 架构调查成果
|
||||
|
||||
必须生成中文调查报告:
|
||||
|
||||
`docs/20_调查/10_架构调查报告_v1.md`
|
||||
|
||||
如果同名文件已经存在,不得覆盖,应递增版本号。
|
||||
|
||||
该报告是后续 `20_架构设计` 的主要输入。
|
||||
|
||||
### 22.2 Codex 完成报告
|
||||
|
||||
必须生成中文执行完成报告:
|
||||
|
||||
`docs/报告/10_架构调查_Codex完成报告_v1.md`
|
||||
|
||||
如果 `docs/报告/` 不存在,则创建该目录。
|
||||
|
||||
如果同名报告已经存在,不得覆盖旧报告,应递增版本号。
|
||||
|
||||
---
|
||||
|
||||
## 23. 架构调查报告必须包含
|
||||
|
||||
`docs/20_调查/10_架构调查报告_v1.md` 至少包含:
|
||||
|
||||
1. 调查环境
|
||||
2. Git / 分支 / 工作区状态
|
||||
3. 总则 v1 读取情况
|
||||
4. 当前项目整体架构
|
||||
5. 核心依赖与版本
|
||||
6. MapLibre 接入链
|
||||
7. MapLibre 生命周期
|
||||
8. 自定义渲染能力
|
||||
9. Android 渲染架构
|
||||
10. iOS 渲染架构
|
||||
11. JS / Native / Render / GPU 边界
|
||||
12. Camera / Matrix / 坐标转换能力
|
||||
13. 帧驱动能力
|
||||
14. 当前数据与粒子相关实验代码
|
||||
15. 当前可复用基础设施
|
||||
16. 性能与线程风险
|
||||
17. 当前架构图
|
||||
18. 已确认事实
|
||||
19. 当前限制
|
||||
20. 候选扩展点
|
||||
21. 未确认问题
|
||||
22. 是否需要追加调查任务
|
||||
23. 是否已经具备进入 `20_架构设计` 的条件
|
||||
|
||||
---
|
||||
|
||||
## 24. Codex 完成报告必须包含
|
||||
|
||||
`docs/报告/10_架构调查_Codex完成报告_v1.md` 至少包含:
|
||||
|
||||
1. 本次实际调查范围
|
||||
2. 必做项逐项状态:`已完成 / 未完成 / 无法验证`
|
||||
3. 未完成或无法验证的原因
|
||||
4. 实际生成或修改的文档清单
|
||||
5. 是否修改生产代码
|
||||
6. 是否安装或升级依赖
|
||||
7. 是否存在需要用户关注的阻塞项
|
||||
8. 下一步建议
|
||||
|
||||
---
|
||||
|
||||
## 25. 必做项完成检查
|
||||
|
||||
完成前逐项检查:
|
||||
|
||||
- [ ] 已读取总则 v1
|
||||
- [ ] 已确认当前实际 Git 基线
|
||||
- [ ] 已调查整体目录和工程形态
|
||||
- [ ] 已确认核心依赖版本
|
||||
- [ ] 已追踪 MapLibre 实际调用链
|
||||
- [ ] 已调查地图生命周期
|
||||
- [ ] 已调查自定义渲染扩展点
|
||||
- [ ] 已分别调查 Android
|
||||
- [ ] 已分别调查 iOS
|
||||
- [ ] 已调查线程与执行边界
|
||||
- [ ] 已调查 Camera / 坐标 / Matrix 能力
|
||||
- [ ] 已调查帧驱动能力
|
||||
- [ ] 已搜索现有粒子/场数据/Shader 实验代码
|
||||
- [ ] 已调查当前可复用的数据基础设施
|
||||
- [ ] 已整理性能风险
|
||||
- [ ] 已绘制必要架构图
|
||||
- [ ] 已区分事实、限制、候选和未知项
|
||||
- [ ] 已判断是否需要追加调查
|
||||
- [ ] 已判断能否进入 `20_架构设计`
|
||||
- [ ] 未修改生产代码
|
||||
- [ ] 未升级或安装依赖
|
||||
- [ ] 已生成 `docs/20_调查/` 下的正式调查报告
|
||||
- [ ] 已生成 `docs/报告/` 下的 Codex 完成报告
|
||||
|
||||
不得静默跳过任何必做项。
|
||||
|
||||
无法完成的项目必须明确写:
|
||||
|
||||
`未完成`
|
||||
|
||||
或:
|
||||
|
||||
`无法验证`
|
||||
|
||||
并说明原因。
|
||||
|
||||
---
|
||||
|
||||
## 26. 完成标准
|
||||
|
||||
本任务完成的标准不是“找到一个粒子方案”。
|
||||
|
||||
真正完成标准是:
|
||||
|
||||
> 已经能够基于 MapLibreParticleLab 当前真实代码,准确描述项目、React Native、Native、MapLibre 和 GPU 之间的现有边界,并明确下一阶段进行粒子系统架构设计时,哪些能力已经确认、哪些扩展位置值得考虑、哪些关键事实仍需要额外验证。
|
||||
|
||||
达到这个状态后,`10_架构调查` 才算完成。
|
||||
|
||||
除非存在真正阻塞架构选择的未知项,否则下一步直接进入:
|
||||
|
||||
`20_架构设计`
|
||||
1092
docs/40_任务/20_架构设计指令书_v1.md
Normal file
1092
docs/40_任务/20_架构设计指令书_v1.md
Normal file
File diff suppressed because it is too large
Load Diff
68
docs/报告/10_架构调查_Codex完成报告_v1.md
Normal file
68
docs/报告/10_架构调查_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# 10_架构调查 Codex 完成报告 v1
|
||||
|
||||
日期:2026-08-29
|
||||
|
||||
任务:`docs/40_任务/10_架构调查指令书_v1.md`
|
||||
|
||||
分支:`particle/main`
|
||||
结论:调查完成,建议直接进入 `20_架构设计`
|
||||
|
||||
## 1. 实际调查范围
|
||||
|
||||
本次对当前仓库的目录和工程形态、Git 基线、项目总则与规范、JavaScript/TypeScript 配置、Android Gradle/JNI/renderer、iOS Pod/MapView/backend、MapLibre Core Map/Style/Source/Renderer/Layer/gfx、CustomLayer、Camera/Matrix、帧驱动、线程边界、粒子搜索结果、fixture 和潜在性能风险进行了只读调查。
|
||||
|
||||
## 2. 必做项状态
|
||||
|
||||
| 必做项 | 状态 | 说明 |
|
||||
|---|---|---|
|
||||
| 读取总则 v1 | 已完成 | 根 AGENTS、`docs/00_项目总则/`、`docs/00_项目规范/` 已完整读取 |
|
||||
| 确认 Git 基线 | 已完成 | 分支、HEAD、远端、submodule 状态已查询 |
|
||||
| 确认整体目录和工程形态 | 已完成 | 确认是 MapLibre Native monorepo,不是 RN app |
|
||||
| 确认核心依赖版本 | 已完成 | package/lock、Android VERSION/Gradle、iOS VERSION/Podspec 已读取 |
|
||||
| 追踪 MapLibre 实际调用链 | 已完成 | Android、iOS、Core 三条链已追踪 |
|
||||
| 调查地图生命周期 | 已完成 | Core、Android、iOS 分别记录 |
|
||||
| 调查自定义渲染扩展点 | 已完成 | A/B/C/D 分类已记录 |
|
||||
| 分别调查 Android | 已完成 | MapView、JNI、renderer thread、OpenGL/Vulkan、CustomLayer |
|
||||
| 分别调查 iOS | 已完成 | MLNMapView、CADisplayLink、OpenGL/Metal、CustomStyleLayer |
|
||||
| 调查线程和执行边界 | 已完成 | Native、Worker、FileSource、Render、GPU 已区分 |
|
||||
| 调查 Camera/坐标/Matrix | 已完成 | Core TransformState、CustomLayer 参数和高层差异已记录 |
|
||||
| 调查帧驱动 | 已完成 | Android requestRender、iOS DisplayLink、Core render callback 已记录 |
|
||||
| 搜索粒子/场数据/Shader 实验 | 已完成 | 无运行时粒子实现;fixture 仅占位,内置 shader 无粒子 shader |
|
||||
| 调查可复用基础设施 | 已完成 | FileSource、immutable、thread pool、gfx resource、upload/shader registry |
|
||||
| 整理性能风险 | 已完成 | 逐项标记高/中/低/尚无法判断 |
|
||||
| 绘制必要架构图 | 已完成 | 总体、JS/Native、Android、iOS、数据边界共五图 |
|
||||
| 区分事实/限制/候选/未知 | 已完成 | 报告第 17 节分类 |
|
||||
| 判断是否追加调查 | 已完成 | 选择 A,不追加子任务 |
|
||||
| 判断能否进入 20_架构设计 | 已完成 | 可以进入,未知项作为设计输入 |
|
||||
| 未修改生产代码 | 已完成 | 无源码改动 |
|
||||
| 未升级或安装依赖 | 已完成 | 无 lock/Gradle/Pod/npm 改动 |
|
||||
| 生成正式调查报告 | 已完成 | `docs/20_调查/10_架构调查报告_v1.md` |
|
||||
| 生成 Codex 完成报告 | 已完成 | 本文件 |
|
||||
|
||||
## 3. 未完成或无法验证
|
||||
|
||||
- “工作区干净”未完成:调查开始时存在任务书自身的未跟踪文件,非本次产生。
|
||||
- RN 页面、RN binding、NativeModule/TurboModule/Fabric 和 JS 每帧回调无法验证:当前仓库没有 RN 宿主或对应配置。
|
||||
- 目标产品最终 iOS backend、目标宿主线程调度、Android 业务侧 raw CustomLayerHost 的稳定用法、设备 GPU 能力和正式粒子数据格式无法仅凭当前静态源码确认。
|
||||
- 本次未重新构建或测试:任务性质为只读调查,构建可能改变产物;已有历史构建结果仅按基线记录引用。
|
||||
|
||||
这些项目已在调查报告中使用“未确认/无法验证”明确标记,没有用外部项目或记忆补全。
|
||||
|
||||
## 4. 实际生成/修改文档
|
||||
|
||||
- 新增 `docs/20_调查/10_架构调查报告_v1.md`。
|
||||
- 新增 `docs/报告/10_架构调查_Codex完成报告_v1.md`。
|
||||
- 追加 `粒子开发记录.md` 的本次调查记录。
|
||||
- 未新增或修改 Step 文件:遵循用户“当前不要自行创建 Step”的明确限制。
|
||||
- 未修改 `docs/00_项目总则/` 或 `docs/00_项目规范/`。
|
||||
|
||||
## 5. 生产代码与依赖
|
||||
|
||||
- 生产代码:未修改。
|
||||
- 实验源码:未修改。
|
||||
- Android/iOS/Gradle/Pod/Metro/Babel/TypeScript 配置:未修改。
|
||||
- 依赖:未安装、未升级、未修改 lock 文件。
|
||||
|
||||
## 6. 阻塞项和下一步
|
||||
|
||||
没有阻塞 `20_架构设计` 的关键未知项。建议下一步进入 `20_架构设计`,将本报告中的 CustomLayer/Core gfx 候选、Android/iOS 差异、离线 fixture contract、Land/Ocean Mask 边界和 context-loss 生命周期作为设计约束;在需要选择最终 GPU/线程/数据格式前,再安排最小平台运行时验证。
|
||||
106
docs/报告/20_架构设计_Codex完成报告_v1.md
Normal file
106
docs/报告/20_架构设计_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# 20_架构设计 Codex 完成报告 v1
|
||||
|
||||
日期:2026-08-29
|
||||
|
||||
任务:`docs/40_任务/20_架构设计指令书_v1.md`
|
||||
|
||||
分支:`particle/main`
|
||||
|
||||
起始提交:`127de2271480000fcc8f390d81cf446e98757800`
|
||||
|
||||
结束提交:`127de2271480000fcc8f390d81cf446e98757800`(本任务未提交)
|
||||
|
||||
执行结论:PASS(文档设计任务)
|
||||
|
||||
下一阶段:B,进入 `30_最小技术验证`
|
||||
|
||||
## 1. 前置文档与 Git 基线
|
||||
|
||||
| 项目 | 状态 | 结果 |
|
||||
|---|---|---|
|
||||
| 根 AGENTS/基线 | 已完成 | 已读取 `AGENTS.md`、`PARTICLE_BASELINE.md` |
|
||||
| 项目总则 | 已完成 | 已完整读取 `docs/00_项目总则/` 全部当前有效文件 |
|
||||
| 项目规范 | 已完成 | 已完整读取 `docs/00_项目规范/` 全部当前有效文件 |
|
||||
| 前置调查/报告 | 已完成 | 已读取调查报告和 Codex 完成报告 |
|
||||
| 当前架构/记录/Step | 已完成 | 已读取现有架构文档、开发记录和 STEP-000 |
|
||||
| 当前设计任务书 | 已完成 | 1092 行已完整读取 |
|
||||
| Git baseline | 已完成 | `particle/main`,HEAD 为上述起始提交;submodule 已查询 |
|
||||
| 工作区干净 | 未完成 | 开始时已有上一阶段文档变更及用户提供的两份未跟踪任务书;均予以保留 |
|
||||
|
||||
## 2. 设计必做项状态
|
||||
|
||||
| 必做项 | 状态 | 结果 |
|
||||
|---|---|---|
|
||||
| 候选渲染路线比较 | 已完成 | 比较 CustomLayer、CustomDrawable、独立 Core layer、Overlay |
|
||||
| 唯一主路线选择 | 已完成 | 独立 Core RasterParticleLayer/RenderRasterParticleLayer + gfx |
|
||||
| Core 模块设计 | 已完成 | Field、Adapter、Simulation、Render、GPU、Mask、Controller |
|
||||
| 与 Style/RenderTree/gfx 关系 | 已完成 | 正式进入 Style/RenderTree,offscreen pass + translucent composite |
|
||||
| WeatherColor/Particle 边界 | 已完成 | 分离 Layer/RenderLayer/GPU 状态/动画生命周期 |
|
||||
| Data/Field contract | 已完成 | RasterArraySource、immutable snapshot、revision、canonical U/V |
|
||||
| Fixture contract | 已完成 | metadata.json、field.f32、mask.u8、字节序、校验和、五个预留场景 |
|
||||
| GPU resource 设计 | 已完成 | velocity、State A/B、Trail A/B、Mask、uniform、shader set |
|
||||
| GPU state/simulation | 已完成 | 逻辑状态、render-pass ping-pong、NoData/mask/respawn;compute 延后 |
|
||||
| Trail 架构 | 已完成 | 第一阶段包含 screen-space ping-pong trail,Camera 变化清空 |
|
||||
| Land/Ocean Mask | 已完成 | 独立 snapshot/cache/texture,OCEAN_ONLY 共享,缺失 fail closed |
|
||||
| 数据流与频率分类 | 已完成 | 低频、每帧、Camera 变化三类已定义 |
|
||||
| 线程模型 | 已完成 | Map/UI、Worker、Render、GPU 的所有权和交接已定义 |
|
||||
| Camera/Matrix 同步 | 已完成 | 只读取当前 render frame Paint/Transform parameters |
|
||||
| 帧驱动 | 已完成 | MapLibre render loop + needsRepaint,禁用独立粒子时钟 |
|
||||
| Android 薄接入 | 已完成 | typed JNI;禁止 raw long;OpenGL 主线、Vulkan gate |
|
||||
| iOS 薄接入 | 已完成 | Core Source/Layer peer;CustomStyleLayer 只作参考;后续 Metal/OpenGL gate |
|
||||
| 生命周期状态机 | 已完成 | DISABLED 至 ERROR/CONTEXT_LOST 的状态、恢复和释放责任已定义 |
|
||||
| 公共 API/配置分层 | 已完成 | backend-independent source/layer/options/status;内部参数不外露 |
|
||||
| 错误/降级 | 已完成 | Field、Mask、GPU、backend、context、shader、memory pressure 均定义 |
|
||||
| 性能预算 | 已完成 | 给出待验证目标和测量档位,未声称性能已达到 |
|
||||
| 最小技术验证清单 | 已完成 | 压缩为四项,并标记阻塞性和最小方法 |
|
||||
| 不采用方案 | 已完成 | 逐项拒绝任务书要求的八类方案及 CPU/compute-only 等 |
|
||||
| 九类架构图 | 已完成 | 总体、Core、fixture 数据流、每帧、Camera、Android、iOS、Mask、状态机 |
|
||||
| 架构冻结项 | 已完成 | 14 项正式冻结;同时列出不得冻结项 |
|
||||
| 下一阶段判断 | 已完成 | B:进入 `30_最小技术验证` |
|
||||
|
||||
## 3. 核心架构选择
|
||||
|
||||
正式主路线是新增独立 Core `RasterParticleLayer`、`RenderRasterParticleLayer` 和 `mbgl::particle` 模块,通过 `RasterArraySource` 接受 immutable Field snapshot,进入 MapLibre Style/RenderTree,并使用 gfx Texture/RenderTarget/Drawable/Shader/UploadPass 完成 GPU simulation、trail 与 translucent composite。
|
||||
|
||||
CustomLayer/CustomDrawable 不构成产品双轨;Android raw `CustomLayerHost` 不进入正式 API。平台只提供类型安全 Source/Layer peer、backend lifecycle 与 capability 薄接入。
|
||||
|
||||
## 4. 实际生成或修改文件
|
||||
|
||||
- 新增 `docs/10_架构/20_粒子系统总体架构设计_v1.md`。
|
||||
- 新增 `docs/报告/20_架构设计_Codex完成报告_v1.md`。
|
||||
- 追加 `粒子开发记录.md`。
|
||||
- 未修改现有 `docs/10_架构/MapLibre粒子总体架构_v1.md`;其简略 Layer 顺序与本设计一致。
|
||||
- 未新增或修改 `docs/50_Step记录/`;符合任务书第 31 节。
|
||||
|
||||
## 5. 无法验证与未完成
|
||||
|
||||
### 无法验证
|
||||
|
||||
- 独立 particle RenderLayer 在 Android OpenGL/Vulkan 的运行时 pass/barrier/context 行为。
|
||||
- State/Trail RenderTarget 的实际格式、性能和 context recreation 时间。
|
||||
- Camera/wrap/Mask 在真机/模拟器上的视觉正确性。
|
||||
- iOS Metal/OpenGL 的粒子 Core layer 薄接入。
|
||||
- 最终 FPS、最大粒子数、最佳 texture format、设备能力和生产 Mask 来源。
|
||||
|
||||
这些均已列入 `30_最小技术验证` 或明确标记为后续 Gate,不影响本次总体设计文档 PASS。
|
||||
|
||||
### 未完成
|
||||
|
||||
- 无。本任务要求的设计输出均已完成;生产实现、构建和运行时验证本来就是本任务禁止项。
|
||||
|
||||
## 6. 代码、依赖、构建与测试
|
||||
|
||||
- 生产/实验源码:未修改。
|
||||
- Android/iOS/Gradle/Pod/CMake/package/lock:未修改。
|
||||
- 依赖:未安装、未升级。
|
||||
- 构建:未执行;任务明确禁止会修改工程状态的构建。
|
||||
- 测试:未执行;本阶段仅做静态架构设计。
|
||||
- APK/AAR/其他产物:无。
|
||||
- 项目总则/项目规范:未修改。
|
||||
|
||||
## 7. 与指令书差异、阻塞和建议
|
||||
|
||||
- 与指令书差异:无。
|
||||
- 与长期总则冲突:无。
|
||||
- 阻塞:没有阻塞本次设计完成的事项;存在四项会阻塞后续正式实现或平台扩展的运行时验证。
|
||||
- 建议:下一份正式指令书进入 `30_最小技术验证`,只覆盖设计文档第 26 节四项验证,不创建完整粒子产品实现,也不重新讨论总体架构。
|
||||
18
粒子开发记录.md
18
粒子开发记录.md
@@ -16,3 +16,21 @@
|
||||
- Vulkan Debug AAR 同时包含 Khronos Validation Layer。
|
||||
- Android SDK 路径已通过 `ANDROID_HOME` 与 `local.properties` 固化。
|
||||
- 下一步进入 STEP-001:MapLibre Native 粒子接入点只读调查。
|
||||
|
||||
## 2026-08-29 — 10_架构调查
|
||||
|
||||
- 按 `docs/40_任务/10_架构调查指令书_v1.md` 完成只读架构调查。
|
||||
- 确认当前仓库是 MapLibre Native monorepo,不是 React Native 应用;确认 Android 13.4.1、iOS 6.28.0 和共用 Core 渲染链。
|
||||
- 确认 Android OpenGL/Vulkan、iOS OpenGL/Metal、Core CustomLayer/gfx、Camera/Matrix、帧驱动和线程边界;粒子 fixture 目前只有空目录占位,暂无粒子运行时代码。
|
||||
- 生成 `docs/20_调查/10_架构调查报告_v1.md` 和 `docs/报告/10_架构调查_Codex完成报告_v1.md`。
|
||||
- 本次未修改生产代码、原生工程、依赖或项目总则;遵循用户要求未自行创建或修改 Step 文件。
|
||||
|
||||
## 2026-08-29 — 20_架构设计
|
||||
|
||||
- 按 `docs/40_任务/20_架构设计指令书_v1.md` 完成第一版正式粒子系统总体架构设计。
|
||||
- 生成 `docs/10_架构/20_粒子系统总体架构设计_v1.md` 和 `docs/报告/20_架构设计_Codex完成报告_v1.md`。
|
||||
- 正式主路线冻结为独立 Core `RasterParticleLayer/RenderRasterParticleLayer` + `RasterArraySource` + 隔离 `mbgl::particle` 模块,进入 Style/RenderTree 并使用 MapLibre gfx;CustomLayer/CustomDrawable 不作为产品主宿主。
|
||||
- 冻结 WeatherColor/Particle 分层、Field/fixture contract、Wind GLOBAL、海洋场 OCEAN_ONLY 共用 Mask、MapLibre frame driver、同帧 Camera Matrix、GPU resource/context-loss 和平台薄接入原则。
|
||||
- 下一阶段选择 `30_最小技术验证`,集中验证独立 RenderLayer、OpenGL/Vulkan ping-pong/trail/context、Camera/wrap/Mask 和 iOS 薄接入。
|
||||
- 最终 FPS、最大粒子数、最佳 texture format、生产 Mask 来源、真实天气协议和生产 Host API 仍未确认。
|
||||
- 本次未修改生产代码、依赖、构建配置、项目总则或 Step,未运行构建/测试。
|
||||
|
||||
Reference in New Issue
Block a user