chore(emu): refactor code
This commit is contained in:
parent
0772259db5
commit
4d99c8c139
121
src/core/emu.zig
121
src/core/emu.zig
|
@ -35,18 +35,55 @@ const RunKind = enum {
|
|||
LimitedFPS,
|
||||
};
|
||||
|
||||
pub fn run(quit: *Atomic(bool), fps: *FpsTracker, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||
if (config.config().guest.audio_sync) log.info("Audio sync enabled", .{});
|
||||
|
||||
// TODO: Support the other modes?
|
||||
const video_sync: RunKind = if (config.config().guest.video_sync) .LimitedFPS else .UnlimitedFPS;
|
||||
pub fn run(quit: *Atomic(bool), scheduler: *Scheduler, cpu: *Arm7tdmi, tracker: *FpsTracker) void {
|
||||
const audio_sync = config.config().guest.audio_sync;
|
||||
if (audio_sync) log.info("Audio sync enabled", .{});
|
||||
|
||||
switch (video_sync) {
|
||||
.Unlimited => runUnsynchronized(audio_sync, quit, sched, cpu, null),
|
||||
.Limited => runSynchronized(audio_sync, quit, sched, cpu, null),
|
||||
.UnlimitedFPS => runUnsynchronized(audio_sync, quit, sched, cpu, fps),
|
||||
.LimitedFPS => runSynchronized(audio_sync, quit, sched, cpu, fps),
|
||||
if (config.config().guest.video_sync) {
|
||||
inner(.LimitedFPS, audio_sync, quit, scheduler, cpu, tracker);
|
||||
} else {
|
||||
inner(.UnlimitedFPS, audio_sync, quit, scheduler, cpu, tracker);
|
||||
}
|
||||
}
|
||||
|
||||
fn inner(comptime kind: RunKind, audio_sync: bool, quit: *Atomic(bool), scheduler: *Scheduler, cpu: *Arm7tdmi, tracker: ?*FpsTracker) void {
|
||||
if (kind == .UnlimitedFPS or kind == .LimitedFPS) {
|
||||
std.debug.assert(tracker != null);
|
||||
log.info("FPS tracking enabled", .{});
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
.Unlimited, .UnlimitedFPS => {
|
||||
log.info("Emulation w/out video sync", .{});
|
||||
|
||||
while (!quit.load(.SeqCst)) {
|
||||
runFrame(scheduler, cpu);
|
||||
audioSync(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||
|
||||
if (kind == .UnlimitedFPS) tracker.?.tick();
|
||||
}
|
||||
},
|
||||
.Limited, .LimitedFPS => {
|
||||
log.info("Emulation w/ video sync", .{});
|
||||
var timer = Timer.start() catch @panic("failed to initalize std.timer.Timer");
|
||||
var wake_time: u64 = frame_period;
|
||||
|
||||
while (!quit.load(.SeqCst)) {
|
||||
runFrame(scheduler, cpu);
|
||||
const new_wake_time = videoSync(&timer, wake_time);
|
||||
|
||||
// Spin to make up the difference of OS scheduler innacuracies
|
||||
// If we happen to also be syncing to audio, we choose to spin on
|
||||
// the amount of time needed for audio to catch up rather than
|
||||
// our expected wake-up time
|
||||
|
||||
audioSync(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||
if (!audio_sync) spinLoop(&timer, wake_time);
|
||||
wake_time = new_wake_time;
|
||||
|
||||
if (kind == .LimitedFPS) tracker.?.tick();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +104,7 @@ pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi) void {
|
|||
}
|
||||
}
|
||||
|
||||
fn syncToAudio(audio_sync: bool, stream: *SDL.SDL_AudioStream, is_buffer_full: *bool) void {
|
||||
fn audioSync(audio_sync: bool, stream: *SDL.SDL_AudioStream, is_buffer_full: *bool) void {
|
||||
const sample_size = 2 * @sizeOf(u16);
|
||||
const max_buf_size: c_int = 0x400;
|
||||
|
||||
|
@ -84,70 +121,16 @@ fn syncToAudio(audio_sync: bool, stream: *SDL.SDL_AudioStream, is_buffer_full: *
|
|||
}
|
||||
}
|
||||
|
||||
pub fn runUnsynchronized(audio_sync: bool, quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, fps: ?*FpsTracker) void {
|
||||
log.info("Emulation thread w/out video sync", .{});
|
||||
|
||||
if (fps) |tracker| {
|
||||
log.info("FPS Tracking Enabled", .{});
|
||||
|
||||
while (!quit.load(.SeqCst)) {
|
||||
runFrame(sched, cpu);
|
||||
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||
|
||||
tracker.tick();
|
||||
}
|
||||
} else {
|
||||
while (!quit.load(.SeqCst)) {
|
||||
runFrame(sched, cpu);
|
||||
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn runSynchronized(audio_sync: bool, quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, fps: ?*FpsTracker) void {
|
||||
log.info("Emulation thread w/ video sync", .{});
|
||||
var timer = Timer.start() catch std.debug.panic("Failed to initialize std.timer.Timer", .{});
|
||||
var wake_time: u64 = frame_period;
|
||||
|
||||
if (fps) |tracker| {
|
||||
log.info("FPS Tracking Enabled", .{});
|
||||
|
||||
while (!quit.load(.SeqCst)) {
|
||||
runFrame(sched, cpu);
|
||||
const new_wake_time = blockOnVideo(&timer, wake_time);
|
||||
|
||||
// Spin to make up the difference of OS scheduler innacuracies
|
||||
// If we happen to also be syncing to audio, we choose to spin on
|
||||
// the amount of time needed for audio to catch up rather than
|
||||
// our expected wake-up time
|
||||
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||
if (!audio_sync) spinLoop(&timer, wake_time);
|
||||
wake_time = new_wake_time;
|
||||
|
||||
tracker.tick();
|
||||
}
|
||||
} else {
|
||||
while (!quit.load(.SeqCst)) {
|
||||
runFrame(sched, cpu);
|
||||
const new_wake_time = blockOnVideo(&timer, wake_time);
|
||||
|
||||
// see above comment
|
||||
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||
if (!audio_sync) spinLoop(&timer, wake_time);
|
||||
wake_time = new_wake_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fn blockOnVideo(timer: *Timer, wake_time: u64) u64 {
|
||||
fn videoSync(timer: *Timer, wake_time: u64) u64 {
|
||||
// Use the OS scheduler to put the emulation thread to sleep
|
||||
const maybe_recalc_wake_time = sleep(timer, wake_time);
|
||||
const recalculated = sleep(timer, wake_time);
|
||||
|
||||
// If sleep() determined we need to adjust our wake up time, do so
|
||||
// otherwise predict our next wake up time according to the frame period
|
||||
return if (maybe_recalc_wake_time) |recalc| recalc else wake_time + frame_period;
|
||||
return recalculated orelse wake_time + frame_period;
|
||||
}
|
||||
|
||||
// TODO: Better sleep impl?
|
||||
fn sleep(timer: *Timer, wake_time: u64) ?u64 {
|
||||
// const step = std.time.ns_per_ms * 10; // 10ms
|
||||
const timestamp = timer.read();
|
||||
|
|
|
@ -60,9 +60,9 @@ pub const Gui = struct {
|
|||
|
||||
pub fn run(self: *Self, cpu: *Arm7tdmi, scheduler: *Scheduler) !void {
|
||||
var quit = std.atomic.Atomic(bool).init(false);
|
||||
var frame_rate = FpsTracker.init();
|
||||
var tracker = FpsTracker.init();
|
||||
|
||||
const thread = try std.Thread.spawn(.{}, emu.run, .{ &quit, &frame_rate, scheduler, cpu });
|
||||
const thread = try std.Thread.spawn(.{}, emu.run, .{ &quit, scheduler, cpu, &tracker });
|
||||
defer thread.join();
|
||||
|
||||
var title_buf: [0x100]u8 = [_]u8{0} ** 0x100;
|
||||
|
@ -129,7 +129,7 @@ pub const Gui = struct {
|
|||
_ = SDL.SDL_RenderCopy(self.renderer, self.texture, null, null);
|
||||
SDL.SDL_RenderPresent(self.renderer);
|
||||
|
||||
const dyn_title = std.fmt.bufPrint(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, frame_rate.value() }) catch unreachable;
|
||||
const dyn_title = std.fmt.bufPrint(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, tracker.value() }) catch unreachable;
|
||||
SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue