zjchina.net

Drogon · ROS 2 · 独立开发笔记

ADR-00092026-10-19

渲染内核新增最小 3D 原语(M7–M10),不引 Bevy

拍板:zj

背景

factorywatch(第三个实战项目,汽车总装 11 工位看板)要升级为数字孪生大屏,需要 3D 可视化。 孪生分层现状:

  • 数据/逻辑层已由 ADR-0008 拍板:zjchina-core::twin 契约 + sim DES 内核(事件为真源、帧为投影);「大屏页面与 SSE」在其冻结清单中,留给本轮。
  • 渲染层:zjchina-render 是 2D 内核(8 管线、MSDF 文本、Scene↔JSON 契约、142 单测、 golden 像素锁),无深度/透视/网格/光照。
  • ADR-0005 路由不变:大屏属真机设备端,自绘为尊;web 永不自绘。

本决策只定视觉层:3D 怎么来、放哪、按什么顺序啃。

选项

  • A. 引入 Bevy。 许可合规(MIT/Apache),生态最全。但 ① Bevy 锁自家 wgpu 版本,与 zjchina-render 的 wgpu 30 冲突(双 wgpu 共存或整仓降级二选一);② 整引擎(ECS/资源/ 调度/插件)反客为主,最小内核身份被吞;③ Scene↔JSON 跨语言逐像素仲裁契约失效。不采纳。
  • B. 2.5D 等轴测(现有 2D 内核画伪 3D)。 零新原语、立刻可上屏,但无自由相机与真实 遮挡,表达不了产线纵深与设备层次,不是孪生。不采纳为路线;仅保留为紧急演示逃生舱。
  • C. zjchina-render 内新增最小 3D 原语(采纳)。 深度/透视/网格/光照作为内核的原语 扩展,复用 instancing、MSDF 文本、blur、HitTree 与 Scene 序列化契约,延续 「一次只加一个原语 + golden 像素锁定」方法论,2D API 面零破坏。

证据

  • 负载模型甜点:工厂孪生 = 静态布局(厂区/设备几何不变量)+ 少量动态实例(AGV 位姿、 状态色每帧微变),正是「instanced 静态几何一次性上传 + 每帧仅写投影 uniform」的 3D 版。 该策略已有两段实测背书:tidewatch spike 10 万图元 120 FPS(0005 修订 2)、 H-2 大屏 5 万蜡烛长稳 60fps。
  • 家底复用率高:instancing 肌肉(bench_50k_path)、MSDF 文本 → 3D 锚定标签、blur → 选中发光、HitTree → 射线拾取、display_list.rs 序列化契约 → 3D DrawItem 延续 C++ 双实现逐像素 diff==0 仲裁。
  • 消费侧就绪:fw-render 2D 看板在跑;0008 已注明 fw model 将来对齐 zjchina_core::twin——3D 面板与 2D 面板消费同一模型层,数据面不为 3D 重做。

拍板

