21 KiB
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. 必须先读取的现有文档
开始设计前必须完整读取并遵守:
- 根目录
AGENTS.md PARTICLE_BASELINE.mddocs/00_项目总则/当前全部有效总则docs/00_项目规范/当前全部有效规范docs/20_调查/10_架构调查报告_v1.mddocs/报告/10_架构调查_Codex完成报告_v1.mddocs/10_架构/下当前已有架构文档粒子开发记录.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 当前候选扩展点
已确认候选包括:
- Core
CustomLayerHost / RenderCustomLayer - Core
CustomDrawableLayer - 独立 Core particle render layer
- 现有 Source / FileSource / tile / fixture 管线的薄接入
- 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 / RenderCustomLayerCustomDrawableLayer- 新建独立 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-eastuniform-northvortexshearcoastal-mask
本阶段可以设计格式,但不得生成真实天气数据下载器。
12. 数据流设计
必须画出完整数据链:
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 同步设计
必须基于当前调查确认的:
TransformStateTransformParametersPaintParametersCustomLayerRenderParameters
设计粒子同帧同步方式。
至少覆盖:
- 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
必须说明:
MLNCustomStyleLayerMLNCustomDrawableStyleLayer- 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
不得为了保险列几十个验证任务。
必须按:
- 是否阻塞正式实现
- 是否会改变架构
- 最小验证方式
进行排序。
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 完成报告格式
必须逐项报告:
已完成未完成无法验证
至少包括:
- 读取前置文档
- 候选渲染路线比较
- 主路线选择
- Core 模块设计
- Data/Field contract
- GPU resource 设计
- Mask 设计
- 线程模型
- Camera/Matrix 同步
- 帧驱动
- Android 薄接入
- iOS 薄接入
- 生命周期
- 错误/降级
- 性能预算
- 最小技术验证清单
- 正式架构冻结项
- 生成文件
- 是否修改生产代码
- 是否修改依赖或构建配置
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,但必须由本次设计结果最终判断。