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.
Files changed (130) hide show
  1. bobframes/__init__.py +3 -0
  2. bobframes/_version.py +1 -0
  3. bobframes/catalog.py +154 -0
  4. bobframes/cli.py +266 -0
  5. bobframes/derive_post_merge.py +365 -0
  6. bobframes/derives/__init__.py +0 -0
  7. bobframes/derives/pass_class_breakdown.py +102 -0
  8. bobframes/derives/texture_usage.py +121 -0
  9. bobframes/discovery.py +132 -0
  10. bobframes/global_entities.py +99 -0
  11. bobframes/html/__init__.py +0 -0
  12. bobframes/html/template.py +1056 -0
  13. bobframes/lint.py +114 -0
  14. bobframes/manifest.py +127 -0
  15. bobframes/parquetize.py +282 -0
  16. bobframes/parsers/__init__.py +0 -0
  17. bobframes/parsers/derive_program_transitions.py +73 -0
  18. bobframes/parsers/parse_init_state.py +675 -0
  19. bobframes/paths.py +111 -0
  20. bobframes/probes/__init__.py +0 -0
  21. bobframes/probes/whatif.py +165 -0
  22. bobframes/qrd_harness.py +119 -0
  23. bobframes/query_examples.py +222 -0
  24. bobframes/rdcmd.py +72 -0
  25. bobframes/replay/__init__.py +26 -0
  26. bobframes/replay/replay_main.py +2305 -0
  27. bobframes/reports/__init__.py +0 -0
  28. bobframes/reports/_dashboard.py +425 -0
  29. bobframes/reports/ab.py +88 -0
  30. bobframes/reports/base.py +114 -0
  31. bobframes/reports/cache.py +147 -0
  32. bobframes/reports/chrome.py +1306 -0
  33. bobframes/reports/cli.py +99 -0
  34. bobframes/reports/delta.py +167 -0
  35. bobframes/reports/discovery.py +118 -0
  36. bobframes/reports/draws_by_class.py +165 -0
  37. bobframes/reports/formatters.py +122 -0
  38. bobframes/reports/instancing_opportunities.py +276 -0
  39. bobframes/reports/orchestrator.py +59 -0
  40. bobframes/reports/overdraw.py +293 -0
  41. bobframes/reports/pass_gpu.py +190 -0
  42. bobframes/reports/shader_hotlist.py +240 -0
  43. bobframes/reports/trend_table.py +444 -0
  44. bobframes/resource_labels.py +162 -0
  45. bobframes/run.py +480 -0
  46. bobframes/schemas.py +426 -0
  47. bobframes/stable_keys.py +83 -0
  48. bobframes/tests/__init__.py +0 -0
  49. bobframes/tests/_render_util.py +84 -0
  50. bobframes/tests/data/golden/_reports/draws_by_class.html +323 -0
  51. bobframes/tests/data/golden/_reports/drill/District 01/2026-05-28_r110600/index.html +1560 -0
  52. bobframes/tests/data/golden/_reports/index.html +264 -0
  53. bobframes/tests/data/golden/_reports/instancing_opportunities.html +266 -0
  54. bobframes/tests/data/golden/_reports/overdraw.html +275 -0
  55. bobframes/tests/data/golden/_reports/pass_gpu.html +277 -0
  56. bobframes/tests/data/golden/_reports/shader_hotlist.html +265 -0
  57. bobframes/tests/data/golden/_reports/trend_table.html +390 -0
  58. bobframes/tests/data/golden/index.html +1175 -0
  59. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/_manifest.json +51 -0
  60. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/buffers.parquet +0 -0
  61. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/clears.parquet +0 -0
  62. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/counters_per_event.parquet +0 -0
  63. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/descriptor_access.parquet +0 -0
  64. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/dispatches.parquet +0 -0
  65. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/draw_bindings.parquet +0 -0
  66. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/draws.parquet +0 -0
  67. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/events.parquet +0 -0
  68. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/fbos.parquet +0 -0
  69. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/frame_totals.parquet +0 -0
  70. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/ibo_samples.parquet +0 -0
  71. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/indirect_args.parquet +0 -0
  72. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/passes.parquet +0 -0
  73. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/pixel_history.parquet +0 -0
  74. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/post_vs_samples.parquet +0 -0
  75. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/program_transitions.parquet +0 -0
  76. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/programs.parquet +0 -0
  77. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/render_targets.parquet +0 -0
  78. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/resource_creation.parquet +0 -0
  79. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/rt_event_timeline.parquet +0 -0
  80. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/samplers.parquet +0 -0
  81. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/shaders.parquet +0 -0
  82. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/state_change_events.parquet +0 -0
  83. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/texture_samples.parquet +0 -0
  84. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/textures.parquet +0 -0
  85. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/vbo_samples.parquet +0 -0
  86. bobframes/tests/data/synthetic/_data/District 01/2026-05-27_r110565/vertex_inputs.parquet +0 -0
  87. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/_manifest.json +51 -0
  88. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/buffers.parquet +0 -0
  89. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/clears.parquet +0 -0
  90. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/counters_per_event.parquet +0 -0
  91. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/descriptor_access.parquet +0 -0
  92. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/dispatches.parquet +0 -0
  93. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/draw_bindings.parquet +0 -0
  94. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/draws.parquet +0 -0
  95. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/events.parquet +0 -0
  96. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/fbos.parquet +0 -0
  97. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/frame_totals.parquet +0 -0
  98. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/ibo_samples.parquet +0 -0
  99. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/indirect_args.parquet +0 -0
  100. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/passes.parquet +0 -0
  101. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/pixel_history.parquet +0 -0
  102. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/post_vs_samples.parquet +0 -0
  103. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/program_transitions.parquet +0 -0
  104. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/programs.parquet +0 -0
  105. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/render_targets.parquet +0 -0
  106. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/resource_creation.parquet +0 -0
  107. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/rt_event_timeline.parquet +0 -0
  108. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/samplers.parquet +0 -0
  109. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/shaders.parquet +0 -0
  110. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/state_change_events.parquet +0 -0
  111. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/texture_samples.parquet +0 -0
  112. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/textures.parquet +0 -0
  113. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/vbo_samples.parquet +0 -0
  114. bobframes/tests/data/synthetic/_data/District 01/2026-05-28_r110600/vertex_inputs.parquet +0 -0
  115. bobframes/tests/make_synthetic.py +171 -0
  116. bobframes/tests/smoke.py +199 -0
  117. bobframes/tests/test_determinism.py +19 -0
  118. bobframes/tests/test_discovery.py +97 -0
  119. bobframes/tests/test_hardening.py +142 -0
  120. bobframes/tests/test_parity.py +22 -0
  121. bobframes/tests/test_perf.py +18 -0
  122. bobframes/tests/test_replay_drift.py +115 -0
  123. bobframes/tests/test_schemas.py +26 -0
  124. bobframes/tests/test_schemas_unit.py +55 -0
  125. bobframes/tests/test_stable_keys.py +61 -0
  126. bobframes-0.1.0.dist-info/METADATA +144 -0
  127. bobframes-0.1.0.dist-info/RECORD +130 -0
  128. bobframes-0.1.0.dist-info/WHEEL +4 -0
  129. bobframes-0.1.0.dist-info/entry_points.txt +2 -0
  130. 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