chore(emu): refactor code
This commit is contained in:
		
							
								
								
									
										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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user