三维显示骨架:可执行合同 + 模块签名 + 分阶段计划 - What: 按 docs/architecture/studio-3d-design.md 与 studio-3d-contracts.pyi 搭建骨架(阶段 0)。shared/src/orisys_app_shared/scene3d/:contracts.py 把 .pyi 的规格变成可执行代码——全部 DTO/枚举(FrameIdentity/FieldDescriptor/SurfaceFieldFrame/MeshPart/SurfaceBinding/CameraPreset/ExpectedGeometry/SceneAssets/ColorScale/DisplaySettings/VertexFields/RenderFrame/CameraMatrices/GLCapabilities/RenderStats/CompileOptions/CompileReport)、标准诊断码表(DIAGNOSTIC_SEVERITY,含 stale_frame=info、field_unavailable=warning 等)、ThreeDError、freeze()(拥有数据 + C 连续 + 只读)、五个校验器(validate_surface_frame/validate_binding/validate_mesh_part/validate_assets/validate_display_settings/validate_render_frame)、LatestFrameMailbox(容量 1、替换计数、锁内只换引用)、UNAVAILABLE_DESCRIPTOR/rest_descriptor() 与 summarize_parts(),以及全部 Protocol;该模块不 import Qt/OpenGL/SDK。assets.py/sampling.py/composer.py 为签名骨架(未实现处抛 NotImplementedError 并写明归属阶段)。apps/studio/src/orisys_studio/scene3d/:camera/adapters/coordinator/gl_backend/widget/panel 签名骨架,其中 configure_surface_format() 已实现(QApplication 之前调用,desktop33=GL3.3 Core / gles30=GLES3.0,深度 24、无 MSAA);模块级一律不 import GL 绑定,PyOpenGL 只在 GLDispatch.bind_current_context 里加载。tools/scene_assets/{probe_gl,validate_scene,compile_scene}.py 提供入口与参数,未实现时输出 not_implemented 并返回 2(避免被 CI 当成通过)。docs/architecture/studio-3d-stages.md:阶段 0-5 的产出、每阶段的验收矩阵、并行度与回归门。测试:shared/tests/test_scene3d_contracts.py 26 项(freeze 拥有数据、可写/非连续数组被拒、scalar 与 valid 形状一致、位移缺 quality 被拒、非有限 scalar 标为 valid 只报警不静默、绑定越界/-1 约定/权重和为 1、三角形索引越界、感知法线单位长度、恰好一个 sensing part、binding 必须属于 sensing part、设备几何/域/尺寸不符被拒、色标 lower<upper、邮箱替换计数与多线程不阻塞生产者、身份区分 source/generation、骨架可导入且不引入 PyOpenGL);apps/studio/tests/test_scene3d_skeleton.py 4 项(骨架不 import OpenGL、surface format 两个 profile、widget 构造不需要 context、各 stub 报出归属阶段)。 - Why: 用户要求"先从骨架搭起,然后计划功能实现顺序,最后逐阶段实现与验收"。骨架阶段的价值在于把接口变成可执行的(.pyi 本身无法被测试),并把"数组只读、单位与质量必须声明、近似位移不得当毫米、参考帧不得冒充测量、显示丢帧不得计入记录丢帧"这些容易在实现时被绕过的规则先固定下来;同时明确不 import Qt/OpenGL 的边界,使采集线程与 CI 能在无 GPU 环境导入它。 - Verification: shared 全量 187 passed(原 161 + 26);studio 95 passed(原 91 + 4);ui_check 554 项检查 0 失败;compileall 通过;probe_gl --profile desktop33 在未实现时返回 2 并说明将产出什么报告。 - Not in this change: 三维功能本身(阶段 1 起);app_window/main.py 未接线,骨架对现有应用完全惰性(无任何模块被应用导入)。

