bobframes 0.1.0__py3-none-any.whl
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/__init__.py +3 -0
- bobframes/_version.py +1 -0
- bobframes/catalog.py +154 -0
- bobframes/cli.py +266 -0
- bobframes/derive_post_merge.py +365 -0
- bobframes/derives/__init__.py +0 -0
- bobframes/derives/pass_class_breakdown.py +102 -0
- bobframes/derives/texture_usage.py +121 -0
- bobframes/discovery.py +132 -0
- bobframes/global_entities.py +99 -0
- bobframes/html/__init__.py +0 -0
- bobframes/html/template.py +1056 -0
- bobframes/lint.py +114 -0
- bobframes/manifest.py +127 -0
- bobframes/parquetize.py +282 -0
- bobframes/parsers/__init__.py +0 -0
- bobframes/parsers/derive_program_transitions.py +73 -0
- bobframes/parsers/parse_init_state.py +675 -0
- bobframes/paths.py +111 -0
- bobframes/probes/__init__.py +0 -0
- bobframes/probes/whatif.py +165 -0
- bobframes/qrd_harness.py +119 -0
- bobframes/query_examples.py +222 -0
- bobframes/rdcmd.py +72 -0
- bobframes/replay/__init__.py +26 -0
- bobframes/replay/replay_main.py +2305 -0
- bobframes/reports/__init__.py +0 -0
- bobframes/reports/_dashboard.py +425 -0
- bobframes/reports/ab.py +88 -0
- bobframes/reports/base.py +114 -0
- bobframes/reports/cache.py +147 -0
- bobframes/reports/chrome.py +1306 -0
- bobframes/reports/cli.py +99 -0
- bobframes/reports/delta.py +167 -0
- bobframes/reports/discovery.py +118 -0
- bobframes/reports/draws_by_class.py +165 -0
- bobframes/reports/formatters.py +122 -0
- bobframes/reports/instancing_opportunities.py +276 -0
- bobframes/reports/orchestrator.py +59 -0
- bobframes/reports/overdraw.py +293 -0
- bobframes/reports/pass_gpu.py +190 -0
- bobframes/reports/shader_hotlist.py +240 -0
- bobframes/reports/trend_table.py +444 -0
- bobframes/resource_labels.py +162 -0
- bobframes/run.py +480 -0
- bobframes/schemas.py +426 -0
- bobframes/stable_keys.py +83 -0
- bobframes/tests/__init__.py +0 -0
- bobframes/tests/_render_util.py +84 -0
- bobframes/tests/data/golden/_reports/draws_by_class.html +323 -0
- bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/index.html +1560 -0
- bobframes/tests/data/golden/_reports/index.html +264 -0
- bobframes/tests/data/golden/_reports/instancing_opportunities.html +266 -0
- bobframes/tests/data/golden/_reports/overdraw.html +275 -0
- bobframes/tests/data/golden/_reports/pass_gpu.html +277 -0
- bobframes/tests/data/golden/_reports/shader_hotlist.html +265 -0
- bobframes/tests/data/golden/_reports/trend_table.html +390 -0
- bobframes/tests/data/golden/index.html +1175 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/_manifest.json +51 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/buffers.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/clears.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/counters_per_event.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/descriptor_access.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/dispatches.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/draw_bindings.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/draws.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/events.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/fbos.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/frame_totals.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/ibo_samples.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/indirect_args.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/passes.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/pixel_history.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/post_vs_samples.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/program_transitions.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/programs.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/render_targets.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/resource_creation.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/rt_event_timeline.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/samplers.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/shaders.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/state_change_events.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/texture_samples.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/textures.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/vbo_samples.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/vertex_inputs.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/_manifest.json +51 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/buffers.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/clears.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/counters_per_event.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/descriptor_access.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/dispatches.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/draw_bindings.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/draws.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/events.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/fbos.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/frame_totals.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/ibo_samples.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/indirect_args.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/passes.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/pixel_history.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/post_vs_samples.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/program_transitions.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/programs.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/render_targets.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/resource_creation.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/rt_event_timeline.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/samplers.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/shaders.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/state_change_events.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/texture_samples.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/textures.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/vbo_samples.parquet +0 -0
- bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/vertex_inputs.parquet +0 -0
- bobframes/tests/make_synthetic.py +171 -0
- bobframes/tests/smoke.py +199 -0
- bobframes/tests/test_determinism.py +19 -0
- bobframes/tests/test_discovery.py +97 -0
- bobframes/tests/test_hardening.py +142 -0
- bobframes/tests/test_parity.py +22 -0
- bobframes/tests/test_perf.py +18 -0
- bobframes/tests/test_replay_drift.py +115 -0
- bobframes/tests/test_schemas.py +26 -0
- bobframes/tests/test_schemas_unit.py +55 -0
- bobframes/tests/test_stable_keys.py +61 -0
- bobframes-0.1.0.dist-info/METADATA +144 -0
- bobframes-0.1.0.dist-info/RECORD +130 -0
- bobframes-0.1.0.dist-info/WHEEL +4 -0
- bobframes-0.1.0.dist-info/entry_points.txt +2 -0
- bobframes-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""Per-drop cache + labels + global-entities loaders."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
import pyarrow as pa
|
|
10
|
+
import pyarrow.parquet as papq
|
|
11
|
+
|
|
12
|
+
from .discovery import DropSet, discover_drops
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@functools.lru_cache(maxsize=64)
|
|
16
|
+
def load_global_entities(root: str):
|
|
17
|
+
p = os.path.join(root, '_global_entities.parquet')
|
|
18
|
+
if not os.path.exists(p):
|
|
19
|
+
return None
|
|
20
|
+
try:
|
|
21
|
+
return papq.read_table(p)
|
|
22
|
+
except Exception:
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@functools.lru_cache(maxsize=64)
|
|
27
|
+
def load_labels(drop_dir: str) -> dict:
|
|
28
|
+
"""Read <drop_dir>/_resource_labels.json. Returns empty dict on miss."""
|
|
29
|
+
p = os.path.join(drop_dir, '_resource_labels.json')
|
|
30
|
+
if not os.path.exists(p):
|
|
31
|
+
return {}
|
|
32
|
+
try:
|
|
33
|
+
with open(p, 'r', encoding='utf-8') as f:
|
|
34
|
+
return json.load(f)
|
|
35
|
+
except Exception:
|
|
36
|
+
return {}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def label_for(drop_dir: str, capture, kind: str, rid) -> str:
|
|
40
|
+
if not rid or rid == 0 or rid == '0':
|
|
41
|
+
return ''
|
|
42
|
+
data = load_labels(drop_dir)
|
|
43
|
+
by_cap = data.get('by_capture', {})
|
|
44
|
+
bucket = by_cap.get(str(capture), {}).get(kind, {})
|
|
45
|
+
return bucket.get(str(rid), '')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def cache_dir(root: str) -> str:
|
|
49
|
+
d = os.path.join(root, '_reports', '_cache')
|
|
50
|
+
os.makedirs(d, exist_ok=True)
|
|
51
|
+
return d
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def cache_path(root: str, table: str) -> str:
|
|
55
|
+
return os.path.join(cache_dir(root), f'{table}_per_drop.parquet')
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _read_drop_parquet(drop: DropSet, table: str, cols: list[str]):
|
|
59
|
+
"""Read one parquet table across all areas of a drop. Returns pyarrow Table or None.
|
|
60
|
+
|
|
61
|
+
drop.rows[i].drop_dir is the per-drop data dir (_data/<area>/<drop>/); parquet
|
|
62
|
+
files sit directly under it.
|
|
63
|
+
"""
|
|
64
|
+
tables = []
|
|
65
|
+
for r in drop.rows:
|
|
66
|
+
p = os.path.join(r.drop_dir, f'{table}.parquet')
|
|
67
|
+
if not os.path.exists(p):
|
|
68
|
+
continue
|
|
69
|
+
try:
|
|
70
|
+
schema_cols = set(papq.read_schema(p).names)
|
|
71
|
+
want = [c for c in cols if c in schema_cols]
|
|
72
|
+
if not want:
|
|
73
|
+
continue
|
|
74
|
+
t = papq.read_table(p, columns=want)
|
|
75
|
+
tables.append(t)
|
|
76
|
+
except Exception:
|
|
77
|
+
continue
|
|
78
|
+
if not tables:
|
|
79
|
+
return None
|
|
80
|
+
try:
|
|
81
|
+
return pa.concat_tables(tables, promote_options='default')
|
|
82
|
+
except TypeError:
|
|
83
|
+
return pa.concat_tables(tables)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def build_per_drop_cache(root: str) -> dict:
|
|
87
|
+
"""Compute heavy per-drop aggregations once.
|
|
88
|
+
|
|
89
|
+
Writes:
|
|
90
|
+
_reports/_cache/draws_summary_per_drop.parquet
|
|
91
|
+
_reports/_cache/shader_summary_per_drop.parquet
|
|
92
|
+
"""
|
|
93
|
+
drops = discover_drops(root)
|
|
94
|
+
out: dict[str, int] = {}
|
|
95
|
+
|
|
96
|
+
draws_rows: list[dict] = []
|
|
97
|
+
for d in drops:
|
|
98
|
+
t = _read_drop_parquet(d, 'draws',
|
|
99
|
+
['area', 'drop_date', 'drop_label', 'capture', 'mesh_hash',
|
|
100
|
+
'program_id', 'vs_shader_id', 'fs_shader_id',
|
|
101
|
+
'parent_pass_path_norm', 'draw_class', 'num_indices', 'num_instances'])
|
|
102
|
+
if t is None or t.num_rows == 0:
|
|
103
|
+
continue
|
|
104
|
+
cols = {c: t.column(c).to_pylist() for c in t.column_names}
|
|
105
|
+
n = t.num_rows
|
|
106
|
+
for i in range(n):
|
|
107
|
+
draws_rows.append({
|
|
108
|
+
'drop_date': d.date,
|
|
109
|
+
'drop_label': d.label,
|
|
110
|
+
'area': cols['area'][i],
|
|
111
|
+
'capture': cols['capture'][i],
|
|
112
|
+
'mesh_hash': cols.get('mesh_hash', [''])[i] if 'mesh_hash' in cols else '',
|
|
113
|
+
'program_id': cols.get('program_id', [0])[i] if 'program_id' in cols else 0,
|
|
114
|
+
'vs_shader_id': cols.get('vs_shader_id', [0])[i] if 'vs_shader_id' in cols else 0,
|
|
115
|
+
'fs_shader_id': cols.get('fs_shader_id', [0])[i] if 'fs_shader_id' in cols else 0,
|
|
116
|
+
'parent_pass_path_norm': cols.get('parent_pass_path_norm', [''])[i] if 'parent_pass_path_norm' in cols else '',
|
|
117
|
+
'draw_class': cols.get('draw_class', [''])[i] if 'draw_class' in cols else '',
|
|
118
|
+
'num_indices': cols.get('num_indices', [0])[i] if 'num_indices' in cols else 0,
|
|
119
|
+
'num_instances': cols.get('num_instances', [1])[i] if 'num_instances' in cols else 1,
|
|
120
|
+
})
|
|
121
|
+
if draws_rows:
|
|
122
|
+
tbl = pa.Table.from_pylist(draws_rows)
|
|
123
|
+
papq.write_table(tbl, cache_path(root, 'draws_summary'), compression='snappy')
|
|
124
|
+
out['draws_summary'] = len(draws_rows)
|
|
125
|
+
|
|
126
|
+
shader_rows: list[dict] = []
|
|
127
|
+
for d in drops:
|
|
128
|
+
t = _read_drop_parquet(d, 'shaders',
|
|
129
|
+
['area', 'drop_date', 'drop_label', 'capture', 'shader_id', 'stable_key',
|
|
130
|
+
'shader_type', 'src_len', 'complexity_score', 'total_branches',
|
|
131
|
+
'total_loops', 'total_discards', 'total_dfdx_dfdy',
|
|
132
|
+
'total_texture_samples', 'used_by_draw_count', 'src_file_path',
|
|
133
|
+
'fb_fetch', 'uses_cubemap'])
|
|
134
|
+
if t is None or t.num_rows == 0:
|
|
135
|
+
continue
|
|
136
|
+
cols = {c: t.column(c).to_pylist() for c in t.column_names}
|
|
137
|
+
n = t.num_rows
|
|
138
|
+
for i in range(n):
|
|
139
|
+
shader_rows.append({k: cols[k][i] for k in cols})
|
|
140
|
+
shader_rows[-1]['drop_date'] = d.date
|
|
141
|
+
shader_rows[-1]['drop_label'] = d.label
|
|
142
|
+
if shader_rows:
|
|
143
|
+
tbl = pa.Table.from_pylist(shader_rows)
|
|
144
|
+
papq.write_table(tbl, cache_path(root, 'shader_summary'), compression='snappy')
|
|
145
|
+
out['shader_summary'] = len(shader_rows)
|
|
146
|
+
|
|
147
|
+
return out
|