test: add core particle render layer validation
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:
@@ -246,6 +246,7 @@ list(APPEND INCLUDE_FILES
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/gfx/types.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/gfx/vertex_buffer.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/background_layer_factory.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/particle_validation_layer_factory.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/circle_layer_factory.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/custom_layer_factory.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/fill_extrusion_layer_factory.hpp
|
||||
@@ -357,6 +358,7 @@ list(APPEND INCLUDE_FILES
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/layer_properties.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/layer.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/background_layer.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/particle_validation_layer.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/circle_layer.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/custom_layer_host.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/custom_layer_init_parameters.hpp
|
||||
@@ -501,6 +503,7 @@ list(APPEND SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/upload_pass.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/vertex_vector.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/background_layer_factory.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/particle_validation_layer_factory.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/circle_layer_factory.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/fill_extrusion_layer_factory.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/fill_layer_factory.cpp
|
||||
@@ -578,6 +581,8 @@ list(APPEND SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/image_manager_observer.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_background_layer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_background_layer.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_particle_validation_layer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_particle_validation_layer.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_circle_layer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_circle_layer.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
|
||||
@@ -747,6 +752,8 @@ list(APPEND SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/layer_impl.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/layer_observer.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/background_layer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/particle_validation_layer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/particle_validation_layer_impl.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/background_layer_impl.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/background_layer_impl.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/background_layer_properties.cpp
|
||||
|
||||
@@ -64,6 +64,7 @@ MLN_GENERATED_STYLE_SOURCE = [
|
||||
"src/mbgl/style/layers/color_relief_layer.cpp",
|
||||
"src/mbgl/style/layers/background_layer_properties.cpp",
|
||||
"src/mbgl/style/layers/background_layer.cpp",
|
||||
"src/mbgl/style/layers/particle_validation_layer.cpp",
|
||||
"src/mbgl/style/layers/location_indicator_layer_properties.cpp",
|
||||
"src/mbgl/style/layers/location_indicator_layer.cpp",
|
||||
"src/mbgl/style/light.cpp",
|
||||
@@ -162,6 +163,7 @@ MLN_CORE_SOURCE = [
|
||||
"src/mbgl/gfx/upload_pass.hpp",
|
||||
"src/mbgl/gfx/vertex_vector.hpp",
|
||||
"src/mbgl/layermanager/background_layer_factory.cpp",
|
||||
"src/mbgl/layermanager/particle_validation_layer_factory.cpp",
|
||||
"src/mbgl/layermanager/circle_layer_factory.cpp",
|
||||
"src/mbgl/layermanager/custom_layer_factory.cpp",
|
||||
"src/mbgl/layermanager/fill_extrusion_layer_factory.cpp",
|
||||
@@ -233,6 +235,8 @@ MLN_CORE_SOURCE = [
|
||||
"src/mbgl/renderer/image_manager_observer.hpp",
|
||||
"src/mbgl/renderer/layers/render_background_layer.cpp",
|
||||
"src/mbgl/renderer/layers/render_background_layer.hpp",
|
||||
"src/mbgl/renderer/layers/render_particle_validation_layer.cpp",
|
||||
"src/mbgl/renderer/layers/render_particle_validation_layer.hpp",
|
||||
"src/mbgl/renderer/layers/render_circle_layer.cpp",
|
||||
"src/mbgl/renderer/layers/render_circle_layer.hpp",
|
||||
"src/mbgl/renderer/layers/render_custom_layer.cpp",
|
||||
@@ -406,6 +410,7 @@ MLN_CORE_SOURCE = [
|
||||
"src/mbgl/style/layer_observer.hpp",
|
||||
"src/mbgl/style/layers/background_layer_impl.cpp",
|
||||
"src/mbgl/style/layers/background_layer_impl.hpp",
|
||||
"src/mbgl/style/layers/particle_validation_layer_impl.hpp",
|
||||
"src/mbgl/style/layers/circle_layer_impl.cpp",
|
||||
"src/mbgl/style/layers/circle_layer_impl.hpp",
|
||||
"src/mbgl/style/layers/custom_layer.cpp",
|
||||
@@ -660,6 +665,7 @@ MLN_CORE_HEADERS = [
|
||||
"include/mbgl/i18n/collator.hpp",
|
||||
"include/mbgl/i18n/number_format.hpp",
|
||||
"include/mbgl/layermanager/background_layer_factory.hpp",
|
||||
"include/mbgl/layermanager/particle_validation_layer_factory.hpp",
|
||||
"include/mbgl/layermanager/circle_layer_factory.hpp",
|
||||
"include/mbgl/layermanager/custom_layer_factory.hpp",
|
||||
"include/mbgl/layermanager/fill_extrusion_layer_factory.hpp",
|
||||
@@ -673,6 +679,7 @@ MLN_CORE_HEADERS = [
|
||||
"include/mbgl/layermanager/location_indicator_layer_factory.hpp",
|
||||
"include/mbgl/layermanager/raster_layer_factory.hpp",
|
||||
"include/mbgl/layermanager/symbol_layer_factory.hpp",
|
||||
"include/mbgl/style/layers/particle_validation_layer.hpp",
|
||||
"include/mbgl/map/bound_options.hpp",
|
||||
"include/mbgl/map/camera.hpp",
|
||||
"include/mbgl/map/change.hpp",
|
||||
|
||||
731
docs/40_任务/30_01_CoreRenderLayer接入验证指令书_v1.md
Normal file
731
docs/40_任务/30_01_CoreRenderLayer接入验证指令书_v1.md
Normal file
@@ -0,0 +1,731 @@
|
||||
# MapLibreParticleLab 30_最小技术验证-01_Core RenderLayer 接入验证指令书 v1
|
||||
|
||||
## 1. 任务基本信息
|
||||
|
||||
- 项目:MapLibreParticleLab
|
||||
- 阶段:`30_最小技术验证`
|
||||
- 子任务:`30-01 Core RenderLayer 接入验证`
|
||||
- 当前主线:`particle/main`
|
||||
- 前置阶段:
|
||||
- `10_架构调查` 已完成
|
||||
- `20_架构设计` 已完成
|
||||
- 前置正式架构文档:
|
||||
- `docs/10_架构/20_粒子系统总体架构设计_v1.md`
|
||||
- 任务性质:最小运行时技术验证
|
||||
- 本任务不是正式粒子功能实现
|
||||
- 本任务允许最少量验证代码,但必须保持隔离、可识别、可移除
|
||||
- 不允许顺手重构 MapLibre
|
||||
- 不允许扩大到 GPU 粒子、真实场数据、Trail 或 Land/Ocean Mask 实现
|
||||
|
||||
---
|
||||
|
||||
## 2. 为什么本阶段先只做 30-01
|
||||
|
||||
当前总体架构已经选择独立 Core 粒子 Layer 作为正式主路线。
|
||||
|
||||
本阶段后续原计划还需要验证:
|
||||
|
||||
1. Core RenderLayer / Style order / 同帧 transform / repaint
|
||||
2. GPU State A/B + Trail A/B ping-pong 与 context recreation
|
||||
3. Camera / world wrap / Land-Ocean Mask
|
||||
4. iOS Core Layer 薄接入
|
||||
|
||||
但第 1 项属于前置门槛。
|
||||
|
||||
如果第 1 项失败,说明当前正式主路线可能需要调整,后续 GPU、Mask 和平台验证的代码位置、生命周期和验证方式都会变化。
|
||||
|
||||
因此本任务只完成 `30-01`。
|
||||
|
||||
只有 `30-01` 得出 PASS 后,才进入下一项。
|
||||
|
||||
不得提前为后续验证大量写代码。
|
||||
|
||||
---
|
||||
|
||||
## 3. 必须先读取
|
||||
|
||||
Codex 开始前必须完整读取:
|
||||
|
||||
1. 根目录 `AGENTS.md`
|
||||
2. `PARTICLE_BASELINE.md`
|
||||
3. `docs/00_项目总则/` 下当前全部有效文档
|
||||
4. `docs/00_项目规范/` 下当前全部有效文档
|
||||
5. `docs/20_调查/10_架构调查报告_v1.md`
|
||||
6. `docs/10_架构/20_粒子系统总体架构设计_v1.md`
|
||||
7. `docs/报告/10_架构调查_Codex完成报告_v1.md`
|
||||
8. `docs/报告/20_架构设计_Codex完成报告_v1.md`
|
||||
9. `粒子开发记录.md`
|
||||
10. 与下列现有实现直接相关的当前源码:
|
||||
- Style Layer 注册链
|
||||
- RenderLayer / RenderTree / RenderOrchestrator
|
||||
- RasterLayer 或其他最接近的现有 Layer 示例
|
||||
- CustomLayer / CustomDrawableLayer 示例
|
||||
- Android MapRenderer / AndroidRendererFrontend
|
||||
- `triggerRepaint` / `requestRender`
|
||||
- `TransformState` / `TransformParameters` / `PaintParameters`
|
||||
|
||||
不得凭记忆或 MapLibre 通用知识直接开写。
|
||||
|
||||
---
|
||||
|
||||
## 4. 开始前 Git 检查
|
||||
|
||||
先执行只读检查并记录:
|
||||
|
||||
- 当前分支
|
||||
- 当前 HEAD
|
||||
- `git status`
|
||||
- `origin` / 当前公开远端
|
||||
- `particle/main` 是否与远端同步
|
||||
|
||||
如果存在与本任务无关的本地修改:
|
||||
|
||||
- 不得删除
|
||||
- 不得覆盖
|
||||
- 不得顺带提交
|
||||
- 在完成报告中记录
|
||||
- 本任务提交时只 stage 本任务相关文件
|
||||
|
||||
如果当前不是 `particle/main`,不要擅自切换覆盖用户工作。
|
||||
|
||||
先明确报告实际状态。
|
||||
|
||||
---
|
||||
|
||||
## 5. 验证目标
|
||||
|
||||
本任务只回答下面四个问题。
|
||||
|
||||
### V1. 独立 Core Layer 能否进入真实 MapLibre Style / RenderTree 顺序
|
||||
|
||||
必须验证一个最小实验 Layer:
|
||||
|
||||
- 由 Core Style/Layer 系统识别
|
||||
- 能生成对应 RenderLayer
|
||||
- 能进入真实 RenderTree
|
||||
- 能按 Style Layer 顺序参与渲染
|
||||
- 不依赖 MapView 外部 Overlay
|
||||
- 不依赖 JS/RN 每帧逻辑
|
||||
- 不把实现塞进现有 RasterLayer
|
||||
|
||||
### V2. 该 Layer 能否在同一 MapLibre render frame 获得当前 Transform
|
||||
|
||||
至少验证能够在真正绘制路径中读取或使用:
|
||||
|
||||
- viewport size
|
||||
- zoom
|
||||
- bearing
|
||||
- pitch
|
||||
- projection / transform matrix 中架构要求的必要部分
|
||||
|
||||
验证重点不是把数值打印出来,而是确认这些值来自:
|
||||
|
||||
> 当前 MapLibre render frame 的真实 `PaintParameters / TransformParameters / TransformState` 链
|
||||
|
||||
而不是另建独立 Camera 状态。
|
||||
|
||||
### V3. Layer 活动时能否驱动持续 repaint
|
||||
|
||||
验证:
|
||||
|
||||
- Layer 处于“动画活动”状态时,可以通过 MapLibre 自己的 render loop 持续获得下一帧
|
||||
- 不建立独立于 MapLibre 的第二套动画循环
|
||||
- 不依赖 Android UI Timer
|
||||
- 不依赖 JS `requestAnimationFrame`
|
||||
|
||||
### V4. Layer 停止活动后能否停止额外 repaint
|
||||
|
||||
验证:
|
||||
|
||||
- Layer 切换为静止/disabled 后,不继续无意义请求下一帧
|
||||
- 地图自身正常 pan/zoom/render 不受影响
|
||||
- 不产生永久 continuous rendering
|
||||
|
||||
---
|
||||
|
||||
## 6. 本任务不是粒子实现
|
||||
|
||||
验证代码只能证明架构入口成立。
|
||||
|
||||
禁止在本任务实现:
|
||||
|
||||
- 速度场
|
||||
- U/V Grid
|
||||
- Field decode
|
||||
- particle position
|
||||
- age
|
||||
- seed
|
||||
- advection
|
||||
- velocity texture
|
||||
- particle state texture
|
||||
- ping-pong
|
||||
- Trail
|
||||
- fade pass
|
||||
- Land/Ocean Mask
|
||||
- Wave/Tide/Current
|
||||
- Wind fixture
|
||||
- 真正 Particle Shader
|
||||
- compute shader
|
||||
|
||||
如需要视觉确认,只允许使用最简单的验证图元或调试绘制。
|
||||
|
||||
例如:
|
||||
|
||||
- 单色矩形
|
||||
- 三角形
|
||||
- 简单全屏/局部 quad
|
||||
- 简单随帧变化的 alpha / position
|
||||
|
||||
不得把验证图元包装成“粒子初版”。
|
||||
|
||||
---
|
||||
|
||||
## 7. 验证代码命名
|
||||
|
||||
验证代码必须明确表明其临时技术验证属性。
|
||||
|
||||
优先使用:
|
||||
|
||||
`ParticleValidation*`
|
||||
|
||||
或:
|
||||
|
||||
`RasterParticleValidation*`
|
||||
|
||||
不要直接把不成熟的验证代码命名成最终生产类并假装已经冻结。
|
||||
|
||||
如果必须使用正式架构类名才能验证注册链,例如:
|
||||
|
||||
- `RasterParticleLayer`
|
||||
- `RenderRasterParticleLayer`
|
||||
|
||||
则必须:
|
||||
|
||||
1. 只实现验证所需最小骨架;
|
||||
2. 在代码和文档中明确 `VALIDATION ONLY`;
|
||||
3. 不提前加入未来未验证业务接口;
|
||||
4. 不把临时调试字段伪装成正式公共 API。
|
||||
|
||||
---
|
||||
|
||||
## 8. 最小实现范围
|
||||
|
||||
Codex 应先调查当前最小实现需要触及哪些文件。
|
||||
|
||||
原则:
|
||||
|
||||
> 能用 5 个文件解决,不要改 15 个文件。
|
||||
|
||||
预计可能涉及,但不得机械照搬:
|
||||
|
||||
- Core style layer 声明
|
||||
- Core style layer 实现
|
||||
- 对应 RenderLayer
|
||||
- LayerManager / 注册位置
|
||||
- Android Test App 或现有测试入口
|
||||
- 最小验证日志/状态
|
||||
|
||||
最终以当前真实代码为准。
|
||||
|
||||
不得为了“以后可能用到”提前创建整套 particle 目录树。
|
||||
|
||||
---
|
||||
|
||||
## 9. Style / RenderTree 验证要求
|
||||
|
||||
必须证明实验 Layer 是通过正式 MapLibre Layer 链进入渲染。
|
||||
|
||||
验证报告至少记录实际链:
|
||||
|
||||
```text
|
||||
Style Layer
|
||||
→ LayerManager / RenderLayer creation
|
||||
→ RenderTree / RenderOrchestrator
|
||||
→ 对应 RenderLayer
|
||||
→ Render pass
|
||||
```
|
||||
|
||||
必须指出:
|
||||
|
||||
- 实际插入哪个 pass
|
||||
- 与前后普通 Style Layer 的 order 是否正确
|
||||
- 是否需要 tile/source
|
||||
- 当前验证 Layer 是否 source-less
|
||||
- 如果 source-less 不可行,实际最小 Source 依赖是什么
|
||||
|
||||
不能只通过 CustomLayer 示例证明。
|
||||
|
||||
本任务要验证的是总体架构选择的“独立 Core Layer 路线”。
|
||||
|
||||
---
|
||||
|
||||
## 10. 同帧 Camera / Matrix 验证
|
||||
|
||||
验证代码必须从当前 render path 内取得 transform。
|
||||
|
||||
至少记录:
|
||||
|
||||
- 获取位置
|
||||
- 对应源码对象
|
||||
- 生命周期
|
||||
- 是否每 frame 更新
|
||||
- 是否与当前 RenderTree frame 同步
|
||||
|
||||
至少验证以下地图变化不会让实验 Layer 明显脱离地图:
|
||||
|
||||
- pan
|
||||
- zoom
|
||||
- bearing
|
||||
- pitch
|
||||
- resize
|
||||
|
||||
本任务不要求完成 world wrap / antimeridian 专项验证。
|
||||
|
||||
这些留给后续 Mask/Camera 技术验证。
|
||||
|
||||
---
|
||||
|
||||
## 11. Repaint 验证
|
||||
|
||||
实验 Layer 必须提供最小“活动/停止”活动模型。
|
||||
|
||||
例如内部验证状态:
|
||||
|
||||
```text
|
||||
ACTIVE
|
||||
STATIC
|
||||
```
|
||||
|
||||
### ACTIVE
|
||||
|
||||
必须证明:
|
||||
|
||||
- 当前 frame 完成后仍会请求 MapLibre 下一帧
|
||||
- 连续帧来自 MapLibre render/repaint 链
|
||||
- 能观察到 frame counter 持续增长
|
||||
|
||||
### STATIC
|
||||
|
||||
必须证明:
|
||||
|
||||
- 不再由该 Layer 主动触发下一帧
|
||||
- 地图静止时 frame counter 不继续无意义增长
|
||||
- 用户操作地图时仍能正常重新绘制
|
||||
|
||||
不得使用平台 Timer 模拟。
|
||||
|
||||
---
|
||||
|
||||
## 12. 日志要求
|
||||
|
||||
验证期间允许增加最少量调试日志。
|
||||
|
||||
日志至少能识别:
|
||||
|
||||
- Layer create
|
||||
- RenderLayer create
|
||||
- first render
|
||||
- frame counter
|
||||
- current active/static
|
||||
- repaint request
|
||||
- relevant transform snapshot
|
||||
- destroy/deinitialize
|
||||
|
||||
禁止每帧输出大量完整 matrix 导致日志爆炸。
|
||||
|
||||
建议:
|
||||
|
||||
- 首帧输出一次完整关键参数
|
||||
- 后续每 N 帧输出 frame counter
|
||||
- 状态变化时输出
|
||||
- 销毁时输出
|
||||
|
||||
验证完成后是否保留日志,由报告明确说明。
|
||||
|
||||
---
|
||||
|
||||
## 13. Android 第一验证平台
|
||||
|
||||
本任务第一运行平台固定为:
|
||||
|
||||
**Android OpenGL**
|
||||
|
||||
原因:
|
||||
|
||||
- 当前架构设计将它作为第一共同验证入口;
|
||||
- 本任务只验证 Core Layer + 同帧 transform + repaint;
|
||||
- 不在本任务同时扩大到 Vulkan / iOS。
|
||||
|
||||
Vulkan 和 iOS 留到后续对应验证。
|
||||
|
||||
---
|
||||
|
||||
## 14. Android 编译规则:必须由用户手工执行
|
||||
|
||||
本项目 Android 编译必须由用户手工执行。
|
||||
|
||||
Codex 禁止运行:
|
||||
|
||||
- Gradle Android build
|
||||
- `./gradlew assemble...`
|
||||
- `./gradlew install...`
|
||||
- Android npm build
|
||||
- 任何等价 Android 编译命令
|
||||
|
||||
Codex 可以:
|
||||
|
||||
- 检查现有 Gradle 配置
|
||||
- 准备代码
|
||||
- 给出用户应执行的准确编译命令
|
||||
- 分析用户返回的编译错误
|
||||
- 在允许范围内修正本任务代码
|
||||
|
||||
Codex 不得代替用户执行 Android 编译。
|
||||
|
||||
---
|
||||
|
||||
## 15. 运行/设备验证规则
|
||||
|
||||
本任务不得默认替用户执行:
|
||||
|
||||
- ADB 安装
|
||||
- ADB 启动
|
||||
- ADB 日志抓取
|
||||
- 真机操作
|
||||
|
||||
除非用户在后续明确授权。
|
||||
|
||||
因此第一轮 Codex 工作完成后,应输出:
|
||||
|
||||
1. 用户手工编译命令
|
||||
2. 应打开的 Test App / 页面 / Activity
|
||||
3. 需要执行的地图操作
|
||||
4. 需要观察的日志
|
||||
5. PASS/FAIL 判定方法
|
||||
|
||||
用户提供编译/运行结果后,再完成最终技术验证结论。
|
||||
|
||||
---
|
||||
|
||||
## 16. 两阶段执行方式
|
||||
|
||||
本任务允许同一份指令书分两次 Codex 执行。
|
||||
|
||||
### 阶段 A:验证代码准备
|
||||
|
||||
Codex:
|
||||
|
||||
1. 读取基线
|
||||
2. 写最小验证代码
|
||||
3. 做静态检查
|
||||
4. 生成暂定验证报告
|
||||
5. 给出用户手工编译/运行步骤
|
||||
6. commit + push 到 `particle/main`
|
||||
|
||||
此时报告结论必须写:
|
||||
|
||||
`WAITING_MANUAL_VALIDATION`
|
||||
|
||||
不得写 PASS。
|
||||
|
||||
### 阶段 B:用户返回实际编译/运行结果后
|
||||
|
||||
Codex:
|
||||
|
||||
1. 读取用户提供的编译结果、日志和现象
|
||||
2. 如果失败,只修复本验证范围内问题
|
||||
3. 如果架构假设本身失败,停止,不强行修成另一套架构
|
||||
4. 更新正式验证报告
|
||||
5. 更新 Codex 完成报告
|
||||
6. 更新 `粒子开发记录.md`
|
||||
7. commit + push 到 `particle/main`
|
||||
|
||||
最终只能是:
|
||||
|
||||
- `PASS`
|
||||
- `FAIL`
|
||||
- `BLOCKED`
|
||||
|
||||
---
|
||||
|
||||
## 17. PASS 标准
|
||||
|
||||
只有同时满足以下条件,`30-01` 才能 PASS:
|
||||
|
||||
- [ ] 独立 Core Style Layer 被正式识别
|
||||
- [ ] 对应 RenderLayer 被创建
|
||||
- [ ] 真正进入 RenderTree / Render pass
|
||||
- [ ] Layer order 正确
|
||||
- [ ] 从当前 render frame 获得必要 Transform
|
||||
- [ ] pan 正常
|
||||
- [ ] zoom 正常
|
||||
- [ ] bearing 正常
|
||||
- [ ] pitch 正常
|
||||
- [ ] resize 正常
|
||||
- [ ] ACTIVE 时由 MapLibre render loop 持续 repaint
|
||||
- [ ] STATIC 时停止该 Layer 的额外 repaint
|
||||
- [ ] 地图普通渲染不受破坏
|
||||
- [ ] 未使用外部 Overlay
|
||||
- [ ] 未建立平台 Timer 动画循环
|
||||
- [ ] 未实现超出本任务范围的粒子系统
|
||||
|
||||
任何一项关键门槛失败,不得标 PASS。
|
||||
|
||||
---
|
||||
|
||||
## 18. FAIL 与停止条件
|
||||
|
||||
出现以下任一情况时,应停止继续向后实现:
|
||||
|
||||
### F1
|
||||
|
||||
独立 Core Layer 无法在不破坏 MapLibre 现有主架构的情况下进入真实 RenderTree。
|
||||
|
||||
### F2
|
||||
|
||||
无法从该 Layer 的真实 render frame 获得足够 transform。
|
||||
|
||||
### F3
|
||||
|
||||
必须依赖外部 Overlay 或独立动画 loop 才能工作。
|
||||
|
||||
### F4
|
||||
|
||||
持续 repaint 必须通过明显侵入 Map 主循环才能实现。
|
||||
|
||||
### F5
|
||||
|
||||
验证要求迫使 Android/iOS 分别拥有不同 particle engine。
|
||||
|
||||
若触发 F1-F5:
|
||||
|
||||
- 停止
|
||||
- 不继续 GPU ping-pong
|
||||
- 不继续 Mask
|
||||
- 不继续 iOS 实现
|
||||
- 报告失败事实
|
||||
- 回到架构设计重新决策
|
||||
|
||||
不得为了让验证“通过”扩大修改范围。
|
||||
|
||||
---
|
||||
|
||||
## 19. 禁止事项
|
||||
|
||||
本任务禁止:
|
||||
|
||||
- 正式实现 Particle Field
|
||||
- 正式实现 RasterArraySource 数据链
|
||||
- 正式实现 GPU Simulation
|
||||
- 正式实现 Trail
|
||||
- 正式实现 Mask
|
||||
- 接真实天气数据
|
||||
- 引入新第三方依赖
|
||||
- 升级 MapLibre
|
||||
- 升级 Android SDK / NDK / Gradle
|
||||
- 修改无关 Layer
|
||||
- 重构 Renderer
|
||||
- 重构 Map
|
||||
- 修改 iOS 代码
|
||||
- 实现 Vulkan 版本
|
||||
- 建立 RN/JS bridge
|
||||
- 新增产品 API
|
||||
- 清理与本任务无关的旧代码
|
||||
|
||||
---
|
||||
|
||||
## 20. 文档输出
|
||||
|
||||
### 20.1 技术验证报告
|
||||
|
||||
生成:
|
||||
|
||||
`docs/60_测试/30_01_CoreRenderLayer接入最小技术验证报告_v1.md`
|
||||
|
||||
如果同主题已有文件,按版本递增,不覆盖旧文件。
|
||||
|
||||
报告必须区分:
|
||||
|
||||
- 静态确认
|
||||
- 用户手工编译结果
|
||||
- 用户运行结果
|
||||
- 实际日志证据
|
||||
- 推论
|
||||
|
||||
没有用户运行结果时,只能写:
|
||||
|
||||
`WAITING_MANUAL_VALIDATION`
|
||||
|
||||
### 20.2 Codex 完成报告
|
||||
|
||||
生成:
|
||||
|
||||
`docs/报告/30_01_CoreRenderLayer接入验证_Codex完成报告_v1.md`
|
||||
|
||||
同主题已有报告时递增版本号。
|
||||
|
||||
### 20.3 开发记录
|
||||
|
||||
追加:
|
||||
|
||||
`粒子开发记录.md`
|
||||
|
||||
不得覆盖历史内容。
|
||||
|
||||
---
|
||||
|
||||
## 21. 不主动创建 Step
|
||||
|
||||
本任务不主动创建或修改 `docs/50_Step记录/`。
|
||||
|
||||
只有用户明确要求时才创建 Step。
|
||||
|
||||
---
|
||||
|
||||
## 22. Git 收尾规则
|
||||
|
||||
本任务凡产生文件修改,都必须提交并 push。
|
||||
|
||||
目标:
|
||||
|
||||
- 本地主线:`particle/main`
|
||||
- 公开远端对应分支:`particle/main`
|
||||
|
||||
### 22.1 提交范围
|
||||
|
||||
只允许提交:
|
||||
|
||||
- 本任务验证代码
|
||||
- 本任务验证文档
|
||||
- 本任务完成报告
|
||||
- 本任务对 `粒子开发记录.md` 的追加
|
||||
|
||||
不得把与本任务无关的本地修改一起提交。
|
||||
|
||||
### 22.2 阶段 A
|
||||
|
||||
验证代码准备完成后:
|
||||
|
||||
1. 检查 `git diff`
|
||||
2. 明确列出本任务文件
|
||||
3. commit
|
||||
4. push 到公开 `particle/main`
|
||||
5. 确认远端可见
|
||||
|
||||
推荐提交信息:
|
||||
|
||||
`test: add core particle render layer validation`
|
||||
|
||||
### 22.3 阶段 B
|
||||
|
||||
用户手工验证结果回填后:
|
||||
|
||||
1. 更新验证报告
|
||||
2. 更新完成报告
|
||||
3. 更新开发记录
|
||||
4. 如有必要修复最小验证代码
|
||||
5. commit
|
||||
6. push 到公开 `particle/main`
|
||||
|
||||
推荐提交信息:
|
||||
|
||||
`test: finalize core particle render layer validation`
|
||||
|
||||
### 22.4 Push 失败
|
||||
|
||||
如果 push 失败:
|
||||
|
||||
- 不得声称任务完整完成
|
||||
- 完成报告必须写明:
|
||||
- push 失败
|
||||
- 错误原因
|
||||
- 本地 commit hash
|
||||
- 目标远端
|
||||
- 目标分支
|
||||
|
||||
---
|
||||
|
||||
## 23. Git 信息记录
|
||||
|
||||
Codex 完成报告必须记录:
|
||||
|
||||
- 任务开始 HEAD
|
||||
- 本次主要变更 commit hash
|
||||
- commit message
|
||||
- push 是否成功
|
||||
- 公开远端
|
||||
- 远端分支
|
||||
- push 后远端可见状态
|
||||
- 是否存在未提交的无关本地改动
|
||||
|
||||
为避免“提交文件记录自己的 commit hash”产生自引用问题:
|
||||
|
||||
- 完成报告中的 `主要变更 commit hash` 记录包含验证代码和主要文档的实质性提交;
|
||||
- 如果随后还有仅用于回填报告元数据的收尾提交,Codex 最终回复中必须另外报告最终远端 HEAD;
|
||||
- 不得为了回填当前提交自己的 hash 无限 amend。
|
||||
|
||||
---
|
||||
|
||||
## 24. Codex 完成报告必须逐项说明
|
||||
|
||||
至少包括:
|
||||
|
||||
1. 前置文档读取
|
||||
2. 当前 Git 基线
|
||||
3. 实际修改文件
|
||||
4. Style Layer 接入位置
|
||||
5. RenderLayer 创建链
|
||||
6. RenderTree / pass
|
||||
7. Transform 获取链
|
||||
8. Repaint 链
|
||||
9. Android OpenGL 验证入口
|
||||
10. 用户手工编译命令
|
||||
11. 用户手工运行步骤
|
||||
12. 当前状态:
|
||||
- WAITING_MANUAL_VALIDATION
|
||||
- PASS
|
||||
- FAIL
|
||||
- BLOCKED
|
||||
13. 是否修改生产代码
|
||||
14. 是否扩大架构范围
|
||||
15. 是否安装/升级依赖
|
||||
16. Git commit
|
||||
17. push 结果
|
||||
18. 是否可以进入下一个最小技术验证
|
||||
|
||||
---
|
||||
|
||||
## 25. 进入下一验证的条件
|
||||
|
||||
只有最终结果:
|
||||
|
||||
`PASS`
|
||||
|
||||
才允许进入下一个技术验证。
|
||||
|
||||
下一项预计验证:
|
||||
|
||||
> GPU State A/B + Trail A/B 的 gfx ping-pong、RenderTarget、同步/barrier 与 context recreation。
|
||||
|
||||
如果本任务是:
|
||||
|
||||
- FAIL
|
||||
- BLOCKED
|
||||
|
||||
则不得提前执行下一项。
|
||||
|
||||
必须先回到架构层决定如何处理。
|
||||
|
||||
---
|
||||
|
||||
## 26. 本任务完成定义
|
||||
|
||||
`30-01` 真正完成必须满足:
|
||||
|
||||
> 用户手工 Android OpenGL 编译和运行后,已经通过实际运行证据确认:独立 Core Layer 可以进入 MapLibre 正式 Style/RenderTree 渲染链,在同一 render frame 使用 MapLibre 当前 Transform,并通过 MapLibre 自己的 repaint 机制在活动时持续渲染、停止时停止额外帧。
|
||||
|
||||
只有静态代码完成、没有实际运行结果,不叫验证完成。
|
||||
|
||||
只能标记:
|
||||
|
||||
`WAITING_MANUAL_VALIDATION`
|
||||
193
docs/60_测试/30_01_CoreRenderLayer接入最小技术验证报告_v1.md
Normal file
193
docs/60_测试/30_01_CoreRenderLayer接入最小技术验证报告_v1.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# 30-01 Core RenderLayer 接入最小技术验证报告 v1
|
||||
|
||||
日期:2026-08-29
|
||||
|
||||
当前状态:`WAITING_MANUAL_VALIDATION`
|
||||
|
||||
本报告只记录阶段 A 的代码准备与静态证据。Android OpenGL 编译、安装、运行、手势和日志结果尚未由用户回填,因此不得标记 PASS。
|
||||
|
||||
## 1. 验证范围
|
||||
|
||||
本验证只回答:独立 Core Style Layer/RenderLayer 是否能进入正式 RenderTree 顺序、读取同帧 transform、在 ACTIVE 时通过 MapLibre 自身 loop 请求下一帧,并在 STATIC 时停止额外 repaint。
|
||||
|
||||
未实现速度场、粒子状态、advection、GPU ping-pong、Trail、Mask、真实天气数据或 compute shader。
|
||||
|
||||
## 2. 最小验证实现
|
||||
|
||||
- Style type:`particle-validation`。
|
||||
- Style class:`style::ParticleValidationLayer`,明确标记 `VALIDATION ONLY`。
|
||||
- Render class:`RenderParticleValidationLayer`。
|
||||
- Source:不要求 source;`LayerTypeInfo::Source::NotRequired`。
|
||||
- Pass:`RenderPass::Translucent`。
|
||||
- 状态:style paint 字段 `particle-validation-active=true/false`,只用于本验证。
|
||||
- Drawable:一个不输出像素的验证 Drawable,用于进入真实 LayerGroup/pass 顺序并在 drawable tweaker 中取得当前 `PaintParameters`;不使用 `CustomLayerHost`,不构成外部 CustomLayer 路线。
|
||||
- Android API:没有新增 Java/JNI 产品 API;Android 只把该类型注册为 JSON style 可用的 core-only layer。
|
||||
|
||||
## 3. Style / RenderTree / pass 静态确认
|
||||
|
||||
实际链路为:
|
||||
|
||||
```text
|
||||
Android Test App inline Style JSON
|
||||
→ style::conversion::Converter<std::unique_ptr<Layer>>
|
||||
→ LayerManagerAndroid::getFactory("particle-validation")
|
||||
→ ParticleValidationLayerFactory::createLayer
|
||||
→ style::ParticleValidationLayer immutable Impl
|
||||
→ RenderOrchestrator layer diff/order
|
||||
→ LayerManagerAndroid::createRenderLayer
|
||||
→ ParticleValidationLayerFactory::createRenderLayer
|
||||
→ RenderParticleValidationLayer
|
||||
→ source-less LayerRenderItem
|
||||
→ RenderParticleValidationLayer::update
|
||||
→ source-less LayerGroup(layerIndex)
|
||||
→ validation Drawable(RenderPass::Translucent)
|
||||
→ main translucent pass 的 LayerGroup 顺序
|
||||
```
|
||||
|
||||
Test App style 顺序固定为:
|
||||
|
||||
```text
|
||||
validation-background
|
||||
→ validation-below(蓝色大圆)
|
||||
→ particle-validation-core
|
||||
→ validation-above(黄色小圆)
|
||||
```
|
||||
|
||||
验证 Drawable 的 `layerIndex` 由 RenderOrchestrator 正式 style order 分配,LayerGroup 以同一 index 注册。运行日志同时记录 `layerIndex` 和 pass 内的 `currentLayer`。实际 Android 顺序仍需用户运行日志确认。
|
||||
|
||||
## 4. 同帧 Transform 静态确认
|
||||
|
||||
验证日志在 DrawableTweaker 的 `execute(gfx::Drawable&, PaintParameters&)` 中生成。该回调发生在 MapLibre main translucent pass 对应 LayerGroup/Drawable 的真实执行路径中,直接读取:
|
||||
|
||||
- `PaintParameters::state.getSize()`:viewport;
|
||||
- `PaintParameters::state.getZoom()`:zoom;
|
||||
- `PaintParameters::state.getBearing()`:bearing;
|
||||
- `PaintParameters::state.getPitch()`:pitch;
|
||||
- `PaintParameters::transformParams.projMatrix`:当前 frame projection matrix 摘要;
|
||||
- `PaintParameters::frameCount`:MapLibre frame number。
|
||||
|
||||
未创建独立 Camera 状态,未从 Android UI 层复制或推算 transform。
|
||||
|
||||
## 5. Repaint 链静态确认
|
||||
|
||||
本任务增加一个薄 Core hook:
|
||||
|
||||
```text
|
||||
RenderParticleValidationLayer::needsRepaint()
|
||||
→ RenderOrchestrator::hasTransitions() 汇总
|
||||
→ RenderTreeParameters::needsRepaint
|
||||
→ RendererObserver::onDidFinishRenderingFrame(needsRepaint)
|
||||
→ Map::Impl::onDidFinishRenderingFrame
|
||||
→ needsRepaint=true 时 Map::Impl::onUpdate()
|
||||
→ AndroidRendererFrontend::update()
|
||||
→ Android MapRenderer.requestRender()
|
||||
→ 下一 MapLibre render frame
|
||||
```
|
||||
|
||||
所有其他 RenderLayer 默认返回 false,因此现有 Layer 行为不变。
|
||||
|
||||
- ACTIVE:验证 Layer 可见且 active 时返回 true。
|
||||
- STATIC:active=false 时返回 false;地图自己的 transition、手势和 source 更新仍可请求 frame。
|
||||
- 未使用 Android Timer、Handler 循环、JS `requestAnimationFrame` 或第二套动画 loop。
|
||||
|
||||
## 6. 日志格式
|
||||
|
||||
日志关键字统一为 `ParticleValidation`,包括:
|
||||
|
||||
- Style Layer create/destroy;
|
||||
- RenderLayer create/destroy;
|
||||
- ACTIVE/STATIC;
|
||||
- validation Drawable create/destroy;
|
||||
- 首帧及每 60 帧:frame counter、MapLibre frame、状态、repaint、pass、layer order、viewport、zoom、bearing、pitch、projection matrix 摘要。
|
||||
|
||||
预期 ACTIVE 样例:
|
||||
|
||||
```text
|
||||
ParticleValidation: render frame=60 mapFrame=... state=ACTIVE repaint=requested pass=translucent layerIndex=... currentLayer=... viewport=... zoom=... bearing=... pitch=... proj=[...]
|
||||
```
|
||||
|
||||
预期 STATIC 样例:
|
||||
|
||||
```text
|
||||
ParticleValidation: state=STATIC ...
|
||||
ParticleValidation: render frame=1 ... state=STATIC repaint=stopped ...
|
||||
```
|
||||
|
||||
地图静止后不应继续出现该 STATIC 实例的周期性 `frame=60/120/...`。
|
||||
|
||||
## 7. Codex 已执行检查
|
||||
|
||||
### 7.1 通过
|
||||
|
||||
- `git diff --check`:PASS。
|
||||
- 新增/修改 C++ 文件 `clang-format`:PASS。
|
||||
- Android Manifest、layout、values XML 使用 Python 标准库只读解析:PASS。
|
||||
- Android `MapLibreMap.getCameraPosition/setCameraPosition` API 静态确认:PASS。
|
||||
- Linux OpenGL CMake 重新配置:PASS。
|
||||
- 精确编译以下 Linux Core 对象:PASS:
|
||||
- `particle_validation_layer_factory.cpp.o`
|
||||
- `particle_validation_layer.cpp.o`
|
||||
- `render_particle_validation_layer.cpp.o`
|
||||
- `render_orchestrator.cpp.o`
|
||||
- default `layer_manager.cpp.o`
|
||||
|
||||
### 7.2 未完成
|
||||
|
||||
- Linux OpenGL 全量增量编译:公共 `RenderLayer` 头变化触发 321 个对象重编译;在 `41/321` 无错误时主动停止,随后改为上述任务相关对象精确编译并通过。
|
||||
- Android OpenGL Gradle/NDK/Test App 编译:按指令书禁止 Codex 执行,等待用户手工完成。
|
||||
- Android 运行、日志、pan/zoom/bearing/pitch/resize、ACTIVE/STATIC:等待用户。
|
||||
|
||||
## 8. 用户手工编译
|
||||
|
||||
在项目 Linux 环境执行:
|
||||
|
||||
```bash
|
||||
cd /mnt/sda1/MapLibreParticleLab
|
||||
source /root/.maplibre-particle-env
|
||||
cd platform/android
|
||||
RENDERER=opengl BUILDTYPE=Debug JOBS=3 make android-x86-64
|
||||
```
|
||||
|
||||
预期 APK:
|
||||
|
||||
```text
|
||||
platform/android/MapLibreAndroidTestApp/build/outputs/apk/opengl/debug/MapLibreAndroidTestApp-opengl-debug.apk
|
||||
```
|
||||
|
||||
若目标设备不是 x86_64,请将 `android-x86-64` 换成对应现有 Make target;不要切换 Vulkan,本任务平台固定 Android OpenGL。
|
||||
|
||||
## 9. 用户手工运行步骤
|
||||
|
||||
1. 安装并打开 `MapLibre Native Android TestApp`。
|
||||
2. 在 `Custom Layer` 分类打开 `Particle Core Layer Validation`。
|
||||
3. 确认页面显示蓝色大圆和其上的黄色小圆,初始状态文字为 ACTIVE。
|
||||
4. 过滤日志关键字:`adb logcat | grep ParticleValidation`;也可使用 Android Studio Logcat 搜索同一关键字。
|
||||
5. ACTIVE 静置至少 3 秒,确认 `frame=60/120/...` 持续增长。
|
||||
6. 分别执行 pan、双指 zoom、rotate、pitch,确认地图正常,并保存变化前后的 transform 日志。
|
||||
7. 旋转设备或改变模拟器窗口尺寸,确认 `viewport` 更新且页面正常。
|
||||
8. 点击 STATIC,静置至少 3 秒;确认不再周期性增长。
|
||||
9. STATIC 下再次 pan/zoom,确认地图仍会按正常地图事件重绘;操作停止后不继续产生验证层额外帧。
|
||||
10. 再点 ACTIVE,确认连续帧恢复。
|
||||
|
||||
## 10. PASS / FAIL 判定
|
||||
|
||||
### PASS 候选证据
|
||||
|
||||
- Style Layer、RenderLayer 和 validation Drawable 均创建;
|
||||
- 日志显示 `pass=translucent` 且 layer order 与 style 顺序一致;
|
||||
- viewport/zoom/bearing/pitch/projection 摘要随对应操作更新;
|
||||
- ACTIVE frame counter 持续增长;
|
||||
- STATIC 静止后停止周期增长,但地图手势仍正常;
|
||||
- 无崩溃、黑屏、永久 continuous rendering 或普通地图破坏。
|
||||
|
||||
### FAIL/停止
|
||||
|
||||
若 layer 无法创建、无法进入 pass、transform 不是同帧、ACTIVE 无法持续、STATIC 无法停止,或必须改用 Overlay/Timer/大改 Map 主循环,则记录 FAIL 并停止后续 GPU/Mask/iOS 验证。
|
||||
|
||||
## 11. 当前结论
|
||||
|
||||
静态代码和 Linux 任务相关对象编译支持该路线具备可验证性,但缺少 Android OpenGL 实际编译与运行证据。
|
||||
|
||||
当前唯一合法结论:`WAITING_MANUAL_VALIDATION`。
|
||||
|
||||
不得进入下一项 GPU State A/B + Trail A/B 验证。
|
||||
141
docs/报告/30_01_CoreRenderLayer接入验证_Codex完成报告_v1.md
Normal file
141
docs/报告/30_01_CoreRenderLayer接入验证_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# 30-01 Core RenderLayer 接入验证 Codex 完成报告 v1
|
||||
|
||||
日期:2026-08-29
|
||||
|
||||
任务:`docs/40_任务/30_01_CoreRenderLayer接入验证指令书_v1.md`
|
||||
|
||||
执行阶段:A(验证代码准备)
|
||||
|
||||
当前结论:`WAITING_MANUAL_VALIDATION`
|
||||
|
||||
## 1. 前置文档读取
|
||||
|
||||
已读取根 `AGENTS.md`、`PARTICLE_BASELINE.md`、`docs/00_项目总则/` 全部当前有效文件、`docs/00_项目规范/` 全部当前有效文件、架构调查报告、总体架构设计、两份前置 Codex 报告、开发记录、当前 STEP-000 和本任务书。已按任务要求调查 Style 注册、RenderLayer/RenderTree/RenderOrchestrator、Background/Raster/Custom 示例、Android renderer/frontend、repaint 和 Transform/Paint 链。
|
||||
|
||||
## 2. Git 基线
|
||||
|
||||
- 分支:`particle/main`。
|
||||
- 任务开始 HEAD:`74be2a5052428717d7c9e133c333da8bd783b958`。
|
||||
- 开始时远端同步:`particle/main...origin/particle/main = 0 0`。
|
||||
- 开始时无已跟踪修改;有用户提供的未跟踪当前任务书。
|
||||
- submodule:已查询,无本任务修改。
|
||||
- 主要变更 commit:阶段 A 实质提交后回填。
|
||||
- commit message:`test: add core particle render layer validation`。
|
||||
- push:阶段 A 实质提交后回填。
|
||||
- 公开远端:`ssh://git@192.168.200.230:2222/tei/MapLibreParticleLab.git`。
|
||||
- 远端分支:`particle/main`。
|
||||
|
||||
## 3. 实际修改文件
|
||||
|
||||
### Core 验证代码
|
||||
|
||||
- 新增 `ParticleValidationLayer`、Impl/properties、factory、`RenderParticleValidationLayer`。
|
||||
- 修改 Core/default 与 Android LayerManager 注册。
|
||||
- 修改 `RenderLayer` 增加默认 false 的 `needsRepaint()`。
|
||||
- 修改 `RenderOrchestrator` 汇总 Layer repaint 请求。
|
||||
- 修改 CMake/Bazel source lists。
|
||||
|
||||
### Android Test App
|
||||
|
||||
- 新增 `ParticleCoreLayerValidationActivity` 和 layout。
|
||||
- 注册 Manifest。
|
||||
- 新增页面标题、说明和 ACTIVE/STATIC 文案。
|
||||
|
||||
### 文档
|
||||
|
||||
- 纳入当前正式任务书。
|
||||
- 新增技术验证报告和本完成报告。
|
||||
- 追加 `粒子开发记录.md`。
|
||||
- 未创建或修改 Step。
|
||||
|
||||
## 4. Style Layer 接入位置
|
||||
|
||||
JSON type `particle-validation` 经 Android `LayerManagerAndroid` 的 core-only factory 正式解析为 `style::ParticleValidationLayer`。其 `LayerTypeInfo` 为 source-less、无 layout、无 tile kind。它不是 RasterLayer 分支,也不是 MapView 外部 Overlay。
|
||||
|
||||
## 5. RenderLayer 创建链
|
||||
|
||||
```text
|
||||
Style JSON → LayerManagerAndroid → ParticleValidationLayerFactory
|
||||
→ ParticleValidationLayer immutable Impl
|
||||
→ RenderOrchestrator layer diff
|
||||
→ ParticleValidationLayerFactory::createRenderLayer
|
||||
→ RenderParticleValidationLayer
|
||||
```
|
||||
|
||||
## 6. RenderTree / pass
|
||||
|
||||
`RenderParticleValidationLayer` 进入 source-less `LayerRenderItem`,按 style index 创建 source-less LayerGroup,并注册一个无像素输出的 validation Drawable。Drawable 属于 `RenderPass::Translucent`,随正常 LayerGroup order 进入 main translucent pass。Test App 将它固定放在蓝色参考圆与黄色参考圆之间。
|
||||
|
||||
## 7. Transform 获取链
|
||||
|
||||
```text
|
||||
UpdateParameters::TransformState
|
||||
→ RenderTreeParameters::TransformParameters
|
||||
→ 当前 frame PaintParameters
|
||||
→ validation DrawableTweaker::execute
|
||||
```
|
||||
|
||||
读取 viewport、zoom、bearing、pitch、projection matrix 摘要和 MapLibre frame count。没有独立 Camera 状态。
|
||||
|
||||
## 8. Repaint 链
|
||||
|
||||
ACTIVE 的 `RenderParticleValidationLayer::needsRepaint()` 返回 true,由 RenderOrchestrator 写入 RenderTree `needsRepaint`,再经 RendererObserver、Map::Impl::onDidFinishRenderingFrame/onUpdate 和 AndroidRendererFrontend.requestRender 请求下一 MapLibre frame。STATIC 返回 false。没有 Timer、Handler loop、JS/RN frame loop。
|
||||
|
||||
## 9. Android OpenGL 验证入口
|
||||
|
||||
Test App → `Custom Layer` 分类 → `Particle Core Layer Validation`。虽然位于现有 UI 分类中,但实现不是 CustomLayer/CustomLayerHost;分类只用于减少 Test App 导航改动。
|
||||
|
||||
## 10. 用户手工编译命令
|
||||
|
||||
```bash
|
||||
cd /mnt/sda1/MapLibreParticleLab
|
||||
source /root/.maplibre-particle-env
|
||||
cd platform/android
|
||||
RENDERER=opengl BUILDTYPE=Debug JOBS=3 make android-x86-64
|
||||
```
|
||||
|
||||
APK 预期路径:`platform/android/MapLibreAndroidTestApp/build/outputs/apk/opengl/debug/MapLibreAndroidTestApp-opengl-debug.apk`。
|
||||
|
||||
## 11. 用户手工运行步骤
|
||||
|
||||
详见技术验证报告第 9 节。核心操作是:打开验证页面;保存 `ParticleValidation` 日志;验证 pan/zoom/rotate/pitch/resize;ACTIVE 静置观察连续 frame;STATIC 静置观察停止;STATIC 下操作地图确认正常重绘。
|
||||
|
||||
## 12. 构建与检查记录
|
||||
|
||||
- Android/Gradle/npm/ADB:未执行,遵循任务书禁止项。
|
||||
- Linux OpenGL CMake configure:PASS。
|
||||
- Linux OpenGL 全量增量:在 `41/321` 无错误时主动停止,原因是公共头变化导致完整 Core 重编译;未宣称全量 PASS。
|
||||
- 本任务新增/直接修改的 5 个 Linux Core 对象精确编译:PASS。
|
||||
- C++ clang-format、`git diff --check`:PASS。
|
||||
- Android XML 标准库解析:PASS。
|
||||
- APK/AAR:本轮未生成新产物。
|
||||
|
||||
## 13. 当前状态
|
||||
|
||||
`WAITING_MANUAL_VALIDATION`。
|
||||
|
||||
用户尚未提供 Android OpenGL 编译、运行、手势、resize、ACTIVE/STATIC 和日志证据,不能写 PASS。
|
||||
|
||||
## 14. 是否修改生产代码
|
||||
|
||||
是,但只包含验证所需薄接入:新增隔离的 VALIDATION ONLY Core Layer/RenderLayer、注册/build list,以及通用默认 false repaint hook。没有粒子生产功能;没有改 RasterLayer、Map、MapView 主循环或 backend 算法。
|
||||
|
||||
## 15. 是否扩大架构范围
|
||||
|
||||
否。未实现 GPU particle、Field、RasterArraySource、Trail、Mask、Vulkan、iOS、RN/JS bridge 或真实数据。
|
||||
|
||||
## 16. 依赖状态
|
||||
|
||||
未安装、未升级、未修改任何依赖、lock、SDK、NDK、Gradle、Java、Node 或 MapLibre baseline。
|
||||
|
||||
## 17. 已知问题与未完成
|
||||
|
||||
- Android OpenGL 编译和运行待用户。
|
||||
- 五项地图变化和 ACTIVE/STATIC 行为待实际证据。
|
||||
- 全量 Linux OpenGL 增量未完成;任务相关对象已编译通过。
|
||||
- 验证 Drawable故意不输出像素;以 pass/order/transform/repaint 日志为技术证据,蓝/黄参考圆用于确认地图与 style layer context。
|
||||
- commit/push 元数据将在提交后回填一次,不循环 amend。
|
||||
|
||||
## 18. 是否可以进入下一验证
|
||||
|
||||
否。只有用户证据回填并将本任务最终结论更新为 PASS 后,才能进入 GPU State A/B + Trail A/B 验证。
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/layermanager/layer_factory.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
// VALIDATION ONLY: factory for the 30-01 Core RenderLayer experiment.
|
||||
class ParticleValidationLayerFactory final : public LayerFactory {
|
||||
public:
|
||||
const style::LayerTypeInfo* getTypeInfo() const noexcept override;
|
||||
std::unique_ptr<style::Layer> createLayer(const std::string&,
|
||||
const style::conversion::Convertible&) noexcept override;
|
||||
std::unique_ptr<RenderLayer> createRenderLayer(Immutable<style::Layer::Impl>) noexcept override;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
32
include/mbgl/style/layers/particle_validation_layer.hpp
Normal file
32
include/mbgl/style/layers/particle_validation_layer.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/layer.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
// VALIDATION ONLY: minimal source-less Core layer for 30-01 architecture validation.
|
||||
class ParticleValidationLayer final : public Layer {
|
||||
public:
|
||||
explicit ParticleValidationLayer(const std::string& layerID);
|
||||
~ParticleValidationLayer() override;
|
||||
|
||||
bool isActive() const;
|
||||
void setActive(bool);
|
||||
|
||||
class Impl;
|
||||
const Impl& impl() const;
|
||||
Mutable<Impl> mutableImpl() const;
|
||||
|
||||
private:
|
||||
explicit ParticleValidationLayer(Immutable<Impl>);
|
||||
|
||||
std::optional<conversion::Error> setPropertyInternal(const std::string&, const conversion::Convertible&) override;
|
||||
StyleProperty getProperty(const std::string&) const override;
|
||||
Value serialize() const override;
|
||||
std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
|
||||
Mutable<Layer::Impl> mutableBaseImpl() const override;
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -19,12 +19,15 @@
|
||||
#include "symbol_layer.hpp"
|
||||
#include "fill_extrusion_layer.hpp"
|
||||
#include "location_indicator_layer.hpp"
|
||||
#include <mbgl/layermanager/particle_validation_layer_factory.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
namespace android {
|
||||
|
||||
LayerManagerAndroid::LayerManagerAndroid() {
|
||||
// VALIDATION ONLY: JSON style access; intentionally no Android product API/JNI peer.
|
||||
addLayerTypeCoreOnly(std::make_unique<ParticleValidationLayerFactory>());
|
||||
#if defined(MBGL_LAYER_FILL_DISABLE_RUNTIME)
|
||||
addLayerTypeCoreOnly(std::make_unique<FillLayerFactory>());
|
||||
#elif !defined(MBGL_LAYER_FILL_DISABLE_ALL)
|
||||
|
||||
@@ -286,6 +286,18 @@
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.particle.ParticleCoreLayerValidationActivity"
|
||||
android:description="@string/description_particle_core_layer_validation"
|
||||
android:exported="true"
|
||||
android:label="@string/activity_particle_core_layer_validation">
|
||||
<meta-data
|
||||
android:name="@string/category"
|
||||
android:value="@string/category_custom_layer" />
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.annotation.PolygonActivity"
|
||||
android:description="@string/description_polygon"
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package org.maplibre.android.testapp.activity.particle
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.maplibre.android.camera.CameraPosition
|
||||
import org.maplibre.android.geometry.LatLng
|
||||
import org.maplibre.android.maps.MapView
|
||||
import org.maplibre.android.maps.MapLibreMap
|
||||
import org.maplibre.android.maps.Style
|
||||
import org.maplibre.android.testapp.R
|
||||
|
||||
/** VALIDATION ONLY: Android OpenGL host for the 30-01 Core RenderLayer experiment. */
|
||||
class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
||||
private lateinit var mapView: MapView
|
||||
private lateinit var maplibreMap: MapLibreMap
|
||||
private lateinit var status: TextView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_particle_core_layer_validation)
|
||||
|
||||
mapView = findViewById(R.id.mapView)
|
||||
status = findViewById(R.id.validationStatus)
|
||||
mapView.onCreate(savedInstanceState)
|
||||
mapView.getMapAsync { map ->
|
||||
maplibreMap = map
|
||||
map.cameraPosition = CameraPosition.Builder()
|
||||
.target(LatLng(0.0, 0.0))
|
||||
.zoom(2.0)
|
||||
.build()
|
||||
loadValidationStyle(active = true)
|
||||
}
|
||||
|
||||
findViewById<Button>(R.id.validationActive).setOnClickListener {
|
||||
if (this::maplibreMap.isInitialized) loadValidationStyle(active = true)
|
||||
}
|
||||
findViewById<Button>(R.id.validationStatic).setOnClickListener {
|
||||
if (this::maplibreMap.isInitialized) loadValidationStyle(active = false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadValidationStyle(active: Boolean) {
|
||||
val state = if (active) "ACTIVE" else "STATIC"
|
||||
Log.i(TAG, "ParticleValidation: Test App switching to $state")
|
||||
maplibreMap.setStyle(Style.Builder().fromJson(validationStyle(active))) {
|
||||
status.text = getString(
|
||||
if (active) R.string.particle_validation_active_status
|
||||
else R.string.particle_validation_static_status
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun validationStyle(active: Boolean) = """
|
||||
{
|
||||
"version": 8,
|
||||
"name": "Particle Core Layer Validation",
|
||||
"sources": {
|
||||
"validation-point": {
|
||||
"type": "geojson",
|
||||
"data": {"type":"Feature","geometry":{"type":"Point","coordinates":[0,0]}}
|
||||
}
|
||||
},
|
||||
"layers": [
|
||||
{"id":"validation-background","type":"background","paint":{"background-color":"#102030"}},
|
||||
{"id":"validation-below","type":"circle","source":"validation-point",
|
||||
"paint":{"circle-radius":46,"circle-color":"#247ba0"}},
|
||||
{"id":"particle-validation-core","type":"particle-validation",
|
||||
"paint":{"particle-validation-active":$active}},
|
||||
{"id":"validation-above","type":"circle","source":"validation-point",
|
||||
"paint":{"circle-radius":16,"circle-color":"#ffe066"}}
|
||||
]
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
override fun onStart() { super.onStart(); mapView.onStart() }
|
||||
override fun onResume() { super.onResume(); mapView.onResume() }
|
||||
override fun onPause() { mapView.onPause(); super.onPause() }
|
||||
override fun onStop() { mapView.onStop(); super.onStop() }
|
||||
override fun onDestroy() { mapView.onDestroy(); super.onDestroy() }
|
||||
override fun onLowMemory() { super.onLowMemory(); mapView.onLowMemory() }
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
mapView.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ParticleValidation"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<org.maplibre.android.maps.MapView
|
||||
android:id="@id/mapView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="#DD102030"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/validationStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/particle_validation_active_status"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/validationActive"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/particle_validation_active" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/validationStatic"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/particle_validation_static" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="description_custom_layer">Overlay a custom native layer on the map</string>
|
||||
<string name="description_particle_core_layer_validation">Validate a source-less Core Style/RenderLayer, frame transform and repaint lifecycle</string>
|
||||
<string name="description_info_window_adapter">Learn how to create a custom InfoWindow</string>
|
||||
<string name="description_cameraposition">CameraPosition capabilities</string>
|
||||
<string name="description_map_fragment">Showcase MapFragment</string>
|
||||
|
||||
@@ -9,4 +9,8 @@
|
||||
<string name="description_world_tour_benchmark">Writes out avg. fps and 1% fps after world tour with .flyTo()</string>
|
||||
<string name="search">Search</string>
|
||||
<string name="category_data">Data Sources</string>
|
||||
<string name="particle_validation_active">ACTIVE</string>
|
||||
<string name="particle_validation_static">STATIC</string>
|
||||
<string name="particle_validation_active_status">ACTIVE: Core Layer requests MapLibre frames. Move, zoom, rotate and pitch the map.</string>
|
||||
<string name="particle_validation_static_status">STATIC: Core Layer stopped requesting frames. Map gestures must still render normally.</string>
|
||||
</resources>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<string name="activity_snapshot">Snapshot Activity</string>
|
||||
<string name="activity_snapshot_within">Snapshot with within expression</string>
|
||||
<string name="activity_custom_layer">Custom Layer</string>
|
||||
<string name="activity_particle_core_layer_validation">Particle Core Layer Validation</string>
|
||||
<string name="activity_map_padding">Map Padding</string>
|
||||
<string name="activity_debug_mode">Debug Mode</string>
|
||||
<string name="activity_offline">Offline Map</string>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mbgl/layermanager/hillshade_layer_factory.hpp>
|
||||
#include <mbgl/layermanager/line_layer_factory.hpp>
|
||||
#include <mbgl/layermanager/location_indicator_layer_factory.hpp>
|
||||
#include <mbgl/layermanager/particle_validation_layer_factory.hpp>
|
||||
#include <mbgl/layermanager/raster_layer_factory.hpp>
|
||||
#include <mbgl/layermanager/symbol_layer_factory.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
@@ -49,6 +50,8 @@ void LayerManagerDefault::addLayerTypeCoreOnly(std::unique_ptr<mbgl::LayerFactor
|
||||
}
|
||||
|
||||
LayerManagerDefault::LayerManagerDefault() {
|
||||
// VALIDATION ONLY: 30-01 source-less Core RenderLayer experiment.
|
||||
addLayerType(std::make_unique<ParticleValidationLayerFactory>());
|
||||
#if !defined(MBGL_LAYER_FILL_DISABLE_ALL)
|
||||
addLayerType(std::make_unique<FillLayerFactory>());
|
||||
#endif
|
||||
|
||||
25
src/mbgl/layermanager/particle_validation_layer_factory.cpp
Normal file
25
src/mbgl/layermanager/particle_validation_layer_factory.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <mbgl/layermanager/particle_validation_layer_factory.hpp>
|
||||
|
||||
#include <mbgl/renderer/layers/render_particle_validation_layer.hpp>
|
||||
#include <mbgl/style/layers/particle_validation_layer.hpp>
|
||||
#include <mbgl/style/layers/particle_validation_layer_impl.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
const style::LayerTypeInfo* ParticleValidationLayerFactory::getTypeInfo() const noexcept {
|
||||
return style::ParticleValidationLayer::Impl::staticTypeInfo();
|
||||
}
|
||||
|
||||
std::unique_ptr<style::Layer> ParticleValidationLayerFactory::createLayer(
|
||||
const std::string& id, const style::conversion::Convertible&) noexcept {
|
||||
return std::make_unique<style::ParticleValidationLayer>(id);
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderLayer> ParticleValidationLayerFactory::createRenderLayer(
|
||||
Immutable<style::Layer::Impl> impl) noexcept {
|
||||
assert(impl->getTypeInfo() == getTypeInfo());
|
||||
return std::make_unique<RenderParticleValidationLayer>(
|
||||
staticImmutableCast<style::ParticleValidationLayer::Impl>(impl));
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
132
src/mbgl/renderer/layers/render_particle_validation_layer.cpp
Normal file
132
src/mbgl/renderer/layers/render_particle_validation_layer.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#include <mbgl/renderer/layers/render_particle_validation_layer.hpp>
|
||||
|
||||
#include <mbgl/gfx/context.hpp>
|
||||
#include <mbgl/gfx/drawable.hpp>
|
||||
#include <mbgl/gfx/drawable_builder.hpp>
|
||||
#include <mbgl/gfx/drawable_tweaker.hpp>
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/renderer/layer_group.hpp>
|
||||
#include <mbgl/renderer/paint_parameters.hpp>
|
||||
#include <mbgl/renderer/render_pass.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
namespace {
|
||||
const style::ParticleValidationLayer::Impl& impl(const Immutable<style::Layer::Impl>& value) {
|
||||
assert(value->getTypeInfo() == style::ParticleValidationLayer::Impl::staticTypeInfo());
|
||||
return static_cast<const style::ParticleValidationLayer::Impl&>(*value);
|
||||
}
|
||||
|
||||
class ParticleValidationDrawableTweaker final : public gfx::DrawableTweaker {
|
||||
public:
|
||||
ParticleValidationDrawableTweaker(std::string id_, bool active_, int32_t layerIndex_)
|
||||
: id(std::move(id_)),
|
||||
active(active_),
|
||||
layerIndex(layerIndex_) {}
|
||||
|
||||
~ParticleValidationDrawableTweaker() override {
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation: validation drawable destroy id=" + id +
|
||||
" validationFrames=" + std::to_string(validationFrameCount));
|
||||
}
|
||||
|
||||
void init(gfx::Drawable&) override {}
|
||||
|
||||
void execute(gfx::Drawable&, PaintParameters& parameters) override {
|
||||
if (parameters.pass != RenderPass::Translucent) {
|
||||
return;
|
||||
}
|
||||
|
||||
++validationFrameCount;
|
||||
if (validationFrameCount != 1 && validationFrameCount % 60 != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto size = parameters.state.getSize();
|
||||
const auto& matrix = parameters.transformParams.projMatrix;
|
||||
std::ostringstream message;
|
||||
message << "ParticleValidation: render frame=" << validationFrameCount << " mapFrame=" << parameters.frameCount
|
||||
<< " state=" << (active ? "ACTIVE" : "STATIC") << " repaint=" << (active ? "requested" : "stopped")
|
||||
<< " pass=translucent" << " layerIndex=" << layerIndex << " currentLayer=" << parameters.currentLayer
|
||||
<< " viewport=" << size.width << 'x' << size.height << " zoom=" << parameters.state.getZoom()
|
||||
<< " bearing=" << parameters.state.getBearing() << " pitch=" << parameters.state.getPitch() << " proj=["
|
||||
<< matrix[0] << ',' << matrix[5] << ',' << matrix[12] << ',' << matrix[13] << ']';
|
||||
Log::Info(Event::Render, message.str());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string id;
|
||||
bool active;
|
||||
int32_t layerIndex;
|
||||
uint64_t validationFrameCount = 0;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
RenderParticleValidationLayer::RenderParticleValidationLayer(Immutable<style::ParticleValidationLayer::Impl> impl_)
|
||||
: RenderLayer(makeMutable<style::ParticleValidationLayerProperties>(impl_, impl_->active)) {
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation: RenderLayer create id=" + getID() + " source-less=true pass=translucent");
|
||||
}
|
||||
|
||||
RenderParticleValidationLayer::~RenderParticleValidationLayer() {
|
||||
Log::Info(Event::Render, "ParticleValidation: RenderLayer destroy id=" + getID());
|
||||
}
|
||||
|
||||
void RenderParticleValidationLayer::evaluate(const PropertyEvaluationParameters&) {
|
||||
const bool active = impl(baseImpl).active;
|
||||
auto properties = makeMutable<style::ParticleValidationLayerProperties>(
|
||||
staticImmutableCast<style::ParticleValidationLayer::Impl>(baseImpl), active);
|
||||
passes = RenderPass::Translucent;
|
||||
properties->renderPasses = mbgl::underlying_type(passes);
|
||||
evaluatedProperties = std::move(properties);
|
||||
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation: state=" + std::string(active ? "ACTIVE" : "STATIC") + " id=" + getID() +
|
||||
" layerIndex=" + std::to_string(getLayerIndex()));
|
||||
}
|
||||
|
||||
bool RenderParticleValidationLayer::needsRepaint() const {
|
||||
const auto& properties = static_cast<const style::ParticleValidationLayerProperties&>(*evaluatedProperties);
|
||||
return properties.active && baseImpl->visibility != style::VisibilityType::None;
|
||||
}
|
||||
|
||||
void RenderParticleValidationLayer::update(gfx::ShaderRegistry&,
|
||||
gfx::Context& context,
|
||||
const TransformState&,
|
||||
const std::shared_ptr<UpdateParameters>&,
|
||||
const RenderTree&,
|
||||
UniqueChangeRequestVec& changes) {
|
||||
if (!layerGroup) {
|
||||
if (auto group = context.createLayerGroup(layerIndex, 1, getID())) {
|
||||
setLayerGroup(std::move(group), changes);
|
||||
}
|
||||
}
|
||||
if (!layerGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* group = static_cast<LayerGroup*>(layerGroup.get());
|
||||
if (group->getDrawableCount() != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool active = static_cast<const style::ParticleValidationLayerProperties&>(*evaluatedProperties).active;
|
||||
auto builder = context.createDrawableBuilder(getID());
|
||||
auto& drawable = builder->getCurrentDrawable(true);
|
||||
drawable->setIsCustom(true);
|
||||
drawable->setRenderPass(RenderPass::Translucent);
|
||||
drawable->addTweaker(std::make_shared<ParticleValidationDrawableTweaker>(getID(), active, getLayerIndex()));
|
||||
auto drawables = builder->clearDrawables();
|
||||
group->addDrawable(std::move(drawables.front()));
|
||||
++stats.drawablesAdded;
|
||||
|
||||
Log::Info(Event::Render,
|
||||
"ParticleValidation: validation drawable create id=" + getID() +
|
||||
" layerIndex=" + std::to_string(getLayerIndex()) + " pass=translucent");
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/renderer/render_layer.hpp>
|
||||
#include <mbgl/style/layers/particle_validation_layer_impl.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
// VALIDATION ONLY: proves Core RenderLayer/frame-transform/repaint integration.
|
||||
class RenderParticleValidationLayer final : public RenderLayer {
|
||||
public:
|
||||
explicit RenderParticleValidationLayer(Immutable<style::ParticleValidationLayer::Impl>);
|
||||
~RenderParticleValidationLayer() override;
|
||||
|
||||
void update(gfx::ShaderRegistry&,
|
||||
gfx::Context&,
|
||||
const TransformState&,
|
||||
const std::shared_ptr<UpdateParameters>&,
|
||||
const RenderTree&,
|
||||
UniqueChangeRequestVec&) override;
|
||||
|
||||
private:
|
||||
void transition(const TransitionParameters&) override {}
|
||||
void evaluate(const PropertyEvaluationParameters&) override;
|
||||
bool hasTransition() const override { return false; }
|
||||
bool hasCrossfade() const override { return false; }
|
||||
bool needsRepaint() const override;
|
||||
void prepare(const LayerPrepareParameters&) override {}
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
@@ -106,6 +106,9 @@ public:
|
||||
// Returns true if the layer has a pattern property and is actively crossfading.
|
||||
virtual bool hasCrossfade() const = 0;
|
||||
|
||||
// Returns true when an active animated layer needs another MapLibre frame.
|
||||
virtual bool needsRepaint() const { return false; }
|
||||
|
||||
// Returns true if layer writes to depth buffer by drawing using PaintParameters::depthModeFor3D().
|
||||
virtual bool is3D() const { return false; }
|
||||
|
||||
|
||||
@@ -818,7 +818,7 @@ bool RenderOrchestrator::hasTransitions(TimePoint timePoint) const {
|
||||
}
|
||||
|
||||
for (const auto& entry : renderLayers) {
|
||||
if (entry.second->hasTransition()) {
|
||||
if (entry.second->hasTransition() || entry.second->needsRepaint()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
110
src/mbgl/style/layers/particle_validation_layer.cpp
Normal file
110
src/mbgl/style/layers/particle_validation_layer.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include <mbgl/style/layers/particle_validation_layer.hpp>
|
||||
|
||||
#include <mbgl/style/conversion/constant.hpp>
|
||||
#include <mbgl/style/conversion_impl.hpp>
|
||||
#include <mbgl/style/layer_observer.hpp>
|
||||
#include <mbgl/style/layers/particle_validation_layer_impl.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
namespace {
|
||||
constexpr const char* ActiveProperty = "particle-validation-active";
|
||||
|
||||
const LayerTypeInfo typeInfo{.type = "particle-validation",
|
||||
.source = LayerTypeInfo::Source::NotRequired,
|
||||
.pass3d = LayerTypeInfo::Pass3D::NotRequired,
|
||||
.layout = LayerTypeInfo::Layout::NotRequired,
|
||||
.fadingTiles = LayerTypeInfo::FadingTiles::NotRequired,
|
||||
.crossTileIndex = LayerTypeInfo::CrossTileIndex::NotRequired,
|
||||
.tileKind = LayerTypeInfo::TileKind::NotRequired};
|
||||
} // namespace
|
||||
|
||||
ParticleValidationLayer::Impl::Impl(const std::string& id)
|
||||
: Layer::Impl(id, std::string()) {}
|
||||
|
||||
bool ParticleValidationLayer::Impl::hasLayoutDifference(const Layer::Impl&) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ParticleValidationLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {}
|
||||
|
||||
const LayerTypeInfo* ParticleValidationLayer::Impl::staticTypeInfo() noexcept {
|
||||
return &typeInfo;
|
||||
}
|
||||
|
||||
ParticleValidationLayer::ParticleValidationLayer(const std::string& layerID)
|
||||
: Layer(makeMutable<Impl>(layerID)) {
|
||||
Log::Info(Event::Render, "ParticleValidation: Style Layer create id=" + layerID + " (VALIDATION ONLY)");
|
||||
}
|
||||
|
||||
ParticleValidationLayer::ParticleValidationLayer(Immutable<Impl> impl_)
|
||||
: Layer(std::move(impl_)) {}
|
||||
|
||||
ParticleValidationLayer::~ParticleValidationLayer() {
|
||||
Log::Info(Event::Render, "ParticleValidation: Style Layer destroy id=" + getID());
|
||||
weakFactory.invalidateWeakPtrs();
|
||||
}
|
||||
|
||||
const ParticleValidationLayer::Impl& ParticleValidationLayer::impl() const {
|
||||
return static_cast<const Impl&>(*baseImpl);
|
||||
}
|
||||
|
||||
Mutable<ParticleValidationLayer::Impl> ParticleValidationLayer::mutableImpl() const {
|
||||
return makeMutable<Impl>(impl());
|
||||
}
|
||||
|
||||
bool ParticleValidationLayer::isActive() const {
|
||||
return impl().active;
|
||||
}
|
||||
|
||||
void ParticleValidationLayer::setActive(bool active) {
|
||||
if (active == isActive()) {
|
||||
return;
|
||||
}
|
||||
auto impl_ = mutableImpl();
|
||||
impl_->active = active;
|
||||
baseImpl = std::move(impl_);
|
||||
observer->onLayerChanged(*this);
|
||||
}
|
||||
|
||||
std::optional<conversion::Error> ParticleValidationLayer::setPropertyInternal(const std::string& name,
|
||||
const conversion::Convertible& value) {
|
||||
if (name != ActiveProperty) {
|
||||
return conversion::Error{"layer doesn't support this property"};
|
||||
}
|
||||
conversion::Error error;
|
||||
auto converted = conversion::convert<bool>(value, error);
|
||||
if (!converted) {
|
||||
return error;
|
||||
}
|
||||
setActive(*converted);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
StyleProperty ParticleValidationLayer::getProperty(const std::string& name) const {
|
||||
if (name == ActiveProperty) {
|
||||
return StyleProperty(Value{isActive()}, StyleProperty::Kind::Constant);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Value ParticleValidationLayer::serialize() const {
|
||||
auto result = Layer::serialize();
|
||||
serializeProperty(result, getProperty(ActiveProperty), ActiveProperty, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<Layer> ParticleValidationLayer::cloneRef(const std::string& id) const {
|
||||
auto impl_ = mutableImpl();
|
||||
impl_->id = id;
|
||||
return std::unique_ptr<Layer>(new ParticleValidationLayer(std::move(impl_)));
|
||||
}
|
||||
|
||||
Mutable<Layer::Impl> ParticleValidationLayer::mutableBaseImpl() const {
|
||||
return staticMutableCast<Layer::Impl>(mutableImpl());
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
36
src/mbgl/style/layers/particle_validation_layer_impl.hpp
Normal file
36
src/mbgl/style/layers/particle_validation_layer_impl.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/layer_impl.hpp>
|
||||
#include <mbgl/style/layer_properties.hpp>
|
||||
#include <mbgl/style/layers/particle_validation_layer.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
// VALIDATION ONLY: no particle data or production API belongs here.
|
||||
class ParticleValidationLayer::Impl final : public Layer::Impl {
|
||||
public:
|
||||
explicit Impl(const std::string& id);
|
||||
Impl(const Impl&) = default;
|
||||
|
||||
bool hasLayoutDifference(const Layer::Impl&) const override;
|
||||
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
|
||||
|
||||
bool active = true;
|
||||
|
||||
DECLARE_LAYER_TYPE_INFO;
|
||||
};
|
||||
|
||||
class ParticleValidationLayerProperties final : public LayerProperties {
|
||||
public:
|
||||
ParticleValidationLayerProperties(Immutable<ParticleValidationLayer::Impl> impl, bool active_)
|
||||
: LayerProperties(std::move(impl)),
|
||||
active(active_) {}
|
||||
|
||||
expression::Dependency getDependencies() const noexcept override { return expression::Dependency::None; }
|
||||
|
||||
bool active;
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
11
粒子开发记录.md
11
粒子开发记录.md
@@ -34,3 +34,14 @@
|
||||
- 下一阶段选择 `30_最小技术验证`,集中验证独立 RenderLayer、OpenGL/Vulkan ping-pong/trail/context、Camera/wrap/Mask 和 iOS 薄接入。
|
||||
- 最终 FPS、最大粒子数、最佳 texture format、生产 Mask 来源、真实天气协议和生产 Host API 仍未确认。
|
||||
- 本次未修改生产代码、依赖、构建配置、项目总则或 Step,未运行构建/测试。
|
||||
|
||||
## 2026-08-29 — 30-01 Core RenderLayer 接入验证(阶段 A)
|
||||
|
||||
- 按 `docs/40_任务/30_01_CoreRenderLayer接入验证指令书_v1.md` 完成最小验证代码准备。
|
||||
- 新增 source-less `ParticleValidationLayer/RenderParticleValidationLayer`,经正式 Style/LayerManager/RenderTree/LayerGroup/translucent pass 链接入;代码明确标记 `VALIDATION ONLY`。
|
||||
- 新增默认 false 的 RenderLayer repaint hook;ACTIVE 通过 MapLibre 自身 `needsRepaint → Map::onUpdate → Android requestRender` 链持续帧,STATIC 停止验证层额外请求。
|
||||
- 验证 Drawable 在真实 pass 内读取当前 `PaintParameters/TransformParameters/TransformState` 的 viewport、zoom、bearing、pitch 和 projection matrix 摘要。
|
||||
- Android Test App 新增 `Particle Core Layer Validation` 页面,提供 ACTIVE/STATIC 切换和蓝/黄普通 Style Layer 顺序参照。
|
||||
- Linux OpenGL 重新配置通过;本任务新增/直接修改的 5 个 Core 对象精确编译通过。全量增量因公共头触发 321 个对象重编译,在 41/321 无错误时主动停止,未宣称全量 PASS。
|
||||
- 遵循指令书未运行 Android/Gradle/npm/ADB;等待用户手工 Android OpenGL 编译、运行、手势、resize 与日志证据。
|
||||
- 当前结论为 `WAITING_MANUAL_VALIDATION`;不得进入下一项 GPU ping-pong/Trail 验证。
|
||||
|
||||
Reference in New Issue
Block a user