a73x

6986eef7

Add BaselineRecord + JSON round-trip for frame stats

a73x   2026-04-17 15:28

Commit message
Add BaselineRecord + JSON round-trip for frame stats

Lays the foundation for bench-baseline / bench-check tooling (Task 10).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

src/bench_stats.zig
Old New
@@ -200,3 +200,68 @@ test "FrameTimingStats handles empty ring" {
200 try std.testing.expectEqual(@as(usize, 0), stats.frame_count); 200 try std.testing.expectEqual(@as(usize, 0), stats.frame_count);
201 try std.testing.expectEqual(@as(u32, 0), stats.snapshot.min); 201 try std.testing.expectEqual(@as(u32, 0), stats.snapshot.min);
202 } 202 }
203
204 pub const BaselineRecord = struct {
205 workload_sha: []const u8,
206 zig_version: []const u8,
207 waystty_sha: []const u8,
208 frame_count: usize,
209 sections: struct {
210 snapshot: SectionStats,
211 row_rebuild: SectionStats,
212 atlas_upload: SectionStats,
213 instance_upload: SectionStats,
214 gpu_submit: SectionStats,
215 },
216 };
217
218 /// Serialize `rec` to JSON and return an owned slice. Caller must free.
219 pub fn writeBaselineJson(alloc: std.mem.Allocator, rec: BaselineRecord) ![]u8 {
220 var out: std.Io.Writer.Allocating = .init(alloc);
221 errdefer out.deinit();
222 try std.json.Stringify.value(rec, .{ .whitespace = .indent_2 }, &out.writer);
223 return out.toOwnedSlice();
224 }
225
226 pub fn readBaselineJson(alloc: std.mem.Allocator, bytes: []const u8) !BaselineRecord {
227 var parsed = try std.json.parseFromSlice(BaselineRecord, alloc, bytes, .{});
228 defer parsed.deinit();
229 return .{
230 .workload_sha = try alloc.dupe(u8, parsed.value.workload_sha),
231 .zig_version = try alloc.dupe(u8, parsed.value.zig_version),
232 .waystty_sha = try alloc.dupe(u8, parsed.value.waystty_sha),
233 .frame_count = parsed.value.frame_count,
234 .sections = parsed.value.sections,
235 };
236 }
237
238 test "baseline JSON round-trip" {
239 const alloc = std.testing.allocator;
240 const rec = BaselineRecord{
241 .workload_sha = "abcdef",
242 .zig_version = "0.15.0",
243 .waystty_sha = "123abc",
244 .frame_count = 256,
245 .sections = .{
246 .snapshot = .{ .min = 1, .avg = 2, .p99 = 3, .max = 4 },
247 .row_rebuild = .{ .min = 10, .avg = 20, .p99 = 30, .max = 40 },
248 .atlas_upload = .{ .min = 0, .avg = 0, .p99 = 0, .max = 0 },
249 .instance_upload = .{ .min = 5, .avg = 6, .p99 = 7, .max = 8 },
250 .gpu_submit = .{ .min = 9, .avg = 9, .p99 = 9, .max = 9 },
251 },
252 };
253
254 const json_bytes = try writeBaselineJson(alloc, rec);
255 defer alloc.free(json_bytes);
256
257 const parsed = try readBaselineJson(alloc, json_bytes);
258 defer {
259 alloc.free(parsed.workload_sha);
260 alloc.free(parsed.zig_version);
261 alloc.free(parsed.waystty_sha);
262 }
263
264 try std.testing.expectEqual(@as(usize, 256), parsed.frame_count);
265 try std.testing.expectEqual(@as(u32, 30), parsed.sections.row_rebuild.p99);
266 try std.testing.expectEqualStrings("abcdef", parsed.workload_sha);
267 }