mach: introduce cross platform Timer abstraction

This Timer uses std.time.Timer as backing timer in native platforms, and
will use custom timers for special platforms (wasm, android?, ios?).

Unlike std.time.Timer, its primary API is focused on floats. Also meant
to provides some convenient functions alongside base ones.

Follows std.time.Timer API, but methods by default return f32 i.e
non-precise variant with precise variants available returning u64.
This commit is contained in:
iddev5 2022-05-17 13:20:19 +05:30 committed by Stephen Gutekanst
parent be935c64ef
commit 3bb45c75a1
9 changed files with 63 additions and 20 deletions

View file

@ -22,7 +22,7 @@ const UniformBufferObject = struct {
mat: zm.Mat, mat: zm.Mat,
}; };
var timer: std.time.Timer = undefined; var timer: mach.Timer = undefined;
pipeline: gpu.RenderPipeline, pipeline: gpu.RenderPipeline,
queue: gpu.Queue, queue: gpu.Queue,
@ -38,7 +38,7 @@ sampler: gpu.Sampler,
bgl: gpu.BindGroupLayout, bgl: gpu.BindGroupLayout,
pub fn init(app: *App, engine: *mach.Engine) !void { pub fn init(app: *App, engine: *mach.Engine) !void {
timer = try std.time.Timer.start(); timer = try mach.Timer.start();
engine.core.setKeyCallback(struct { engine.core.setKeyCallback(struct {
fn callback(_: *App, eng: *mach.Engine, key: mach.Key, action: mach.Action) void { fn callback(_: *App, eng: *mach.Engine, key: mach.Key, action: mach.Action) void {
@ -269,7 +269,7 @@ pub fn update(app: *App, engine: *mach.Engine) !bool {
}; };
{ {
const time = @intToFloat(f32, timer.read()) / @as(f32, std.time.ns_per_s); const time = timer.read();
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0))); const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
const view = zm.lookAtRh( const view = zm.lookAtRh(
zm.f32x4(0, -4, 0, 1), zm.f32x4(0, -4, 0, 1),

View file

@ -10,7 +10,7 @@ const UniformBufferObject = struct {
mat: zm.Mat, mat: zm.Mat,
}; };
var timer: std.time.Timer = undefined; var timer: mach.Timer = undefined;
pipeline: gpu.RenderPipeline, pipeline: gpu.RenderPipeline,
queue: gpu.Queue, queue: gpu.Queue,
@ -21,7 +21,7 @@ bind_group: gpu.BindGroup,
const App = @This(); const App = @This();
pub fn init(app: *App, engine: *mach.Engine) !void { pub fn init(app: *App, engine: *mach.Engine) !void {
timer = try std.time.Timer.start(); timer = try mach.Timer.start();
engine.core.setKeyCallback(struct { engine.core.setKeyCallback(struct {
fn callback(_: *App, eng: *mach.Engine, key: mach.Key, action: mach.Action) void { fn callback(_: *App, eng: *mach.Engine, key: mach.Key, action: mach.Action) void {
@ -171,7 +171,7 @@ pub fn update(app: *App, engine: *mach.Engine) !bool {
); );
var ubos: [16]UniformBufferObject = undefined; var ubos: [16]UniformBufferObject = undefined;
const time = @intToFloat(f32, timer.read()) / @as(f32, std.time.ns_per_s); const time = timer.read();
const step: f32 = 4.0; const step: f32 = 4.0;
var m: u8 = 0; var m: u8 = 0;
var x: u8 = 0; var x: u8 = 0;

View file

@ -12,7 +12,7 @@ const UniformBufferObject = struct {
mat: zm.Mat, mat: zm.Mat,
}; };
var timer: std.time.Timer = undefined; var timer: mach.Timer = undefined;
pipeline: gpu.RenderPipeline, pipeline: gpu.RenderPipeline,
queue: gpu.Queue, queue: gpu.Queue,
@ -21,7 +21,7 @@ uniform_buffer: gpu.Buffer,
bind_group: gpu.BindGroup, bind_group: gpu.BindGroup,
pub fn init(app: *App, engine: *mach.Engine) !void { pub fn init(app: *App, engine: *mach.Engine) !void {
timer = try std.time.Timer.start(); timer = try mach.Timer.start();
// TODO: higher level input handlers // TODO: higher level input handlers
engine.core.setKeyCallback(struct { engine.core.setKeyCallback(struct {
@ -173,7 +173,7 @@ pub fn update(app: *App, engine: *mach.Engine) !bool {
}; };
{ {
const time = @intToFloat(f32, timer.read()) / @as(f32, std.time.ns_per_s); const time = timer.read();
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0))); const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
const view = zm.lookAtRh( const view = zm.lookAtRh(
zm.f32x4(0, 4, 2, 1), zm.f32x4(0, 4, 2, 1),

View file

@ -11,7 +11,7 @@ const UniformBufferObject = struct {
mat: zm.Mat, mat: zm.Mat,
}; };
var timer: std.time.Timer = undefined; var timer: mach.Timer = undefined;
pipeline: gpu.RenderPipeline, pipeline: gpu.RenderPipeline,
queue: gpu.Queue, queue: gpu.Queue,
@ -24,7 +24,7 @@ depth_size: mach.Size,
const App = @This(); const App = @This();
pub fn init(app: *App, engine: *mach.Engine) !void { pub fn init(app: *App, engine: *mach.Engine) !void {
timer = try std.time.Timer.start(); timer = try mach.Timer.start();
engine.core.setKeyCallback(struct { engine.core.setKeyCallback(struct {
fn callback(_: *App, eng: *mach.Engine, key: mach.Key, action: mach.Action) void { fn callback(_: *App, eng: *mach.Engine, key: mach.Key, action: mach.Action) void {
@ -239,7 +239,7 @@ pub fn update(app: *App, engine: *mach.Engine) !bool {
}; };
{ {
const time = @intToFloat(f32, timer.read()) / @as(f32, std.time.ns_per_s); const time = timer.read();
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0))); const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
const view = zm.lookAtRh( const view = zm.lookAtRh(
zm.f32x4(0, 4, 2, 1), zm.f32x4(0, 4, 2, 1),

View file

@ -10,7 +10,7 @@ const UniformBufferObject = struct {
mat: zm.Mat, mat: zm.Mat,
}; };
var timer: std.time.Timer = undefined; var timer: mach.Timer = undefined;
pipeline: gpu.RenderPipeline, pipeline: gpu.RenderPipeline,
queue: gpu.Queue, queue: gpu.Queue,
@ -22,7 +22,7 @@ bind_group2: gpu.BindGroup,
const App = @This(); const App = @This();
pub fn init(app: *App, engine: *mach.Engine) !void { pub fn init(app: *App, engine: *mach.Engine) !void {
timer = try std.time.Timer.start(); timer = try mach.Timer.start();
engine.core.setKeyCallback(struct { engine.core.setKeyCallback(struct {
fn callback(_: *App, eng: *mach.Engine, key: mach.Key, action: mach.Action) void { fn callback(_: *App, eng: *mach.Engine, key: mach.Key, action: mach.Action) void {
@ -189,7 +189,7 @@ pub fn update(app: *App, engine: *mach.Engine) !bool {
}; };
{ {
const time = @intToFloat(f32, timer.read()) / @as(f32, std.time.ns_per_s); const time = timer.read();
const rotation1 = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0))); const rotation1 = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
const rotation2 = zm.mul(zm.rotationZ(time * (std.math.pi / 2.0)), zm.rotationX(time * (std.math.pi / 2.0))); const rotation2 = zm.mul(zm.rotationZ(time * (std.math.pi / 2.0)), zm.rotationX(time * (std.math.pi / 2.0)));
const model1 = zm.mul(rotation1, zm.translation(-2, 0, 0)); const model1 = zm.mul(rotation1, zm.translation(-2, 0, 0));

View file

@ -5,6 +5,7 @@ const gpu = @import("gpu");
const App = @import("app"); const App = @import("app");
const structs = @import("structs.zig"); const structs = @import("structs.zig");
const enums = @import("enums.zig"); const enums = @import("enums.zig");
const Timer = @import("Timer.zig");
const Engine = @This(); const Engine = @This();
@ -23,9 +24,9 @@ options: structs.Options,
/// For example, if you are animating a cube which should rotate 360 degrees every second, /// For example, if you are animating a cube which should rotate 360 degrees every second,
/// instead of writing (360.0 / 60.0) and assuming the frame rate is 60hz, write /// instead of writing (360.0 / 60.0) and assuming the frame rate is 60hz, write
/// (360.0 * engine.delta_time) /// (360.0 * engine.delta_time)
delta_time: f64 = 0, delta_time: f32 = 0,
delta_time_ns: u64 = 0, delta_time_ns: u64 = 0,
timer: std.time.Timer, timer: Timer,
pub const Core = struct { pub const Core = struct {
internal: GetCoreInternalType(), internal: GetCoreInternalType(),
@ -64,7 +65,7 @@ pub fn init(allocator: std.mem.Allocator, options: structs.Options) !Engine {
var engine = Engine{ var engine = Engine{
.allocator = allocator, .allocator = allocator,
.options = options, .options = options,
.timer = try std.time.Timer.start(), .timer = try Timer.start(),
.core = undefined, .core = undefined,
.gpu_driver = undefined, .gpu_driver = undefined,
}; };

41
src/Timer.zig Normal file
View file

@ -0,0 +1,41 @@
const std = @import("std");
const builtin = @import("builtin");
const Timer = @This();
backing_timer: BackingTimerType = undefined,
// TODO: verify declarations and its signatures
const BackingTimerType = if (builtin.cpu.arch == .wasm32) void else std.time.Timer;
/// Initialize the timer.
pub fn start() !Timer {
return Timer{
.backing_timer = try BackingTimerType.start(),
};
}
/// Reads the timer value since start or the last reset in nanoseconds.
pub fn readPrecise(timer: *Timer) u64 {
return timer.backing_timer.read();
}
/// Reads the timer value since start or the last reset in seconds.
pub fn read(timer: *Timer) f32 {
return @intToFloat(f32, timer.readPrecise()) / @intToFloat(f32, std.time.ns_per_s);
}
/// Resets the timer value to 0/now.
pub fn reset(timer: *Timer) void {
timer.backing_timer.reset();
}
/// Returns the current value of the timer in nanoseconds, then resets it.
pub fn lapPrecise(timer: *Timer) u64 {
return timer.backing_timer.lap();
}
/// Returns the current value of the timer in seconds, then resets it.
pub fn lap(timer: *Timer) f32 {
return @intToFloat(f32, timer.lapPrecise()) / @intToFloat(f32, std.time.ns_per_s);
}

View file

@ -1,3 +1,4 @@
pub usingnamespace @import("structs.zig"); pub usingnamespace @import("structs.zig");
pub usingnamespace @import("enums.zig"); pub usingnamespace @import("enums.zig");
pub const Engine = @import("Engine.zig"); pub const Engine = @import("Engine.zig");
pub const Timer = @import("Timer.zig");

View file

@ -387,8 +387,8 @@ pub fn main() !void {
while (!window.shouldClose()) { while (!window.shouldClose()) {
try glfw.pollEvents(); try glfw.pollEvents();
engine.delta_time_ns = engine.timer.lap(); engine.delta_time_ns = engine.timer.lapPrecise();
engine.delta_time = @intToFloat(f64, engine.delta_time_ns) / @intToFloat(f64, std.time.ns_per_s); engine.delta_time = @intToFloat(f32, engine.delta_time_ns) / @intToFloat(f32, std.time.ns_per_s);
var framebuffer_size = try window.getFramebufferSize(); var framebuffer_size = try window.getFramebufferSize();
engine.gpu_driver.target_desc.width = framebuffer_size.width; engine.gpu_driver.target_desc.width = framebuffer_size.width;