feat: pause emu when UI reads emu state
This commit is contained in:
		| @@ -4,7 +4,7 @@ const config = @import("../config.zig"); | ||||
|  | ||||
| const Scheduler = @import("scheduler.zig").Scheduler; | ||||
| const Arm7tdmi = @import("cpu.zig").Arm7tdmi; | ||||
| const FpsTracker = @import("../util.zig").FpsTracker; | ||||
| const Tracker = @import("../util.zig").FpsTracker; | ||||
|  | ||||
| const Timer = std.time.Timer; | ||||
| const Atomic = std.atomic.Atomic; | ||||
| @@ -35,18 +35,18 @@ const RunKind = enum { | ||||
|     LimitedFPS, | ||||
| }; | ||||
|  | ||||
| pub fn run(quit: *Atomic(bool), scheduler: *Scheduler, cpu: *Arm7tdmi, tracker: *FpsTracker) void { | ||||
| pub fn run(quit: *Atomic(bool), pause: *Atomic(bool), cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: *Tracker) void { | ||||
|     const audio_sync = config.config().guest.audio_sync and !config.config().host.mute; | ||||
|     if (audio_sync) log.info("Audio sync enabled", .{}); | ||||
|  | ||||
|     if (config.config().guest.video_sync) { | ||||
|         inner(.LimitedFPS, audio_sync, quit, scheduler, cpu, tracker); | ||||
|         inner(.LimitedFPS, audio_sync, quit, pause, cpu, scheduler, tracker); | ||||
|     } else { | ||||
|         inner(.UnlimitedFPS, audio_sync, quit, scheduler, cpu, tracker); | ||||
|         inner(.UnlimitedFPS, audio_sync, quit, pause, cpu, scheduler, tracker); | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn inner(comptime kind: RunKind, audio_sync: bool, quit: *Atomic(bool), scheduler: *Scheduler, cpu: *Arm7tdmi, tracker: ?*FpsTracker) void { | ||||
| fn inner(comptime kind: RunKind, audio_sync: bool, quit: *Atomic(bool), pause: *Atomic(bool), cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: ?*Tracker) void { | ||||
|     if (kind == .UnlimitedFPS or kind == .LimitedFPS) { | ||||
|         std.debug.assert(tracker != null); | ||||
|         log.info("FPS tracking enabled", .{}); | ||||
| @@ -57,6 +57,8 @@ fn inner(comptime kind: RunKind, audio_sync: bool, quit: *Atomic(bool), schedule | ||||
|             log.info("Emulation w/out video sync", .{}); | ||||
|  | ||||
|             while (!quit.load(.Monotonic)) { | ||||
|                 if (pause.load(.Monotonic)) continue; | ||||
|  | ||||
|                 runFrame(scheduler, cpu); | ||||
|                 audioSync(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full); | ||||
|  | ||||
| @@ -69,6 +71,8 @@ fn inner(comptime kind: RunKind, audio_sync: bool, quit: *Atomic(bool), schedule | ||||
|             var wake_time: u64 = frame_period; | ||||
|  | ||||
|             while (!quit.load(.Monotonic)) { | ||||
|                 if (pause.load(.Monotonic)) continue; | ||||
|  | ||||
|                 runFrame(scheduler, cpu); | ||||
|                 const new_wake_time = videoSync(&timer, wake_time); | ||||
|  | ||||
|   | ||||
| @@ -389,8 +389,9 @@ pub const Gui = struct { | ||||
|         const fbo_id = try Self.genFrameBufObject(out_tex); | ||||
|         defer gl.deleteFramebuffers(1, &fbo_id); | ||||
|  | ||||
|         var quit = std.atomic.Atomic(bool).init(false); | ||||
|         var tracker = FpsTracker.init(); | ||||
|         var quit = std.atomic.Atomic(bool).init(false); | ||||
|         var pause = std.atomic.Atomic(bool).init(false); | ||||
|  | ||||
|         var title_buf: [0x100]u8 = undefined; | ||||
|  | ||||
| @@ -441,12 +442,6 @@ pub const Gui = struct { | ||||
|                             SDL.SDLK_s => keyinput.shoulder_r.set(), | ||||
|                             SDL.SDLK_RETURN => keyinput.start.set(), | ||||
|                             SDL.SDLK_RSHIFT => keyinput.select.set(), | ||||
|                             SDL.SDLK_i => { | ||||
|                                 comptime std.debug.assert(sample_format == SDL.AUDIO_U16); | ||||
|                                 log.err("Sample Count: {}", .{@intCast(u32, SDL.SDL_AudioStreamAvailable(cpu.bus.apu.stream)) / (2 * @sizeOf(u16))}); | ||||
|                             }, | ||||
|                             // SDL.SDLK_j => log.err("Scheduler Capacity: {} | Scheduler Event Count: {}", .{ scheduler.queue.capacity(), scheduler.queue.count() }), | ||||
|                             SDL.SDLK_k => {}, | ||||
|                             else => {}, | ||||
|                         } | ||||
|  | ||||
| @@ -456,37 +451,44 @@ pub const Gui = struct { | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // We Access non-atomic parts of the Emulator here | ||||
|             { | ||||
|                 gl.bindFramebuffer(gl.FRAMEBUFFER, fbo_id); | ||||
|                 defer gl.bindFramebuffer(gl.FRAMEBUFFER, 0); | ||||
|                 pause.store(true, .Monotonic); | ||||
|                 defer pause.store(false, .Monotonic); | ||||
|  | ||||
|                 const buf = cpu.bus.ppu.framebuf.get(.Renderer); | ||||
|                 gl.viewport(0, 0, gba_width, gba_height); | ||||
|                 self.drawGbaTexture(obj_ids, emu_tex, buf); | ||||
|                 self.state.fps_hist.push(tracker.value()) catch {}; | ||||
|  | ||||
|                 // Draw GBA Screen to Texture | ||||
|                 { | ||||
|                     gl.bindFramebuffer(gl.FRAMEBUFFER, fbo_id); | ||||
|                     defer gl.bindFramebuffer(gl.FRAMEBUFFER, 0); | ||||
|  | ||||
|                     const buf = cpu.bus.ppu.framebuf.get(.Renderer); | ||||
|                     gl.viewport(0, 0, gba_width, gba_height); | ||||
|                     self.drawGbaTexture(obj_ids, emu_tex, buf); | ||||
|                 } | ||||
|  | ||||
|                 // Background Colour | ||||
|                 const size = zgui.io.getDisplaySize(); | ||||
|                 gl.viewport(0, 0, @floatToInt(c_int, size[0]), @floatToInt(c_int, size[1])); | ||||
|                 gl.clearColor(0, 0, 0, 1.0); | ||||
|                 gl.clear(gl.COLOR_BUFFER_BIT); | ||||
|  | ||||
|                 if (tracker) |t| { | ||||
|                     const emu_fps = t.value(); | ||||
|                     self.state.fps_hist.push(emu_fps) catch {}; | ||||
|  | ||||
|                     const dyn_title = std.fmt.bufPrintZ(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, emu_fps }) catch unreachable; | ||||
|                     SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr); | ||||
|                 } | ||||
|  | ||||
|                 zgui.backend.newFrame(width, height); | ||||
|                 self.draw(out_tex, cpu); | ||||
|                 zgui.backend.draw(); | ||||
|             } | ||||
|  | ||||
|             // Background | ||||
|             const size = zgui.io.getDisplaySize(); | ||||
|             gl.viewport(0, 0, @floatToInt(c_int, size[0]), @floatToInt(c_int, size[1])); | ||||
|             gl.clearColor(0, 0, 0, 1.0); | ||||
|             gl.clear(gl.COLOR_BUFFER_BIT); | ||||
|  | ||||
|             zgui.backend.newFrame(width, height); | ||||
|             self.draw(out_tex, cpu); | ||||
|             zgui.backend.draw(); | ||||
|  | ||||
|             SDL.SDL_GL_SwapWindow(self.window); | ||||
|  | ||||
|             if (tracker) |t| { | ||||
|                 const emu_fps = t.value(); | ||||
|                 self.state.fps_hist.push(emu_fps) catch {}; | ||||
|  | ||||
|                 const dyn_title = std.fmt.bufPrintZ(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, emu_fps }) catch unreachable; | ||||
|                 SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         quit.store(true, .Monotonic); // Terminate Emulator Thread | ||||
|     } | ||||
|  | ||||
|     fn glGetProcAddress(ctx: SDL.SDL_GLContext, proc: [:0]const u8) ?*anyopaque { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user