bobframes 0.1.0__tar.gz → 0.2.0__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.1.0 → bobframes-0.2.0}/CHANGELOG.md +32 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/PKG-INFO +26 -2
- {bobframes-0.1.0 → bobframes-0.2.0}/README.md +24 -1
- bobframes-0.2.0/bobframes/_default_config.toml +61 -0
- bobframes-0.2.0/bobframes/_version.py +1 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/catalog.py +8 -15
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/cli.py +109 -13
- bobframes-0.2.0/bobframes/config.py +462 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/derive_post_merge.py +22 -44
- bobframes-0.2.0/bobframes/derives/classifier.py +142 -0
- bobframes-0.2.0/bobframes/derives/draw_classifier.toml +78 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/derives/pass_class_breakdown.py +3 -1
- bobframes-0.2.0/bobframes/derives/presets/custom-template.toml +52 -0
- bobframes-0.2.0/bobframes/derives/presets/godot.toml +60 -0
- bobframes-0.2.0/bobframes/derives/presets/unity.toml +61 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/discovery.py +18 -2
- bobframes-0.2.0/bobframes/errors.py +65 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/global_entities.py +17 -10
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/html/template.py +207 -487
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/lint.py +8 -20
- bobframes-0.2.0/bobframes/lint_banlist.toml +70 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/manifest.py +25 -4
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/parquetize.py +107 -64
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/parsers/parse_init_state.py +13 -3
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/paths.py +43 -9
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/qrd_harness.py +3 -13
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/rdcmd.py +4 -14
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/replay/replay_main.py +21 -41
- bobframes-0.2.0/bobframes/reports/__init__.py +40 -0
- bobframes-0.2.0/bobframes/reports/_tokens.py +69 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/reports/ab.py +10 -18
- bobframes-0.2.0/bobframes/reports/assets/Inter-OFL.txt +92 -0
- bobframes-0.2.0/bobframes/reports/assets/README.md +32 -0
- bobframes-0.2.0/bobframes/reports/assets/inter-subset.woff2 +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/reports/base.py +49 -8
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/reports/cache.py +91 -5
- bobframes-0.2.0/bobframes/reports/charts.py +549 -0
- bobframes-0.2.0/bobframes/reports/chrome.py +2395 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/reports/cli.py +32 -13
- bobframes-0.2.0/bobframes/reports/dashboard.py +519 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/reports/delta.py +17 -5
- bobframes-0.2.0/bobframes/reports/design_tokens.toml +127 -0
- bobframes-0.2.0/bobframes/reports/discovery.py +231 -0
- bobframes-0.2.0/bobframes/reports/draws_by_class.py +202 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/reports/formatters.py +38 -15
- bobframes-0.2.0/bobframes/reports/instancing_opportunities.py +355 -0
- bobframes-0.2.0/bobframes/reports/orchestrator.py +79 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/reports/overdraw.py +136 -70
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/reports/pass_gpu.py +95 -42
- bobframes-0.2.0/bobframes/reports/preview.py +154 -0
- bobframes-0.2.0/bobframes/reports/shader_hotlist.py +377 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/reports/trend_table.py +101 -53
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/run.py +125 -50
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/schemas.py +64 -33
- bobframes-0.2.0/bobframes/tests/_render_util.py +202 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_pagedata/catalog.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/draws_by_class.html +844 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/buffers.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/clears.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/counters_per_event.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/descriptor_access.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/dispatches.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/draw_bindings.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/draws.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/events.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/fbos.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/frame_totals.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/ibo_samples.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/passes.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/pixel_history.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/post_vs_samples.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/program_transitions.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/programs.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/render_targets.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/resource_creation.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/rt_event_timeline.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/samplers.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/shaders.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/state_change_events.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/texture_samples.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/textures.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/vbo_samples.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/_pagedata/vertex_inputs.js +1 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/index.html +2064 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/index.html +787 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/instancing_opportunities.html +789 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/overdraw.html +798 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/pass_gpu.html +799 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/draws_by_class.html +843 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/index.html +788 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/instancing_opportunities.html +787 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/overdraw.html +797 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/pass_gpu.html +796 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/run/2026-05-27_r110565/shader_hotlist.html +787 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/shader_hotlist.html +789 -0
- bobframes-0.2.0/bobframes/tests/data/golden/_reports/trend_table.html +920 -0
- bobframes-0.2.0/bobframes/tests/data/golden/index.html +1681 -0
- bobframes-0.2.0/bobframes/tests/data/golden_parquet/digests.json +4966 -0
- bobframes-0.2.0/bobframes/tests/data/golden_preview/_chrome_preview.html +804 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/_manifest.json +11 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/_manifest.json +11 -0
- bobframes-0.2.0/bobframes/tests/make_golden.py +44 -0
- bobframes-0.2.0/bobframes/tests/make_parquet_golden.py +37 -0
- bobframes-0.2.0/bobframes/tests/make_preview_golden.py +23 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/make_synthetic.py +20 -3
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/smoke.py +4 -4
- bobframes-0.2.0/bobframes/tests/test_cache.py +68 -0
- bobframes-0.2.0/bobframes/tests/test_charts.py +147 -0
- bobframes-0.2.0/bobframes/tests/test_classifier.py +189 -0
- bobframes-0.2.0/bobframes/tests/test_config.py +293 -0
- bobframes-0.2.0/bobframes/tests/test_delta.py +33 -0
- bobframes-0.2.0/bobframes/tests/test_design_tokens.py +265 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/test_discovery.py +2 -1
- bobframes-0.2.0/bobframes/tests/test_fonts.py +56 -0
- bobframes-0.2.0/bobframes/tests/test_hardening.py +278 -0
- bobframes-0.2.0/bobframes/tests/test_manifest_guard.py +68 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/test_parity.py +12 -0
- bobframes-0.2.0/bobframes/tests/test_parquet_parity.py +36 -0
- bobframes-0.2.0/bobframes/tests/test_parquetize.py +107 -0
- bobframes-0.2.0/bobframes/tests/test_report_polish.py +102 -0
- bobframes-0.2.0/bobframes/tests/test_report_structure.py +521 -0
- bobframes-0.2.0/bobframes/tests/test_run_model.py +241 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/test_schemas_unit.py +7 -5
- {bobframes-0.1.0 → bobframes-0.2.0}/pyproject.toml +3 -0
- bobframes-0.1.0/bobframes/_version.py +0 -1
- bobframes-0.1.0/bobframes/reports/_dashboard.py +0 -425
- bobframes-0.1.0/bobframes/reports/chrome.py +0 -1306
- bobframes-0.1.0/bobframes/reports/discovery.py +0 -118
- bobframes-0.1.0/bobframes/reports/draws_by_class.py +0 -165
- bobframes-0.1.0/bobframes/reports/instancing_opportunities.py +0 -276
- bobframes-0.1.0/bobframes/reports/orchestrator.py +0 -59
- bobframes-0.1.0/bobframes/reports/shader_hotlist.py +0 -240
- bobframes-0.1.0/bobframes/tests/__init__.py +0 -0
- bobframes-0.1.0/bobframes/tests/_render_util.py +0 -84
- bobframes-0.1.0/bobframes/tests/data/golden/_reports/draws_by_class.html +0 -323
- bobframes-0.1.0/bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/index.html +0 -1560
- bobframes-0.1.0/bobframes/tests/data/golden/_reports/index.html +0 -264
- bobframes-0.1.0/bobframes/tests/data/golden/_reports/instancing_opportunities.html +0 -266
- bobframes-0.1.0/bobframes/tests/data/golden/_reports/overdraw.html +0 -275
- bobframes-0.1.0/bobframes/tests/data/golden/_reports/pass_gpu.html +0 -277
- bobframes-0.1.0/bobframes/tests/data/golden/_reports/shader_hotlist.html +0 -265
- bobframes-0.1.0/bobframes/tests/data/golden/_reports/trend_table.html +0 -390
- bobframes-0.1.0/bobframes/tests/data/golden/index.html +0 -1175
- bobframes-0.1.0/bobframes/tests/test_hardening.py +0 -142
- {bobframes-0.1.0 → bobframes-0.2.0}/.gitignore +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/LICENSE +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/__init__.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/derives/__init__.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/derives/texture_usage.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/html/__init__.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/parsers/__init__.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/parsers/derive_program_transitions.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/probes/__init__.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/probes/whatif.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/query_examples.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/replay/__init__.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/resource_labels.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/stable_keys.py +0 -0
- {bobframes-0.1.0/bobframes/reports → bobframes-0.2.0/bobframes/tests}/__init__.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/buffers.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/clears.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/counters_per_event.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/descriptor_access.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/dispatches.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/draw_bindings.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/draws.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/events.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/fbos.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/frame_totals.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/ibo_samples.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/indirect_args.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/passes.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/pixel_history.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/post_vs_samples.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/program_transitions.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/programs.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/render_targets.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/resource_creation.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/rt_event_timeline.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/samplers.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/shaders.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/state_change_events.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/texture_samples.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/textures.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/vbo_samples.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/vertex_inputs.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/buffers.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/clears.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/counters_per_event.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/descriptor_access.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/dispatches.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/draw_bindings.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/draws.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/events.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/fbos.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/frame_totals.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/ibo_samples.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/indirect_args.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/passes.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/pixel_history.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/post_vs_samples.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/program_transitions.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/programs.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/render_targets.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/resource_creation.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/rt_event_timeline.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/samplers.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/shaders.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/state_change_events.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/texture_samples.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/textures.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/vbo_samples.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/vertex_inputs.parquet +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/test_determinism.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/test_perf.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/test_replay_drift.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/test_schemas.py +0 -0
- {bobframes-0.1.0 → bobframes-0.2.0}/bobframes/tests/test_stable_keys.py +0 -0
|
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-06-03
|
|
11
|
+
|
|
12
|
+
De-hardcoding + a full report overhaul. No schema change (still schema 3); extraction output is stable
|
|
13
|
+
where the pipeline is unchanged, so parquet digests are byte-identical to 0.1.0 on the same captures.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Inline-SVG chart toolkit (`reports/charts.py`): a flagship chart per report (pass-GPU treemap,
|
|
17
|
+
draws-by-class donut, shader complexity-vs-cost scatter, overdraw reject-rate bars, instancing
|
|
18
|
+
wasted-index bars, per-KPI trend lines), deterministic and dependency-free (no `random`/`Date`).
|
|
19
|
+
- Report design language: hero KPI strip, callouts with config-driven severity, provenance/device
|
|
20
|
+
strip, section cards, sticky section headers, copy buttons on long IDs, vendored Inter subset font
|
|
21
|
+
(offline, base64-inlined), and an auto-tint heatmap on ranked numeric columns.
|
|
22
|
+
- Run model: per-run truth (each report names "run N of M"), a run picker, A/B compare, and a trend
|
|
23
|
+
table across runs; older runs are pre-rendered up to a configurable limit.
|
|
24
|
+
- One unified `rdc-table` engine (progressive enhancement, two modes): server-baked `static` for reports
|
|
25
|
+
(JS-off / print / Ctrl-F / golden all hold) and windowed `virtual` for the catalog + per-drop drill.
|
|
26
|
+
Shared natural-numeric sort, type-split, heatmap, column groups, search/filter, controllable cell
|
|
27
|
+
truncation with hover-reveal, and full sort/filter a11y (aria-sort + keyboard-operable headers + a
|
|
28
|
+
labelled filter input) across both modes.
|
|
29
|
+
- TOML config (`.bobframes.toml`, `[report]`/`[pipeline]` sections), `design_tokens.toml` token system,
|
|
30
|
+
and a state-capable draw classifier driven by table columns.
|
|
31
|
+
- Reports cache with a SHA256 sidecar (corrupt/missing/mismatch falls back to a live scan, never
|
|
32
|
+
silent-empty); manifest schema-version guard (`ingest --force` hint on mismatch).
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- Catalog + drill readability: roomier rows, type-split mono/sans, and the heavy per-table row data moved
|
|
36
|
+
out of the HTML into `_pagedata/*.js` companions (the ~21 MB time-to-interactive fix) loaded via a
|
|
37
|
+
file://-safe `<script defer src>`.
|
|
38
|
+
- External tool resolution + clearer pipeline error messages and exit codes; environment variables
|
|
39
|
+
renamed `RDC_*` -> `BOBFRAMES_*` (one-release legacy fallback with a deprecation warning).
|
|
40
|
+
- Project paths, the report registry, and the drill-size budget are configurable rather than hardcoded.
|
|
41
|
+
|
|
10
42
|
## [0.1.0] - 2026-05-31
|
|
11
43
|
|
|
12
44
|
First standalone release. v1 is Windows-only (the replay stage drives `qrenderdoc`).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bobframes
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
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
|
|
@@ -21,6 +21,7 @@ Classifier: Topic :: Multimedia :: Graphics
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Debuggers
|
|
22
22
|
Requires-Python: <3.15,>=3.10
|
|
23
23
|
Requires-Dist: pyarrow<22,>=17
|
|
24
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
24
25
|
Provides-Extra: dev
|
|
25
26
|
Requires-Dist: build; extra == 'dev'
|
|
26
27
|
Requires-Dist: hatchling; extra == 'dev'
|
|
@@ -65,19 +66,42 @@ time to rebuild the HTML from existing Parquet without re-replaying captures.
|
|
|
65
66
|
| Command | Purpose |
|
|
66
67
|
|---|---|
|
|
67
68
|
| `ingest [root] [--area X] [--label Y] [--capture N] [--force] [--pixel-grid 4] [--render-only]` | Full pipeline: export, parse, replay, parquetize, derive, manifest, commit, catalog, render. |
|
|
68
|
-
| `render [root] [--area X] [--label Y]` | Rebuild HTML and catalog from existing Parquet. |
|
|
69
|
+
| `render [root] [--area X] [--label Y] [--watch]` | Rebuild HTML and catalog from existing Parquet. `--watch` re-renders on token or chrome edits (alpha). |
|
|
69
70
|
| `ab [root] --baseline-label X --compare-label Y` | All reports for one drop pair under `_reports/ab/<pair>/`. |
|
|
70
71
|
| `report [root] <name>` | Build one named report (draws-by-class, trend, instancing, pass-gpu, shader, overdraw, dashboard). |
|
|
71
72
|
| `catalog [root]` | Rebuild `_data/_catalog.parquet` only. |
|
|
72
73
|
| `lint <file>...` | Check HTML or markdown against the banlist. |
|
|
73
74
|
| `check` | Print resolved tool paths; non-zero when a tool is missing. |
|
|
74
75
|
| `serve [root] [--port 8000] [--bind 127.0.0.1]` | Static preview via the stdlib HTTP server. |
|
|
76
|
+
| `preview [root]` | Render the chrome gallery to `_reports/_chrome_preview.html`; no capture data needed. |
|
|
77
|
+
| `export-tokens [--format toml\|json\|css]` | Print the design tokens to stdout in the chosen format. |
|
|
75
78
|
| `smoke [--data DIR]` | End-to-end check; render-only against the bundled fixture when `--data` is omitted. |
|
|
76
79
|
| `version` | Print `bobframes`, schema, and pyarrow versions. |
|
|
77
80
|
|
|
78
81
|
`<root>` is positional and defaults to `.`. Flags are long-form only. Exit codes: `0` success,
|
|
79
82
|
`1` pipeline or build failure, `2` usage error, `3` external tool missing, `4` interrupted.
|
|
80
83
|
|
|
84
|
+
## Customizing reports
|
|
85
|
+
|
|
86
|
+
The report palette lives in `bobframes/reports/design_tokens.toml` (colors, spacing, type, motion,
|
|
87
|
+
and base layout sizes). A designer can edit values there without touching Python. The CSS variable
|
|
88
|
+
name is fixed by its key: `surface_0` becomes `--surface-0`, `accent_primary` becomes
|
|
89
|
+
`--accent-primary`.
|
|
90
|
+
|
|
91
|
+
Workflow:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
bobframes preview # writes _reports/_chrome_preview.html (every primitive, no data)
|
|
95
|
+
# edit a value in design_tokens.toml
|
|
96
|
+
bobframes preview # under a second; reload the page in a browser
|
|
97
|
+
bobframes render C:\captures # apply the change to real reports from existing Parquet
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`bobframes render --watch` re-runs render-only whenever the token file or a chrome module changes
|
|
101
|
+
(alpha; a 500ms poll, no extra dependency). `bobframes export-tokens --format css` prints the live
|
|
102
|
+
`:root` block; `--format json` prints the nested table; `--format toml` prints the file itself. For
|
|
103
|
+
prototyping, a browser's dev tools can live-edit the same variables before you commit a value.
|
|
104
|
+
|
|
81
105
|
## External tools
|
|
82
106
|
|
|
83
107
|
The export stage runs `renderdoccmd convert`; the replay stage runs `qrenderdoc --python`. v1 looks
|
|
@@ -35,19 +35,42 @@ time to rebuild the HTML from existing Parquet without re-replaying captures.
|
|
|
35
35
|
| Command | Purpose |
|
|
36
36
|
|---|---|
|
|
37
37
|
| `ingest [root] [--area X] [--label Y] [--capture N] [--force] [--pixel-grid 4] [--render-only]` | Full pipeline: export, parse, replay, parquetize, derive, manifest, commit, catalog, render. |
|
|
38
|
-
| `render [root] [--area X] [--label Y]` | Rebuild HTML and catalog from existing Parquet. |
|
|
38
|
+
| `render [root] [--area X] [--label Y] [--watch]` | Rebuild HTML and catalog from existing Parquet. `--watch` re-renders on token or chrome edits (alpha). |
|
|
39
39
|
| `ab [root] --baseline-label X --compare-label Y` | All reports for one drop pair under `_reports/ab/<pair>/`. |
|
|
40
40
|
| `report [root] <name>` | Build one named report (draws-by-class, trend, instancing, pass-gpu, shader, overdraw, dashboard). |
|
|
41
41
|
| `catalog [root]` | Rebuild `_data/_catalog.parquet` only. |
|
|
42
42
|
| `lint <file>...` | Check HTML or markdown against the banlist. |
|
|
43
43
|
| `check` | Print resolved tool paths; non-zero when a tool is missing. |
|
|
44
44
|
| `serve [root] [--port 8000] [--bind 127.0.0.1]` | Static preview via the stdlib HTTP server. |
|
|
45
|
+
| `preview [root]` | Render the chrome gallery to `_reports/_chrome_preview.html`; no capture data needed. |
|
|
46
|
+
| `export-tokens [--format toml\|json\|css]` | Print the design tokens to stdout in the chosen format. |
|
|
45
47
|
| `smoke [--data DIR]` | End-to-end check; render-only against the bundled fixture when `--data` is omitted. |
|
|
46
48
|
| `version` | Print `bobframes`, schema, and pyarrow versions. |
|
|
47
49
|
|
|
48
50
|
`<root>` is positional and defaults to `.`. Flags are long-form only. Exit codes: `0` success,
|
|
49
51
|
`1` pipeline or build failure, `2` usage error, `3` external tool missing, `4` interrupted.
|
|
50
52
|
|
|
53
|
+
## Customizing reports
|
|
54
|
+
|
|
55
|
+
The report palette lives in `bobframes/reports/design_tokens.toml` (colors, spacing, type, motion,
|
|
56
|
+
and base layout sizes). A designer can edit values there without touching Python. The CSS variable
|
|
57
|
+
name is fixed by its key: `surface_0` becomes `--surface-0`, `accent_primary` becomes
|
|
58
|
+
`--accent-primary`.
|
|
59
|
+
|
|
60
|
+
Workflow:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
bobframes preview # writes _reports/_chrome_preview.html (every primitive, no data)
|
|
64
|
+
# edit a value in design_tokens.toml
|
|
65
|
+
bobframes preview # under a second; reload the page in a browser
|
|
66
|
+
bobframes render C:\captures # apply the change to real reports from existing Parquet
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`bobframes render --watch` re-runs render-only whenever the token file or a chrome module changes
|
|
70
|
+
(alpha; a 500ms poll, no extra dependency). `bobframes export-tokens --format css` prints the live
|
|
71
|
+
`:root` block; `--format json` prints the nested table; `--format toml` prints the file itself. For
|
|
72
|
+
prototyping, a browser's dev tools can live-edit the same variables before you commit a value.
|
|
73
|
+
|
|
51
74
|
## External tools
|
|
52
75
|
|
|
53
76
|
The export stage runs `renderdoccmd convert`; the replay stage runs `qrenderdoc --python`. v1 looks
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# bobframes bundled default config (c07). Single source of truth for de-hardcoded literals.
|
|
2
|
+
# Values here reproduce today's output byte-identically (ADR-6). A user .bobframes.toml is
|
|
3
|
+
# deep-merged ON TOP of this (override one key without restating the rest — ADR-25).
|
|
4
|
+
#
|
|
5
|
+
# Regex / format-string values MUST be TOML *literal* (single-quoted) strings: a basic
|
|
6
|
+
# (double-quoted) string interprets backslash escapes and breaks on \d. Do NOT "fix" a decode
|
|
7
|
+
# error by double-escaping — that silently changes the pattern (ADR-23 trap).
|
|
8
|
+
|
|
9
|
+
schema_version = 1
|
|
10
|
+
|
|
11
|
+
[tools]
|
|
12
|
+
# renderdoccmd = "..." # unset by default; resolve_tool falls through to PATH / known paths
|
|
13
|
+
# qrenderdoc = "..."
|
|
14
|
+
|
|
15
|
+
[pipeline]
|
|
16
|
+
replay_timeout_s = 600.0 # H-12 — per-capture qrenderdoc replay budget (seconds)
|
|
17
|
+
convert_timeout_s = 120.0 # H-13 — per-file renderdoccmd convert budget (seconds)
|
|
18
|
+
# workers / pixel_grid / keep_stage are §6 keys but NOT wired in c07 (argparse remains the
|
|
19
|
+
# source; workers default is min(4, os.cpu_count()), so wiring it would change behavior).
|
|
20
|
+
|
|
21
|
+
[discovery]
|
|
22
|
+
dated_re = '^(\d{4}-\d{2}-\d{2})(?:_(.*))?$' # H-30 — dated drop-folder pattern
|
|
23
|
+
|
|
24
|
+
[formatting]
|
|
25
|
+
id_short_n = 12 # H-23 — id/hash truncation length
|
|
26
|
+
text_trunc_max = 60 # H-23 — mid/left text truncation length
|
|
27
|
+
chrome_scrub_chars = '[—–…“”‘’→←↑↓×·]' # H-16 — chars scrubbed from rendered chrome
|
|
28
|
+
|
|
29
|
+
[delta]
|
|
30
|
+
fmt = '{:+,.0f}' # H-22 — default signed delta format
|
|
31
|
+
bar_label_min_pct = 8.0 # H-21 — min segment % to draw an inline label
|
|
32
|
+
|
|
33
|
+
[scoring.complexity] # H-17 / Q-3 — shader complexity_score weights
|
|
34
|
+
w_texture_samples = 2.0
|
|
35
|
+
w_branches = 0.5
|
|
36
|
+
w_loops = 2.0
|
|
37
|
+
w_discards = 0.3
|
|
38
|
+
w_dfdx_dfdy = 0.5
|
|
39
|
+
w_mat4 = 0.3
|
|
40
|
+
src_len_divisor = 100.0
|
|
41
|
+
src_len_cap = 50.0
|
|
42
|
+
|
|
43
|
+
[lint]
|
|
44
|
+
# User-appended banned tokens, same shape as lint_banlist.toml [[banned]] entries:
|
|
45
|
+
# [[lint.extra_banned]]
|
|
46
|
+
# pattern = 'foo'
|
|
47
|
+
# label = 'why'
|
|
48
|
+
# flags = ['I']
|
|
49
|
+
extra_banned = []
|
|
50
|
+
|
|
51
|
+
[classifier] # c09 — draw-classification preset (H-1..H-5)
|
|
52
|
+
preset = "ue" # draw_classifier.toml (UE default); "unity"/"godot"/... -> presets/<name>.toml
|
|
53
|
+
# custom_path = "C:/path/to/my_engine.toml" # absolute path to a custom preset (wins over `preset`)
|
|
54
|
+
|
|
55
|
+
[report] # c16 — thresholds that drive report callout severity. Tune per project.
|
|
56
|
+
shader_complexity_high = 60.0 # fragment shaders at/above this complexity are flagged
|
|
57
|
+
overdraw_reject_warn_pct = 40.0 # RT sample-rejection % -> warn callout
|
|
58
|
+
overdraw_reject_alarm_pct = 70.0 # RT sample-rejection % -> alarm callout
|
|
59
|
+
instancing_repeat_min = 4 # min mesh repeat to count as an instancing candidate
|
|
60
|
+
gpu_regression_pct = 10.0 # latest-vs-previous GPU increase % -> regression callout
|
|
61
|
+
max_prerendered_runs = 10 # c16f — cap on pre-rendered older-run pages (run selector)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
@@ -21,22 +21,12 @@ import pyarrow as pa
|
|
|
21
21
|
import pyarrow.csv as pacsv
|
|
22
22
|
import pyarrow.parquet as papq
|
|
23
23
|
|
|
24
|
-
from . import paths as _paths
|
|
24
|
+
from . import manifest as _manifest, paths as _paths, schemas
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'samplers', 'fbos', 'state_change_events',
|
|
31
|
-
'counters_per_event', 'descriptor_access',
|
|
32
|
-
'passes', 'frame_totals',
|
|
33
|
-
'clears', 'dispatches', 'rt_event_timeline',
|
|
34
|
-
'vertex_inputs', 'resource_creation',
|
|
35
|
-
'draw_bindings', 'program_transitions',
|
|
36
|
-
'pixel_history', 'vbo_samples', 'ibo_samples',
|
|
37
|
-
'post_vs_samples', 'texture_samples', 'indirect_args',
|
|
38
|
-
'pass_class_breakdown', 'texture_usage',
|
|
39
|
-
]
|
|
27
|
+
# Derived from the single registry (H-10). schemas.TABLES key order IS the catalog order, so the
|
|
28
|
+
# row_count_* column order baked into the golden root index.html stays byte-identical.
|
|
29
|
+
_CATALOG_TABLE_KEYS = tuple(schemas.TABLES.keys())
|
|
40
30
|
|
|
41
31
|
|
|
42
32
|
def _find_manifests(root: str) -> list[tuple[str, str, dict]]:
|
|
@@ -55,7 +45,7 @@ def _find_manifests(root: str) -> list[tuple[str, str, dict]]:
|
|
|
55
45
|
drop_dir = os.path.join(area_dir, drop_entry)
|
|
56
46
|
if not os.path.isdir(drop_dir):
|
|
57
47
|
continue
|
|
58
|
-
mf = os.path.join(drop_dir,
|
|
48
|
+
mf = os.path.join(drop_dir, _paths.MANIFEST_NAME)
|
|
59
49
|
if not os.path.exists(mf):
|
|
60
50
|
continue
|
|
61
51
|
try:
|
|
@@ -113,6 +103,9 @@ def build_catalog(root: str) -> dict:
|
|
|
113
103
|
manifests = _find_manifests(root)
|
|
114
104
|
all_rows: list[dict] = []
|
|
115
105
|
for data_dir, rel_path, m in manifests:
|
|
106
|
+
# D-7: refuse to (re)build over a drop written under a different SCHEMA_VERSION. This is the
|
|
107
|
+
# shared chokepoint for `render` (which rebuilds the catalog first) and `catalog`.
|
|
108
|
+
_manifest.check_schema_version(m, source=rel_path)
|
|
116
109
|
all_rows.extend(_capture_rows(data_dir, rel_path, m))
|
|
117
110
|
|
|
118
111
|
cols = [
|
|
@@ -17,6 +17,8 @@ import logging
|
|
|
17
17
|
import os
|
|
18
18
|
import sys
|
|
19
19
|
|
|
20
|
+
from .errors import BobFramesError
|
|
21
|
+
|
|
20
22
|
# CLI report name -> reports submodule. `build(root)` on each builds one report.
|
|
21
23
|
_REPORTS = {
|
|
22
24
|
'draws-by-class': 'draws_by_class',
|
|
@@ -25,7 +27,7 @@ _REPORTS = {
|
|
|
25
27
|
'pass-gpu': 'pass_gpu',
|
|
26
28
|
'shader': 'shader_hotlist',
|
|
27
29
|
'overdraw': 'overdraw',
|
|
28
|
-
'dashboard': '
|
|
30
|
+
'dashboard': 'dashboard',
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
|
|
@@ -68,19 +70,80 @@ def _cmd_ingest(args: argparse.Namespace) -> int:
|
|
|
68
70
|
if args.render_only:
|
|
69
71
|
rargv += ['--render-only']
|
|
70
72
|
rargv += ['--workers', str(args.workers), '--pixel-grid', str(args.pixel_grid)]
|
|
73
|
+
if args.replay_timeout is not None:
|
|
74
|
+
rargv += ['--replay-timeout', str(args.replay_timeout)]
|
|
75
|
+
if args.convert_timeout is not None:
|
|
76
|
+
rargv += ['--convert-timeout', str(args.convert_timeout)]
|
|
71
77
|
return run.main(rargv)
|
|
72
78
|
|
|
73
79
|
|
|
74
80
|
def _cmd_render(args: argparse.Namespace) -> int:
|
|
75
81
|
from . import run
|
|
76
|
-
|
|
82
|
+
root = os.path.abspath(args.root)
|
|
83
|
+
rargv = ['--root', root, '--render-only']
|
|
77
84
|
if args.area:
|
|
78
85
|
rargv += ['--area', args.area]
|
|
79
86
|
if args.label:
|
|
80
87
|
rargv += ['--label', args.label]
|
|
88
|
+
if getattr(args, 'watch', False):
|
|
89
|
+
return _render_watch(rargv)
|
|
81
90
|
return run.main(rargv)
|
|
82
91
|
|
|
83
92
|
|
|
93
|
+
def _watch_paths() -> list[str]:
|
|
94
|
+
"""Files the render --watch loop polls: the design tokens + the modules that emit chrome."""
|
|
95
|
+
from .reports import chrome as _c, delta as _d, formatters as _fm
|
|
96
|
+
tokens = os.path.join(os.path.dirname(_c.__file__), 'design_tokens.toml')
|
|
97
|
+
return [tokens, _c.__file__, _fm.__file__, _d.__file__]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _render_watch(rargv: list[str]) -> int:
|
|
101
|
+
"""Alpha hot-reload (DESIGNER Track A): 500ms mtime poll on the token/chrome files; re-render in a
|
|
102
|
+
fresh subprocess on change so edited modules/TOML are reloaded. No watchdog dependency."""
|
|
103
|
+
import subprocess
|
|
104
|
+
import time
|
|
105
|
+
log = logging.getLogger('bobframes')
|
|
106
|
+
watched = _watch_paths()
|
|
107
|
+
|
|
108
|
+
def snapshot() -> dict:
|
|
109
|
+
return {p: (os.stat(p).st_mtime if os.path.exists(p) else 0.0) for p in watched}
|
|
110
|
+
|
|
111
|
+
cmd = [sys.executable, '-m', 'bobframes.run', *rargv]
|
|
112
|
+
rc = subprocess.run(cmd).returncode
|
|
113
|
+
log.info(f'watching {len(watched)} files; save an edit to re-render (Ctrl+C to stop)')
|
|
114
|
+
last = snapshot()
|
|
115
|
+
try:
|
|
116
|
+
while True:
|
|
117
|
+
time.sleep(0.5)
|
|
118
|
+
now = snapshot()
|
|
119
|
+
if now != last:
|
|
120
|
+
last = now
|
|
121
|
+
log.info('change detected; re-rendering')
|
|
122
|
+
rc = subprocess.run(cmd).returncode
|
|
123
|
+
except KeyboardInterrupt:
|
|
124
|
+
return 4
|
|
125
|
+
return rc
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _cmd_preview(args: argparse.Namespace) -> int:
|
|
129
|
+
from .reports import preview
|
|
130
|
+
out = preview.build(os.path.abspath(args.root))
|
|
131
|
+
print(f'wrote {out}')
|
|
132
|
+
return 0
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _cmd_export_tokens(args: argparse.Namespace) -> int:
|
|
136
|
+
import json
|
|
137
|
+
from .reports import _tokens, chrome
|
|
138
|
+
if args.format == 'toml':
|
|
139
|
+
sys.stdout.write(_tokens.tokens_toml_text())
|
|
140
|
+
elif args.format == 'json':
|
|
141
|
+
print(json.dumps(_tokens.load_tokens(), indent=2))
|
|
142
|
+
else: # css
|
|
143
|
+
sys.stdout.write(chrome.design_tokens_css())
|
|
144
|
+
return 0
|
|
145
|
+
|
|
146
|
+
|
|
84
147
|
def _cmd_ab(args: argparse.Namespace) -> int:
|
|
85
148
|
from .reports import ab
|
|
86
149
|
argv = [os.path.abspath(args.root),
|
|
@@ -130,20 +193,24 @@ def _cmd_check(args: argparse.Namespace) -> int:
|
|
|
130
193
|
print('bobframes v1 is Windows-only (qrenderdoc replay requirement). '
|
|
131
194
|
'Track GH issue for Linux/macOS support.', file=sys.stderr)
|
|
132
195
|
return 3
|
|
196
|
+
from . import config
|
|
133
197
|
if args.write_config:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return
|
|
137
|
-
from . import
|
|
198
|
+
path, written = config.write_config_stub(os.path.abspath('.'))
|
|
199
|
+
print(f'wrote {path}' if written else f'{path} already exists; left unchanged')
|
|
200
|
+
return 0
|
|
201
|
+
from .errors import EXIT_TOOL_MISSING, ToolNotFound
|
|
138
202
|
missing = False
|
|
139
|
-
for name
|
|
140
|
-
('qrenderdoc', qrd_harness.find_qrenderdoc)):
|
|
203
|
+
for name in ('renderdoccmd', 'qrenderdoc'):
|
|
141
204
|
try:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
205
|
+
path, source = config.resolve_tool_verbose(name)
|
|
206
|
+
# `source` is the winning step's description; for env/config/PATH it explains the
|
|
207
|
+
# precedence, but for a known-path hit it IS the path -> don't print it twice.
|
|
208
|
+
suffix = '' if source == path else f' (via {source})'
|
|
209
|
+
print(f'{name}: {path}{suffix}')
|
|
210
|
+
except ToolNotFound as e:
|
|
211
|
+
print(str(e), file=sys.stderr)
|
|
145
212
|
missing = True
|
|
146
|
-
return
|
|
213
|
+
return EXIT_TOOL_MISSING if missing else 0
|
|
147
214
|
|
|
148
215
|
|
|
149
216
|
def _cmd_serve(args: argparse.Namespace) -> int:
|
|
@@ -191,6 +258,10 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
191
258
|
sp.add_argument('--render-only', action='store_true')
|
|
192
259
|
sp.add_argument('--workers', type=int, default=min(4, os.cpu_count() or 4))
|
|
193
260
|
sp.add_argument('--pixel-grid', type=int, default=4)
|
|
261
|
+
sp.add_argument('--replay-timeout', type=float, default=None,
|
|
262
|
+
help='per-capture qrenderdoc replay budget (s); overrides config')
|
|
263
|
+
sp.add_argument('--convert-timeout', type=float, default=None,
|
|
264
|
+
help='per-file renderdoccmd convert budget (s); overrides config')
|
|
194
265
|
sp.set_defaults(func=_cmd_ingest)
|
|
195
266
|
|
|
196
267
|
sp = sub.add_parser('render', parents=[common],
|
|
@@ -198,6 +269,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
198
269
|
sp.add_argument('root', nargs='?', default='.')
|
|
199
270
|
sp.add_argument('--area')
|
|
200
271
|
sp.add_argument('--label')
|
|
272
|
+
sp.add_argument('--watch', action='store_true',
|
|
273
|
+
help='re-render on design_tokens.toml / chrome edits (alpha, 500ms mtime poll)')
|
|
201
274
|
sp.set_defaults(func=_cmd_render)
|
|
202
275
|
|
|
203
276
|
sp = sub.add_parser('ab', parents=[common], help='all reports for one drop pair')
|
|
@@ -222,12 +295,23 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
222
295
|
sp.set_defaults(func=_cmd_lint)
|
|
223
296
|
|
|
224
297
|
sp = sub.add_parser('check', parents=[common], help='resolve external tool paths')
|
|
225
|
-
sp.add_argument('--write-config', action='store_true',
|
|
298
|
+
sp.add_argument('--write-config', action='store_true',
|
|
299
|
+
help='write a starter .bobframes.toml to the current dir (skip if present)')
|
|
226
300
|
sp.set_defaults(func=_cmd_check)
|
|
227
301
|
|
|
228
302
|
sp = sub.add_parser('version', parents=[common], help='print version, schema, pyarrow')
|
|
229
303
|
sp.set_defaults(func=_cmd_version)
|
|
230
304
|
|
|
305
|
+
sp = sub.add_parser('preview', parents=[common],
|
|
306
|
+
help='render the chrome preview gallery (_reports/_chrome_preview.html; no data)')
|
|
307
|
+
sp.add_argument('root', nargs='?', default='.')
|
|
308
|
+
sp.set_defaults(func=_cmd_preview)
|
|
309
|
+
|
|
310
|
+
sp = sub.add_parser('export-tokens', parents=[common],
|
|
311
|
+
help='print design tokens to stdout as toml|json|css')
|
|
312
|
+
sp.add_argument('--format', choices=['toml', 'json', 'css'], default='toml')
|
|
313
|
+
sp.set_defaults(func=_cmd_export_tokens)
|
|
314
|
+
|
|
231
315
|
sp = sub.add_parser('serve', parents=[common], help='static preview server')
|
|
232
316
|
sp.add_argument('root', nargs='?', default='.')
|
|
233
317
|
sp.add_argument('--port', type=int, default=8000)
|
|
@@ -256,10 +340,22 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
256
340
|
|
|
257
341
|
_configure_logging(getattr(args, 'verbose', False))
|
|
258
342
|
try:
|
|
343
|
+
# Load the config singleton against the verb's root so <root>/.bobframes.toml is honored
|
|
344
|
+
# (§6) for report/catalog/ab/render/ingest/serve. Verbs without a root (version/check/lint)
|
|
345
|
+
# lazily load bundled defaults (cwd) on first get_config(). ingest/render re-load via
|
|
346
|
+
# run.main (idempotent). ConfigError surfaces as a typed BobFramesError below.
|
|
347
|
+
root = getattr(args, 'root', None)
|
|
348
|
+
if root is not None:
|
|
349
|
+
from . import config
|
|
350
|
+
config.load_config(os.path.abspath(root))
|
|
259
351
|
return args.func(args)
|
|
260
352
|
except KeyboardInterrupt:
|
|
261
353
|
print('interrupted', file=sys.stderr)
|
|
262
354
|
return 4
|
|
355
|
+
except BobFramesError as e:
|
|
356
|
+
# Typed pipeline/tool errors carry their own exit code (errors.py / ARCHITECTURE §4).
|
|
357
|
+
print(str(e), file=sys.stderr)
|
|
358
|
+
return e.exit_code
|
|
263
359
|
|
|
264
360
|
|
|
265
361
|
if __name__ == '__main__':
|