daq-view/src/my-profiler.zig
2025-03-10 01:01:13 +02:00

112 lines
3.8 KiB
Zig

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;
}
}