Files
MapLibreParticleLab/docs/40_任务/20_架构设计指令书_v1.md
OpenAI Codex 74be2a5052
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
docs: freeze particle architecture investigation and design
2026-08-29 12:33:09 +08:00

1093 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MapLibreParticleLab 20_架构设计指令书 v1
## 1. 任务基本信息
- 项目:MapLibreParticleLab
- 大阶段:`20_架构设计`
- 当前基线分支:`particle/main`
- 前置阶段:`10_架构调查` 已完成
- 前置调查报告:`docs/20_调查/10_架构调查报告_v1.md`
- 前置完成报告:`docs/报告/10_架构调查_Codex完成报告_v1.md`
- 任务性质:架构设计
- 是否允许修改生产代码:否
- 是否允许实现粒子系统:否
- 是否允许安装或升级依赖:否
- 是否允许运行会修改工程状态的构建/测试:否
---
## 2. 任务目标
基于 `10_架构调查` 已确认的真实仓库结构、MapLibre Core / Android / iOS 渲染链、CustomLayer / CustomDrawable / gfx 扩展能力、Camera/Matrix、帧驱动和生命周期事实,完成 MapLibreParticleLab 第一版正式粒子系统总体架构设计。
本阶段必须完成的是:
> 从已经确认的候选扩展点中做出明确架构选择,冻结粒子系统的模块边界、数据边界、渲染边界、线程边界、平台边界、生命周期、Mask 机制和第一阶段离线 fixture 接口,并明确哪些内容必须进入后续最小技术验证。
本阶段不是实现阶段。
不得直接创建粒子 Shader、Renderer、Source、CustomLayerHost 实现或平台业务代码。
---
## 3. 必须先读取的现有文档
开始设计前必须完整读取并遵守:
1. 根目录 `AGENTS.md`
2. `PARTICLE_BASELINE.md`
3. `docs/00_项目总则/` 当前全部有效总则
4. `docs/00_项目规范/` 当前全部有效规范
5. `docs/20_调查/10_架构调查报告_v1.md`
6. `docs/报告/10_架构调查_Codex完成报告_v1.md`
7. `docs/10_架构/` 下当前已有架构文档
8. `粒子开发记录.md`
如果现有架构文档与本次真实调查结果有冲突:
- 不得静默覆盖;
- 必须在设计文档中明确指出冲突;
- 以当前有效总则、规范和 `10_架构调查报告_v1` 中的已确认事实作为设计输入;
- 旧文档仅作为历史参考。
---
## 4. 已冻结的调查事实
本任务不得重新把下列已确认事实当成“未知问题”反复调查。
### 4.1 仓库形态
当前仓库是 MapLibre Native monorepo,不是 React Native / Expo / Bare RN App。
当前不存在:
- RN 页面
- RN MapLibre binding
- Metro
- Babel
- NativeModule / TurboModule / Fabric 粒子宿主
- JS 每帧粒子运行时
因此当前架构设计必须先以 Native Core + Android/iOS 平台薄接入为事实基础。
### 4.2 Core 主渲染链
当前已确认主链:
`mbgl::Map`
→ `RendererFrontend`
→ `UpdateParameters`
→ `Renderer`
→ `RenderTree / RenderOrchestrator`
→ `RenderLayer / LayerGroup / Drawable`
→ `gfx`
→ 平台 GPU backend
### 4.3 当前候选扩展点
已确认候选包括:
1. Core `CustomLayerHost / RenderCustomLayer`
2. Core `CustomDrawableLayer`
3. 独立 Core particle render layer
4. 现有 Source / FileSource / tile / fixture 管线的薄接入
5. Android/iOS 仅负责必要的 backend 与生命周期薄适配
本任务必须在这些真实候选基础上做架构选择。
### 4.4 平台差异
- Android 当前存在 OpenGL / Vulkan backend。
- iOS 当前存在 OpenGL / Metal backend。
- 两个平台共享 Core,但高层 CustomLayer、context、encoder、surface 生命周期不完全对称。
- Android raw `CustomLayerHost` 业务侧稳定用法尚未经过运行时验证。
- iOS 最终目标构建采用 OpenGL 还是 Metal,需要以后结合目标工程确认。
### 4.5 当前粒子实现状态
当前仓库没有:
- 正式粒子运行时
- 速度场解析器
- 粒子 shader
- GPU ping-pong particle state
- trail render target
- 正式 Grid 数据模型
- 正式粒子数据 contract
`particle-fixtures/` 当前只有占位目录。
---
## 5. 架构设计原则
### 5.1 粒子系统必须高度隔离
不得把粒子逻辑散落到:
- RasterLayer
- 普通 Style Layer
- Map 主循环
- Camera
- Source 通用逻辑
- Android MapView
- iOS MLNMapView
粒子系统必须形成清晰、可独立关闭、可独立测试、可独立演进的模块边界。
### 5.2 不复制外部闭源或其他产品实现
不得复制:
- Windy APK 代码
- Mapbox 新版闭源实现
- 其他商业产品的反编译实现
允许参考当前仓库已有 MapLibre 结构和公开 API 形成自己的架构。
### 5.3 跨平台共用优先
尽量把:
- 粒子状态
- 场数据
- 坐标转换
- 粒子更新
- 渲染资源描述
- Shader contract
- Mask contract
- 生命周期状态机
放在 Core 层。
Android / iOS 只保留不可避免的:
- backend resource 接入
- surface/context/device 生命周期
- 平台 render callback 连接
- 平台能力探测
不得分别实现两套粒子系统。
### 5.4 设计必须支持 backend 差异
第一版架构不得假定所有平台永远使用同一种 GPU API。
设计必须至少能表达:
- Android OpenGL
- Android Vulkan
- iOS OpenGL
- iOS Metal
但本阶段不要求全部 backend 同时实现。
必须区分:
- 架构必须支持
- 第一阶段实际验证
- 后续扩展
---
## 6. 必须完成的核心架构决策
本任务最重要的输出不是列候选,而是明确选择。
必须对以下问题逐项给出:
- 推荐方案
- 为什么
- 为什么不选其他候选
- 风险
- 后续验证点
### 6.1 粒子渲染核心入口
必须在以下候选中做主路线选择:
- `CustomLayerHost / RenderCustomLayer`
- `CustomDrawableLayer`
- 新建独立 Core particle render layer
- 其他基于当前源码能够成立的方案
不得只写“都可以”。
必须明确:
> 第一阶段正式主路线是什么。
如果采用“两阶段方案”,例如先 CustomLayer 验证、后独立 RenderLayer 正式化,也必须说明每阶段退出条件,不能无限双轨。
### 6.2 与现有 MapLibre RenderTree 的关系
设计必须说明粒子模块:
- 是否进入 Style / Layer 系统
- 是否进入 RenderTree
- 是否属于 translucent pass
- 是否需要自己的 render target
- 是否需要 trail accumulation
- 是否参与 MapLibre repaint
- 是否需要主动 `triggerRepaint`
- Camera 更新时如何取得同帧 Transform
### 6.3 gfx 层使用边界
明确粒子系统可使用哪些现有 gfx 能力:
- Texture2D
- DynamicTexture
- Buffer
- UniformBuffer
- RenderTarget
- DrawableBuilder
- DrawableTweaker
- Shader registry
- UploadPass
- CommandEncoder
并明确哪些需要新增粒子专用抽象。
不得直接把 backend-specific OpenGL/Vulkan/Metal 调用散到公共粒子业务逻辑。
---
## 7. 模块划分
必须给出明确的模块图和建议源码目录。
至少考虑以下职责,名称可根据现有 MapLibre 目录风格调整:
### 7.1 Particle Field
职责:
- 描述风、浪、潮、海流等矢量场
- 范围
- 网格
- 时间片
- U/V 分量
- NoData
- 有效区
- 数据版本
### 7.2 Particle Data Adapter
职责:
- 从第一阶段 fixture 转为统一 Particle Field
- 后续可替换为真实数据源
- 不把真实天气 Server 协议耦合到 Renderer
### 7.3 Particle Simulation
职责:
- 粒子状态
- age
- seed
- advection
- respawn
- field sample
- timestep
必须明确哪些在 CPU,哪些预期在 GPU。
### 7.4 Particle Render
职责:
- 粒子绘制
- trail
- fade
- opacity
- viewport
- transform
- render pass
### 7.5 Particle GPU Resources
职责:
- velocity texture
- particle state
- trail target
- uniforms
- shader
- context loss / recreate
### 7.6 Particle Mask
职责:
- GLOBAL
- OCEAN_ONLY
- Land/Ocean Mask
- mask texture / geometry contract
- mask lifecycle
### 7.7 Particle Controller / Layer State
职责:
- enable/disable
- type
- data ready
- GPU ready
- map ready
- context lost
- renderable state
- repaint request
不得让 UI 直接管理 GPU resource。
---
## 8. WeatherColorLayer 与 RasterParticleLayer 边界
必须明确区分:
### WeatherColorLayer
用于:
- 颜色场
- 栅格填色
- 连续色带
- 不承担粒子动画
### RasterParticleLayer
用于:
- 风向/海流等动态粒子表现
- 粒子状态
- 动画
- trail
- GPU simulation/render
两者可以共享:
- 数据来源
- grid metadata
- mask
- time slice
但不得共享:
- RenderLayer 实例
- 粒子 GPU 状态
- 动画生命周期
设计文档必须明确二者不是一个 Layer 的两种开关模式。
---
## 9. Wind / Wave / Tide / Current 显示域
当前规则必须进入正式架构:
### Wind
`GLOBAL`
允许覆盖:
- 海洋
- 陆地
### Wave / Tide / Current
`OCEAN_ONLY`
只显示海洋部分。
不能要求每种海洋数据各自实现一套陆地裁剪。
必须设计统一可复用:
`Land/Ocean Mask`
机制。
---
## 10. Land/Ocean Mask 架构
必须给出独立设计。
至少明确:
- Mask 数据来源属于哪个模块
- Mask 是 CPU geometry 还是 GPU texture,或允许两级实现
- Mask 坐标系
- Mask 分辨率
- viewport / tile / world 范围
- zoom 时的行为
- wrap / antimeridian
- pitch / bearing 下如何与地图保持一致
- Mask 更新条件
- Mask 缓存
- 海岸边界 artifact 风险
- context loss 后如何恢复
Mask 必须可以被:
- Wave
- Tide
- Current
- 未来其他 OCEAN_ONLY 场
共享。
不得把 Mask 写死到某一种粒子 Layer 内。
---
## 11. 第一阶段离线 fixture contract
第一阶段仍然只使用离线 fixture。
架构设计必须冻结一个“最小 fixture contract”。
至少定义:
- fixture id
- field type
- bounds
- grid width / height
- coordinate reference
- U/V 分量
- NoData
- timestamp 或 timeIndex
- data version
- mask 信息
- endian / precision,如涉及二进制
- metadata 文件
- binary/data 文件
必须覆盖现有预留 fixture:
- `uniform-east`
- `uniform-north`
- `vortex`
- `shear`
- `coastal-mask`
本阶段可以设计格式,但不得生成真实天气数据下载器。
---
## 12. 数据流设计
必须画出完整数据链:
```text
Offline Fixture
→ Decode / Validate
→ Particle Field
→ CPU/GPU Resource Preparation
→ Velocity Resource
→ Simulation
→ Render
→ MapLibre Render Pass
→ GPU
```
同时说明:
### 哪些步骤低频
例如:
- fixture load
- data decode
- texture upload
### 哪些步骤每帧
例如:
- particle simulation
- trail update
- draw
### 哪些步骤 Camera 变化时更新
例如:
- transform uniform
- viewport
- visible bounds
不得让每帧流程重新解析 field 或重建 texture。
---
## 13. 线程模型设计
必须明确:
- Map / UI thread
- worker thread
- render thread
- GPU
各自负责什么。
至少回答:
### Field 解码在哪执行
### CPU 插值在哪执行
### GPU resource 创建在哪执行
### 每帧 Simulation 在哪里执行
### Camera Matrix 从哪里读取
### Data 更新如何安全交给 Render Thread
### 如何避免大数组频繁跨线程复制
### immutable snapshot 或 resource handle 如何使用
### context loss 时哪个线程负责恢复
---
## 14. Camera / Matrix 同步设计
必须基于当前调查确认的:
- `TransformState`
- `TransformParameters`
- `PaintParameters`
- `CustomLayerRenderParameters`
设计粒子同帧同步方式。
至少覆盖:
- pan
- zoom
- bearing
- pitch
- resize
- pixel ratio
- world wrap
- antimeridian
- projection/unprojection
必须明确:
> 粒子 Renderer 读取的是 MapLibre 当前 render frame 的 transform,而不是由另一个独立 UI/JS 动画循环猜 Camera。
---
## 15. 帧驱动与动画时钟
必须正式定义谁驱动粒子动画。
至少说明:
- 是否依赖 MapLibre render loop
- 粒子活动时如何持续请求下一帧
- 粒子关闭时如何停止额外 repaint
- 地图静止但粒子活动时如何继续帧
- App 后台如何暂停
- View 不可见如何暂停
- Map/Style 销毁如何停止
- context loss 如何停止并恢复
不得设计独立于 MapLibre Renderer 的第二套无同步动画循环作为正式主路线。
---
## 16. GPU 粒子状态模型
本阶段必须设计,但不实现。
至少定义:
- particle count
- position state
- age
- seed/random
- current / next state
- velocity sampling
- timestep
- respawn
- viewport clipping
- field bounds
- NoData 处理
如果采用 GPU simulation,需要明确:
- ping-pong resource
- compute 或 render-pass simulation
- backend capability gate
- 无 compute 时的 fallback 思路
如果第一阶段不使用 compute,也必须明确原因。
---
## 17. Trail 架构
必须说明是否第一阶段就包含 trail。
如果包含:
至少设计:
- accumulation target
- fade pass
- particle draw pass
- compositing
- resize
- camera movement
- zoom/bearing/pitch 改变时旧 trail 如何处理
- context loss
- clear conditions
如果第一阶段不包含 trail:
必须明确第一阶段视觉范围,并说明后续如何扩展。
---
## 18. 生命周期状态机
必须设计粒子 Layer/Renderer 状态机。
建议至少包含:
- DISABLED
- WAITING_MAP
- WAITING_FIELD
- PREPARING
- READY
- RENDERING
- PAUSED
- CONTEXT_LOST
- ERROR
可以调整名称,但必须覆盖:
- Map 创建
- Style reload
- 数据更新
- App background
- App foreground
- Surface recreation
- GPU context loss
- Layer removal
- Map destroy
必须明确资源释放和重新创建责任。
---
## 19. Android / iOS 薄接入
### 19.1 Android
架构必须说明:
- Java/Kotlin 业务 API 应保持多薄
- JNI 是否需要新增稳定 wrapper
- raw `long CustomLayerHost` 是否允许进入正式接口
- OpenGL / Vulkan backend 差异如何封装
- SurfaceView / TextureView 生命周期影响
- requestRender 如何连接
不得把 Android 平台代码变成第二套 particle engine。
### 19.2 iOS
必须说明:
- `MLNCustomStyleLayer`
- `MLNCustomDrawableStyleLayer`
- Metal / OpenGL resource
- CADisplayLink / MapLibre render callback
- backend resource
在正式架构中的位置。
不得因为 iOS 高层 CustomStyleLayer 比 Android 方便,就把 Core 共用逻辑放到 ObjC/Swift。
---
## 20. 公共 API 设计
本阶段需要设计未来供宿主使用的最小 API,但不实现。
例如需要能够表达:
- 创建/删除粒子 layer
- enable/disable
- 设置 field
- 设置 display domain
- 设置 particle options
- 设置 mask
- 数据更新
- 状态查询
必须遵循 MapLibre 当前 API 风格。
公共 API 不得暴露:
- GL texture id
- VkImage
- MTLTexture
- backend-specific command encoder
- raw native host pointer
除非是严格内部接口。
---
## 21. 配置与可调参数
架构设计应区分:
### 稳定业务参数
例如:
- particle count
- speed factor
- lifetime
- opacity
- trail fade
### 内部 backend 参数
例如:
- texture format
- buffer alignment
- workgroup size
- render target format
业务层不得直接依赖 backend 参数。
---
## 22. 错误与降级策略
设计必须定义:
- field 无效
- field 越界
- mask 无效
- GPU resource 创建失败
- backend 不支持
- context loss
- shader 创建失败
- memory pressure
发生时的行为。
原则:
- 地图本身不得因此崩溃
- 粒子层可以禁用或进入 ERROR
- 不得破坏其他 Style Layer
- 资源失败后必须可安全释放
- 是否自动重试必须明确
---
## 23. 性能预算
本阶段不要求真实性能测试,但必须建立设计预算。
至少定义需要后续验证的指标:
- FPS
- frame time
- CPU time
- GPU time
- particle count
- texture memory
- buffer memory
- field upload time
- context recreation time
并明确:
- 哪些属于第一阶段 gate
- 哪些属于后续优化
不得写未经测试的“能稳定 60 FPS”等结论。
---
## 24. 后续最小技术验证清单
架构设计完成后,必须给出“最少数量”的技术验证。
只列会影响实现路线的关键验证。
可能包括但不限于:
- Android CustomLayerHost 稳定生命周期
- Android OpenGL/Vulkan 中粒子资源能否共用 MapLibre render context
- iOS Metal / OpenGL CustomStyleLayer 同帧资源接入
- context loss / Surface recreate
- MapLibre transform 下的同帧矩阵同步
- trail render target
- OCEAN_ONLY mask
不得为了保险列几十个验证任务。
必须按:
1. 是否阻塞正式实现
2. 是否会改变架构
3. 最小验证方式
进行排序。
---
## 25. 必须明确拒绝的架构
设计报告必须专门列出“不采用的方案”。
至少评估:
- React Native / JS 每帧粒子
- MapView 外部 Overlay
- 每种海洋场各自一套 Mask
- 直接改造现有 RasterLayer 承担粒子
- Android/iOS 各写一套独立粒子引擎
- 每帧重新创建 field texture
- 每帧大数组跨 Java/ObjC/JS 边界
- backend-specific GPU API 暴露到公共业务 API
说明拒绝原因。
---
## 26. 必须生成的架构图
正式设计文档至少包含:
### 26.1 粒子系统总体架构图
### 26.2 Core 模块边界图
### 26.3 Offline Fixture → GPU 数据流图
### 26.4 每帧渲染链图
### 26.5 Camera / Matrix 同步图
### 26.6 Android 薄接入图
### 26.7 iOS 薄接入图
### 26.8 Land/Ocean Mask 共用图
### 26.9 生命周期状态机图
全部使用 Markdown / Mermaid / ASCII 均可。
图中不得补入当前不存在且未经设计定义的外部系统。
---
## 27. 正式设计结论必须冻结的内容
设计报告结尾必须列出“架构冻结项”。
至少包括:
- 主粒子渲染路线
- Core / Platform 分工
- Layer 边界
- WeatherColorLayer / RasterParticleLayer 边界
- Field 模型
- Fixture contract
- Wind GLOBAL
- Wave/Tide/Current OCEAN_ONLY
- Land/Ocean Mask
- Frame Driver
- Camera Matrix 来源
- GPU resource 生命周期
- Android/iOS 薄接入原则
- 第一阶段 backend 验证范围
- 第一阶段粒子视觉范围
- 后续最小技术验证项
冻结项是后续 `30_最小技术验证` 和实现任务的依据。
---
## 28. 不得在本阶段冻结的内容
以下如果没有运行时证据,不得假装已确定:
- 最终 FPS
- 最终最大 particle count
- 最终最佳 texture format
- 最终 Vulkan/Metal compute workgroup
- 具体 GPU 厂商优化
- 真实天气 Server 数据格式
- NavSea / RN 最终 Host API
- 最终生产设备能力矩阵
这些只能记录为后续验证项。
---
## 29. 禁止事项
本任务禁止:
- 修改生产源码
- 创建粒子 Renderer 实现
- 创建粒子 Shader
- 创建 GPU Buffer/Texture 实现
- 创建 CustomLayerHost 实现
- 创建 Android JNI 粒子模块
- 创建 iOS 粒子类
- 修改现有 MapLibre Renderer
- 修改 RasterLayer
- 修改 Style parser
- 安装/升级依赖
- 改 Gradle
- 改 Pod
- 改 CMake
- 改 package lock
- 运行会写入工程状态的自动构建
- 顺手修复无关问题
本任务只允许:
- 阅读
- 设计
- 生成/更新架构文档
- 生成 Codex 完成报告
- 追加 `粒子开发记录.md`
---
## 30. 必做输出
### 30.1 正式架构文档
生成:
`docs/10_架构/20_粒子系统总体架构设计_v1.md`
如果同名文件已经存在,不得覆盖,应递增版本号。
该文件是本阶段最主要的正式输出。
### 30.2 Codex 完成报告
生成:
`docs/报告/20_架构设计_Codex完成报告_v1.md`
如果同名文件已经存在,不得覆盖,应递增版本号。
### 30.3 开发记录
追加:
`粒子开发记录.md`
记录:
- `20_架构设计` 已执行
- 生成的架构文档
- 核心架构选择
- 是否需要进入最小技术验证
- 仍未确认的关键事项
不得重写历史记录。
---
## 31. 不主动创建 Step
本任务不主动新建或修改 `docs/50_Step记录/` 下的 Step 文件。
除非用户已有明确 Step 指令,否则只生成本任务规定的:
- 架构文档
- Codex 完成报告
- 开发记录追加
---
## 32. Codex 完成报告格式
必须逐项报告:
- `已完成`
- `未完成`
- `无法验证`
至少包括:
1. 读取前置文档
2. 候选渲染路线比较
3. 主路线选择
4. Core 模块设计
5. Data/Field contract
6. GPU resource 设计
7. Mask 设计
8. 线程模型
9. Camera/Matrix 同步
10. 帧驱动
11. Android 薄接入
12. iOS 薄接入
13. 生命周期
14. 错误/降级
15. 性能预算
16. 最小技术验证清单
17. 正式架构冻结项
18. 生成文件
19. 是否修改生产代码
20. 是否修改依赖或构建配置
---
## 33. 完成标准
本任务完成,不是指“列出几个可选方案”。
必须达到:
> 已经基于当前 MapLibreParticleLab 的真实 MapLibre Native 架构,为粒子系统选择唯一主架构路线,并冻结 Core / Platform 边界、Particle Field、Simulation、Render、GPU Resource、Mask、Frame Driver、Camera Matrix、生命周期和第一阶段 fixture contract,使下一阶段能够直接针对少量关键未知项做最小技术验证,而不再重新讨论总体架构。
如果仍然存在两个以上彼此冲突的主路线且没有选择,则本任务不能标记为完成。
---
## 34. 下一阶段判定
设计结束时必须明确选择:
### A. 直接进入实现
只有所有关键架构假设都已有运行时证据时才允许。
或:
### B. 进入 `30_最小技术验证`
如果仍存在会影响实现成败的 backend、CustomLayer、context、matrix、trail 或 Mask 等运行时未知项,应选择 B。
按照当前调查结果,预期大概率选择 B,但必须由本次设计结果最终判断。