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

21 KiB
Raw Permalink Blame History

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. 数据流设计

必须画出完整数据链:

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,但必须由本次设计结果最终判断。