Add a GUI to ZBA #7
|
@ -4,7 +4,7 @@ const config = @import("../config.zig");
|
||||||
|
|
||||||
const Scheduler = @import("scheduler.zig").Scheduler;
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
|
const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
|
||||||
const FpsTracker = @import("../util.zig").FpsTracker;
|
const Tracker = @import("../util.zig").FpsTracker;
|
||||||
|
|
||||||
const Timer = std.time.Timer;
|
const Timer = std.time.Timer;
|
||||||
const Atomic = std.atomic.Atomic;
|
const Atomic = std.atomic.Atomic;
|
||||||
|
@ -35,18 +35,18 @@ const RunKind = enum {
|
||||||
LimitedFPS,
|
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;
|
const audio_sync = config.config().guest.audio_sync and !config.config().host.mute;
|
||||||
if (audio_sync) log.info("Audio sync enabled", .{});
|
if (audio_sync) log.info("Audio sync enabled", .{});
|
||||||
|
|
||||||
if (config.config().guest.video_sync) {
|
if (config.config().guest.video_sync) {
|
||||||
inner(.LimitedFPS, audio_sync, quit, scheduler, cpu, tracker);
|
inner(.LimitedFPS, audio_sync, quit, pause, cpu, scheduler, tracker);
|
||||||
} else {
|
} 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) {
|
if (kind == .UnlimitedFPS or kind == .LimitedFPS) {
|
||||||
std.debug.assert(tracker != null);
|
std.debug.assert(tracker != null);
|
||||||
log.info("FPS tracking enabled", .{});
|
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", .{});
|
log.info("Emulation w/out video sync", .{});
|
||||||
|
|
||||||
while (!quit.load(.Monotonic)) {
|
while (!quit.load(.Monotonic)) {
|
||||||
|
if (pause.load(.Monotonic)) continue;
|
||||||
|
|
||||||
runFrame(scheduler, cpu);
|
runFrame(scheduler, cpu);
|
||||||
audioSync(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
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;
|
var wake_time: u64 = frame_period;
|
||||||
|
|
||||||
while (!quit.load(.Monotonic)) {
|
while (!quit.load(.Monotonic)) {
|
||||||
|
if (pause.load(.Monotonic)) continue;
|
||||||
|
|
||||||
runFrame(scheduler, cpu);
|
runFrame(scheduler, cpu);
|
||||||
const new_wake_time = videoSync(&timer, wake_time);
|
const new_wake_time = videoSync(&timer, wake_time);
|
||||||
|
|
||||||
|
|
|
@ -389,8 +389,9 @@ pub const Gui = struct {
|
||||||
const fbo_id = try Self.genFrameBufObject(out_tex);
|
const fbo_id = try Self.genFrameBufObject(out_tex);
|
||||||
defer gl.deleteFramebuffers(1, &fbo_id);
|
defer gl.deleteFramebuffers(1, &fbo_id);
|
||||||
|
|
||||||
var quit = std.atomic.Atomic(bool).init(false);
|
|
||||||
var tracker = FpsTracker.init();
|
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;
|
var title_buf: [0x100]u8 = undefined;
|
||||||
|
|
||||||
|
@ -441,12 +442,6 @@ pub const Gui = struct {
|
||||||
SDL.SDLK_s => keyinput.shoulder_r.set(),
|
SDL.SDLK_s => keyinput.shoulder_r.set(),
|
||||||
SDL.SDLK_RETURN => keyinput.start.set(),
|
SDL.SDLK_RETURN => keyinput.start.set(),
|
||||||
SDL.SDLK_RSHIFT => keyinput.select.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 => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,37 +451,44 @@ pub const Gui = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We Access non-atomic parts of the Emulator here
|
||||||
{
|
{
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo_id);
|
pause.store(true, .Monotonic);
|
||||||
defer gl.bindFramebuffer(gl.FRAMEBUFFER, 0);
|
defer pause.store(false, .Monotonic);
|
||||||
|
|
||||||
const buf = cpu.bus.ppu.framebuf.get(.Renderer);
|
self.state.fps_hist.push(tracker.value()) catch {};
|
||||||
gl.viewport(0, 0, gba_width, gba_height);
|
|
||||||
self.drawGbaTexture(obj_ids, emu_tex, buf);
|
// 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);
|
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 {
|
fn glGetProcAddress(ctx: SDL.SDL_GLContext, proc: [:0]const u8) ?*anyopaque {
|
||||||
|
|
Loading…
Reference in New Issue