docs(studio-3d): 阶段 7–9 交付说明 + 接口骨架同步 + 保护包的应用侧配置

- What:
  1. `docs/architecture/studio-3d-stages.md` 新增三节:**阶段 7**(二维图像后端统一与回放入口收口,含起因、
     做法、验收表格)、**阶段 8 第一批**(能力控件/真实色标/复位语义/命名)、**阶段 9 上半**(限速、真实统计、
     无位移复用几何、保护包密钥),并新开一节「尚未完成」把两项写清楚:阶段 8 第二批(画布布局与曲线量程)与
     阶段 9 的门禁运行(GUI 2 小时长时 + 两块 ARM 板的探针/性能/长时报告),外加阶段 6 遗留的真机验证项。
     文首进度与依赖关系图同步。
  2. `docs/architecture/studio-3d-contracts.pyi`:新增 `SceneCapabilities`;`RenderStats` 增
     `paint_calls`/`scale_lower`/`scale_upper`/`scale_mode` 并注明 `displayed_frames` 是"按帧身份的唯一帧"
     而不是绘制次数;`RenderFrame` 增 `compose_ms`。
  3. `tools/scene_assets/README.md`:新增「Studio 应用侧的配置」——`scene3d_verify_key`/
     `scene3d_content_key_file` + `scene3d_content_key_id`/`scene3d_require_signature` 的用法、为什么没有验签
     公钥就打不开签名包、以及"密钥文件缺失是状态而非崩溃、记录里只留布尔与路径"。
- Why: 规格 §13 要求每阶段同时交出实现、测试、输入样例、失败样例、兼容说明与平台诊断报告;阶段 6 的文档提交
  之后又落了阶段 7/8/9 的接口改动,骨架文件必须跟上,否则下一阶段会照着过期文档实现。同时把"没做完的两项"
  写在文档里(而不是留在对话里),下一个接手的人不需要猜。
- Verification: 文档改动本身不执行测试;其中的数字全部来自本仓可复现的命令:`pytest`(shared+apps 同一次
  运行 588 项 0 失败 15 跳过)、真 GL 冒烟 14 项 0 失败、`ui_check.py` 587 项 0 失败、`locale_check.py`
  0 缺失、`bench_scene.py --cpu-only`(20k 顶点 1.03 ms / 真实包 0.17 ms)、`validate_scene.py`(重编包
  全项 ok)与两份 `.repro/probe_stage6_*.json`。
- Not in this change: 不改代码;阶段 8 第二批与阶段 9 门禁仍未做(见文档「尚未完成」一节);ARM 板卡报告
  按用户先前的意思仍是暂缓项。
This commit is contained in:
ykcheungab
2026-09-14 22:02:43 +08:00
parent ef487098c9
commit cf45e654e1
3 changed files with 192 additions and 9 deletions
+17 -2
View File
@@ -178,6 +178,7 @@ class RenderFrame:
scalar_descriptor: FieldDescriptor
diagnostics: tuple[Diagnostic, ...]
computed_frame_id: int | None # Carried through so the display side re-checks it.
compose_ms: float # This frame's own compose cost; the shared counter is rewritten.
@dataclass(frozen=True)
@@ -199,14 +200,28 @@ class GLCapabilities:
diagnostics: tuple[Diagnostic, ...]
@dataclass(frozen=True)
class SceneCapabilities:
"""What the *data source* can provide — not what the pane can draw."""
quantity: str
unit: str
displacement_available: bool # False for the current adapters: they colour in place.
displacement_quality: Quality | None
source_kind: str # live, recompute, replay.
@dataclass(frozen=True)
class RenderStats:
displayed_frames: int
displayed_frames: int # Unique frames presented, by identity (not paint calls).
superseded_frames: int
compose_ms: float
paint_calls: int # Every paintGL, so a slow camera move is not a slow pipeline.
compose_ms: float # The frame's own measured cost (RenderFrame.compose_ms).
upload_ms: float # CPU submission time unless a separate GPU timer is used.
paint_ms: float
estimated_gpu_bytes: int
scale_lower: float # The colour bounds the shader was actually given.
scale_upper: float
scale_mode: str # 'automatic' or 'fixed'.
class KeyProvider(Protocol):