拍板者 zj(AI 协作者 TRAE 出三问选项对比,拍板权属作者)。日期:2026-10-19。 路线 C + 首版全厂区块孪生 + 先 ADR/skill 再动手。

  1. 落点:zjchina-render 内新增 3D 原语(透视相机/变换/网格实例/深度,文件名实现期 定),2D API 面零破坏;不引 Bevy、不新开 crate。
  2. 首版范围 = 全厂区块孪生:厂区布局 + 设备色块 + AGV 轨迹 + 告警高亮;单产线精细 模型(glTF 资产管线)推后。样例布局守 0008 通用化红线:虚构工位名,零真实工厂数据。
  3. 原语包顺序(沿用引擎 M 序列):
    • M7 基石:深度缓冲/测试、透视相机+轨道控制、MVP 变换、实例化立方体、背面剔除。 验收 = fw-render「方块孪生」跑起 11 工位布局。
    • M8 数据绑定:设备状态色/告警高亮、AGV 轨迹线(路径管线 3D 化)、3D→屏幕标签 锚定(复用 MSDF)。
    • M9 资产与拾取:参数化工厂图元(传送带/机械臂,先不引 glTF)、射线拾取、选中描边。
    • M10 光照质感:Lambert + 方向光、雾、可选阴影;着色器预编译/管线缓存策略在此 一并立起(Impeller 对标作业并入,不再单排)。
  4. 依赖准入:M7 唯一候选新依赖 glam(MIT/Apache-2.0,矩阵/四元数)——经 transform facade 单一入口 + 固定测试向量锁行为 + scripts/gen-dependencies.py 重跑清单(脚本 若不覆盖 zjchina-render 独立 workspace,则 DEPENDENCIES.md 手工补记该节)。除此之外 零新增依赖(立方体/着色器自持,同 0008 自持 PRNG 纪律)。
  5. 与 0008 的边界:本 ADR 只管「看」。数据源腿/时间轴腿/sim 建模不动;3D 中 AGV 位姿 来自看板数据面,不因 3D 可视化解冻 sim 的 AGV 连续运动建模(0008 冻结项)。
  6. 验证环:每原语包 golden 像素断言(offscreen PNG;透视相机参数整数化/固定种子防 跨端浮点漂移,同 0008 整数毫秒纪律);2D 零回归门槛 = 既有 142 单测全绿。

附录:Impeller 对标作业(M10 收口,2026-10-20)

借的(互为印证,非新抄)

  1. 无运行时管线编译:Impeller 消灭 jank 的核心 = 不像 Skia 那样运行时生成 SkSL, 全部管线预编译。zjchina-render 自 M2 起 Renderer::new 一次建齐全部管线(2D 全家 + M7/M8 的 cube/line 3D),着色器是编译期 const 字符串,帧循环零管线创建、零运行时 shader 编译——M10 复核确认该策略已在,无需另立。
  2. 显式状态 + 内容寻址复用:段 revision + 脏表 + 逐项内容指纹 = 「变什么重传什么」 协议;与 Impeller 的 render pass 显式化、避免隐式状态同步同思想。M8/M9/M10 的增量 路径(40B 逐实例写、224B 全局 uniform)都在这条协议上跑。
  3. stencil-then-cover:M2/M3 路径填充即采用(与 Skia/Impeller 同源思路),路线一致 属互相验证,非 M10 新借。

不借的

  1. 3D 场景图/光照:Impeller 是 2D UI 合成器,无 3D 段可抄;深度/光照/拾取是 zjchina-render 自研独有(M7–M10 已落),Flutter 生态要到 Filament 量级才对得上。
  2. 运行时 tessellation 缓存:Impeller 为路径细分做了缓存体系;我们 M2/M3 选 CPU 展开 + GPU stencil,当前路径规模(工业看板量级)实测够用,规模上来再评估。
  3. 着色器变体治理:Flutter 为变体爆炸建了管理系统;我们管线数是个位数,无此问题。

结论:Impeller 验证「预编译 + 显式状态」是消灭渲染卡顿的正确路线,与我们既有实践 互为印证;3D 能力不同赛道,无可借亦无须追。

附录:M7–M10 落地差异记(收口对账)

  • M9:射线拾取 + 选中高亮落地;「参数化工厂图元(传送带/机械臂)」与「选中描边」 未做(选中用高亮色替代描边;参数化图元待真实消费侧提需求再动,避免闭门造形)。
  • M10:Lambert + 方向光 + Blinn-Phong 镜面落地(光向/眼点入 uniform,材质常数 烘焙 fs);拍板清单中的「雾、可选阴影」未做——首版光照以工业看板克制风格为先, 雾/阴影属表现增强,待 factorywatch 大屏实际观感反馈再排。
  • 验收基线:lib 168 单测 + bin/twin 三代像素断言(M8 增量/M9 拾取闭环/M10 亮暗 双档)+ 窗口 120 FPS;2D 零回归(既有全量测试 + clippy 0)。
← 返回决策日志