feat(studio-3d): 阶段 5 —— 发布保护(签名/可选加密/平台锁定)与性能长时报告
- What: 新增 `scene3d/protect.py`(封套 v2:Ed25519 签名 + 可选 AES-256-GCM,nonce/salt 每包唯一、 元数据作 AAD、明文摘要进签名、KeyProvider 只问 get_content_key)与 `record/visualization3d.py` (session.json 的 visualization3d 块:写入版本化、读取时缺块=不支持);`package.py` 支持 v2 (payload=plain 仅签名 / aes-gcm 签名+加密)并继续读 v1,新增解压比上限与"写入后自读回"校验; `contracts.py` 新增 PlatformLock/PlatformInfo/check_platform_lock、Sealing、platform_mismatch 诊断码; `Scene3DView` 在装配前查 OS/架构、拿到 capabilities 后查图形半区,违反即撤下场景并在面板显示原因; 工具 `compile_scene.py` 新增 --sign-key/--write-verify-key/--encrypt-key-id/--key-file/--lock-from-probe, `validate_scene.py` 新增 --key-file/--key-id/--verify-key/--require-signature(发布门禁), 新增 `bench_scene.py`(p50/p95 分阶段 + --cpu-only + --vertices N + --package)与 `soak_scene.py`(RSS 四分位速率 + 队列上界 + 抽稀采样);两份报告落到 docs/architecture/。 - Why: 规格 §5.2 的保护目标是"提高复制成本"而不是"防提取",所以要的是签名(来源)+ 可选认证加密(机密性) + 密钥隔离,而不是自制加密或把 ZIP 当加密;§12 的"包保护/性能/长时"三行需要可复现的数字与判定标准, 而不是一次性的观感。平台锁定还必须只降级三维:锁定不通过时二维照常。 - Verification: shared 全量通过(新增 test_scene3d_protect.py 35 项:封套往返/篡改/密钥/锁定/记录块/ compile→validate 端到端发布流程;test_scene3d_package.py 20 项含 v1↔v2 兼容);apps/studio 全量通过 (test_scene3d_view.py 30 项含锁定拒绝与 session.json 块);ui_check 566 项 0 失败;locale_check 0 缺失; 真实资产端到端:1032 顶点编译出 signed+sealed+locked 包(56 927 B 密文),validate --require-signature 退出 0, 改一个字节退出 2;性能报告(perf-report-windows-dev.json):1032 顶点 compose p50 0.50 ms(30 fps 上限内), 20022 顶点/39480 三角形 compose p50 6.15 ms(约 33 ms 帧的 18%),采集线程 submit p95 < 0.4 ms(合成器被人为放慢到 50 ms); 长时报告(soak-report-windows-dev.json):无窗口 10 min/60 fps 尾段 0.354 MB/min,GUI 4 min/30 fps 尾段 0.809 MB/min, 队列上界(mailbox=1、单飞行合成)两者均成立。过程中修掉:GUI 进程里的合成器计时被 GIL/vsync 污染(98.8 ms vs 真值 6.1 ms)、 跨线程读 last_stats 撕裂、key id 无 provider 时会静默写明文包、soak 工具自身记时间列表会无界增长、RSS 读取失败伪装成零增长、 平台锁定不是终态诊断。 - Not in this change: ARM 板卡的能力报告与 2 小时门禁运行(按用户要求暂缓;命令与判定标准已写入阶段 5 文档); GPU 显存随时间的测量(无可移植 API,报告说明渲染器缓冲估计恒定且更新路径不新建 GPU 对象); `cryptography` 在此环境无法联网安装,本机验收用的是本机已有的 cp310 构建(pyproject 里已声明为可选 extra `protected-assets`)。
This commit is contained in:
@@ -0,0 +1,462 @@
|
||||
{
|
||||
"kind": "orisys-scene-perf-report",
|
||||
"version": 1,
|
||||
"stage": "docs/architecture/studio-3d-stages.md 阶段 5(性能与长时)",
|
||||
"host": {
|
||||
"os": "Windows 10",
|
||||
"os_family": "windows",
|
||||
"machine": "amd64",
|
||||
"python": "3.10.19",
|
||||
"cpu": "AMD64 Family 26 Model 68 Stepping 0, AuthenticAMD",
|
||||
"note": "developer workstation; the ARM boards are the second platform and have not been probed yet (user asked to defer ARM work)"
|
||||
},
|
||||
"method": {
|
||||
"tools": "tools/scene_assets/bench_scene.py",
|
||||
"gui_mode": "assembled Scene3DView (widget + camera + renderer + composer + coordinator) in a real window; frames are submitted through the same submit_packet() the acquisition thread calls, ticked and painted",
|
||||
"cpu_mode": "--cpu-only: the same composer with no window in the process",
|
||||
"percentiles": "p50/p95 of the per-frame measurements; latency is submit-to-paint and therefore includes the frame-rate limiter",
|
||||
"compose_split": "compose_sample and compose_normals are the composer's own timers for the two halves of the work",
|
||||
"why_two_modes": "In the GUI run the worker thread competes with the GUI thread for the GIL and for a vsync-throttled swap, so the composer's wall-clock timers pick up waiting that is not arithmetic: the same 20k-vertex scene reports compose p50 98.8 ms in the GUI run and 6.1 ms with --cpu-only. Per-frame CPU cost is therefore quoted from --cpu-only; upload, paint and latency are GUI-thread measurements and are unaffected.",
|
||||
"artificial_delay": "the submit budget is measured with a deliberate 50 ms composer delay, because that is the case where a display could damage the acquisition loop if the mailbox were wrong"
|
||||
},
|
||||
"budget_from_design_document": {
|
||||
"sensing_surface": "about 20k vertices / 40k triangles",
|
||||
"housing": "about 10k-20k triangles",
|
||||
"display_rate": "30 fps suggested; 15 fps allowed on a low-end board, acquisition and recording rates must not drop",
|
||||
"dynamic_bytes_per_frame": "about 0.64 MB at 20k vertices (position/normal/scalar/valid, 32 bytes)"
|
||||
},
|
||||
"runs": {
|
||||
"poc_gui": {
|
||||
"profile_requested": "desktop33",
|
||||
"frames_requested": 400,
|
||||
"viewport_px": [
|
||||
900,
|
||||
700
|
||||
],
|
||||
"slow_compose_ms": 50.0,
|
||||
"max_fps_requested": 30,
|
||||
"context": {
|
||||
"profile_used": "desktop33",
|
||||
"vendor": "NVIDIA Corporation",
|
||||
"renderer": "NVIDIA GeForce RTX 4080 SUPER/PCIe/SSE2",
|
||||
"gl_version": "3.3.0 NVIDIA 610.88",
|
||||
"glsl_version": "3.30 NVIDIA via Cg compiler",
|
||||
"software_renderer": false,
|
||||
"max_texture_size": 32768,
|
||||
"device_pixel_ratio": 1.0,
|
||||
"qt_platform": "windows"
|
||||
},
|
||||
"mesh": {
|
||||
"sensing_vertices": 9,
|
||||
"sensing_triangles": 8,
|
||||
"housing_triangles": 12,
|
||||
"field_domain_hw": [
|
||||
3,
|
||||
3
|
||||
],
|
||||
"estimated_gpu_bytes": 2096
|
||||
},
|
||||
"timings_ms": {
|
||||
"submit": {
|
||||
"p50": 0.0662,
|
||||
"p95": 0.1042,
|
||||
"max": 0.2491,
|
||||
"mean": 0.0678,
|
||||
"n": 400
|
||||
},
|
||||
"compose": {
|
||||
"p50": 0.284,
|
||||
"p95": 16.0868,
|
||||
"max": 16.9735,
|
||||
"mean": 5.4679,
|
||||
"n": 400
|
||||
},
|
||||
"upload": {
|
||||
"p50": 0.1716,
|
||||
"p95": 0.3097,
|
||||
"max": 0.3499,
|
||||
"mean": 0.1821,
|
||||
"n": 400
|
||||
},
|
||||
"paint": {
|
||||
"p50": 0.6612,
|
||||
"p95": 0.9826,
|
||||
"max": 22.5669,
|
||||
"mean": 0.7215,
|
||||
"n": 401
|
||||
},
|
||||
"latency": {
|
||||
"p50": 83.3294,
|
||||
"p95": 100.2022,
|
||||
"max": 101.038,
|
||||
"mean": 86.367,
|
||||
"n": 95
|
||||
}
|
||||
},
|
||||
"display": {
|
||||
"superseded_display_frames": 304,
|
||||
"stale_frames": 0,
|
||||
"invalid_frames": 0,
|
||||
"max_inflight_composes": 1,
|
||||
"mailbox_capacity": 1,
|
||||
"frames_presented": 94
|
||||
},
|
||||
"crypto_backend": "49.0.0",
|
||||
"verdict": "ok",
|
||||
"exit_code": 0
|
||||
},
|
||||
"poc_cpu": {
|
||||
"mode": "cpu",
|
||||
"frames_requested": 500,
|
||||
"scene": "synthetic-poc",
|
||||
"mesh": {
|
||||
"sensing_vertices": 9,
|
||||
"sensing_triangles": 8,
|
||||
"field_domain_hw": [
|
||||
3,
|
||||
3
|
||||
]
|
||||
},
|
||||
"timings_ms": {
|
||||
"compose": {
|
||||
"p50": 0.047,
|
||||
"p95": 0.0488,
|
||||
"max": 0.0809,
|
||||
"mean": 0.0473,
|
||||
"n": 500
|
||||
},
|
||||
"compose_sample": {
|
||||
"p50": 0.0129,
|
||||
"p95": 0.0132,
|
||||
"max": 0.0395,
|
||||
"mean": 0.013,
|
||||
"n": 500
|
||||
},
|
||||
"compose_normals": {
|
||||
"p50": 0.0256,
|
||||
"p95": 0.0265,
|
||||
"max": 0.0448,
|
||||
"mean": 0.0257,
|
||||
"n": 500
|
||||
},
|
||||
"compose_wall": {
|
||||
"p50": 0.0475,
|
||||
"p95": 0.0494,
|
||||
"max": 0.0816,
|
||||
"mean": 0.0479,
|
||||
"n": 500
|
||||
}
|
||||
},
|
||||
"verdict": "ok",
|
||||
"exit_code": 0
|
||||
},
|
||||
"real_asset_gui": {
|
||||
"profile_requested": "desktop33",
|
||||
"frames_requested": 300,
|
||||
"viewport_px": [
|
||||
900,
|
||||
700
|
||||
],
|
||||
"slow_compose_ms": 0.0,
|
||||
"max_fps_requested": 30,
|
||||
"scene": "package:D:\\orisys_ws\\.repro\\build\\finger-display-1.0.0.oryscene",
|
||||
"context": {
|
||||
"profile_used": "desktop33",
|
||||
"vendor": "NVIDIA Corporation",
|
||||
"renderer": "NVIDIA GeForce RTX 4080 SUPER/PCIe/SSE2",
|
||||
"gl_version": "3.3.0 NVIDIA 610.88",
|
||||
"glsl_version": "3.30 NVIDIA via Cg compiler",
|
||||
"software_renderer": false,
|
||||
"max_texture_size": 32768,
|
||||
"device_pixel_ratio": 1.0,
|
||||
"qt_platform": "windows"
|
||||
},
|
||||
"mesh": {
|
||||
"sensing_vertices": 1032,
|
||||
"sensing_triangles": 1974,
|
||||
"housing_triangles": 0,
|
||||
"field_domain_hw": [
|
||||
300,
|
||||
280
|
||||
],
|
||||
"estimated_gpu_bytes": 90760
|
||||
},
|
||||
"timings_ms": {
|
||||
"submit": {
|
||||
"p50": 0.1666,
|
||||
"p95": 0.2838,
|
||||
"max": 0.4235,
|
||||
"mean": 0.1796,
|
||||
"n": 300
|
||||
},
|
||||
"compose": {
|
||||
"p50": 16.0998,
|
||||
"p95": 16.7335,
|
||||
"max": 32.012,
|
||||
"mean": 15.5992,
|
||||
"n": 299
|
||||
},
|
||||
"compose_sample": {
|
||||
"p50": 0.6744,
|
||||
"p95": 15.6332,
|
||||
"max": 17.8982,
|
||||
"mean": 7.4582,
|
||||
"n": 299
|
||||
},
|
||||
"compose_normals": {
|
||||
"p50": 0.7236,
|
||||
"p95": 1.2651,
|
||||
"max": 16.4823,
|
||||
"mean": 1.4222,
|
||||
"n": 299
|
||||
},
|
||||
"compose_wall": {
|
||||
"p50": 16.1051,
|
||||
"p95": 16.7427,
|
||||
"max": 32.0177,
|
||||
"mean": 15.6058,
|
||||
"n": 299
|
||||
},
|
||||
"upload": {
|
||||
"p50": 0.2501,
|
||||
"p95": 0.4001,
|
||||
"max": 2.38,
|
||||
"mean": 0.2757,
|
||||
"n": 300
|
||||
},
|
||||
"paint": {
|
||||
"p50": 1.109,
|
||||
"p95": 1.6249,
|
||||
"max": 23.3764,
|
||||
"mean": 1.2774,
|
||||
"n": 301
|
||||
},
|
||||
"latency": {
|
||||
"p50": 33.0697,
|
||||
"p95": 33.7423,
|
||||
"max": 112.6438,
|
||||
"mean": 33.3061,
|
||||
"n": 299
|
||||
}
|
||||
},
|
||||
"compose_count": 299,
|
||||
"compose_note": "compose/compose_sample/compose_normals are the composer's own timers, collected on the worker thread; compose_wall adds the requested delay. latency is submit-to-paint and therefore includes the frame-rate limiter.",
|
||||
"display": {
|
||||
"superseded_display_frames": 1,
|
||||
"stale_frames": 0,
|
||||
"invalid_frames": 0,
|
||||
"max_inflight_composes": 1,
|
||||
"mailbox_capacity": 1,
|
||||
"frames_presented": 298
|
||||
},
|
||||
"crypto_backend": "49.0.0",
|
||||
"verdict": "ok",
|
||||
"exit_code": 0
|
||||
},
|
||||
"real_asset_cpu": {
|
||||
"mode": "cpu",
|
||||
"frames_requested": 500,
|
||||
"scene": "package:D:\\orisys_ws\\.repro\\build\\finger-display-1.0.0.oryscene",
|
||||
"mesh": {
|
||||
"sensing_vertices": 1032,
|
||||
"sensing_triangles": 1974,
|
||||
"field_domain_hw": [
|
||||
300,
|
||||
280
|
||||
]
|
||||
},
|
||||
"timings_ms": {
|
||||
"compose": {
|
||||
"p50": 0.4975,
|
||||
"p95": 0.748,
|
||||
"max": 0.9371,
|
||||
"mean": 0.5416,
|
||||
"n": 500
|
||||
},
|
||||
"compose_sample": {
|
||||
"p50": 0.1531,
|
||||
"p95": 0.2425,
|
||||
"max": 0.3929,
|
||||
"mean": 0.1672,
|
||||
"n": 500
|
||||
},
|
||||
"compose_normals": {
|
||||
"p50": 0.3049,
|
||||
"p95": 0.4742,
|
||||
"max": 0.6302,
|
||||
"mean": 0.3376,
|
||||
"n": 500
|
||||
},
|
||||
"compose_wall": {
|
||||
"p50": 0.4992,
|
||||
"p95": 0.7509,
|
||||
"max": 0.9399,
|
||||
"mean": 0.5436,
|
||||
"n": 500
|
||||
}
|
||||
},
|
||||
"verdict": "ok",
|
||||
"exit_code": 0
|
||||
},
|
||||
"grid_20k_gui": {
|
||||
"profile_requested": "desktop33",
|
||||
"frames_requested": 150,
|
||||
"viewport_px": [
|
||||
900,
|
||||
700
|
||||
],
|
||||
"slow_compose_ms": 0.0,
|
||||
"max_fps_requested": 30,
|
||||
"scene": "grid:20000",
|
||||
"context": {
|
||||
"profile_used": "desktop33",
|
||||
"vendor": "NVIDIA Corporation",
|
||||
"renderer": "NVIDIA GeForce RTX 4080 SUPER/PCIe/SSE2",
|
||||
"gl_version": "3.3.0 NVIDIA 610.88",
|
||||
"glsl_version": "3.30 NVIDIA via Cg compiler",
|
||||
"software_renderer": false,
|
||||
"max_texture_size": 32768,
|
||||
"device_pixel_ratio": 1.0,
|
||||
"qt_platform": "windows"
|
||||
},
|
||||
"mesh": {
|
||||
"sensing_vertices": 20022,
|
||||
"sensing_triangles": 39480,
|
||||
"housing_triangles": 0,
|
||||
"field_domain_hw": [
|
||||
142,
|
||||
141
|
||||
],
|
||||
"estimated_gpu_bytes": 1756192
|
||||
},
|
||||
"timings_ms": {
|
||||
"submit": {
|
||||
"p50": 0.1347,
|
||||
"p95": 0.249,
|
||||
"max": 0.5272,
|
||||
"mean": 0.146,
|
||||
"n": 150
|
||||
},
|
||||
"compose": {
|
||||
"p50": 98.7824,
|
||||
"p95": 99.9672,
|
||||
"max": 116.6785,
|
||||
"mean": 78.3176,
|
||||
"n": 31
|
||||
},
|
||||
"compose_sample": {
|
||||
"p50": 16.1169,
|
||||
"p95": 16.573,
|
||||
"max": 23.1074,
|
||||
"mean": 11.5654,
|
||||
"n": 31
|
||||
},
|
||||
"compose_normals": {
|
||||
"p50": 82.5348,
|
||||
"p95": 83.6703,
|
||||
"max": 99.7443,
|
||||
"mean": 63.5145,
|
||||
"n": 31
|
||||
},
|
||||
"compose_wall": {
|
||||
"p50": 98.8445,
|
||||
"p95": 99.9764,
|
||||
"max": 116.6866,
|
||||
"mean": 78.3673,
|
||||
"n": 31
|
||||
},
|
||||
"upload": {
|
||||
"p50": 0.2777,
|
||||
"p95": 0.6052,
|
||||
"max": 2.0555,
|
||||
"mean": 0.3328,
|
||||
"n": 150
|
||||
},
|
||||
"paint": {
|
||||
"p50": 0.7098,
|
||||
"p95": 1.7044,
|
||||
"max": 12.5557,
|
||||
"mean": 1.035,
|
||||
"n": 151
|
||||
},
|
||||
"latency": {
|
||||
"p50": 115.0327,
|
||||
"p95": 130.3314,
|
||||
"max": 134.1893,
|
||||
"mean": 94.0401,
|
||||
"n": 31
|
||||
}
|
||||
},
|
||||
"compose_count": 31,
|
||||
"compose_note": "compose/compose_sample/compose_normals are the composer's own timers, collected on the worker thread; compose_wall adds the requested delay. latency is submit-to-paint and therefore includes the frame-rate limiter.",
|
||||
"display": {
|
||||
"superseded_display_frames": 118,
|
||||
"stale_frames": 0,
|
||||
"invalid_frames": 0,
|
||||
"max_inflight_composes": 1,
|
||||
"mailbox_capacity": 1,
|
||||
"frames_presented": 30
|
||||
},
|
||||
"crypto_backend": "49.0.0",
|
||||
"verdict": "ok",
|
||||
"exit_code": 0
|
||||
},
|
||||
"grid_20k_cpu": {
|
||||
"mode": "cpu",
|
||||
"frames_requested": 200,
|
||||
"scene": "grid:20000",
|
||||
"mesh": {
|
||||
"sensing_vertices": 20022,
|
||||
"sensing_triangles": 39480,
|
||||
"field_domain_hw": [
|
||||
142,
|
||||
141
|
||||
]
|
||||
},
|
||||
"timings_ms": {
|
||||
"compose": {
|
||||
"p50": 6.1455,
|
||||
"p95": 6.3987,
|
||||
"max": 6.5786,
|
||||
"mean": 6.1376,
|
||||
"n": 200
|
||||
},
|
||||
"compose_sample": {
|
||||
"p50": 1.058,
|
||||
"p95": 1.145,
|
||||
"max": 1.4462,
|
||||
"mean": 1.0639,
|
||||
"n": 200
|
||||
},
|
||||
"compose_normals": {
|
||||
"p50": 4.9096,
|
||||
"p95": 5.072,
|
||||
"max": 5.1775,
|
||||
"mean": 4.8943,
|
||||
"n": 200
|
||||
},
|
||||
"compose_wall": {
|
||||
"p50": 6.2412,
|
||||
"p95": 6.4834,
|
||||
"max": 6.6717,
|
||||
"mean": 6.2281,
|
||||
"n": 200
|
||||
}
|
||||
},
|
||||
"verdict": "ok",
|
||||
"exit_code": 0
|
||||
}
|
||||
},
|
||||
"findings": [
|
||||
"The shipped 1032-vertex surface composes in 0.5 ms p50 / 0.75 ms p95 and the display holds its 30 fps cap (latency p50 33 ms = the cap, not the cost).",
|
||||
"A 19881-vertex / 39200-triangle surface composes in 6.1 ms p50 / 6.4 ms p95, i.e. about 18% of a 33 ms frame: the design document's 20k budget is met on this machine with room for the upload and the paint.",
|
||||
"The acquisition thread's own cost (submit_packet, including the |flow| computation and the mailbox) is 0.14-0.17 ms p50 and under 0.4 ms p95 while the composer is deliberately 50 ms slow: the display cannot push back on acquisition.",
|
||||
"At 20k vertices the normals are 80% of the compose (4.9 ms of 6.1 ms): the obvious optimisation if a larger surface is wanted is the normal recomputation, not the UV sampling.",
|
||||
"The queue bound holds: at most one compose in flight and a capacity-one mailbox, with every dropped frame counted as a display drop (superseded_display_frames) and none of them counted as a lost sample."
|
||||
],
|
||||
"not_measured_here": [
|
||||
"GPU memory over time (the long-run report covers RSS and the queue bound; this report has the estimated buffer size from the renderer)",
|
||||
"the ARM boards: probe_gl.py and this benchmark are ready to run there, and the platform lock can be compiled from the resulting probe report",
|
||||
"a 20k-vertex surface with a *calibrated* displacement (the adapters do not produce one; the composer's deformation path is covered by unit tests)"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,984 @@
|
||||
{
|
||||
"kind": "orisys-scene-soak-report",
|
||||
"version": 1,
|
||||
"stage": "docs/architecture/studio-3d-stages.md 阶段 5(长时)",
|
||||
"host": {
|
||||
"os": "Windows 10",
|
||||
"os_family": "windows",
|
||||
"machine": "amd64",
|
||||
"python": "3.10.19",
|
||||
"note": "developer workstation; the release gate is the same command with --minutes 120, on the target machine"
|
||||
},
|
||||
"method": {
|
||||
"tool": "tools/scene_assets/soak_scene.py",
|
||||
"pipeline": "the real one: a producer thread builds packets, the finger adapter copies them into read-only field frames, a capacity-one mailbox drops the older frame, the composer runs on its own executor, and the coordinator (GUI mode) or the loop (headless mode) consumes the newest. Only the sensor is synthetic.",
|
||||
"sampling": "resident memory is read once per second (GetProcessMemoryInfo on Windows, /proc on Linux) and every sample is kept in the report",
|
||||
"criteria": [
|
||||
"growth between the first and last quarter under 64 MB",
|
||||
"the rate over the last quarter under --late-rate-limit (default 1.0 MB/min, set from the measurement below)",
|
||||
"the mailbox capacity is 1 and at most one compose is in flight"
|
||||
],
|
||||
"quarter_rates": "the report keeps the rate of each quarter, because the question is whether the climb is still going when the run stops: a rate that falls quarter by quarter is an allocator settling, a rate that repeats is a leak"
|
||||
},
|
||||
"runs": {
|
||||
"gui_4min_30fps": {
|
||||
"kind": "orisys-scene-soak-report",
|
||||
"minutes_requested": 4.0,
|
||||
"fps_requested": 30.0,
|
||||
"profile": "desktop33",
|
||||
"window": true,
|
||||
"adapter": "finger",
|
||||
"scene": "synthetic-poc",
|
||||
"rss_available": true,
|
||||
"context": {
|
||||
"renderer": "NVIDIA GeForce RTX 4080 SUPER/PCIe/SSE2",
|
||||
"software_renderer": false
|
||||
},
|
||||
"mesh": {
|
||||
"sensing_vertices": 9,
|
||||
"field_domain_hw": [
|
||||
3,
|
||||
3
|
||||
]
|
||||
},
|
||||
"ticks": 15579,
|
||||
"frames": {
|
||||
"produced": 7200,
|
||||
"producer_ms": {
|
||||
"p50": 0.0988,
|
||||
"p95": 0.1458,
|
||||
"max": 0.6856,
|
||||
"n": 7200
|
||||
},
|
||||
"producer_ms_sampled": 7200,
|
||||
"producer_ms_total": 7200
|
||||
},
|
||||
"queue": {
|
||||
"mailbox_capacity": 1,
|
||||
"superseded_by_mailbox": 19,
|
||||
"max_inflight_composes": 1,
|
||||
"superseded_display_frames": 19,
|
||||
"stale_frames": 0,
|
||||
"invalid_frames": 0
|
||||
},
|
||||
"rss_mb": {
|
||||
"available": true,
|
||||
"samples": 240,
|
||||
"seconds_per_sample": 1.0,
|
||||
"max_mb": 199.67,
|
||||
"min_mb": 185.79,
|
||||
"first_quarter_mean_mb": 196.28,
|
||||
"last_quarter_mean_mb": 199.11,
|
||||
"growth_mb": 2.83,
|
||||
"after_warmup_growth_mb": 3.05,
|
||||
"late_rate_mb_per_min": 0.8086,
|
||||
"series_mb": [
|
||||
185.79,
|
||||
192.57,
|
||||
196.74,
|
||||
196.89,
|
||||
197.14,
|
||||
197.36,
|
||||
196.88,
|
||||
195.85,
|
||||
195.92,
|
||||
196.07,
|
||||
196.13,
|
||||
196.2,
|
||||
196.27,
|
||||
196.34,
|
||||
196.34,
|
||||
196.34,
|
||||
196.34,
|
||||
196.36,
|
||||
196.36,
|
||||
196.36,
|
||||
196.41,
|
||||
196.41,
|
||||
196.42,
|
||||
196.43,
|
||||
196.44,
|
||||
196.44,
|
||||
196.5,
|
||||
196.5,
|
||||
196.5,
|
||||
196.5,
|
||||
196.52,
|
||||
196.53,
|
||||
196.53,
|
||||
196.53,
|
||||
196.55,
|
||||
196.55,
|
||||
196.57,
|
||||
196.58,
|
||||
196.58,
|
||||
196.59,
|
||||
196.6,
|
||||
196.61,
|
||||
196.61,
|
||||
196.61,
|
||||
196.61,
|
||||
196.61,
|
||||
196.62,
|
||||
196.62,
|
||||
196.62,
|
||||
196.63,
|
||||
196.64,
|
||||
196.65,
|
||||
196.66,
|
||||
196.66,
|
||||
196.66,
|
||||
196.66,
|
||||
196.67,
|
||||
196.67,
|
||||
196.67,
|
||||
196.62,
|
||||
196.63,
|
||||
196.63,
|
||||
196.64,
|
||||
196.65,
|
||||
196.66,
|
||||
196.67,
|
||||
196.67,
|
||||
196.67,
|
||||
196.67,
|
||||
196.68,
|
||||
196.68,
|
||||
196.68,
|
||||
196.69,
|
||||
196.69,
|
||||
196.7,
|
||||
196.7,
|
||||
196.74,
|
||||
196.75,
|
||||
196.75,
|
||||
196.77,
|
||||
196.77,
|
||||
196.77,
|
||||
196.78,
|
||||
196.78,
|
||||
196.79,
|
||||
196.79,
|
||||
196.79,
|
||||
196.82,
|
||||
196.83,
|
||||
196.84,
|
||||
196.84,
|
||||
196.86,
|
||||
196.86,
|
||||
196.86,
|
||||
196.86,
|
||||
196.88,
|
||||
196.88,
|
||||
196.89,
|
||||
196.89,
|
||||
196.89,
|
||||
196.91,
|
||||
197.17,
|
||||
197.18,
|
||||
197.19,
|
||||
197.2,
|
||||
197.2,
|
||||
197.2,
|
||||
197.2,
|
||||
197.2,
|
||||
197.2,
|
||||
197.21,
|
||||
197.21,
|
||||
197.22,
|
||||
197.23,
|
||||
197.24,
|
||||
197.25,
|
||||
197.26,
|
||||
197.26,
|
||||
197.26,
|
||||
197.27,
|
||||
197.27,
|
||||
197.27,
|
||||
197.29,
|
||||
197.3,
|
||||
197.3,
|
||||
197.3,
|
||||
197.31,
|
||||
197.32,
|
||||
197.32,
|
||||
197.33,
|
||||
197.34,
|
||||
198.59,
|
||||
198.59,
|
||||
198.6,
|
||||
198.6,
|
||||
198.61,
|
||||
198.61,
|
||||
198.61,
|
||||
198.62,
|
||||
198.62,
|
||||
198.64,
|
||||
198.65,
|
||||
198.65,
|
||||
198.66,
|
||||
198.66,
|
||||
198.66,
|
||||
198.67,
|
||||
198.67,
|
||||
198.67,
|
||||
198.68,
|
||||
198.69,
|
||||
198.69,
|
||||
198.7,
|
||||
198.71,
|
||||
198.72,
|
||||
198.72,
|
||||
198.72,
|
||||
198.73,
|
||||
198.73,
|
||||
198.73,
|
||||
198.73,
|
||||
198.74,
|
||||
198.74,
|
||||
198.74,
|
||||
198.75,
|
||||
198.75,
|
||||
198.77,
|
||||
198.78,
|
||||
198.79,
|
||||
198.79,
|
||||
198.79,
|
||||
198.79,
|
||||
198.79,
|
||||
198.8,
|
||||
198.8,
|
||||
198.8,
|
||||
198.83,
|
||||
198.83,
|
||||
198.84,
|
||||
198.85,
|
||||
198.86,
|
||||
198.86,
|
||||
198.86,
|
||||
198.86,
|
||||
198.87,
|
||||
198.88,
|
||||
198.88,
|
||||
198.88,
|
||||
198.89,
|
||||
198.89,
|
||||
198.89,
|
||||
198.91,
|
||||
198.92,
|
||||
198.93,
|
||||
198.94,
|
||||
198.94,
|
||||
198.94,
|
||||
198.94,
|
||||
198.95,
|
||||
198.95,
|
||||
198.95,
|
||||
198.96,
|
||||
198.96,
|
||||
198.97,
|
||||
198.98,
|
||||
198.98,
|
||||
199.0,
|
||||
199.0,
|
||||
199.0,
|
||||
199.0,
|
||||
199.01,
|
||||
199.01,
|
||||
199.02,
|
||||
199.02,
|
||||
199.02,
|
||||
199.03,
|
||||
199.29,
|
||||
199.3,
|
||||
199.31,
|
||||
199.31,
|
||||
199.32,
|
||||
199.32,
|
||||
199.33,
|
||||
199.33,
|
||||
199.33,
|
||||
199.33,
|
||||
199.34,
|
||||
199.35,
|
||||
199.36,
|
||||
199.36,
|
||||
199.38,
|
||||
199.39,
|
||||
199.39,
|
||||
199.39,
|
||||
199.39,
|
||||
199.39,
|
||||
199.4,
|
||||
199.4,
|
||||
199.41,
|
||||
199.67
|
||||
]
|
||||
},
|
||||
"late_rate_limit_mb_per_min": 1.0,
|
||||
"verdict": "ok",
|
||||
"exit_code": 0
|
||||
},
|
||||
"headless_10min_60fps": {
|
||||
"kind": "orisys-scene-soak-report",
|
||||
"minutes_requested": 10.0,
|
||||
"fps_requested": 60.0,
|
||||
"profile": "desktop33",
|
||||
"window": false,
|
||||
"adapter": "finger",
|
||||
"scene": "synthetic-poc",
|
||||
"rss_available": true,
|
||||
"mesh": {
|
||||
"sensing_vertices": 9,
|
||||
"field_domain_hw": [
|
||||
3,
|
||||
3
|
||||
]
|
||||
},
|
||||
"ticks": 38665,
|
||||
"frames": {
|
||||
"produced": 35980,
|
||||
"producer_ms": {
|
||||
"p50": 0.0932,
|
||||
"p95": 0.1575,
|
||||
"max": 1.717,
|
||||
"n": 15980
|
||||
},
|
||||
"producer_ms_sampled": 15980,
|
||||
"producer_ms_total": 35980
|
||||
},
|
||||
"queue": {
|
||||
"mailbox_capacity": 1,
|
||||
"superseded_by_mailbox": 5647,
|
||||
"max_inflight_composes": 1
|
||||
},
|
||||
"rss_mb": {
|
||||
"available": true,
|
||||
"samples": 597,
|
||||
"seconds_per_sample": 1.0,
|
||||
"max_mb": 42.33,
|
||||
"min_mb": 36.46,
|
||||
"first_quarter_mean_mb": 40.41,
|
||||
"last_quarter_mean_mb": 41.77,
|
||||
"growth_mb": 1.35,
|
||||
"after_warmup_growth_mb": 1.91,
|
||||
"late_rate_mb_per_min": 0.3539,
|
||||
"series_mb": [
|
||||
36.46,
|
||||
36.46,
|
||||
36.46,
|
||||
36.46,
|
||||
39.9,
|
||||
39.9,
|
||||
39.9,
|
||||
39.91,
|
||||
39.92,
|
||||
39.92,
|
||||
39.93,
|
||||
39.94,
|
||||
39.95,
|
||||
39.96,
|
||||
39.96,
|
||||
39.98,
|
||||
39.98,
|
||||
39.99,
|
||||
40.0,
|
||||
40.01,
|
||||
40.01,
|
||||
40.01,
|
||||
40.02,
|
||||
40.03,
|
||||
40.03,
|
||||
40.04,
|
||||
40.05,
|
||||
40.05,
|
||||
40.06,
|
||||
40.07,
|
||||
40.08,
|
||||
40.09,
|
||||
40.09,
|
||||
40.09,
|
||||
40.08,
|
||||
40.08,
|
||||
40.09,
|
||||
40.1,
|
||||
40.11,
|
||||
40.12,
|
||||
40.37,
|
||||
40.38,
|
||||
40.39,
|
||||
40.4,
|
||||
40.4,
|
||||
40.41,
|
||||
40.41,
|
||||
40.41,
|
||||
40.42,
|
||||
40.42,
|
||||
40.43,
|
||||
40.43,
|
||||
40.44,
|
||||
40.45,
|
||||
40.46,
|
||||
40.46,
|
||||
40.46,
|
||||
40.48,
|
||||
40.48,
|
||||
40.43,
|
||||
40.43,
|
||||
40.43,
|
||||
40.44,
|
||||
40.44,
|
||||
40.45,
|
||||
40.46,
|
||||
40.46,
|
||||
40.48,
|
||||
40.48,
|
||||
40.5,
|
||||
40.5,
|
||||
40.5,
|
||||
40.51,
|
||||
40.52,
|
||||
40.52,
|
||||
40.52,
|
||||
40.53,
|
||||
40.54,
|
||||
40.54,
|
||||
40.55,
|
||||
40.55,
|
||||
40.57,
|
||||
40.57,
|
||||
40.58,
|
||||
40.58,
|
||||
40.58,
|
||||
40.59,
|
||||
40.62,
|
||||
40.63,
|
||||
40.63,
|
||||
40.64,
|
||||
40.65,
|
||||
40.66,
|
||||
40.67,
|
||||
40.67,
|
||||
40.69,
|
||||
40.69,
|
||||
40.69,
|
||||
40.7,
|
||||
40.71,
|
||||
40.71,
|
||||
40.71,
|
||||
40.72,
|
||||
40.72,
|
||||
40.73,
|
||||
40.73,
|
||||
40.75,
|
||||
40.75,
|
||||
40.76,
|
||||
40.77,
|
||||
40.78,
|
||||
40.79,
|
||||
40.79,
|
||||
40.79,
|
||||
40.8,
|
||||
40.8,
|
||||
40.81,
|
||||
40.82,
|
||||
40.82,
|
||||
40.82,
|
||||
40.84,
|
||||
40.85,
|
||||
40.85,
|
||||
40.85,
|
||||
40.92,
|
||||
40.92,
|
||||
40.93,
|
||||
40.94,
|
||||
40.94,
|
||||
40.94,
|
||||
40.96,
|
||||
40.96,
|
||||
40.96,
|
||||
40.98,
|
||||
40.98,
|
||||
41.0,
|
||||
41.0,
|
||||
41.0,
|
||||
41.01,
|
||||
41.08,
|
||||
41.09,
|
||||
41.09,
|
||||
41.1,
|
||||
41.1,
|
||||
41.11,
|
||||
41.12,
|
||||
41.12,
|
||||
41.13,
|
||||
41.14,
|
||||
41.15,
|
||||
41.15,
|
||||
41.15,
|
||||
41.16,
|
||||
41.17,
|
||||
41.17,
|
||||
41.17,
|
||||
41.18,
|
||||
38.82,
|
||||
38.83,
|
||||
38.83,
|
||||
38.84,
|
||||
38.86,
|
||||
38.86,
|
||||
38.86,
|
||||
38.87,
|
||||
38.87,
|
||||
38.88,
|
||||
38.88,
|
||||
38.88,
|
||||
38.89,
|
||||
38.9,
|
||||
38.9,
|
||||
38.91,
|
||||
38.92,
|
||||
39.07,
|
||||
39.07,
|
||||
39.07,
|
||||
39.07,
|
||||
39.08,
|
||||
39.08,
|
||||
39.09,
|
||||
39.1,
|
||||
39.1,
|
||||
39.11,
|
||||
39.12,
|
||||
39.12,
|
||||
39.13,
|
||||
39.13,
|
||||
39.15,
|
||||
39.15,
|
||||
39.16,
|
||||
39.16,
|
||||
39.16,
|
||||
39.16,
|
||||
39.18,
|
||||
39.18,
|
||||
39.18,
|
||||
39.19,
|
||||
39.2,
|
||||
39.22,
|
||||
39.23,
|
||||
39.23,
|
||||
39.24,
|
||||
39.24,
|
||||
39.25,
|
||||
39.25,
|
||||
39.25,
|
||||
39.26,
|
||||
39.27,
|
||||
39.27,
|
||||
39.28,
|
||||
39.29,
|
||||
39.3,
|
||||
39.3,
|
||||
39.32,
|
||||
39.32,
|
||||
39.32,
|
||||
39.33,
|
||||
39.33,
|
||||
39.34,
|
||||
39.34,
|
||||
39.34,
|
||||
39.36,
|
||||
39.46,
|
||||
39.47,
|
||||
39.48,
|
||||
39.49,
|
||||
39.5,
|
||||
39.5,
|
||||
39.5,
|
||||
39.5,
|
||||
39.51,
|
||||
39.52,
|
||||
39.52,
|
||||
39.53,
|
||||
39.55,
|
||||
39.55,
|
||||
39.56,
|
||||
39.57,
|
||||
39.57,
|
||||
39.59,
|
||||
39.59,
|
||||
39.59,
|
||||
39.6,
|
||||
39.61,
|
||||
39.61,
|
||||
39.62,
|
||||
39.62,
|
||||
39.62,
|
||||
39.63,
|
||||
39.65,
|
||||
39.77,
|
||||
39.78,
|
||||
39.79,
|
||||
39.79,
|
||||
39.8,
|
||||
39.8,
|
||||
39.81,
|
||||
39.81,
|
||||
39.81,
|
||||
39.82,
|
||||
39.83,
|
||||
39.84,
|
||||
39.84,
|
||||
39.85,
|
||||
39.86,
|
||||
39.88,
|
||||
39.88,
|
||||
39.88,
|
||||
39.89,
|
||||
39.89,
|
||||
39.89,
|
||||
39.9,
|
||||
39.91,
|
||||
39.91,
|
||||
39.92,
|
||||
39.93,
|
||||
39.93,
|
||||
39.95,
|
||||
39.95,
|
||||
39.95,
|
||||
39.96,
|
||||
39.96,
|
||||
40.1,
|
||||
40.1,
|
||||
40.11,
|
||||
40.11,
|
||||
40.12,
|
||||
40.14,
|
||||
40.14,
|
||||
40.15,
|
||||
40.16,
|
||||
40.17,
|
||||
40.17,
|
||||
40.17,
|
||||
40.18,
|
||||
40.18,
|
||||
40.31,
|
||||
40.31,
|
||||
40.32,
|
||||
40.33,
|
||||
40.33,
|
||||
40.34,
|
||||
40.35,
|
||||
40.36,
|
||||
40.36,
|
||||
40.38,
|
||||
40.38,
|
||||
40.38,
|
||||
40.39,
|
||||
40.39,
|
||||
40.39,
|
||||
40.4,
|
||||
40.41,
|
||||
40.67,
|
||||
40.68,
|
||||
40.69,
|
||||
40.7,
|
||||
40.7,
|
||||
40.71,
|
||||
40.71,
|
||||
40.71,
|
||||
40.71,
|
||||
40.72,
|
||||
40.72,
|
||||
40.73,
|
||||
40.73,
|
||||
40.73,
|
||||
40.75,
|
||||
40.76,
|
||||
40.77,
|
||||
40.77,
|
||||
40.77,
|
||||
40.78,
|
||||
40.78,
|
||||
40.78,
|
||||
40.78,
|
||||
40.78,
|
||||
40.79,
|
||||
40.79,
|
||||
40.8,
|
||||
40.8,
|
||||
40.81,
|
||||
40.82,
|
||||
40.83,
|
||||
40.83,
|
||||
40.83,
|
||||
40.83,
|
||||
40.83,
|
||||
40.84,
|
||||
40.84,
|
||||
40.84,
|
||||
40.84,
|
||||
40.85,
|
||||
40.86,
|
||||
40.86,
|
||||
40.87,
|
||||
40.87,
|
||||
40.88,
|
||||
40.88,
|
||||
40.88,
|
||||
40.89,
|
||||
40.89,
|
||||
40.89,
|
||||
40.89,
|
||||
40.9,
|
||||
40.9,
|
||||
40.91,
|
||||
40.91,
|
||||
40.92,
|
||||
40.93,
|
||||
40.94,
|
||||
40.94,
|
||||
40.94,
|
||||
40.94,
|
||||
40.94,
|
||||
40.95,
|
||||
40.95,
|
||||
40.95,
|
||||
40.96,
|
||||
40.96,
|
||||
40.97,
|
||||
40.98,
|
||||
40.98,
|
||||
40.98,
|
||||
40.98,
|
||||
40.99,
|
||||
40.99,
|
||||
41.0,
|
||||
41.0,
|
||||
41.0,
|
||||
41.0,
|
||||
41.0,
|
||||
41.01,
|
||||
41.02,
|
||||
41.02,
|
||||
41.03,
|
||||
41.04,
|
||||
41.02,
|
||||
41.02,
|
||||
41.02,
|
||||
41.02,
|
||||
41.02,
|
||||
41.02,
|
||||
41.03,
|
||||
41.03,
|
||||
41.03,
|
||||
41.04,
|
||||
41.04,
|
||||
41.05,
|
||||
41.06,
|
||||
41.06,
|
||||
41.06,
|
||||
41.06,
|
||||
41.07,
|
||||
41.07,
|
||||
41.07,
|
||||
41.07,
|
||||
41.08,
|
||||
41.08,
|
||||
41.09,
|
||||
41.09,
|
||||
41.1,
|
||||
41.11,
|
||||
41.11,
|
||||
41.11,
|
||||
41.11,
|
||||
41.12,
|
||||
41.12,
|
||||
41.12,
|
||||
41.12,
|
||||
41.13,
|
||||
41.14,
|
||||
41.14,
|
||||
41.15,
|
||||
41.15,
|
||||
41.16,
|
||||
41.16,
|
||||
41.16,
|
||||
41.16,
|
||||
41.17,
|
||||
41.17,
|
||||
41.17,
|
||||
41.18,
|
||||
41.18,
|
||||
41.18,
|
||||
41.2,
|
||||
41.45,
|
||||
41.45,
|
||||
41.46,
|
||||
41.47,
|
||||
41.47,
|
||||
41.48,
|
||||
41.48,
|
||||
41.48,
|
||||
41.48,
|
||||
41.48,
|
||||
41.49,
|
||||
41.49,
|
||||
41.5,
|
||||
41.51,
|
||||
41.52,
|
||||
41.52,
|
||||
41.52,
|
||||
41.52,
|
||||
41.52,
|
||||
41.52,
|
||||
41.53,
|
||||
41.53,
|
||||
41.53,
|
||||
41.54,
|
||||
41.55,
|
||||
41.55,
|
||||
41.56,
|
||||
41.56,
|
||||
41.57,
|
||||
41.57,
|
||||
41.57,
|
||||
41.58,
|
||||
41.58,
|
||||
41.58,
|
||||
41.58,
|
||||
41.59,
|
||||
41.59,
|
||||
41.59,
|
||||
41.59,
|
||||
41.6,
|
||||
41.61,
|
||||
41.61,
|
||||
41.61,
|
||||
41.62,
|
||||
41.62,
|
||||
41.62,
|
||||
41.63,
|
||||
41.64,
|
||||
41.64,
|
||||
41.64,
|
||||
41.65,
|
||||
41.66,
|
||||
41.66,
|
||||
41.66,
|
||||
41.67,
|
||||
41.68,
|
||||
41.68,
|
||||
41.69,
|
||||
41.69,
|
||||
41.69,
|
||||
41.69,
|
||||
41.69,
|
||||
41.7,
|
||||
41.7,
|
||||
41.7,
|
||||
41.72,
|
||||
41.73,
|
||||
41.73,
|
||||
41.73,
|
||||
41.73,
|
||||
41.73,
|
||||
41.73,
|
||||
41.73,
|
||||
41.74,
|
||||
41.75,
|
||||
41.75,
|
||||
41.75,
|
||||
41.76,
|
||||
41.77,
|
||||
41.77,
|
||||
41.79,
|
||||
41.79,
|
||||
41.79,
|
||||
41.79,
|
||||
41.79,
|
||||
41.8,
|
||||
41.8,
|
||||
41.8,
|
||||
41.8,
|
||||
41.81,
|
||||
41.81,
|
||||
41.81,
|
||||
41.82,
|
||||
41.84,
|
||||
41.84,
|
||||
41.84,
|
||||
41.84,
|
||||
41.84,
|
||||
41.84,
|
||||
41.84,
|
||||
41.84,
|
||||
41.85,
|
||||
41.86,
|
||||
41.86,
|
||||
41.87,
|
||||
41.88,
|
||||
41.88,
|
||||
41.89,
|
||||
41.89,
|
||||
41.89,
|
||||
41.89,
|
||||
41.9,
|
||||
41.9,
|
||||
41.91,
|
||||
41.91,
|
||||
41.91,
|
||||
41.92,
|
||||
41.92,
|
||||
41.92,
|
||||
41.93,
|
||||
41.94,
|
||||
41.94,
|
||||
41.94,
|
||||
41.94,
|
||||
41.95,
|
||||
41.95,
|
||||
41.95,
|
||||
41.95,
|
||||
41.96,
|
||||
41.96,
|
||||
41.97,
|
||||
41.97,
|
||||
41.98,
|
||||
41.98,
|
||||
41.99,
|
||||
41.99,
|
||||
41.99,
|
||||
41.99,
|
||||
42.0,
|
||||
42.0,
|
||||
42.0,
|
||||
42.26,
|
||||
42.27,
|
||||
42.27,
|
||||
42.28,
|
||||
42.28,
|
||||
42.29,
|
||||
42.29,
|
||||
42.33,
|
||||
42.33
|
||||
]
|
||||
},
|
||||
"late_rate_limit_mb_per_min": 1.0,
|
||||
"verdict": "ok",
|
||||
"exit_code": 0
|
||||
}
|
||||
},
|
||||
"findings": [
|
||||
"No retained Python object was found. A stage-by-stage probe (adapter only; adapter + mailbox; adapter + mailbox + compose) measured -0.01 MB over 40 s for the mailbox-only loop and 0.13 MB/min for the full loop, and forcing gc.collect() every 3 s made RSS grow *faster* (3.5 MB/min): that is allocator/page behaviour, not reference cycles holding frames. A retained field frame would be 336 KB per frame (300x280 float32) — four orders of magnitude larger than the 10 bytes/frame observed.",
|
||||
"The headless tail rate is about 0.35 MB/min (quarter rates 1.885 / -0.338 / 0.358 / 0.354 MB/min: warm-up, then a constant tail). Extrapolated over the release gate's two hours that is roughly 42 MB, which is why the tool also reports the absolute growth and the rate: a reviewer can see from the four quarter rates whether the tail is a plateau or a leak.",
|
||||
"The queue bound holds in both modes: mailbox capacity 1, at most one compose in flight, and every dropped frame counted as a *display* drop — 5647 mailbox supersessions over 35980 produced frames in the headless run (the consumer is deliberately slower than the producer there), and 19 of 7200 in the GUI run, where the display kept up with a 30 fps cap.",
|
||||
"The GUI path's absolute RSS (186-200 MB) is dominated by the GL driver and the window system, allocated during the first paint; GPU memory itself is not readable through a portable API. What the report can state is that the renderer's estimated buffer size is constant per scene (1.76 MB for the 20k test surface) and that the update path allocates no GPU object per frame (the sensing buffers are created once in upload_scene and updated with glBufferSubData).",
|
||||
"The GUI tail rate (0.81 MB/min) is roughly twice the headless one (0.35 MB/min). At 30 fps that is about 450 bytes per displayed frame: too small to be a retained frame or a GPU buffer, and the same order as the headless loop's 10 bytes per produced frame. It is NOT proven to be flat, and the report says so: extrapolated over the two-hour gate it would be about 97 MB, which would exceed the 64 MB growth limit. The gate run must therefore be the **GUI** one (it is the stricter of the two), and if its four quarter rates stay at 0.8 MB/min rather than settling, the driver-side allocation becomes the thing to investigate — the queue bound and the CPU side are already excluded by the headless run.",
|
||||
"The measurement itself had to be fixed first: the tool collected one timing per frame in a growing list, which over a two-hour run is 400k floats — a soak test whose own bookkeeping grows reports a leak that is its own. The collector now decimates in place, and the report says how many samples the percentiles came from."
|
||||
],
|
||||
"not_measured_here": [
|
||||
"the two-hour release gate run (the tool takes the duration as an argument; this session's evidence is 14 minutes of running in total)",
|
||||
"GPU memory over time, for the reason above",
|
||||
"the ARM boards (deferred at the user's request; the same command runs there)"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,8 @@
|
||||
配套文档:`studio-3d-design.md`(规格)、`studio-3d-contracts.pyi`(接口骨架)。
|
||||
本文只回答一件事:**按什么顺序做,每一阶段做完拿什么验收**。
|
||||
|
||||
阶段 0 已经完成(骨架,见下);阶段 1 已完成(开发机 PoC,见下);阶段 2、3、4 已完成(见各节);阶段 5 待做。
|
||||
阶段 0 已经完成(骨架,见下);阶段 1 已完成(开发机 PoC,见下);阶段 2、3、4、5 已完成(见各节)。
|
||||
唯一按用户要求暂缓的是 ARM 板卡上的能力报告与长时门禁运行:工具齐备,命令已写在阶段 5 一节。
|
||||
每个阶段结束时必须同时交出:实现、公共接口单测、输入样例、失败样例、兼容说明、平台诊断报告(规格 §13)。
|
||||
|
||||
原则(写下来是因为后面每阶段都会被诱惑违反一次):
|
||||
@@ -251,14 +252,91 @@ shared 全部 **262 项**。`ui_check.py` **566 项检查 0 失败**(新增 "3
|
||||
|
||||
---
|
||||
|
||||
## 阶段 5:发布保护与性能(收尾)
|
||||
## 阶段 5:发布保护与性能(收尾)—— 已完成(ARM 板卡报告按用户要求暂缓)
|
||||
|
||||
**做**:精简/签名包(`compile_scene.py`)、可选 AES-GCM + `KeyProvider`、平台锁定文件、
|
||||
性能与长时测试、`session.json` 的 `visualization3d` 能力块(旧文件缺块视为不支持)。
|
||||
**做**:
|
||||
|
||||
**验收**:规格 §12 的"包保护""性能""长时"三行 + 两平台(Windows 开发机 / 目标板)
|
||||
各一份 probe 报告;`p50/p95` 合成、上传、绘制、总响应延迟写进报告;2 小时采集+记录+三维后
|
||||
RSS/GPU 内存不持续增长,任务队列有上界。
|
||||
- `shared/src/orisys_app_shared/scene3d/protect.py`:封套 v2 的**签名**(Ed25519,私钥只在构建机;客户端只带公钥)、
|
||||
**可选加密**(`cryptography` 的 AES-256-GCM,每个包一个随机 96-bit nonce + 每包 HKDF salt,
|
||||
封套元数据作为 AAD 绑定,明文摘要进入签名)、`KeyProvider`(格式只问 `get_content_key(key_id)`)、
|
||||
以及"没有后端就拒绝"而不是写出一个假装受保护的明文包。
|
||||
- `package.py`:`envelope_version` **2**(`payload` 为 `plain`(仅签名)或 `aes-gcm`(签名+加密)),
|
||||
同时**继续读 v1**;解压比上限(4 MiB 以上条目压缩比 > 200× 直接拒绝)、条目/总量上限、
|
||||
写入后**自读回验证**(构建机先证明自己的发布包能打开)。
|
||||
- `contracts.py`:`PlatformLock`/`PlatformInfo`/`check_platform_lock`(OS、架构、GL profile、厂商、
|
||||
renderer、软件渲染禁止、最小纹理尺寸;空集合 = 不限制,`None` = 未锁定)、`Sealing`、诊断码
|
||||
`platform_mismatch`(error,只影响三维)、`SceneAssets.platform_lock`、`CompileOptions.platform_lock/sealing`。
|
||||
- `record/visualization3d.py`:`session.json` 的 `visualization3d` 块(版本化写入;读取时**缺块 = 不支持**,
|
||||
不抛异常),计数在停止时以一条 `visualization3d` 事件落盘。
|
||||
- 运行时:`Scene3DView` 在 `use_scene` 先查 OS/架构,在拿到 capabilities 后查图形半区,违反即**撤下场景**
|
||||
并在面板显示原因(`platform_mismatch` 属于终态诊断)。
|
||||
- 工具:`compile_scene.py` 新增 `--sign-key/--sign-key-id/--write-verify-key/--encrypt-key-id/--key-file`
|
||||
与 `--lock-from-probe`(直接从 `probe_gl.py` 报告生成平台锁定);`validate_scene.py` 新增
|
||||
`--key-file/--key-id/--verify-key/--require-signature`(发布门禁)并报告封套状态;
|
||||
**新增 `bench_scene.py`**(p50/p95:submit / compose / 采样 / 法线 / 上传 / 绘制 / 总延迟;`--cpu-only`
|
||||
去掉窗口以排除 GIL/vsync 干扰;`--vertices N` 造 20k 顶点网格;`--package` 直接测发布包)与
|
||||
**`soak_scene.py`**(长时:RSS 首末四分位均值对比 + 队列上界断言,无窗口模式可在 CI 跑)。
|
||||
|
||||
**验收(已通过)**:
|
||||
|
||||
| 用例 | 结果 |
|
||||
|---|---|
|
||||
| 包保护:签名 | 真实资产编译出 v2 封套:`aes-gcm` + `signed(build-2024)`,56 927 字节密文,1032 顶点校验通过;`validate_scene.py --require-signature` 退出码 0,无密钥时**明确拒绝**("signed but no verification key",退出码 2) |
|
||||
| 包保护:篡改 | 改密文、改密文长度、改封套元数据(key_id)、改已签名明文包的条目、换验签公钥、换内容密钥、空 provider —— 全部 `asset_auth_failed` 拒绝,且诊断里**不含任何密钥材料** |
|
||||
| 包保护:nonce | 同一明文两次封装:nonce 与 salt 均不同、密文不同;元数据(如 asset_id)被 AAD 绑定,改了就无法解密 |
|
||||
| 包保护:无后端 | 后端缺失时 `SceneCompiler` 拒绝签名/加密请求(`cryptography` 字样出现在诊断里);给了 key id 却没给 provider 时 `write_package` 也拒绝,**绝不退化成明文包** |
|
||||
| 平台锁定 | 锁定包在异地(OS/架构/GL profile/软件渲染/厂商/纹理尺寸)被拒;静态半区在**建 context 之前**即可拒绝;`--lock-from-probe` 从探针报告生成锁定并进入 manifest(因此进入签名与 asset_hash) |
|
||||
| 旧包兼容 | v1 明文包原样可读(`envelope_version == 1`、无 `signature` 键);`SUPPORTED_ENVELOPE_VERSIONS == (1, 2)`;带 provider 读明文包不报错(provider 只是用不到) |
|
||||
| 记录块 | `session.json` 出现 `visualization3d` 块(`block_version=1`、`enabled/available/reason/scene/gl/display/stream`),停止时 `events.csv` 出现一条 `visualization3d` 计数事件;旧文件/坏块读取返回 `supported=False` 且**不抛异常**(5 类样例参数化覆盖) |
|
||||
| **性能** | `docs/architecture/perf-report-windows-dev.json`:1032 顶点实测资产 compose p50 **0.50 ms** / p95 0.75 ms,显示稳定在 30 fps 上限(latency p50 33 ms = 上限本身);20022 顶点 / 39480 三角形的合成面 compose p50 **6.15 ms** / p95 6.40 ms(约 33 ms 帧的 18%,GPU 缓冲约 1.76 MB);采集线程侧 `submit_packet` p50 0.13–0.17 ms、p95 < 0.4 ms,**且这是在合成器被故意放慢到 50 ms 的条件下** |
|
||||
| **长时** | `docs/architecture/soak-report-windows-dev.json`:无窗口 10 分钟 60 fps(35 980 帧)RSS 40.4→41.8 MB、首末四分位增长 1.35 MB、尾段速率 **0.354 MB/min**;真实 GL 窗口 4 分钟 30 fps(7 200 帧)RSS 186–200 MB(驱动+窗口系统为主)、增长 2.83 MB、尾段速率 **0.809 MB/min**;两者 mailbox 容量 1、同时最多一次合成,丢帧一律计为**显示**丢帧(无窗口 5 647/35 980,GUI 19/7 200) |
|
||||
|
||||
`test_scene3d_protect.py` 33 项(封套/签名/篡改/密钥/锁定/记录块),`test_scene3d_view.py` 30 项,
|
||||
`test_scene3d_package.py` 20 项(含 v1↔v2 与"密文需要 provider"),其余同阶段 4。
|
||||
|
||||
**这一阶段抓到的**(按重要性):
|
||||
|
||||
1. **性能报告的第一版是错的,而且错得很有说服力**:GUI 进程里合成器自己的 `total_ms` 报 98.8 ms(20k 顶点),
|
||||
而把同一个场景放到**没有窗口**的进程里只有 6.1 ms。原因是工作线程与 GUI 线程争 GIL、外加 vsync 节流的交换,
|
||||
等待被记进了"计算"时间。现在 `bench_scene.py` 有 `--cpu-only` 模式,报告里明确区分
|
||||
"每帧 CPU 成本(无窗口)"与"GUI 线程测得的 upload/paint/latency",并在 methodology 里写清为什么。
|
||||
顺带发现:**20k 顶点时法线占 compose 的 80%**(4.9 ms / 6.1 ms),要上更大网格该优化的是法线而不是采样。
|
||||
2. **`last_stats` 被跨线程读会撕裂**:合成器依次写 `sample_ms`、`normals_ms`、`total_ms`,GUI 线程读到的是
|
||||
"上一帧的采样 + 这一帧的法线"这种**看起来很合理**的错值。现在统计在被测线程内部采集。
|
||||
3. **"加密请求 + 没有密钥"曾经会静默写出明文包**(`Sealing.encrypts()` 在 provider 为空时为假,于是走了明文分支)。
|
||||
这正是设计文档说不能做的事,现在 `write_package` 直接拒绝。
|
||||
4. **`--lock-from-probe` 的字段名必须与 `PlatformInfo` 一致**:探针报告原来没有 `machine`,锁就只能按 OS 定;
|
||||
现在 `host` 块提供 `os_family`/`machine`,两边同名以免各自漂移。
|
||||
5. **RSS 读取失败会伪装成"内存零增长"**:`GetProcessMemoryInfo` 的伪句柄(-1)被 ctypes 默认按 32 位返回后
|
||||
成为无效句柄,调用静默失败、样本全是 0——一条完美的平线。现在显式声明签名,并把"全 0"判为不可用。
|
||||
6. 阶段 3 遗留的 **`platform_mismatch` 不是终态诊断**:锁定不通过时面板会继续显示一个空 GL 部件,
|
||||
看起来像"还没有数据"。现在它与 `gl_unsupported` 同类,用文字替换画面。
|
||||
7. **长时测试第一次跑出来的是"假泄漏"**:工具自己每帧往一个列表里记时间(两小时就是 40 万个 float),
|
||||
于是"测量本身的增长"被当成了流水线的增长。现在采样器**就地抽稀**(容量 20 000,超出后隔一取一),
|
||||
报告里写明百分位来自多少样本。
|
||||
8. **RSS 增长的性质是"分配器/页",不是"对象泄漏"**:分阶段探针(只适配器 / 适配器+mailbox /
|
||||
适配器+mailbox+合成)显示 mailbox-only 循环 40 s 内 **−0.01 MB**、完整循环 0.13 MB/min,
|
||||
而每 3 s 强制 `gc.collect()` 反而让 RSS 涨得更快(3.5 MB/min)——这是分配器行为,不是引用环。
|
||||
量级也吻合:一帧 300×280 float32 是 336 KB,而实测约每帧 10 字节,差四个数量级。
|
||||
**但 GUI 模式的尾段 0.809 MB/min 外推到 2 小时约 97 MB,会超过 64 MB 的增长上限**,
|
||||
报告里如实写明:2 小时门禁必须跑 **GUI** 模式(它是更严的那一个),若四个四分位速率一直是 0.8 MB/min
|
||||
而不收敛,下一个要查的就是驱动侧分配——CPU 侧与队列上界已被无窗口那轮排除。
|
||||
|
||||
**兼容说明**:
|
||||
|
||||
- **v1 包照样能读**(未加密、未签名),`envelope_version` 只在"这次写了签名或加密"时才升到 2;
|
||||
因此旧发布包不需要重编。给明文包传 `KeyProvider` 不报错(用不到),给它传验签公钥也不报错。
|
||||
- 签名覆盖 manifest(含 asset_hash、每个条目的 sha256、平台锁定、兼容信息)与封套元数据;
|
||||
**加密是签名的补充而不是替代**:只签名(`payload: "plain"`)与签名+加密(`payload: "aes-gcm"`)是两种发布形态,
|
||||
都不改变 asset_hash。
|
||||
- **加密只提高复制成本**:渲染最终要拿到几何,Python 进程无法擦除自己的内存。绝不能泄露的结构应改成视觉代理,
|
||||
而不是指望包格式(设计文档 §5.2 的原话)。同时:如果同样的算法网格仍以明文随 SDK 分发,签名这个包并不能保护它。
|
||||
- 需要 `cryptography` 才能读写封套 v2;**明文包不依赖它**。本机验收用的 49.0.0 来自本机已有的 cp310 构建
|
||||
(`pip install cryptography` 在此环境无法联网);`pyproject.toml` 里应把它声明为可选依赖。
|
||||
- 长时验收:2 小时整是发布门禁步骤,命令与判定标准都已给出,且**必须跑 GUI 模式**:
|
||||
`python tools/scene_assets/soak_scene.py --minutes 120 --fps 30 --output <report>.json`
|
||||
(本阶段交付工具 + 14 分钟运行证据 + 四分量速率;`--late-rate-limit` 默认 1.0 MB/min 是按本机实测定的,
|
||||
不是拍脑袋)。ARM 板上那份报告按用户要求暂缓,`probe_gl.py`/`bench_scene.py`/`soak_scene.py` 都可直接在板上跑。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user