This commit is contained in:
ykcheungab
2026-09-14 14:32:38 +08:00
parent c10e579371
commit 0b309e7edf
18 changed files with 2327 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
# Studio 三维显示:实施阶段与验收
配套文档:`studio-3d-design.md`(规格)、`studio-3d-contracts.pyi`(接口骨架)。
本文只回答一件事:**按什么顺序做,每一阶段做完拿什么验收**。
阶段 0 已经完成(骨架,见下);阶段 1 起逐段实现。每个阶段结束时必须同时交出:
实现、公共接口单测、输入样例、失败样例、兼容说明、平台诊断报告(规格 §13)。
原则(写下来是因为后面每阶段都会被诱惑违反一次):
- **骨架已经能跑测试,就不要为了快而绕过合同**:`scene3d.contracts` 的校验是唯一入口,
适配器/合成器/加载器都不许自己再定义一遍形状和单位。
- **二维永远可用**:三维持有任何问题(资产、GL、性能)都只降级三维视图,不阻塞采集、记录、力曲线。
- **不在没有证据时造物理量**:近似位移不写成毫米,未标定的空间尺度不造高度场,
壳体的顶点不参与测量统计。
---
## 阶段 0:骨架(已完成)
产出:
- `shared/src/orisys_app_shared/scene3d/`:`contracts.py`(全部 DTO、枚举、诊断码、
`freeze`、五个校验器、`LatestFrameMailbox`、协议)+ `assets.py`/`sampling.py`/`composer.py` 签名骨架。
- `apps/studio/src/orisys_studio/scene3d/`:`camera.py`/`adapters.py`/`coordinator.py`/
`gl_backend.py`/`widget.py`/`panel.py` 签名骨架;`configure_surface_format()` 已实现。
- `tools/scene_assets/`:`probe_gl.py`/`validate_scene.py`/`compile_scene.py` 入口(未实现时返回 2)。
- 测试:`shared/tests/test_scene3d_contracts.py`(26 项)、
`apps/studio/tests/test_scene3d_skeleton.py`(4 项)。
验收(已通过):合同校验的输入/失败样例全部覆盖;`LatestFrameMailbox` 在多线程下不阻塞生产者;
骨架导入不引入 PyOpenGL;`configure_surface_format` 两个 profile 的版本/类型/深度缓冲可断言;
所有未实现入口都抛 `NotImplementedError` 并写明归属阶段。
---
## 阶段 1:平台 PoC(图形栈先落地)
**做**:`probe_gl`(真实 Qt 窗口 + 真实 context)、`GLDispatch`(探测 + 绑定 PyOpenGL)、
`GLRenderer` 最小集(一个 shader、一个 VBO/EBO、静态三角形)、静态感知面 + 外壳两个 part 各一次 draw、
`OrbitCamera` 的 `fit`/`orbit`、`Sensor3DWidget` 的 `initializeGL`/`paintGL`/`resizeGL`/`release_resources`
与 `aboutToBeDestroyed` 清理。
**验收**:
| 用例 | 通过标准 |
|---|---|
| 能力报告 | 开发机 Windows 出 `desktop33` 报告:vendor/renderer/GL/GLSL/DPR/framebuffer,且明确标注是否软件渲染 |
| 静态场景 | 三角形与感知面/外壳都可见;外壳可隐藏;感知面与外壳不共用缓冲 |
| 生命周期 | 反复 resize(含 DPR 变化)、隐藏/显示、关闭、context 重建各 20 次无崩溃、无泄漏 |
| 失败样例 | 强制 shader 编译失败 → 三维 Unavailable + 诊断,二维照常;`gl_unsupported` 路径同样 |
| 记录隔离 | 开/关三维各录 30 s,CSV 行数与有效样本完全一致 |
**明确不做**:ARM 板卡适配(按用户要求暂缓),但 probe 脚本要能直接在板上跑出报告。
---
## 阶段 2:资产合同(几何与坐标)
**做**:`LegacyFingerImporter`(旧 NPZ:`mesh.npz`/`unroll_map_uv_s0.4.npz`/`camera_intrinsics.npz`)、
坐标与单位统一(`meters_per_source_unit` → 刚体 `transform_sensor_from_source`)、
`SurfaceBinding` 的生成与自检、`validate_scene.py`、一份简化外壳 GLB 的编译路径、manifest/schema。
**验收**:
| 用例 | 通过标准 |
|---|---|
| 拓扑不混用 | 1032 与 1005 两套网格分别加载;错配(binding 顶点数 ≠ 感知面顶点数)被拒 |
| UV 域 | 300×280 与 500×500 两个域的 `shape_hw`/scale/flip_v 各不相同,错配被拒 |
| 采样表 | 每顶点权重和为 1;未用项 `-1`/0;越界下标被拒;接缝不跨域插值 |
| 单位 | 三个已知非共线点在表面与外壳中对齐;米/毫米换算与法线方向正确 |
| 身份 | 改 manifest 任一哈希、改 binding、改 geometry_id 均被拒并给出 `geometry_mismatch`/`field_domain_mismatch` |
**产出样例**:一张 `finger` 场景包 + 一份 `validate_scene.py --json` 报告。
---
## 阶段 3:实时数据(字段 → 渲染帧)
**做**:`FingerFrameAdapter`/`BoxFrameAdapter`、`SurfaceSampler`、`SceneComposer`
(`compose`/`rest_frame`)、`ThreeDCoordinator` 的双 mailbox 与 generation/过期拒绝、`submit` 的
`accepted/replaced/stale/disabled/invalid` 语义。
**验收**:
| 用例 | 通过标准 |
|---|---|
| 采样正确性 | 常量场/斜坡/单点脉冲/NaN/遮罩场与 CPU 参考实现逐位一致 |
| 无效语义 | 任一正权重点无效 ⇒ 该顶点无效;非有限 scalar 不显示为 0;参考帧 `scalar` 全不可用 |
| 色标语义 | `display_gain` 只改显示位置:增大倍率前后 `scalar` 与力值逐位不变 |
| 过期帧 | 旧 `generation`/`request_id` 永远不能覆盖新场景;`stale_frame` 安静丢弃并计数 |
| 不阻塞采集 | 合成人为放慢 10 倍,`submit` 调用耗时 p99 < 1 ms,记录行数不变 |
| 缺数据 | 无字段 → `no_data`,清掉旧测量颜色,允许显示参考模型 |
---
## 阶段 4:Qt 产品接入(界面与生命周期)
**做**:`Sensor3DWidget` 完整、`ThreeDPanel`(开关/预设/复位/外壳/倍率/色标/状态)、
`OrbitCamera` 全部交互、preferences 持久化、`app_window` 装配(视图切换、`closeEvent`)、
`main.py` 在 `QApplication` 之前的 profile 选择。
**验收**:
| 用例 | 通过标准 |
|---|---|
| 交互 | 旋转/平移/缩放/复位/预设;观察相机变化不改算法相机、标定与力轴(矩阵断言) |
| 单位与质量显示 | `approximate` 位移显示为"相对形变示意 ×N",不出现在毫米文案里 |
| 视图切换 | 三维开关不改变采集、记录与力曲线状态;切换 20 次无泄漏 |
| 关停 | `closeEvent` → `release_resources` 幂等;重复关闭不抛异常 |
| 界面文案 | 新增 key 中英齐全(沿用 `tools`/locale 校验方式) |
---
## 阶段 5:发布保护与性能(收尾)
**做**:精简/签名包(`compile_scene.py`)、可选 AES-GCM + `KeyProvider`、平台锁定文件、
性能与长时测试、`session.json` 的 `visualization3d` 能力块(旧文件缺块视为不支持)。
**验收**:规格 §12 的"包保护""性能""长时"三行 + 两平台(Windows 开发机 / 目标板)
各一份 probe 报告;`p50/p95` 合成、上传、绘制、总响应延迟写进报告;2 小时采集+记录+三维后
RSS/GPU 内存不持续增长,任务队列有上界。
---
## 依赖关系(并行度)
```
阶段 1 (图形栈) ─┬─> 阶段 4 (界面) ─┐
│ ├─> 阶段 5
阶段 2 (资产) ──┴─> 阶段 3 (数据) ─┘
```
阶段 1 与阶段 2 无依赖,可并行;阶段 3 需要阶段 2 的绑定;阶段 4 需要 1 和 3。
每阶段开始前先确认上一阶段的失败样例仍然被覆盖(回归门)。