Files
orisys-app/tools/scene_assets/README.md
T
ykcheungab cf45e654e1 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 板卡报告
  按用户先前的意思仍是暂缓项。
2026-09-14 22:02:43 +08:00

7.7 KiB
Raw Blame History

Scene asset tools

Offline tools for the three-dimensional view. They never run in the application: the app loads a compiled package (.oryscene) and nothing else.

tool what it is for
compile_scene.py a Blender export (+ optional housing GLB) → one .oryscene package
validate_scene.py the checks the loader and the compiler must agree on; the release gate
probe_gl.py what this machine's OpenGL actually is (exit 0 GPU, 3 software, 4 no context)
bench_scene.py per-stage p50/p95 of the running view (--cpu-only for the pure CPU cost)
soak_scene.py long run: RSS trend and the queue bound

Run them with the repository's own interpreter, from orisys-app:

$py = '.venv-build\Scripts\python.exe'

Compiling the shipped finger sensor

The runtime asset is sensors/finger/assets/30fps-0703: it is what sensors/finger/manifest.json names (assets_dir), and its unroll map (unroll_map_uv_s0.4.npz, out size 300×280, source_scale 0.4) matches camera.resolution_stitch in sensor.json, which is the grid the live optical flow is computed on.

Do not compile assets/visualization/flatten_remap.npz: it is a 1024×1024 domain, so every live frame would be refused with field_domain_mismatch (300x280 vs 1024x1024). That map belongs to offline visualisation.

$sdk = 'D:\orisys_ws\orisys-sdk\sensors\finger\assets\30fps-0703'   # or the copy in apps/studio/config

# 1) state the unit (the importer never guesses it) and read what it measured
& $py tools\scene_assets\validate_scene.py --legacy-dir $sdk --map "$sdk\unroll_map_uv_s0.4.npz"

# 2) compile
& $py tools\scene_assets\compile_scene.py `
  --legacy-dir $sdk `
  --map "$sdk\unroll_map_uv_s0.4.npz" `
  --meters-per-source-unit 0.001 `
  --output apps\studio\config\sensors\finger\scene\finger-display-1.0.0.oryscene

# 3) verify
& $py tools\scene_assets\validate_scene.py apps\studio\config\sensors\finger\scene\finger-display-1.0.0.oryscene

--meters-per-source-unit is a physical statement about the sensor, not a formatting choice. The measured mesh extent is 15.81 × 15.0 × 7.5 source units (1032 vertices, 1974 triangles); the shipped finger surface is about 15.8 mm across, so one source unit is one millimetre and the value is 0.001. If the mechanical drawing says otherwise, state that number instead — nothing downstream can tell the difference, and a wrong factor looks plausible.

Expected result: 1032 vertices, 1974 triangles, geometry_id starting 1a2e0e92…, a 56 KB package, and validate_scene.py reporting every check ok.

The package also records a unit-free source_geometry_id (46a03a5f… for the shipped bundle). It is the only geometry identity the acquisition side can reproduce without the unit statement above, so it is what lets the running application verify that the scene it is drawing was built from the same surface — and the same mapping, via field_domain_id — that the device is actually running. A package that predates this record is still accepted, and reported as unverified rather than as a match.

Where the application looks for a package

  1. preference.json → "scene3d_package": "<path>" (an absolute path; also the way to point a developer build at a freshly compiled package);
  2. <config>/sensors/<sensor>/scene/*.oryscene — for the studio app that is apps/studio/config/sensors/finger/scene/;
  3. nothing: the pane reports the directory it searched and the compile command above, and the two-dimensional views keep working.

The SDK's own sensors/ tree is not searched (the app prefers its local config/sensors bundles). Compile into the app's scene/ directory, or set scene3d_package.

Release packages (signature, optional encryption, platform lock)

All three are optional and independent of the plain package above:

& $py tools\scene_assets\compile_scene.py --legacy-dir $sdk --map "$sdk\unroll_map_uv_s0.4.npz" `
  --meters-per-source-unit 0.001 `
  --sign-key keys\build-ed25519.pem --sign-key-id build-2024 `
  --write-verify-key keys\build-ed25519.pub.pem `
  --encrypt-key-id finger-display --key-file keys\finger-display.key `
  --lock-from-probe docs\architecture\gl-report-windows-dev.json `
  --output apps\studio\config\sensors\finger\scene\finger-display-1.0.0.oryscene
  • --sign-key signs the manifest (and therefore every entry hash, the compatibility information and the platform lock). The private key stays on the build machine; the client ships only --write-verify-key's public half.
  • --encrypt-key-id + --key-file additionally seal the content with AES-256-GCM. Encryption never replaces signing. Reading a sealed package needs the protected-assets extra (cryptography); a plain package does not.
  • --lock-from-probe takes the platform lock (OS, architecture, GL profile, vendor, renderer, minimum texture size, "no software rasteriser") from a probe_gl.py report. A machine the lock excludes loses the 3D pane, with the reason, and keeps everything else.
  • --encrypt-key-id without --key-file, or with no crypto backend installed, is refused rather than silently writing a plain package that looks protected.

Release gate:

& $py tools\scene_assets\validate_scene.py <package> `
  --key-file keys\finger-display.key --key-id finger-display `
  --verify-key keys\build-ed25519.pub.pem --require-signature

What the Studio application needs for a protected package

The application reads the same material from its preferences file (paths only — the private half never leaves the build machine):

{
  "scene3d_verify_key": "C:\\keys\\build-ed25519.pub.pem",
  "scene3d_content_key_file": "C:\\keys\\finger-display.key",
  "scene3d_content_key_id": "finger-display",
  "scene3d_require_signature": true
}
  • scene3d_verify_key is the public key --write-verify-key produced. Without it a signed package is refused by the container ("a verification key is required"), which is why the application could not open a protected release at all before this existed.
  • scene3d_content_key_file + scene3d_content_key_id are only needed for a sealed (aes-gcm) package. The file may hold the key as hex, base64 or raw bytes.
  • scene3d_require_signature is the application's own release gate: with it on, an unsigned package is refused (asset_auth_failed) instead of being quietly drawn. Leave it off for engineering builds — the shipped finger-display-1.0.0.oryscene is deliberately plain.
  • A missing or unreadable file is not a crash: the pane reports why, and the two-dimensional application keeps running. session.json records which of these were configured (booleans and paths, never key material).

Performance and long runs

# per-stage p50/p95 of the running view (needs a real window system)
$env:QT_QPA_PLATFORM = 'windows'
& $py tools\scene_assets\bench_scene.py --frames 600 --output perf.json
& $py tools\scene_assets\bench_scene.py --cpu-only --frames 500 --vertices 20000 --output perf-cpu.json
& $py tools\scene_assets\bench_scene.py --package <package> --key-file keys\finger-display.key `
    --key-id finger-display --verify-key keys\build-ed25519.pub.pem --output perf-real.json

# long run: the release gate is two hours in the GUI mode (it is the stricter one)
& $py tools\scene_assets\soak_scene.py --minutes 120 --fps 30 --output soak.json
& $py tools\scene_assets\soak_scene.py --no-window --minutes 10 --fps 60 --output soak-cpu.json

The committed reports for the developer machine are docs/architecture/perf-report-windows-dev.json and docs/architecture/soak-report-windows-dev.json; the stage-by-stage acceptance, the failure samples and the compatibility notes are in docs/architecture/studio-3d-stages.md.