const std = @import("std"); const rl = @import("raylib"); const Assets = @import("./assets.zig"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const FontId = Assets.FontId; const srcery = @import("./srcery.zig"); const rect_utils = @import("./rect-utils.zig"); allocator: Allocator, history: []u128, history_size: usize, history_head: usize, started_at: i128, ns_budget: u128, font: FontId, pub fn init(allocator: Allocator, data_points: usize, ns_budget: u128, font: FontId) !@This() { return @This(){ .allocator = allocator, .history = try allocator.alloc(u128, data_points), .history_size = 0, .history_head = 0, .started_at = 0, .ns_budget = ns_budget, .font = font, }; } pub fn deinit(self: @This()) void { self.allocator.free(self.history); } pub fn start(self: *@This()) void { self.started_at = std.time.nanoTimestamp(); } pub fn stop(self: *@This()) void { const stopped_at = std.time.nanoTimestamp(); assert(stopped_at >= self.started_at); const duration: u128 = @intCast(stopped_at - self.started_at); if (self.history_size < self.history.len) { self.history[self.history_size] = duration; self.history_size += 1; } else { self.history_head = @mod(self.history_head + 1, self.history.len); self.history[self.history_head] = duration; } } pub fn showResults(self: *const @This()) !void { const screen_width: f32 = @floatFromInt(rl.getScreenWidth()); const screen_height: f32 = @floatFromInt(rl.getScreenHeight()); const profile_height = 200; const profile_box = rl.Rectangle.init(0, screen_height - profile_height, screen_width, profile_height); const color = srcery.bright_white; rl.drawRectangleLinesEx(profile_box, 1, color); const ns_budget: f32 = @floatFromInt(self.ns_budget); const measurement_width = profile_box.width / @as(f32, @floatFromInt(self.history.len)); for (0..self.history_size) |i| { const measurement = self.history[@mod(self.history_head + i, self.history.len)]; const measurement_height = @as(f32, @floatFromInt(measurement)) / ns_budget * profile_box.height; rl.drawRectangleV( .{ .x = profile_box.x + measurement_width * @as(f32, @floatFromInt(i + self.history.len - self.history_size)), .y = profile_box.y + profile_box.height - measurement_height }, .{ .x = measurement_width, .y = measurement_height }, color ); } var min_time_taken = self.history[0]; var max_time_taken = self.history[0]; var sum_time_taken: u128 = 0; for (self.history[0..self.history_size]) |measurement| { min_time_taken = @min(min_time_taken, measurement); max_time_taken = @max(max_time_taken, measurement); sum_time_taken += measurement; } const avg_time_taken = @as(f32, @floatFromInt(sum_time_taken)) / @as(f32, @floatFromInt(self.history_size)); const content_rect = rect_utils.shrink(profile_box, 10); var layout_offset: f32 = 0; const allocator = self.allocator; const font_face = Assets.font(self.font); const font_size = font_face.getSize(); const labels = .{ .{ "Min", @as(f32, @floatFromInt(min_time_taken)) }, .{ "Max", @as(f32, @floatFromInt(max_time_taken)) }, .{ "Avg", avg_time_taken } }; inline for (labels) |label| { const label_name = label[0]; const time_taken = label[1]; const min_time_str = try std.fmt.allocPrintZ(allocator, "{s}: {d:10.0}us ({d:.3}%)", .{ label_name, time_taken / std.time.ns_per_us, time_taken / ns_budget * 100 }); defer allocator.free(min_time_str); font_face.drawText(min_time_str, .{ .x = content_rect.x, .y = content_rect.y + layout_offset }, color); layout_offset += font_size; } }