From 24808b3092eea33c35bdc441dda548184a4b55ce Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Fri, 28 Jun 2024 15:41:11 -0700 Subject: [PATCH 01/14] settings: add fpsCap --- src/main.zig | 15 +++++++++++---- src/settings.zig | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main.zig b/src/main.zig index a4c733c7..c77bc56f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -446,9 +446,6 @@ pub fn main() void { std.time.sleep(16_000_000); } - Window.handleEvents(); - file_monitor.handleEvents(); - const newTime = std.time.nanoTimestamp(); const deltaTime = @as(f64, @floatFromInt(newTime -% lastTime))/1e9; if(settings.developerGPUInfiniteLoopDetection and deltaTime > 5) { // On linux a process that runs 10 seconds or longer on the GPU will get stopped. This allows detecting an infinite loop on the GPU. @@ -456,7 +453,17 @@ pub fn main() void { std.posix.exit(1); } lastFrameTime.store(deltaTime, .monotonic); - lastTime = newTime; + + if(settings.fpsCap) |fpsCap| { + const minFrameTime = @divFloor(1000*1000, fpsCap); + const sleep = @min(minFrameTime, @max(0, minFrameTime - (newTime -% lastTime))); + std.time.sleep(sleep); + } + lastTime = std.time.nanoTimestamp(); + + Window.handleEvents(); + file_monitor.handleEvents(); + if(game.world != null) { // Update the game game.update(deltaTime); } diff --git a/src/settings.zig b/src/settings.zig index e029a885..d691eba6 100644 --- a/src/settings.zig +++ b/src/settings.zig @@ -20,6 +20,8 @@ pub var cpuThreads: ?u64 = null; pub var anisotropicFiltering: u8 = 4.0; +pub var fpsCap: ?u32 = null; + pub var fov: f32 = 70; pub var mouseSensitivity: f32 = 1; From 685927f77b71a17e475c7ed22f97a68da006eab3 Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Fri, 28 Jun 2024 15:50:03 -0700 Subject: [PATCH 02/14] fpsCap: convert to nanos correctly --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index c77bc56f..8b556712 100644 --- a/src/main.zig +++ b/src/main.zig @@ -455,7 +455,7 @@ pub fn main() void { lastFrameTime.store(deltaTime, .monotonic); if(settings.fpsCap) |fpsCap| { - const minFrameTime = @divFloor(1000*1000, fpsCap); + const minFrameTime = @divFloor(1000*1000*1000, fpsCap); const sleep = @min(minFrameTime, @max(0, minFrameTime - (newTime -% lastTime))); std.time.sleep(sleep); } From a5d8905860f5922da5f95ee0e15c52988001b5ad Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Fri, 28 Jun 2024 21:18:39 -0700 Subject: [PATCH 03/14] main.zig: use specific names for lastBeginRendering and frameTime --- src/main.zig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main.zig b/src/main.zig index 8b556712..996ed9c0 100644 --- a/src/main.zig +++ b/src/main.zig @@ -426,7 +426,7 @@ pub fn main() void { c.glDepthFunc(c.GL_LESS); c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); Window.GLFWCallbacks.framebufferSize(undefined, Window.width, Window.height); - var lastTime = std.time.nanoTimestamp(); + var lastBeginRendering = std.time.nanoTimestamp(); if(settings.developerAutoEnterWorld.len != 0) { // Speed up the dev process by entering the world directly. @@ -446,26 +446,26 @@ pub fn main() void { std.time.sleep(16_000_000); } - const newTime = std.time.nanoTimestamp(); - const deltaTime = @as(f64, @floatFromInt(newTime -% lastTime))/1e9; - if(settings.developerGPUInfiniteLoopDetection and deltaTime > 5) { // On linux a process that runs 10 seconds or longer on the GPU will get stopped. This allows detecting an infinite loop on the GPU. - std.log.err("Frame got too long with {} seconds. Infinite loop on GPU?", .{deltaTime}); + const endRendering = std.time.nanoTimestamp(); + const frameTime = @as(f64, @floatFromInt(endRendering -% lastBeginRendering))/1e9; + if(settings.developerGPUInfiniteLoopDetection and frameTime > 5) { // On linux a process that runs 10 seconds or longer on the GPU will get stopped. This allows detecting an infinite loop on the GPU. + std.log.err("Frame got too long with {} seconds. Infinite loop on GPU?", .{frameTime}); std.posix.exit(1); } - lastFrameTime.store(deltaTime, .monotonic); + lastFrameTime.store(frameTime, .monotonic); if(settings.fpsCap) |fpsCap| { const minFrameTime = @divFloor(1000*1000*1000, fpsCap); - const sleep = @min(minFrameTime, @max(0, minFrameTime - (newTime -% lastTime))); + const sleep = @min(minFrameTime, @max(0, minFrameTime - (endRendering -% lastBeginRendering))); std.time.sleep(sleep); } - lastTime = std.time.nanoTimestamp(); + lastBeginRendering = std.time.nanoTimestamp(); Window.handleEvents(); file_monitor.handleEvents(); if(game.world != null) { // Update the game - game.update(deltaTime); + game.update(frameTime); } if(!isHidden) { From 6f3427608127e69bf6a25704e7e0216daaa260fa Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Fri, 28 Jun 2024 21:29:30 -0700 Subject: [PATCH 04/14] physics/fps window: use real time between frames --- src/gui/windows/debug.zig | 4 ++-- src/main.zig | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/gui/windows/debug.zig b/src/gui/windows/debug.zig index cc13dfaf..dbb3bf11 100644 --- a/src/gui/windows/debug.zig +++ b/src/gui/windows/debug.zig @@ -25,7 +25,7 @@ pub var window = GuiWindow { pub fn render() void { draw.setColor(0xffffffff); var y: f32 = 0; - draw.print(" fps: {d:.0} Hz{s}", .{1.0/main.lastFrameTime.load(.monotonic), if(main.settings.vsync) @as([]const u8, " (vsync)") else ""}, 0, y, 8, .left); + draw.print(" fps: {d:.0} Hz{s}", .{1.0/main.lastDeltaTime.load(.monotonic), if(main.settings.vsync) @as([]const u8, " (vsync)") else ""}, 0, y, 8, .left); y += 8; draw.print(" frameTime: {d:.1} ms", .{main.lastFrameTime.load(.monotonic)*1000.0}, 0, y, 8, .left); y += 8; @@ -57,4 +57,4 @@ pub fn render() void { draw.print("Opaque faces: {}, Transparent faces: {}", .{main.renderer.chunk_meshing.quadsDrawn, main.renderer.chunk_meshing.transparentQuadsDrawn}, 0, y, 8, .left); y += 8; } -} \ No newline at end of file +} diff --git a/src/main.zig b/src/main.zig index 996ed9c0..617229be 100644 --- a/src/main.zig +++ b/src/main.zig @@ -345,7 +345,10 @@ pub const KeyBoard = struct { } }; +/// Records gpu time per frame. pub var lastFrameTime = std.atomic.Value(f64).init(0); +/// Measures time between different frames' beginnings. +pub var lastDeltaTime = std.atomic.Value(f64).init(0); pub fn main() void { seed = @bitCast(std.time.milliTimestamp()); @@ -459,13 +462,16 @@ pub fn main() void { const sleep = @min(minFrameTime, @max(0, minFrameTime - (endRendering -% lastBeginRendering))); std.time.sleep(sleep); } - lastBeginRendering = std.time.nanoTimestamp(); + const begin = std.time.nanoTimestamp(); + const deltaTime = @as(f64, @floatFromInt(begin - lastBeginRendering))/1e9; + lastDeltaTime.store(deltaTime, .monotonic); + lastBeginRendering = begin; Window.handleEvents(); file_monitor.handleEvents(); if(game.world != null) { // Update the game - game.update(frameTime); + game.update(deltaTime); } if(!isHidden) { From 29574a57d41b6be980288a3c315754963ae2772a Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Sat, 29 Jun 2024 15:09:08 -0700 Subject: [PATCH 05/14] graphics menu: add fps slider --- src/gui/windows/graphics.zig | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gui/windows/graphics.zig b/src/gui/windows/graphics.zig index 14f00537..98b27c37 100644 --- a/src/gui/windows/graphics.zig +++ b/src/gui/windows/graphics.zig @@ -24,6 +24,14 @@ const anisotropy = [_]u8{1, 2, 4, 8, 16}; const resolutions = [_]u16{25, 50, 100}; +fn fpsCapFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 { + return std.fmt.allocPrint(allocator.allocator, "#ffffffFPS Limit: {d:.0}", .{value}) catch unreachable; +} + +fn fpsCapCallback(newValue: f32) void { + settings.fpsCap = if(newValue == 144.0) null else @intFromFloat(newValue); +} + fn renderDistanceCallback(newValue: u16) void { settings.renderDistance = newValue + renderDistances[0]; } @@ -64,6 +72,7 @@ fn resolutionScaleCallback(newValue: u16) void { pub fn onOpen() void { const list = VerticalList.init(.{padding, 16 + padding}, 300, 16); + list.add(ContinuousSlider.init(.{0, 0}, 128, 10.0, 144.0, @floatFromInt(settings.fpsCap orelse 144), &fpsCapCallback, &fpsCapFormatter)); list.add(DiscreteSlider.init(.{0, 0}, 128, "#ffffffRender Distance: ", "{}", &renderDistances, settings.renderDistance - renderDistances[0], &renderDistanceCallback)); list.add(ContinuousSlider.init(.{0, 0}, 128, 40.0, 120.0, settings.fov, &fovCallback, &fovFormatter)); list.add(CheckBox.init(.{0, 0}, 128, "Bloom", settings.bloom, &bloomCallback)); @@ -80,4 +89,4 @@ pub fn onClose() void { if(window.rootComponent) |*comp| { comp.deinit(); } -} \ No newline at end of file +} From 244e983543ba9212cb505a10912748fada60f146 Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Sun, 30 Jun 2024 12:38:54 -0700 Subject: [PATCH 06/14] fpsCap: round slider + fix formatter --- src/gui/windows/graphics.zig | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/gui/windows/graphics.zig b/src/gui/windows/graphics.zig index 98b27c37..9c3ca9ff 100644 --- a/src/gui/windows/graphics.zig +++ b/src/gui/windows/graphics.zig @@ -24,12 +24,25 @@ const anisotropy = [_]u8{1, 2, 4, 8, 16}; const resolutions = [_]u16{25, 50, 100}; +fn fpsCapRound(newValue: f32) ?u32 { + if(newValue < 144.0) { + return @as(u32, @intFromFloat(newValue/5.0))*5; + } else if (newValue < 149.0) { + return 144; + } else { + return null; + } +} + fn fpsCapFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 { - return std.fmt.allocPrint(allocator.allocator, "#ffffffFPS Limit: {d:.0}", .{value}) catch unreachable; + const cap = fpsCapRound(value); + const limit = if(cap == null) "Unlimited" else std.fmt.allocPrint(allocator.allocator, "{d:.0}", .{cap.?}) catch unreachable; + return std.fmt.allocPrint(allocator.allocator, "#ffffffFPS: {s}", .{limit}) catch unreachable; } fn fpsCapCallback(newValue: f32) void { - settings.fpsCap = if(newValue == 144.0) null else @intFromFloat(newValue); + std.log.debug("value {}", .{newValue}); + settings.fpsCap = fpsCapRound(newValue); } fn renderDistanceCallback(newValue: u16) void { @@ -72,7 +85,7 @@ fn resolutionScaleCallback(newValue: u16) void { pub fn onOpen() void { const list = VerticalList.init(.{padding, 16 + padding}, 300, 16); - list.add(ContinuousSlider.init(.{0, 0}, 128, 10.0, 144.0, @floatFromInt(settings.fpsCap orelse 144), &fpsCapCallback, &fpsCapFormatter)); + list.add(ContinuousSlider.init(.{0, 0}, 128, 10.0, 154.0, @floatFromInt(settings.fpsCap orelse 144), &fpsCapCallback, &fpsCapFormatter)); list.add(DiscreteSlider.init(.{0, 0}, 128, "#ffffffRender Distance: ", "{}", &renderDistances, settings.renderDistance - renderDistances[0], &renderDistanceCallback)); list.add(ContinuousSlider.init(.{0, 0}, 128, 40.0, 120.0, settings.fov, &fovCallback, &fovFormatter)); list.add(CheckBox.init(.{0, 0}, 128, "Bloom", settings.bloom, &bloomCallback)); From f0c2314304b2dcfbb198aeaeca16307492a10d97 Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Sun, 30 Jun 2024 12:41:07 -0700 Subject: [PATCH 07/14] fpsCap: prevent overflow --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 617229be..b0ffb562 100644 --- a/src/main.zig +++ b/src/main.zig @@ -463,7 +463,7 @@ pub fn main() void { std.time.sleep(sleep); } const begin = std.time.nanoTimestamp(); - const deltaTime = @as(f64, @floatFromInt(begin - lastBeginRendering))/1e9; + const deltaTime = @as(f64, @floatFromInt(begin -% lastBeginRendering))/1e9; lastDeltaTime.store(deltaTime, .monotonic); lastBeginRendering = begin; From 4bb5ce2d80178069fc75f4ae7e836475915f068a Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Sun, 30 Jun 2024 12:42:37 -0700 Subject: [PATCH 08/14] fpsCap: remove debug spam --- src/gui/windows/graphics.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/windows/graphics.zig b/src/gui/windows/graphics.zig index 9c3ca9ff..27089619 100644 --- a/src/gui/windows/graphics.zig +++ b/src/gui/windows/graphics.zig @@ -41,7 +41,6 @@ fn fpsCapFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []co } fn fpsCapCallback(newValue: f32) void { - std.log.debug("value {}", .{newValue}); settings.fpsCap = fpsCapRound(newValue); } From 28171c53d4430c2e658787a26d28c0d3dd971018 Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Sun, 30 Jun 2024 13:09:51 -0700 Subject: [PATCH 09/14] fpsCap setting: add 'Limit' --- src/gui/windows/graphics.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/windows/graphics.zig b/src/gui/windows/graphics.zig index 27089619..54afd05f 100644 --- a/src/gui/windows/graphics.zig +++ b/src/gui/windows/graphics.zig @@ -36,8 +36,9 @@ fn fpsCapRound(newValue: f32) ?u32 { fn fpsCapFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 { const cap = fpsCapRound(value); - const limit = if(cap == null) "Unlimited" else std.fmt.allocPrint(allocator.allocator, "{d:.0}", .{cap.?}) catch unreachable; - return std.fmt.allocPrint(allocator.allocator, "#ffffffFPS: {s}", .{limit}) catch unreachable; + if(cap == null) + return "#ffffffFPS: Unlimited"; + return std.fmt.allocPrint(allocator.allocator, "#ffffffFPS Limit: {d:.0}", .{cap.?}) catch unreachable; } fn fpsCapCallback(newValue: f32) void { From fffcd9337cd6e18d90d83d81856acef2c7815781 Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Sun, 30 Jun 2024 13:22:35 -0700 Subject: [PATCH 10/14] debug menu: note fps limit --- src/gui/windows/debug.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/windows/debug.zig b/src/gui/windows/debug.zig index dbb3bf11..be182338 100644 --- a/src/gui/windows/debug.zig +++ b/src/gui/windows/debug.zig @@ -25,7 +25,7 @@ pub var window = GuiWindow { pub fn render() void { draw.setColor(0xffffffff); var y: f32 = 0; - draw.print(" fps: {d:.0} Hz{s}", .{1.0/main.lastDeltaTime.load(.monotonic), if(main.settings.vsync) @as([]const u8, " (vsync)") else ""}, 0, y, 8, .left); + draw.print(" fps: {d:.0} Hz{s}{s}", .{1.0/main.lastDeltaTime.load(.monotonic), if(main.settings.vsync) @as([]const u8, " (vsync)") else "", if(main.settings.fpsCap == null) " (unlimited)" else std.fmt.allocPrint(main.stackAllocator.allocator, " (limit: {d:.0} Hz)", .{main.settings.fpsCap.?}) catch unreachable}, 0, y, 8, .left); y += 8; draw.print(" frameTime: {d:.1} ms", .{main.lastFrameTime.load(.monotonic)*1000.0}, 0, y, 8, .left); y += 8; From 9267d39163fc98fc6c25ccac0eecaad196c8fadd Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Sun, 30 Jun 2024 13:33:11 -0700 Subject: [PATCH 11/14] fpsCap formatter: dupe constant string to prevent free failure --- src/gui/windows/graphics.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/windows/graphics.zig b/src/gui/windows/graphics.zig index 54afd05f..7eff5caf 100644 --- a/src/gui/windows/graphics.zig +++ b/src/gui/windows/graphics.zig @@ -37,7 +37,7 @@ fn fpsCapRound(newValue: f32) ?u32 { fn fpsCapFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 { const cap = fpsCapRound(value); if(cap == null) - return "#ffffffFPS: Unlimited"; + return allocator.dupe(u8, "#ffffffFPS: Unlimited"); return std.fmt.allocPrint(allocator.allocator, "#ffffffFPS Limit: {d:.0}", .{cap.?}) catch unreachable; } From c828f06769e2ca7f391e19f5ba0aa94ec579fa45 Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Sun, 30 Jun 2024 13:50:38 -0700 Subject: [PATCH 12/14] debug menu: remove contradictory (vsync) (unlimited) --- src/gui/windows/debug.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gui/windows/debug.zig b/src/gui/windows/debug.zig index be182338..b0665f5d 100644 --- a/src/gui/windows/debug.zig +++ b/src/gui/windows/debug.zig @@ -25,7 +25,11 @@ pub var window = GuiWindow { pub fn render() void { draw.setColor(0xffffffff); var y: f32 = 0; - draw.print(" fps: {d:.0} Hz{s}{s}", .{1.0/main.lastDeltaTime.load(.monotonic), if(main.settings.vsync) @as([]const u8, " (vsync)") else "", if(main.settings.fpsCap == null) " (unlimited)" else std.fmt.allocPrint(main.stackAllocator.allocator, " (limit: {d:.0} Hz)", .{main.settings.fpsCap.?}) catch unreachable}, 0, y, 8, .left); + const fpsLimit = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}{s}", .{ + if(main.settings.fpsCap) |fpsCap| std.fmt.allocPrint(main.stackAllocator.allocator, " (limit: {d:.0} Hz)", .{fpsCap}) catch unreachable else "", + if(main.settings.vsync) " (vsync)" else "", + }) catch unreachable; + draw.print(" fps: {d:.0} Hz{s}", .{1.0/main.lastDeltaTime.load(.monotonic), fpsLimit}, 0, y, 8, .left); y += 8; draw.print(" frameTime: {d:.1} ms", .{main.lastFrameTime.load(.monotonic)*1000.0}, 0, y, 8, .left); y += 8; From 1b173d217d32ae6910c55fe31a188a37e97d2ddc Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Sun, 30 Jun 2024 13:58:59 -0700 Subject: [PATCH 13/14] debug menu: fix mem leak in fps format --- src/gui/windows/debug.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/windows/debug.zig b/src/gui/windows/debug.zig index b0665f5d..b62f1fc7 100644 --- a/src/gui/windows/debug.zig +++ b/src/gui/windows/debug.zig @@ -25,10 +25,13 @@ pub var window = GuiWindow { pub fn render() void { draw.setColor(0xffffffff); var y: f32 = 0; + const fpsCapText = if(main.settings.fpsCap) |fpsCap| std.fmt.allocPrint(main.stackAllocator.allocator, " (limit: {d:.0} Hz)", .{fpsCap}) catch unreachable else null; const fpsLimit = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}{s}", .{ - if(main.settings.fpsCap) |fpsCap| std.fmt.allocPrint(main.stackAllocator.allocator, " (limit: {d:.0} Hz)", .{fpsCap}) catch unreachable else "", + fpsCapText orelse "", if(main.settings.vsync) " (vsync)" else "", }) catch unreachable; + defer main.stackAllocator.allocator.free(fpsLimit); + if(fpsCapText) |t| main.stackAllocator.allocator.free(t); draw.print(" fps: {d:.0} Hz{s}", .{1.0/main.lastDeltaTime.load(.monotonic), fpsLimit}, 0, y, 8, .left); y += 8; draw.print(" frameTime: {d:.1} ms", .{main.lastFrameTime.load(.monotonic)*1000.0}, 0, y, 8, .left); From 2df3e799f9a1fd1697a6ad2162b5dddb9429dda1 Mon Sep 17 00:00:00 2001 From: Archbirdplus Date: Mon, 1 Jul 2024 01:51:46 -0700 Subject: [PATCH 14/14] debug manu: simplify fps limit text allocation --- src/gui/windows/debug.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/windows/debug.zig b/src/gui/windows/debug.zig index b62f1fc7..9b22966f 100644 --- a/src/gui/windows/debug.zig +++ b/src/gui/windows/debug.zig @@ -25,13 +25,13 @@ pub var window = GuiWindow { pub fn render() void { draw.setColor(0xffffffff); var y: f32 = 0; - const fpsCapText = if(main.settings.fpsCap) |fpsCap| std.fmt.allocPrint(main.stackAllocator.allocator, " (limit: {d:.0} Hz)", .{fpsCap}) catch unreachable else null; + const fpsCapText = if(main.settings.fpsCap) |fpsCap| std.fmt.allocPrint(main.stackAllocator.allocator, " (limit: {d:.0} Hz)", .{fpsCap}) catch unreachable else ""; + defer main.stackAllocator.allocator.free(fpsCapText); const fpsLimit = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}{s}", .{ - fpsCapText orelse "", + fpsCapText, if(main.settings.vsync) " (vsync)" else "", }) catch unreachable; defer main.stackAllocator.allocator.free(fpsLimit); - if(fpsCapText) |t| main.stackAllocator.allocator.free(t); draw.print(" fps: {d:.0} Hz{s}", .{1.0/main.lastDeltaTime.load(.monotonic), fpsLimit}, 0, y, 8, .left); y += 8; draw.print(" frameTime: {d:.1} ms", .{main.lastFrameTime.load(.monotonic)*1000.0}, 0, y, 8, .left);