bobframes 0.2.6__tar.gz → 0.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bobframes-0.2.6 → bobframes-0.2.8}/CHANGELOG.md +71 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/PKG-INFO +17 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/README.md +16 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/_default_config.toml +5 -1
- bobframes-0.2.8/bobframes/_version.py +1 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/aggregates.py +66 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/cli.py +22 -13
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/config.py +10 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/package.py +27 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/components.js +13 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/dashboard.py +28 -20
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/discovery.py +9 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/draws_by_class.py +16 -7
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/instancing_opportunities.py +4 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/orchestrator.py +11 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/overdraw.py +3 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/pass_gpu.py +16 -6
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/preview.py +4 -4
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/shader_hotlist.py +1 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/summary.py +13 -8
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/trend_table.py +84 -32
- bobframes-0.2.8/bobframes/serve.py +36 -0
- bobframes-0.2.8/bobframes/tests/_ui_util.py +57 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/draws_by_class.html +9 -2
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/index.html +10 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/instancing_opportunities.html +9 -2
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/overdraw.html +8 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/pass_gpu.html +11 -4
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/draws_by_class.html +11 -5
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/index.html +10 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/instancing_opportunities.html +9 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/overdraw.html +10 -4
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/pass_gpu.html +9 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/shader_hotlist.html +9 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/summary.html +10 -4
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/shader_hotlist.html +8 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/summary.html +10 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/trend_table.html +24 -17
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/draws_by_class.html +9 -2
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/index.html +10 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/instancing_opportunities.html +9 -2
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/overdraw.html +8 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/pass_gpu.html +11 -4
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/run/2026-05-27_r110565/draws_by_class.html +11 -5
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/run/2026-05-27_r110565/index.html +10 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/run/2026-05-27_r110565/instancing_opportunities.html +9 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/run/2026-05-27_r110565/overdraw.html +10 -4
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/run/2026-05-27_r110565/pass_gpu.html +9 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/run/2026-05-27_r110565/shader_hotlist.html +9 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/run/2026-05-27_r110565/summary.html +10 -4
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/shader_hotlist.html +8 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/summary.html +10 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/trend_table.html +24 -17
- {bobframes-0.2.6/bobframes/tests/data/golden_package/shared_redacted → bobframes-0.2.8/bobframes/tests/data/golden_package/shared}/_assets/report.js +8 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/draws_by_class.html +1 -1
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared/_reports/index.html +44 -0
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared/_reports/instancing_opportunities.html +46 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/pass_gpu.html +3 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/run/2026-05-27_r110565/draws_by_class.html +3 -4
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared/_reports/run/2026-05-27_r110565/index.html +45 -0
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared/_reports/run/2026-05-27_r110565/instancing_opportunities.html +43 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/run/2026-05-27_r110565/overdraw.html +2 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/run/2026-05-27_r110565/pass_gpu.html +1 -2
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared/_reports/run/2026-05-27_r110565/shader_hotlist.html +43 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/run/2026-05-27_r110565/summary.html +2 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/summary.html +2 -2
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared/_reports/trend_table.html +57 -0
- {bobframes-0.2.6/bobframes/tests/data/golden_package/shared → bobframes-0.2.8/bobframes/tests/data/golden_package/shared_redacted}/_assets/report.js +8 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/draws_by_class.html +1 -1
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared_redacted/_reports/index.html +44 -0
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared_redacted/_reports/instancing_opportunities.html +46 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/pass_gpu.html +3 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/run/2026-05-27_r110565/draws_by_class.html +3 -4
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared_redacted/_reports/run/2026-05-27_r110565/index.html +45 -0
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared_redacted/_reports/run/2026-05-27_r110565/instancing_opportunities.html +43 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/run/2026-05-27_r110565/overdraw.html +2 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/run/2026-05-27_r110565/pass_gpu.html +1 -2
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared_redacted/_reports/run/2026-05-27_r110565/shader_hotlist.html +43 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/run/2026-05-27_r110565/summary.html +2 -3
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/summary.html +2 -2
- bobframes-0.2.8/bobframes/tests/data/golden_package/shared_redacted/_reports/trend_table.html +57 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_preview/_chrome_preview.html +9 -2
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_aggregates.py +69 -2
- bobframes-0.2.8/bobframes/tests/test_browser_shots.py +90 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_config.py +6 -1
- bobframes-0.2.8/bobframes/tests/test_draws_by_class_kpis.py +34 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_package.py +31 -5
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_report_polish.py +3 -2
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_report_structure.py +41 -10
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_run_model.py +32 -1
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_summary.py +6 -6
- bobframes-0.2.8/bobframes/tests/test_trend_regression_basis.py +103 -0
- bobframes-0.2.8/bobframes/tests/test_ui_ab_theme.py +162 -0
- bobframes-0.2.8/bobframes/tests/test_ui_browser.py +78 -0
- bobframes-0.2.8/bobframes/tests/test_ui_jobs.py +82 -0
- bobframes-0.2.8/bobframes/tests/test_ui_js_parses.py +34 -0
- bobframes-0.2.8/bobframes/tests/test_ui_progress.py +64 -0
- bobframes-0.2.8/bobframes/tests/test_ui_security.py +44 -0
- bobframes-0.2.8/bobframes/tests/test_ui_share.py +135 -0
- bobframes-0.2.8/bobframes/tests/test_ui_smoke.py +45 -0
- bobframes-0.2.8/bobframes/tests/test_ui_state.py +33 -0
- bobframes-0.2.8/bobframes/ui/__init__.py +17 -0
- bobframes-0.2.8/bobframes/ui/assets/panel.css +50 -0
- bobframes-0.2.8/bobframes/ui/assets/panel.js +151 -0
- bobframes-0.2.8/bobframes/ui/jobs.py +119 -0
- bobframes-0.2.8/bobframes/ui/progress.py +64 -0
- bobframes-0.2.8/bobframes/ui/server.py +513 -0
- bobframes-0.2.6/bobframes/_version.py +0 -1
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared/_reports/index.html +0 -44
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared/_reports/instancing_opportunities.html +0 -46
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared/_reports/run/2026-05-27_r110565/index.html +0 -45
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared/_reports/run/2026-05-27_r110565/instancing_opportunities.html +0 -44
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared/_reports/run/2026-05-27_r110565/shader_hotlist.html +0 -44
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared/_reports/trend_table.html +0 -57
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared_redacted/_reports/index.html +0 -44
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared_redacted/_reports/instancing_opportunities.html +0 -46
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared_redacted/_reports/run/2026-05-27_r110565/index.html +0 -45
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared_redacted/_reports/run/2026-05-27_r110565/instancing_opportunities.html +0 -44
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared_redacted/_reports/run/2026-05-27_r110565/shader_hotlist.html +0 -44
- bobframes-0.2.6/bobframes/tests/data/golden_package/shared_redacted/_reports/trend_table.html +0 -57
- bobframes-0.2.6/bobframes/tests/test_browser_shots.py +0 -49
- {bobframes-0.2.6 → bobframes-0.2.8}/.gitignore +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/LICENSE +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/__init__.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/catalog.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/derive_post_merge.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/derives/__init__.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/derives/classifier.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/derives/draw_classifier.toml +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/derives/pass_class_breakdown.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/derives/presets/custom-template.toml +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/derives/presets/godot.toml +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/derives/presets/unity.toml +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/derives/texture_usage.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/discovery.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/errors.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/global_entities.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/health.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/html/__init__.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/html/template.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/lint.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/lint_banlist.toml +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/manifest.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/parquetize.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/parsers/__init__.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/parsers/derive_program_transitions.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/parsers/parse_init_state.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/paths.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/probes/__init__.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/probes/whatif.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/qrd_harness.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/query_examples.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/rdcmd.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/replay/__init__.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/replay/replay_main.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/__init__.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/_tokens.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/ab.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/Inter-OFL.txt +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/README.md +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/chrome.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/components.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/container.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/design_tokens.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/icon_sprite.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/inter-subset.woff2 +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/link_kind.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/per_drop.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/print.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/rdc_table.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/rdc_table.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/assets/sticky.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/base.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/cache.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/charts.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/chrome.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/cli.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/delta.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/design_tokens.toml +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/reports/formatters.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/resource_labels.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/run.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/schemas.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/stable_keys.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/__init__.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/_render_util.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_pagedata/catalog.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/buffers.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/clears.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/counters_per_event.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/descriptor_access.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/dispatches.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/draw_bindings.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/draws.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/events.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/fbos.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/frame_totals.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/ibo_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/passes.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/pixel_history.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/post_vs_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/program_transitions.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/programs.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/render_targets.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/resource_creation.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/rt_event_timeline.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/samplers.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/shaders.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/state_change_events.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/texture_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/textures.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/vbo_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/vertex_inputs.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/index.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden/index.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/README.txt +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_pagedata/catalog.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/buffers.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/clears.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/counters_per_event.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/descriptor_access.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/dispatches.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/draw_bindings.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/draws.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/events.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/fbos.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/frame_totals.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/ibo_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/passes.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/pixel_history.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/post_vs_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/program_transitions.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/programs.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/render_targets.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/resource_creation.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/rt_event_timeline.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/samplers.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/shaders.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/state_change_events.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/texture_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/textures.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/vbo_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/vertex_inputs.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/_reports/drill/District 01/2026-05-28_r110600/index.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/redacted/index.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/README.txt +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_assets/catalog.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_assets/catalog.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_assets/report.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_pagedata/catalog.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/buffers.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/clears.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/counters_per_event.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/descriptor_access.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/dispatches.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/draw_bindings.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/draws.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/events.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/fbos.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/frame_totals.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/ibo_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/passes.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/pixel_history.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/post_vs_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/program_transitions.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/programs.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/render_targets.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/resource_creation.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/rt_event_timeline.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/samplers.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/shaders.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/state_change_events.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/texture_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/textures.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/vbo_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/_pagedata/vertex_inputs.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/drill/District 01/2026-05-28_r110600/index.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/overdraw.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/_reports/shader_hotlist.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared/index.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/README.txt +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_assets/catalog.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_assets/catalog.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_assets/report.css +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_pagedata/catalog.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/buffers.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/clears.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/counters_per_event.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/descriptor_access.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/dispatches.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/draw_bindings.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/draws.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/events.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/fbos.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/frame_totals.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/ibo_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/passes.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/pixel_history.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/post_vs_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/program_transitions.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/programs.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/render_targets.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/resource_creation.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/rt_event_timeline.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/samplers.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/shaders.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/state_change_events.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/texture_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/textures.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/vbo_samples.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/_pagedata/vertex_inputs.js +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/drill/District 01/2026-05-28_r110600/index.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/overdraw.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/_reports/shader_hotlist.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_package/shared_redacted/index.html +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/golden_parquet/digests.json +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/_manifest.json +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/buffers.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/clears.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/counters_per_event.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/descriptor_access.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/dispatches.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/draw_bindings.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/draws.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/events.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/fbos.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/frame_totals.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/ibo_samples.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/indirect_args.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/passes.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/pixel_history.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/post_vs_samples.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/program_transitions.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/programs.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/render_targets.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/resource_creation.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/rt_event_timeline.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/samplers.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/shaders.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/state_change_events.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/texture_samples.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/textures.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/vbo_samples.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/vertex_inputs.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/_manifest.json +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/buffers.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/clears.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/counters_per_event.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/descriptor_access.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/dispatches.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/draw_bindings.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/draws.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/events.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/fbos.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/frame_totals.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/ibo_samples.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/indirect_args.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/passes.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/pixel_history.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/post_vs_samples.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/program_transitions.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/programs.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/render_targets.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/resource_creation.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/rt_event_timeline.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/samplers.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/shaders.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/state_change_events.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/texture_samples.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/textures.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/vbo_samples.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/vertex_inputs.parquet +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/make_golden.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/make_package_golden.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/make_parquet_golden.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/make_preview_golden.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/make_synthetic.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/smoke.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_assets.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_cache.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_catalog_backup_dedup.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_charts.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_classifier.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_components.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_contrast.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_delta.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_design_tokens.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_determinism.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_discovery.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_element_builder.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_fonts.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_hardening.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_head_assets.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_health.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_js_coupled_classes.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_manifest_guard.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_multicapture_normalize.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_parity.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_parquet_parity.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_parquetize.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_perf.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_replay_drift.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_schemas.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_schemas_unit.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_stable_keys.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_table_component.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_theme.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/bobframes/tests/test_token_guard.py +0 -0
- {bobframes-0.2.6 → bobframes-0.2.8}/pyproject.toml +0 -0
|
@@ -7,6 +7,72 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.8] - 2026-06-24
|
|
11
|
+
|
|
12
|
+
A guided local-web control panel (`bobframes ui`) for QA and product teammates who are not comfortable
|
|
13
|
+
in a terminal. Zero new runtime dependency (stdlib `http.server` only) and no report-output change --
|
|
14
|
+
the panel drives the existing verbs and emits no report HTML, so the rendered HTML and parquet digests
|
|
15
|
+
stay byte-identical on the same captures. No schema change (still schema 3). `_version` 0.2.7 -> 0.2.8.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `bobframes ui`: a zero-dependency local-web control panel that drives the whole pipeline from a
|
|
19
|
+
browser (ADR-47). It detects the RenderDoc tools and discovers capture drops, runs ingest /
|
|
20
|
+
re-generate / package / A-B as subprocesses with live per-capture progress streamed over Server-Sent
|
|
21
|
+
Events, opens or serves the rendered report, applies accent theming to a re-render, and can scaffold a
|
|
22
|
+
convention-correct capture folder. Server-rendered HTML + vanilla JS -- no framework, no build step,
|
|
23
|
+
no client router (the hard governance line in ADR-47). Binds `127.0.0.1` only and gates every
|
|
24
|
+
`/api/*` call with a per-session token. Install with `pipx install bobframes`, then run `bobframes ui`.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- The static preview server body moved from `cli._cmd_serve` into a reusable `serve.make_server` /
|
|
28
|
+
`serve.serve_forever` so the panel's background serve and the `bobframes serve` verb share it; the
|
|
29
|
+
`serve` verb's behavior is unchanged.
|
|
30
|
+
- The control panel's JS and CSS are now served as static files (`bobframes/ui/assets/panel.{js,css}`,
|
|
31
|
+
at `GET /panel.js` / `GET /panel.css`) instead of being embedded in a Python string -- so the client
|
|
32
|
+
is a real file that `node --check`/lint can validate (no build step; still no framework or router).
|
|
33
|
+
|
|
34
|
+
### Tests / CI
|
|
35
|
+
- Added an automated guard that the panel JS parses and runs: a `node --check` gate (a pytest test +
|
|
36
|
+
an unconditional CI step) and an opt-in `browser`-marked headless smoke that drives the live panel in
|
|
37
|
+
Chrome and asserts it populates -- closing the gap that let a broken `<script>` ship undetected.
|
|
38
|
+
|
|
39
|
+
## [0.2.7] - 2026-06-24
|
|
40
|
+
|
|
41
|
+
An aggregation-consistency pass plus four report/sharing correctness fixes. Every KPI now names its
|
|
42
|
+
estimator and rate KPIs read on one per-frame basis, so the same metric agrees across the summary,
|
|
43
|
+
dashboard, and trend. No schema change (still schema 3); parquet digests stay byte-identical on the
|
|
44
|
+
same captures. `_version` 0.2.6 -> 0.2.7.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
- One canonical aggregation policy (ADR-46): every rendered KPI label names its estimator precisely --
|
|
48
|
+
`Pooled mean`, `Mean (per area)`, `Median`, `Total` -- and never a bare "avg". Rate KPIs (GPU, draws)
|
|
49
|
+
read per-frame on a single basis, so the same area's GPU now reads identically across the summary,
|
|
50
|
+
dashboard, and trend (previously e.g. `0.0356`/frame on the summary vs a raw `0.178` total on the
|
|
51
|
+
dashboard with no bridge). A naming gate (`test_no_vague_estimator_labels`) fails the build on any
|
|
52
|
+
vague estimator word. (D-13..D-16, Q-10..Q-13)
|
|
53
|
+
- Regression detection unified on the per-frame basis with config-driven thresholds: `trend_table`
|
|
54
|
+
reads every KPI per frame, so a regression flag is capture-count-independent and agrees with the
|
|
55
|
+
build-health verdict (a 7-vs-5-capture run no longer reports a false +40%); the per-KPI thresholds
|
|
56
|
+
(`draws`/`vbo`/`ibo`/`program_switches`) move from code literals to the `[report]` config, with
|
|
57
|
+
defaults that reproduce the prior values. (H-41)
|
|
58
|
+
- `draws_by_class` ratios use `statistics.median` (was the upper-middle `sorted[n//2]`, an off-by-one
|
|
59
|
+
on even N).
|
|
60
|
+
- `aggregates.frame_counts` is the single owner of every per-(drop, area) frame count; when the
|
|
61
|
+
GPU/draws denominator and the entity-rate denominator legitimately differ (a capture that replayed
|
|
62
|
+
but exported partial entity rows), the divergence is logged as a WARNING rather than silently
|
|
63
|
+
normalized away.
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
- Run-selector dropdown did nothing on every report: the component JS runs in `<head>`, so the custom
|
|
67
|
+
element upgraded before its child `<select>` had parsed; initialization now defers to
|
|
68
|
+
`DOMContentLoaded`. (R-20)
|
|
69
|
+
- Exported standalone one-pager carried dead tree-relative navigation -- a run dropdown pointing at
|
|
70
|
+
siblings not in the bundle, a breadcrumb, the dashboard link, and a "viewing an older run" banner.
|
|
71
|
+
The detached summary now strips them; the in-tree summary keeps its working navigation. (R-21)
|
|
72
|
+
- Older-run report pages showed cross-drop graphs and tables spanning all runs, including ones newer
|
|
73
|
+
than the page's own run. Cross-drop renderers now scope to run history (up to and including the
|
|
74
|
+
current run) while the run picker keeps the full list. (R-22)
|
|
75
|
+
|
|
10
76
|
## [0.2.6] - 2026-06-06
|
|
11
77
|
|
|
12
78
|
A build-health one-pager, a shareable `package` bundle, a server-side component system, and a full visual
|
|
@@ -123,5 +189,9 @@ First standalone release. v1 is Windows-only (the replay stage drives `qrenderdo
|
|
|
123
189
|
`python -m _analysis.*` entry points no longer work; switch to the `bobframes` commands (see the
|
|
124
190
|
migration table in the README). This is a hard rename with no compatibility shim.
|
|
125
191
|
|
|
126
|
-
[Unreleased]: https://github.com/altpsyche/bobframes/compare/v0.
|
|
192
|
+
[Unreleased]: https://github.com/altpsyche/bobframes/compare/v0.2.8...HEAD
|
|
193
|
+
[0.2.8]: https://github.com/altpsyche/bobframes/compare/v0.2.7...v0.2.8
|
|
194
|
+
[0.2.7]: https://github.com/altpsyche/bobframes/compare/v0.2.6...v0.2.7
|
|
195
|
+
[0.2.6]: https://github.com/altpsyche/bobframes/compare/v0.2.0...v0.2.6
|
|
196
|
+
[0.2.0]: https://github.com/altpsyche/bobframes/compare/v0.1.0...v0.2.0
|
|
127
197
|
[0.1.0]: https://github.com/altpsyche/bobframes/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bobframes
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: RenderDoc capture pipeline: ingest, analyze, render.
|
|
5
5
|
Project-URL: Homepage, https://github.com/altpsyche/bobframes
|
|
6
6
|
Project-URL: Issues, https://github.com/altpsyche/bobframes/issues
|
|
@@ -55,6 +55,21 @@ bobframes check
|
|
|
55
55
|
`bobframes check` prints the resolved paths for `renderdoccmd` and `qrenderdoc` and exits non-zero if
|
|
56
56
|
either is missing, so you can confirm the toolchain before a long ingest.
|
|
57
57
|
|
|
58
|
+
## Guided mode (recommended for QA / product)
|
|
59
|
+
|
|
60
|
+
Not comfortable in a terminal? `bobframes ui` opens a local-web control panel in your browser that
|
|
61
|
+
drives the whole pipeline point-and-click: detect the RenderDoc tools, pick a capture folder, ingest
|
|
62
|
+
with live per-capture progress, then open / serve / package the report or compare two runs.
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
pipx install bobframes
|
|
66
|
+
bobframes ui # opens http://127.0.0.1:8765 in your browser
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The panel binds `127.0.0.1` only and carries a one-time session token in the opened URL, so only your
|
|
70
|
+
own browser can drive it. It is stdlib-only -- one `pipx install` gives every feature, no extra setup --
|
|
71
|
+
and it emits no report files of its own; it just runs the same verbs as the CLI.
|
|
72
|
+
|
|
58
73
|
## Quickstart
|
|
59
74
|
|
|
60
75
|
```
|
|
@@ -78,6 +93,7 @@ time to rebuild the HTML from existing Parquet without re-replaying captures.
|
|
|
78
93
|
| `lint <file>...` | Check HTML or markdown against the banlist. |
|
|
79
94
|
| `check` | Print resolved tool paths; non-zero when a tool is missing. |
|
|
80
95
|
| `serve [root] [--port 8000] [--bind 127.0.0.1]` | Static preview via the stdlib HTTP server. |
|
|
96
|
+
| `ui [root] [--port 8765] [--bind 127.0.0.1] [--no-open]` | Guided local-web control panel (ADR-47): ingest / render / package / A-B / open / serve / scaffold from a browser, with live progress. Localhost-bound + per-session token; emits no report output of its own. |
|
|
81
97
|
| `package [root] [--inline] [--light] [--redact] [--redact-paths {strip,fail}] [--out PATH] [--run KEY] [--no-summary-file] [--stage]` | Bundle a rendered tree into a shareable `<project>-<rundate>-report.zip` + a standalone `<project>-<rundate>-summary.html`, both written OUTSIDE `<root>` (non-mutating). `--redact` scrubs device/host provenance + absolute paths for external sharing. |
|
|
82
98
|
| `preview [root] [--accent OKLCH] [--accent-data OKLCH]` | Render the chrome gallery to `_reports/_chrome_preview.html`; no capture data needed. `--accent` previews a theme override before you commit it. |
|
|
83
99
|
| `export-tokens [--format toml\|json\|css] [--theme-template]` | Print the design tokens to stdout in the chosen format. `--theme-template` emits a paste-ready `[theme]` block for `.bobframes.toml`. |
|
|
@@ -24,6 +24,21 @@ bobframes check
|
|
|
24
24
|
`bobframes check` prints the resolved paths for `renderdoccmd` and `qrenderdoc` and exits non-zero if
|
|
25
25
|
either is missing, so you can confirm the toolchain before a long ingest.
|
|
26
26
|
|
|
27
|
+
## Guided mode (recommended for QA / product)
|
|
28
|
+
|
|
29
|
+
Not comfortable in a terminal? `bobframes ui` opens a local-web control panel in your browser that
|
|
30
|
+
drives the whole pipeline point-and-click: detect the RenderDoc tools, pick a capture folder, ingest
|
|
31
|
+
with live per-capture progress, then open / serve / package the report or compare two runs.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
pipx install bobframes
|
|
35
|
+
bobframes ui # opens http://127.0.0.1:8765 in your browser
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The panel binds `127.0.0.1` only and carries a one-time session token in the opened URL, so only your
|
|
39
|
+
own browser can drive it. It is stdlib-only -- one `pipx install` gives every feature, no extra setup --
|
|
40
|
+
and it emits no report files of its own; it just runs the same verbs as the CLI.
|
|
41
|
+
|
|
27
42
|
## Quickstart
|
|
28
43
|
|
|
29
44
|
```
|
|
@@ -47,6 +62,7 @@ time to rebuild the HTML from existing Parquet without re-replaying captures.
|
|
|
47
62
|
| `lint <file>...` | Check HTML or markdown against the banlist. |
|
|
48
63
|
| `check` | Print resolved tool paths; non-zero when a tool is missing. |
|
|
49
64
|
| `serve [root] [--port 8000] [--bind 127.0.0.1]` | Static preview via the stdlib HTTP server. |
|
|
65
|
+
| `ui [root] [--port 8765] [--bind 127.0.0.1] [--no-open]` | Guided local-web control panel (ADR-47): ingest / render / package / A-B / open / serve / scaffold from a browser, with live progress. Localhost-bound + per-session token; emits no report output of its own. |
|
|
50
66
|
| `package [root] [--inline] [--light] [--redact] [--redact-paths {strip,fail}] [--out PATH] [--run KEY] [--no-summary-file] [--stage]` | Bundle a rendered tree into a shareable `<project>-<rundate>-report.zip` + a standalone `<project>-<rundate>-summary.html`, both written OUTSIDE `<root>` (non-mutating). `--redact` scrubs device/host provenance + absolute paths for external sharing. |
|
|
51
67
|
| `preview [root] [--accent OKLCH] [--accent-data OKLCH]` | Render the chrome gallery to `_reports/_chrome_preview.html`; no capture data needed. `--accent` previews a theme override before you commit it. |
|
|
52
68
|
| `export-tokens [--format toml\|json\|css] [--theme-template]` | Print the design tokens to stdout in the chosen format. `--theme-template` emits a paste-ready `[theme]` block for `.bobframes.toml`. |
|
|
@@ -57,5 +57,9 @@ shader_complexity_high = 60.0 # fragment shaders at/above this complexity a
|
|
|
57
57
|
overdraw_reject_warn_pct = 40.0 # RT sample-rejection % -> warn callout
|
|
58
58
|
overdraw_reject_alarm_pct = 70.0 # RT sample-rejection % -> alarm callout
|
|
59
59
|
instancing_repeat_min = 4 # min mesh repeat PER FRAME to flag an instancing candidate (c16v: repeat + shader cost are per-frame rates, normalized by the drop's captured-frame count)
|
|
60
|
-
gpu_regression_pct = 10.0 # latest-vs-previous GPU increase % -> regression
|
|
60
|
+
gpu_regression_pct = 10.0 # latest-vs-previous GPU/frame increase % -> regression (per frame, capture-count-independent)
|
|
61
|
+
draws_regression_pct = 10.0 # H-41 — per-KPI trend regression thresholds (were baked in trend_table.KPIS literals)
|
|
62
|
+
vbo_regression_pct = 15.0 # " vertex-buffer bytes / frame
|
|
63
|
+
ibo_regression_pct = 15.0 # " index-buffer bytes / frame
|
|
64
|
+
program_switches_regression_pct = 20.0 # " program switches / frame
|
|
61
65
|
max_prerendered_runs = 10 # c16f — cap on pre-rendered older-run pages (run selector)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.8"
|
|
@@ -163,3 +163,69 @@ def shader_aggregates(root: str, drops: list, *, stage: str | None = None) -> Sh
|
|
|
163
163
|
sa.cplx[k] = max(sa.cplx.get(k, 0.0), c_val)
|
|
164
164
|
sa.stype[k] = stype
|
|
165
165
|
return sa
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# --- frame counts (D-15: the single owner of every per-(drop, area) frame count) ----------------
|
|
169
|
+
|
|
170
|
+
def _frame_total_captures(root: str, drops: list) -> dict:
|
|
171
|
+
"""``{(drop_key, area): {captures}}`` from ``frame_totals.parquet`` -- the authoritative
|
|
172
|
+
captured-frame set (the distinct ``capture`` values present in the per-frame GPU/draws table that
|
|
173
|
+
``dashboard._run_totals`` / ``_top_areas_gpu`` divide by). Read live per drop_dir (frame_totals has
|
|
174
|
+
no per-drop cache, mirroring the dashboard readers). If a frame_totals has no ``capture`` column,
|
|
175
|
+
each row is treated as one distinct frame (num_rows) so the count never under-reports."""
|
|
176
|
+
out: dict = defaultdict(set)
|
|
177
|
+
for d in drops:
|
|
178
|
+
for r in d.rows:
|
|
179
|
+
p = os.path.join(r.drop_dir, 'frame_totals.parquet')
|
|
180
|
+
if not os.path.exists(p):
|
|
181
|
+
continue
|
|
182
|
+
try:
|
|
183
|
+
names = set(papq.read_schema(p).names)
|
|
184
|
+
if 'capture' in names:
|
|
185
|
+
caps = papq.read_table(p, columns=['capture']).column('capture').to_pylist()
|
|
186
|
+
else:
|
|
187
|
+
caps = [f'__row_{i}' for i in range(papq.read_metadata(p).num_rows)]
|
|
188
|
+
except Exception:
|
|
189
|
+
continue
|
|
190
|
+
out[(drop_key(r.drop_date, r.drop_label), r.area)].update(caps)
|
|
191
|
+
return out
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def frame_counts(root: str, drops: list) -> dict:
|
|
195
|
+
"""The ONE owner of every per-(drop, area) frame count (D-15 / D-A4). Returns
|
|
196
|
+
``{(drop_key, area): {'frames', 'draw_frames', 'shader_frames'}}`` where ``frames`` = distinct
|
|
197
|
+
captures in frame_totals (the GPU/draws-per-frame denominator) and ``draw_frames``/``shader_frames``
|
|
198
|
+
= distinct captures in the draws/shaders entity data (the c16v entity-rate denominators).
|
|
199
|
+
|
|
200
|
+
The three legitimately DIFFER when a capture replayed "ok" but exported no entity rows (the c16v
|
|
201
|
+
as-built, ADR-23): per-frame GPU divides by the frames that produced GPU data, while per-frame mesh/
|
|
202
|
+
shader rates divide by the frames that produced that entity -- both correct for their metric, on
|
|
203
|
+
different populations. ``frame_count_divergences`` surfaces the difference so the different-N
|
|
204
|
+
normalization is visible across reports rather than silent. Raw counts (no >=1 guard) so divergence
|
|
205
|
+
is detectable; the per-frame no-op guard stays in ``DrawAgg.frames`` / ``ShaderAgg.frames``.
|
|
206
|
+
"""
|
|
207
|
+
da = draw_aggregates(root, drops)
|
|
208
|
+
sa = shader_aggregates(root, drops)
|
|
209
|
+
ft = _frame_total_captures(root, drops)
|
|
210
|
+
keys = set(ft) | set(da.captures) | set(sa.captures)
|
|
211
|
+
return {
|
|
212
|
+
k: {
|
|
213
|
+
'frames': len(ft.get(k, ())),
|
|
214
|
+
'draw_frames': len(da.captures.get(k, ())),
|
|
215
|
+
'shader_frames': len(sa.captures.get(k, ())),
|
|
216
|
+
}
|
|
217
|
+
for k in keys
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def frame_count_divergences(root: str, drops: list) -> list:
|
|
222
|
+
"""``[(drop_key, area, frames, draw_frames, shader_frames), ...]`` for every (drop, area) where an
|
|
223
|
+
entity count that EXISTS (>0) differs from the frame_totals frame count -- i.e. a capture produced
|
|
224
|
+
GPU/draw data but not the matching entity rows (or vice versa). Sorted for deterministic logging.
|
|
225
|
+
Empty when every layer agrees (the uniform-capture case). Drives the render-time warning (D-15)."""
|
|
226
|
+
out = []
|
|
227
|
+
for (dk, area), c in sorted(frame_counts(root, drops).items()):
|
|
228
|
+
ftn, dfr, sfr = c['frames'], c['draw_frames'], c['shader_frames']
|
|
229
|
+
if (dfr and dfr != ftn) or (sfr and sfr != ftn):
|
|
230
|
+
out.append((dk, area, ftn, dfr, sfr))
|
|
231
|
+
return out
|
|
@@ -251,20 +251,19 @@ def _cmd_check(args: argparse.Namespace) -> int:
|
|
|
251
251
|
|
|
252
252
|
|
|
253
253
|
def _cmd_serve(args: argparse.Namespace) -> int:
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
import
|
|
254
|
+
# The static preview server body lives in serve.serve_forever so the `ui` panel can reuse the
|
|
255
|
+
# same builder (serve.make_server) for its background click-to-serve (v028_3).
|
|
256
|
+
from . import serve
|
|
257
|
+
return serve.serve_forever(os.path.abspath(args.root), bind=args.bind, port=args.port)
|
|
257
258
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
return 4
|
|
267
|
-
return 0
|
|
259
|
+
|
|
260
|
+
def _cmd_ui(args: argparse.Namespace) -> int:
|
|
261
|
+
# ADR-47: a zero-dependency local-web control panel that DRIVES the verbs for non-terminal users.
|
|
262
|
+
# A frontend SURFACE above the verb taxonomy -- it emits no report HTML (golden gate untouched) and
|
|
263
|
+
# pulls no dep into core. Lazy-imported so a core install never loads the ui package.
|
|
264
|
+
from .ui import server
|
|
265
|
+
return server.serve(os.path.abspath(args.root), bind=args.bind, port=args.port,
|
|
266
|
+
open_browser=not args.no_open)
|
|
268
267
|
|
|
269
268
|
|
|
270
269
|
def _cmd_package(args: argparse.Namespace) -> int:
|
|
@@ -380,6 +379,16 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
380
379
|
sp.add_argument('--bind', default='127.0.0.1')
|
|
381
380
|
sp.set_defaults(func=_cmd_serve)
|
|
382
381
|
|
|
382
|
+
sp = sub.add_parser('ui', parents=[common],
|
|
383
|
+
help='guided local-web control panel (ingest/generate/package in a browser)')
|
|
384
|
+
sp.add_argument('root', nargs='?', default='.')
|
|
385
|
+
sp.add_argument('--port', type=int, default=8765)
|
|
386
|
+
sp.add_argument('--bind', default='127.0.0.1',
|
|
387
|
+
help='bind address; localhost only by design (ADR-47)')
|
|
388
|
+
sp.add_argument('--no-open', action='store_true',
|
|
389
|
+
help="don't auto-open the browser")
|
|
390
|
+
sp.set_defaults(func=_cmd_ui)
|
|
391
|
+
|
|
383
392
|
sp = sub.add_parser('package', parents=[common],
|
|
384
393
|
help='bundle a rendered tree into a shareable zip + standalone summary')
|
|
385
394
|
sp.add_argument('root', nargs='?', default='.')
|
|
@@ -131,6 +131,12 @@ class ReportCfg: # c16 — report-presentation thresholds (
|
|
|
131
131
|
overdraw_reject_alarm_pct: float = 70.0
|
|
132
132
|
instancing_repeat_min: int = 4 # c16v — min mesh repeat PER FRAME (repeat + shader cost are per-frame rates)
|
|
133
133
|
gpu_regression_pct: float = 10.0
|
|
134
|
+
# H-41 (v027_1) — per-KPI trend regression thresholds; were baked in `trend_table.KPIS` literals.
|
|
135
|
+
# Trend-only (health's verdict has only a gpu trajectory rule). Defaults reproduce the old literals.
|
|
136
|
+
draws_regression_pct: float = 10.0
|
|
137
|
+
vbo_regression_pct: float = 15.0
|
|
138
|
+
ibo_regression_pct: float = 15.0
|
|
139
|
+
program_switches_regression_pct: float = 20.0
|
|
134
140
|
max_prerendered_runs: int = 10 # c16f — cap on pre-rendered older-run pages (per-run UX)
|
|
135
141
|
|
|
136
142
|
|
|
@@ -327,6 +333,10 @@ def _build_config(root: str | None) -> Config:
|
|
|
327
333
|
overdraw_reject_alarm_pct=rpt.get('overdraw_reject_alarm_pct', 70.0),
|
|
328
334
|
instancing_repeat_min=rpt.get('instancing_repeat_min', 4),
|
|
329
335
|
gpu_regression_pct=rpt.get('gpu_regression_pct', 10.0),
|
|
336
|
+
draws_regression_pct=rpt.get('draws_regression_pct', 10.0),
|
|
337
|
+
vbo_regression_pct=rpt.get('vbo_regression_pct', 15.0),
|
|
338
|
+
ibo_regression_pct=rpt.get('ibo_regression_pct', 15.0),
|
|
339
|
+
program_switches_regression_pct=rpt.get('program_switches_regression_pct', 20.0),
|
|
330
340
|
max_prerendered_runs=rpt.get('max_prerendered_runs', 10),
|
|
331
341
|
),
|
|
332
342
|
theme=ThemeCfg(tokens=tuple(theme_tokens.items())) if theme_tokens else None,
|
|
@@ -217,6 +217,9 @@ def build(root: str, *, out: str | None = None, light: bool = False,
|
|
|
217
217
|
shutil.rmtree(staging, ignore_errors=True)
|
|
218
218
|
|
|
219
219
|
if summary_file:
|
|
220
|
+
# The standalone one-pager is a DETACHED single file -- strip the tree-relative navigation that
|
|
221
|
+
# cannot resolve beside a lone HTML (R-21), so it no longer carries a dead run dropdown.
|
|
222
|
+
summary_bytes = _detach_summary(summary_bytes)
|
|
220
223
|
with open(summary_path, 'wb') as f:
|
|
221
224
|
f.write(summary_bytes)
|
|
222
225
|
else:
|
|
@@ -503,6 +506,30 @@ def _redact_text_files(entries: list[tuple[str, str]], *, mode: str) -> int:
|
|
|
503
506
|
return n
|
|
504
507
|
|
|
505
508
|
|
|
509
|
+
# Tree-relative navigation that only resolves inside the rendered tree -- dead in a detached one-pager.
|
|
510
|
+
# Each is a cleanly-delimited, non-nesting element generated by the chrome builders.
|
|
511
|
+
_DETACH_RES = (
|
|
512
|
+
# the run selector: its options point to run/<key>/summary.html siblings not bundled with a lone file
|
|
513
|
+
re.compile(rb'<rdc-ab-picker><label for="rdc-run-select">.*?</rdc-ab-picker>', re.S),
|
|
514
|
+
re.compile(rb'<nav class="crumb">.*?</nav>', re.S), # breadcrumb (root catalog / dashboard)
|
|
515
|
+
re.compile(rb'<a class="sb-link"[^>]*>.*?</a>', re.S), # summary-bar "dashboard" link
|
|
516
|
+
# the "viewing an older run / go to newest" nav banner (older-run one-pagers); its link points to a
|
|
517
|
+
# sibling page absent beside a lone file. Anchored on its title so the health alarm-banner is kept.
|
|
518
|
+
re.compile(rb'<rdc-alarm-banner\b[^>]*>(?:(?!</rdc-alarm-banner>).)*?viewing an older run'
|
|
519
|
+
rb'(?:(?!</rdc-alarm-banner>).)*?</rdc-alarm-banner>', re.S),
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _detach_summary(html: bytes) -> bytes:
|
|
524
|
+
"""Make the standalone one-pager genuinely self-contained (R-21): strip the tree-relative navigation
|
|
525
|
+
that cannot work in a LONE file -- the run selector (a silently-dead dropdown), the breadcrumb, and
|
|
526
|
+
the summary-bar dashboard link. The page's own content (verdict, KPIs, movement, by-area) is
|
|
527
|
+
self-contained and untouched; the in-tree `_reports/summary.html` keeps its working nav."""
|
|
528
|
+
for rx in _DETACH_RES:
|
|
529
|
+
html = rx.sub(b'', html)
|
|
530
|
+
return html
|
|
531
|
+
|
|
532
|
+
|
|
506
533
|
def _redacted_summary_bytes(staging: str, summary_rel: str, *, newest: bool, target,
|
|
507
534
|
build_ts: str, shared: bool, redact_paths: str) -> bytes:
|
|
508
535
|
"""The standalone one-pager for a redacted bundle: self-contained (INLINE) + redacted.
|
|
@@ -6,7 +6,19 @@
|
|
|
6
6
|
connectedCallback(){
|
|
7
7
|
if (this._rdcUp) return;
|
|
8
8
|
this._rdcUp = true;
|
|
9
|
-
|
|
9
|
+
const run = () => {
|
|
10
|
+
try { this.init(); } catch(e) { console.error('rdc init error', this.tagName, e); }
|
|
11
|
+
};
|
|
12
|
+
// The component JS rides in <head> (adjacent to the CSS), so a custom element upgrades as its
|
|
13
|
+
// START tag is parsed -- BEFORE its light-DOM children (e.g. the run <select>) exist. Inits that
|
|
14
|
+
// querySelector a child would see nothing and bail. Defer to DOMContentLoaded while the document
|
|
15
|
+
// is still parsing so children are present; run immediately for elements created after load.
|
|
16
|
+
// (Mirrors the _wireRowDrill deferral below.)
|
|
17
|
+
if (document.readyState === 'loading'){
|
|
18
|
+
document.addEventListener('DOMContentLoaded', run, { once: true });
|
|
19
|
+
} else {
|
|
20
|
+
run();
|
|
21
|
+
}
|
|
10
22
|
}
|
|
11
23
|
init(){}
|
|
12
24
|
}
|
|
@@ -301,11 +301,11 @@ def _global_kpis(drops: list) -> list:
|
|
|
301
301
|
avg_gpu_frame = (total_gpu / n_frames) if n_frames else 0.0
|
|
302
302
|
avg_draws_frame = (total_draws / n_frames) if n_frames else 0.0
|
|
303
303
|
return [
|
|
304
|
-
{'label': '
|
|
305
|
-
{'label': '
|
|
306
|
-
{'label': '
|
|
307
|
-
{'label': '
|
|
308
|
-
{'label': 'areas',
|
|
304
|
+
{'label': 'pooled mean gpu / frame (s)', 'value': base.fmt_float(avg_gpu_frame, 4)},
|
|
305
|
+
{'label': 'total gpu (s) over captures', 'value': base.fmt_float(total_gpu, 3)},
|
|
306
|
+
{'label': 'pooled mean draws / frame', 'value': base.fmt_int(round(avg_draws_frame))},
|
|
307
|
+
{'label': 'total draws over captures', 'value': base.fmt_int(total_draws)},
|
|
308
|
+
{'label': 'areas', 'value': base.fmt_int(len(areas))},
|
|
309
309
|
]
|
|
310
310
|
|
|
311
311
|
|
|
@@ -351,6 +351,9 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
351
351
|
rc = base.run_context(drops, run_label=run_label, run_date=run_date)
|
|
352
352
|
cur = rc.current
|
|
353
353
|
cur_drops = [cur] if cur else []
|
|
354
|
+
# v027_4: per-area captured-frame counts (the single owner, D-15) so the mini cards can read
|
|
355
|
+
# per-frame and stay consistent with summary + the trend card (no bare cross-capture totals).
|
|
356
|
+
area_frames = {a: c['frames'] for (_dk, a), c in _agg.frame_counts(root, cur_drops).items()}
|
|
354
357
|
|
|
355
358
|
# c16f: a per-run dashboard lives at _reports/run/<key>/index.html; its sibling reports (instancing
|
|
356
359
|
# etc.) are pre-rendered there too, so their links stay bare. trend_table is NOT per-run (it is the
|
|
@@ -398,22 +401,23 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
398
401
|
*[base.el('a', {'href': href, 'data-link-kind': 'primary'}, lbl)
|
|
399
402
|
for href, lbl in _NAV]))
|
|
400
403
|
|
|
401
|
-
# Card: trend table - GPU
|
|
404
|
+
# Card: trend table - GPU PER FRAME per area (D-16: leads with the per-frame mean so the same area
|
|
405
|
+
# reads the SAME number here and on summary; the raw run total rides alongside as the labeled bridge).
|
|
402
406
|
top_a = top_a[:3]
|
|
403
407
|
chart_tt = base.figure(base.bar_chart(
|
|
404
|
-
[(
|
|
405
|
-
title='gpu
|
|
408
|
+
[(t[0], t[4]) for t in top_a], value_fmt=lambda v: f'{v:.4f}', width=280,
|
|
409
|
+
title='gpu / frame per area', desc='top areas by GPU per captured frame', chart_id='dash-tt'))
|
|
406
410
|
body_tt = _card_table(
|
|
407
411
|
top_a,
|
|
408
412
|
[
|
|
409
413
|
('area', lambda r: r[0], False),
|
|
410
|
-
('gpu
|
|
411
|
-
('
|
|
412
|
-
('
|
|
414
|
+
('mean gpu / frame', lambda r: base.fmt_float(r[4], 4), True),
|
|
415
|
+
('total gpu (s)', lambda r: base.fmt_float(r[1], 3), True),
|
|
416
|
+
('mean draws / frame', lambda r: base.fmt_int(round(r[3])), True),
|
|
413
417
|
],
|
|
414
|
-
caption='per-area GPU +
|
|
415
|
-
sub_tt = ('GPU
|
|
416
|
-
+ (f' worst: {top_a[0][0]} {base.fmt_float(top_a[0][
|
|
418
|
+
caption='per-area GPU per captured frame + the run total (mean = area total / area frames)')
|
|
419
|
+
sub_tt = ('GPU per frame per area in the current run.'
|
|
420
|
+
+ (f' worst: {top_a[0][0]} {base.fmt_float(top_a[0][4], 4)}s / frame' if top_a else ''))
|
|
417
421
|
cards.append(_card(tt_href, 'trend table', sub_tt, chart_tt, body_tt))
|
|
418
422
|
|
|
419
423
|
# Card: instancing - repeat per mesh (mini bars).
|
|
@@ -426,7 +430,7 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
426
430
|
[
|
|
427
431
|
('mesh', lambda r: r[0], False),
|
|
428
432
|
('repeat', lambda r: base.fmt_int(r[1]), True),
|
|
429
|
-
('
|
|
433
|
+
('median verts', lambda r: base.fmt_int(r[2]), True),
|
|
430
434
|
],
|
|
431
435
|
caption='most-repeated meshes')
|
|
432
436
|
sub_im = ('Repeated meshes worth instancing or batching.'
|
|
@@ -438,7 +442,7 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
438
442
|
top_p = _top_passes(cur_drops)
|
|
439
443
|
chart_pg = base.figure(base.bar_chart(
|
|
440
444
|
[(pl, g) for _, pl, g in top_p], value_fmt=lambda v: f'{v:.3f}', width=280,
|
|
441
|
-
title='gpu (s) per pass', desc='heaviest passes by GPU seconds', chart_id='dash-pg'))
|
|
445
|
+
title='total gpu (s) per pass', desc='heaviest passes by total GPU seconds', chart_id='dash-pg'))
|
|
442
446
|
body_pg = _card_table(
|
|
443
447
|
top_p,
|
|
444
448
|
[
|
|
@@ -447,9 +451,10 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
447
451
|
# so hover could never reveal it). The CSS clip now truncates the display + the td title=
|
|
448
452
|
# reveals the full value on hover - consistent with every other mini text column.
|
|
449
453
|
('marker', lambda r: base.scrub_chrome_text(r[1]), False),
|
|
450
|
-
('
|
|
454
|
+
# per-pass GPU is a cross-capture total (the pass report's native basis); labeled as such.
|
|
455
|
+
('total gpu (s)', lambda r: base.fmt_float(r[2], 3), True),
|
|
451
456
|
],
|
|
452
|
-
caption='heaviest passes by GPU time')
|
|
457
|
+
caption='heaviest passes by total GPU time')
|
|
453
458
|
sub_pg = ('Heaviest GPU passes.'
|
|
454
459
|
+ (f' top: {top_p[0][1]} {base.fmt_float(top_p[0][2], 3)}s in {top_p[0][0]}'
|
|
455
460
|
if top_p else ''))
|
|
@@ -503,10 +508,13 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
503
508
|
pa_rows,
|
|
504
509
|
[
|
|
505
510
|
('area', lambda r: r[0], False),
|
|
506
|
-
|
|
511
|
+
# per-frame to match summary's By-area + the trend card (D-16 consistency), not a bare total.
|
|
512
|
+
('mean draws / frame',
|
|
513
|
+
lambda r: base.fmt_int(round(base.per_frame(r[1]['n_draws'], area_frames.get(r[0], 0)))),
|
|
514
|
+
True),
|
|
507
515
|
('dominant', lambda r: r[1]['dominant_class'], False),
|
|
508
516
|
],
|
|
509
|
-
caption='top areas by
|
|
517
|
+
caption='top areas by mean draws per captured frame')
|
|
510
518
|
dom = class_totals.most_common(1)[0] if class_totals else None
|
|
511
519
|
sub_dc = ('Draw mix by class.'
|
|
512
520
|
+ (f' dominant: {dom[0]} {base.fmt_pct(100.0 * dom[1] / grand)}'
|
|
@@ -182,6 +182,15 @@ class RunContext:
|
|
|
182
182
|
def is_newest(self) -> bool:
|
|
183
183
|
return bool(self.current) and self.index == self.n_runs - 1
|
|
184
184
|
|
|
185
|
+
@property
|
|
186
|
+
def history(self) -> list:
|
|
187
|
+
"""Drops up to AND INCLUDING the current run (chronological) -- the data scope for cross-drop
|
|
188
|
+
CHARTS/TABLES on a per-run page, so an OLDER run's page never shows data for runs that came
|
|
189
|
+
AFTER it (ADR-35; R-22). The run picker still lists every run (from `drops`); only the rendered
|
|
190
|
+
data is scoped. Equals `drops` on the newest page (index == n_runs - 1)."""
|
|
191
|
+
i = self.index
|
|
192
|
+
return list(self.drops) if i < 0 else self.drops[:i + 1]
|
|
193
|
+
|
|
185
194
|
@property
|
|
186
195
|
def run_label(self) -> str:
|
|
187
196
|
"""current.key (e.g. '2026-06-01_r110788'); '' when there is no current run."""
|
|
@@ -7,12 +7,14 @@ Segments colored by draw_class. Below: raw-count table.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
|
+
import statistics
|
|
10
11
|
import sys
|
|
11
12
|
from collections import Counter, defaultdict
|
|
12
13
|
|
|
13
14
|
import pyarrow.parquet as papq
|
|
14
15
|
|
|
15
16
|
from . import base
|
|
17
|
+
from .. import aggregates as _agg
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
def _gather_from_drops(drops: list) -> tuple[dict, list, list, int]:
|
|
@@ -79,8 +81,11 @@ def _build_table(counts: dict, drop_keys: list) -> str:
|
|
|
79
81
|
caption='raw draw counts per class, per area and drop')
|
|
80
82
|
|
|
81
83
|
|
|
82
|
-
def _compute_kpis(counts: dict, areas: list) -> list:
|
|
83
|
-
"""Hero KPIs:
|
|
84
|
+
def _compute_kpis(counts: dict, areas: list, total_frames: int) -> list:
|
|
85
|
+
"""Hero KPIs (current run): mean draws / frame + the labeled raw total, n areas, the MEDIAN
|
|
86
|
+
prepass/opaque ratio across areas, dominant class. Q-12: lead with the per-frame mean (the
|
|
87
|
+
capture-count-independent number) and label the total. Q-11: `statistics.median` (not the
|
|
88
|
+
upper-middle `sorted[n//2]`), and the label names the estimator + population."""
|
|
84
89
|
total = sum(sum(cc.values()) for cc in counts.values())
|
|
85
90
|
ratios = []
|
|
86
91
|
class_totals: Counter = Counter()
|
|
@@ -90,14 +95,15 @@ def _compute_kpis(counts: dict, areas: list) -> list:
|
|
|
90
95
|
op = cc.get('opaque', 0)
|
|
91
96
|
if op:
|
|
92
97
|
ratios.append(cc.get('prepass', 0) / op)
|
|
93
|
-
median_ratio = (
|
|
94
|
-
if ratios else 0.0)
|
|
98
|
+
median_ratio = statistics.median(ratios) if ratios else 0.0
|
|
95
99
|
dominant_cls = (class_totals.most_common(1)[0][0]
|
|
96
100
|
if class_totals else '')
|
|
101
|
+
mean_draws = base.per_frame(total, total_frames) # per frame (no-op when frames<=1)
|
|
97
102
|
return [
|
|
98
|
-
{'label': '
|
|
103
|
+
{'label': 'mean draws / frame', 'value': base.fmt_int(round(mean_draws))},
|
|
104
|
+
{'label': 'total draws over captures', 'value': base.fmt_int(total)},
|
|
99
105
|
{'label': 'areas', 'value': base.fmt_int(len(areas))},
|
|
100
|
-
{'label': 'prepass/opaque (
|
|
106
|
+
{'label': 'median prepass / opaque (across areas)', 'value': base.fmt_float(median_ratio, 2)},
|
|
101
107
|
{'label': 'dominant class', 'value': dominant_cls or '-'},
|
|
102
108
|
]
|
|
103
109
|
|
|
@@ -112,9 +118,12 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
112
118
|
# table below keeps every run as the breakdown / comparison view.
|
|
113
119
|
rc = base.run_context(drops, run_label=run_label, run_date=run_date)
|
|
114
120
|
cur = rc.current
|
|
121
|
+
drops = rc.history # R-22: cross-drop data scoped to <= current (picker still lists all via rc)
|
|
115
122
|
counts, areas, drop_keys, total_captures = _gather_from_drops(drops)
|
|
116
123
|
cur_counts = {k: cc for k, cc in counts.items() if cur and k[1] == cur.key}
|
|
117
124
|
cur_areas = cur.areas if cur else []
|
|
125
|
+
# Q-12: per-frame mean draws needs the current run's captured-frame count (the single owner, D-15).
|
|
126
|
+
cur_frames = sum(v['frames'] for v in (_agg.frame_counts(root, [cur]) if cur else {}).values())
|
|
118
127
|
|
|
119
128
|
parts = []
|
|
120
129
|
|
|
@@ -183,7 +192,7 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
183
192
|
drops=len(drops), captures=total_captures, build_ts=build_ts or base.now_iso(),
|
|
184
193
|
crumb_depth=base.crumb_depth(ab, run=rc), ab=ab, root=root, report_key='draws_by_class',
|
|
185
194
|
sink=sink, theme=theme,
|
|
186
|
-
kpis=_compute_kpis(cur_counts, cur_areas), run=rc,
|
|
195
|
+
kpis=_compute_kpis(cur_counts, cur_areas, cur_frames), run=rc,
|
|
187
196
|
device=base.provenance_strip(*base.newest_drop_provenance(root, [cur] if cur else []), redact=redact))])
|
|
188
197
|
|
|
189
198
|
|
|
@@ -71,6 +71,7 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
71
71
|
# Run model (ADR-35): live candidates are meshes drawn in the CURRENT run; prior runs supply the
|
|
72
72
|
# per-drop comparison columns + the resolved-since section, never inflate the live list.
|
|
73
73
|
rc = base.run_context(drops, run_label=run_label, run_date=run_date)
|
|
74
|
+
drops = rc.history # R-22: cross-drop trend scoped to <= current (picker still lists all via rc)
|
|
74
75
|
cur = rc.current
|
|
75
76
|
bl = rc.baseline
|
|
76
77
|
ck = cur.key if cur else None
|
|
@@ -220,7 +221,7 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
220
221
|
if chart_items:
|
|
221
222
|
mbody.append(base.figure(
|
|
222
223
|
base.bar_chart(chart_items, title='estimated wasted indices',
|
|
223
|
-
desc='(max repeat - 1) x
|
|
224
|
+
desc='(max repeat - 1) x median indices, per mesh',
|
|
224
225
|
chart_id='inst-wasted'),
|
|
225
226
|
'estimated wasted indices (top meshes)'))
|
|
226
227
|
|
|
@@ -237,9 +238,9 @@ def build(root: str, *, drops: list | None = None, ab=None,
|
|
|
237
238
|
cols += [
|
|
238
239
|
base.Column('areas', 'areas', clip='default'),
|
|
239
240
|
base.Column('dompass', 'dominant pass', clip='narrow'),
|
|
240
|
-
base.Column('ityp', 'indices
|
|
241
|
+
base.Column('ityp', 'median indices', numeric=True),
|
|
241
242
|
base.Column('wasted', 'wasted indices', numeric=True,
|
|
242
|
-
title='(max repeat - 1) x
|
|
243
|
+
title='(max repeat - 1) x median indices: index data re-submitted across repeats',
|
|
243
244
|
render=lambda value, row: base.heatmap_cell(value, 0, row['_maxw'],
|
|
244
245
|
text=base.fmt_int(value))),
|
|
245
246
|
]
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import time
|
|
6
6
|
|
|
7
|
+
from .. import aggregates as _agg
|
|
7
8
|
from .. import lint
|
|
8
9
|
from ..config import get_config
|
|
9
10
|
from ..html import template
|
|
@@ -43,6 +44,16 @@ def render_all_reports(root: str, log, *,
|
|
|
43
44
|
cache_out = reports_base.build_per_drop_cache(root)
|
|
44
45
|
log(f' built per-drop cache: {cache_out} ({time.monotonic()-t0:.1f}s)')
|
|
45
46
|
|
|
47
|
+
# D-15 (D-A4): one owner of every per-(drop, area) frame count. Per-frame GPU/draws divide by the
|
|
48
|
+
# frame_totals frame count; per-frame mesh/shader rates divide by the entity-capture count (c16v).
|
|
49
|
+
# They legitimately differ when a capture replayed "ok" but exported no entity rows -- surface that
|
|
50
|
+
# so cross-report per-frame normalization uses different N visibly, not silently (ADR-23, warn not
|
|
51
|
+
# assert: the divergence is a real data condition, present even in the synthetic fixture).
|
|
52
|
+
for dk, area, ftn, dfr, sfr in _agg.frame_count_divergences(root, reports_base.discover_drops(root)):
|
|
53
|
+
log(f' WARNING: frame-count divergence {area} {dk}: frame_totals={ftn} draws={dfr} '
|
|
54
|
+
f'shaders={sfr} -- per-frame GPU/draws and per-frame entity rates use different '
|
|
55
|
+
f'denominators (a capture replayed but exported partial entity data)')
|
|
56
|
+
|
|
46
57
|
for mod in all_reports():
|
|
47
58
|
try:
|
|
48
59
|
rep = mod.build(root, sink=sink, build_ts=build_ts, redact=redact, theme=theme)
|