From 3623362f7266ec4d915636aad6290abd4407fd04 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Mon, 14 Mar 2022 08:54:48 -0300 Subject: [PATCH] chore: improve accuracy of thread sleep in emu thread --- src/emu.zig | 38 ++++++++++++++++++++++++++++---------- src/main.zig | 15 ++++++++++----- src/util.zig | 27 +++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/emu.zig b/src/emu.zig index 47cf391..f4fb969 100644 --- a/src/emu.zig +++ b/src/emu.zig @@ -8,13 +8,15 @@ const Timer = std.time.Timer; const Thread = std.Thread; const Atomic = std.atomic.Atomic; -const cycles_per_frame: u64 = 160 * (308 * 4); +const cycles_per_frame: u64 = 228 * (308 * 4); const clock_rate: u64 = 1 << 24; const clock_period: u64 = std.time.ns_per_s / clock_rate; const frame_period = (clock_period * cycles_per_frame); const sync_to_video: bool = true; +// One frame operates at 59.7275005696Hz + const log = std.log.scoped(.Emulation); pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void { @@ -31,6 +33,9 @@ pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void { pub fn runEmuThread(quit: *Atomic(bool), pause: *Atomic(bool), fps: *Atomic(u64), sched: *Scheduler, cpu: *Arm7tdmi, bus: *Bus) void { var timer = Timer.start() catch unreachable; + var fps_timer = Timer.start() catch unreachable; + + var wake_time: u64 = frame_period; log.info("EmuThread has begun execution", .{}); @@ -38,19 +43,32 @@ pub fn runEmuThread(quit: *Atomic(bool), pause: *Atomic(bool), fps: *Atomic(u64) if (!pause.load(.Unordered)) { runFrame(sched, cpu, bus); - const diff = timer.lap(); + const timestamp = timer.read(); + fps.store(emuFps(fps_timer.lap()), .Unordered); - var ns_late: u64 = undefined; - const didUnderflow = @subWithOverflow(u64, diff, frame_period, &ns_late); + // ns_late is non zero if we are late. + var ns_late = timestamp -| wake_time; - // We were more than an entire frame late.... - if (!didUnderflow and ns_late > frame_period) continue; + // Calculate the new Thread wake time + wake_time += frame_period; - // Negate the u64 so that we add to the amount of time sleeping - if (didUnderflow) ns_late = ~ns_late +% 1; + // If we're more than a frame late, skip the rest of this loop + if (ns_late > frame_period) continue; - if (sync_to_video) std.time.sleep(frame_period -% ns_late); - fps.store(emuFps(diff), .Unordered); + if (sync_to_video) std.time.sleep(frame_period - ns_late); + + // Backup Busy Loop Routine + // if (sync_to_video) spinLoop(&timer, wake_time); + } + } +} + +fn spinLoop(timer: *Timer, wake_time: u64) void { + while (true) { + const timestamp = timer.read(); + + if (timestamp >= wake_time) { + break; } } } diff --git a/src/main.zig b/src/main.zig index 0360e3a..767f8ab 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,6 +6,7 @@ const emu = @import("emu.zig"); const Bus = @import("Bus.zig"); const Arm7tdmi = @import("cpu.zig").Arm7tdmi; const Scheduler = @import("scheduler.zig").Scheduler; +const FpsAverage = @import("util.zig").FpsAverage; const Timer = std.time.Timer; const Thread = std.Thread; @@ -118,11 +119,12 @@ pub fn main() anyerror!void { // Init FPS Timer var dyn_title_buf: [0x100]u8 = [_]u8{0x00} ** 0x100; + var fps_avg = FpsAverage.init(); + emu_loop: while (true) { var event: SDL.SDL_Event = undefined; if (SDL.SDL_PollEvent(&event) != 0) { // Pause Emulation Thread during Input Writing - pause.store(true, .Unordered); switch (event.type) { SDL.SDL_QUIT => break :emu_loop, @@ -165,15 +167,18 @@ pub fn main() anyerror!void { } // FIXME: Is it OK just to copy the Emulator's Frame Buffer to SDL? + pause.store(true, .Unordered); const buf_ptr = bus.ppu.framebuf.ptr; _ = SDL.SDL_UpdateTexture(texture, null, buf_ptr, framebuf_pitch); _ = SDL.SDL_RenderCopy(renderer, texture, null, null); SDL.SDL_RenderPresent(renderer); - - const dyn_title = std.fmt.bufPrint(&dyn_title_buf, "{s} [Emu FPS: {d}] ", .{ title, emu_fps.load(.Unordered) }) catch unreachable; - SDL.SDL_SetWindowTitle(window, dyn_title.ptr); - pause.store(false, .Unordered); + + fps_avg.add(emu_fps.load(.Unordered)); + const avg = fps_avg.calc(); + + const dyn_title = std.fmt.bufPrint(&dyn_title_buf, "{s} [Emu: {d:0>3}fps, {d:0>3}%] ", .{ title, avg, (avg * 100 / 59) }) catch unreachable; + SDL.SDL_SetWindowTitle(window, dyn_title.ptr); } quit.store(true, .Unordered); // Terminate Emulator Thread diff --git a/src/util.zig b/src/util.zig index 2a4bc6d..05568a8 100644 --- a/src/util.zig +++ b/src/util.zig @@ -6,3 +6,30 @@ pub fn sext(comptime bits: comptime_int, value: u32) u32 { return @bitCast(u32, @bitCast(i32, value << amount) >> amount); } + +pub const FpsAverage = struct { + const Self = @This(); + + total: u64, + sample_count: u64, + + pub fn init() Self { + return .{ .total = 0, .sample_count = 0 }; + } + + pub fn add(self: *Self, sample: u64) void { + if (self.sample_count == 1000) return self.reset(sample); + + self.total += sample; + self.sample_count += 1; + } + + pub fn calc(self: *const Self) u64 { + return self.total / self.sample_count; + } + + fn reset(self: *Self, sample: u64) void { + self.total = sample; + self.sample_count = 1; + } +};