mach/src/core/Frequency.zig
Stephen Gutekanst 38f296ecce src/core: move mach-core@9a4d09707d9f1cb6ea5602bdf58caeefc46146be package to here
Helps hexops/mach#1165

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
2024-03-05 00:22:22 -07:00

66 lines
1.9 KiB
Zig

const std = @import("std");
const core = @import("main.zig");
const Timer = @import("Timer.zig");
pub const Frequency = @This();
/// The target frequency (e.g. 60hz) or zero for unlimited
target: u32 = 0,
/// The estimated delay that is needed to achieve the target frequency. Updated during tick()
delay_ns: u64 = 0,
/// The actual measured frequency. This is updated every second.
rate: u32 = 0,
delta_time: ?*f32 = null,
delta_time_ns: *u64 = undefined,
/// Internal fields, this must be initialized via a call to start().
internal: struct {
// The frame number in this second's cycle. e.g. zero to 59
count: u32,
timer: Timer,
last_time: u64,
} = undefined,
/// Starts the timer used for frequency calculation. Must be called once before anything else.
pub fn start(f: *Frequency) !void {
f.internal = .{
.count = 0,
.timer = try Timer.start(),
.last_time = 0,
};
}
/// Tick should be called at each occurrence (e.g. frame)
pub inline fn tick(f: *Frequency) void {
var current_time = f.internal.timer.readPrecise();
if (f.delta_time) |delta_time| {
f.delta_time_ns.* = current_time -| f.internal.last_time;
delta_time.* = @as(f32, @floatFromInt(f.delta_time_ns.*)) / @as(f32, @floatFromInt(std.time.ns_per_s));
}
if (current_time >= std.time.ns_per_s) {
f.rate = f.internal.count;
f.internal.count = 0;
f.internal.timer.reset();
current_time -= std.time.ns_per_s;
}
f.internal.last_time = current_time;
f.internal.count += 1;
if (f.target != 0) {
const limited_count = @min(f.target, f.internal.count);
const target_time_per_tick: u64 = (std.time.ns_per_s / f.target);
const target_time = target_time_per_tick * limited_count;
if (current_time > target_time) {
f.delay_ns = 0;
} else {
f.delay_ns = target_time - current_time;
}
} else {
f.delay_ns = 0;
}
}