Compare commits
No commits in common. "9183e6850de553adc3ab53f3768f1fa6314c9c26" and "78b849b6ff13f6eaaaffc986d94daeedec983065" have entirely different histories.
9183e6850d
...
78b849b6ff
|
@ -4,6 +4,3 @@
|
||||||
[submodule "lib/zgui"]
|
[submodule "lib/zgui"]
|
||||||
path = lib/zgui
|
path = lib/zgui
|
||||||
url = https://git.musuka.dev/paoda/zgui
|
url = https://git.musuka.dev/paoda/zgui
|
||||||
[submodule "lib/zba-gdbstub"]
|
|
||||||
path = lib/zba-gdbstub
|
|
||||||
url = https://git.musuka.dev/paoda/zba-gdbstub
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ const builtin = @import("builtin");
|
||||||
|
|
||||||
const Sdk = @import("lib/SDL.zig/Sdk.zig");
|
const Sdk = @import("lib/SDL.zig/Sdk.zig");
|
||||||
const zgui = @import("lib/zgui/build.zig");
|
const zgui = @import("lib/zgui/build.zig");
|
||||||
const gdbstub = @import("lib/zba-gdbstub/build.zig");
|
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
// Minimum Zig Version
|
// Minimum Zig Version
|
||||||
|
@ -27,12 +26,11 @@ pub fn build(b: *std.Build) void {
|
||||||
exe.addModule("known_folders", b.dependency("known-folders", .{}).module("known-folders")); // https://github.com/ziglibs/known-folders
|
exe.addModule("known_folders", b.dependency("known-folders", .{}).module("known-folders")); // https://github.com/ziglibs/known-folders
|
||||||
exe.addModule("datetime", b.dependency("zig-datetime", .{}).module("zig-datetime")); // https://github.com/frmdstryr/zig-datetime
|
exe.addModule("datetime", b.dependency("zig-datetime", .{}).module("zig-datetime")); // https://github.com/frmdstryr/zig-datetime
|
||||||
exe.addModule("clap", b.dependency("zig-clap", .{}).module("clap")); // https://github.com/Hejsil/zig-clap
|
exe.addModule("clap", b.dependency("zig-clap", .{}).module("clap")); // https://github.com/Hejsil/zig-clap
|
||||||
|
exe.addModule("gdbstub", b.dependency("zba-gdbstub", .{}).module("gdbstub")); // https://git.musuka.dev/paoda/zba-gdbstub
|
||||||
exe.addModule("zba-util", b.dependency("zba-util", .{}).module("zba-util")); // https://git.musuka.dev/paoda/zba-util
|
exe.addModule("zba-util", b.dependency("zba-util", .{}).module("zba-util")); // https://git.musuka.dev/paoda/zba-util
|
||||||
exe.addModule("tomlz", b.dependency("tomlz", .{}).module("tomlz")); // https://github.com/mattyhall/tomlz
|
exe.addModule("tomlz", b.dependency("tomlz", .{}).module("tomlz")); // https://github.com/mattyhall/tomlz
|
||||||
exe.addModule("arm32", b.dependency("arm32", .{}).module("arm32")); // https://git.musuka.dev/paoda/arm32
|
exe.addModule("arm32", b.dependency("arm32", .{}).module("arm32")); // https://git.musuka.dev/paoda/arm32
|
||||||
|
|
||||||
exe.addModule("gdbstub", gdbstub.module(b)); // https://git.musuka.dev/paoda/gdbstub
|
|
||||||
|
|
||||||
// https://github.com/fabioarnold/nfd-zig
|
// https://github.com/fabioarnold/nfd-zig
|
||||||
const nfd_dep = b.dependency("nfd", .{ .target = target, .optimize = optimize });
|
const nfd_dep = b.dependency("nfd", .{ .target = target, .optimize = optimize });
|
||||||
exe.linkLibrary(nfd_dep.artifact("nfd"));
|
exe.linkLibrary(nfd_dep.artifact("nfd"));
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
.url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz",
|
.url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz",
|
||||||
.hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2",
|
.hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2",
|
||||||
},
|
},
|
||||||
|
.@"zba-gdbstub" = .{
|
||||||
|
.url = "https://git.musuka.dev/paoda/zba-gdbstub/archive/d3655e8f61a4943edfce40629dd2a82f9c7bf21c.tar.gz",
|
||||||
|
.hash = "122076f8e22bb55b555c9812d72d908419eda1bf07b6bb7a1f6d9ecb2bb2a49c56d0",
|
||||||
|
},
|
||||||
.@"zba-util" = .{
|
.@"zba-util" = .{
|
||||||
.url = "https://git.musuka.dev/paoda/zba-util/archive/322c798e384a0d24cc84ffcfa2e4a3ca807798a0.tar.gz",
|
.url = "https://git.musuka.dev/paoda/zba-util/archive/322c798e384a0d24cc84ffcfa2e4a3ca807798a0.tar.gz",
|
||||||
.hash = "12209ce0e729460b997706e47a53a32f1842672cd120189e612f4871731780a30ed0",
|
.hash = "12209ce0e729460b997706e47a53a32f1842672cd120189e612f4871731780a30ed0",
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 479319e7cad78c3fd38b6865c56e9fe9e78d495c
|
|
|
@ -17,10 +17,13 @@ const Timer = std.time.Timer;
|
||||||
pub const Synchro = struct {
|
pub const Synchro = struct {
|
||||||
const AtomicBool = std.atomic.Atomic(bool);
|
const AtomicBool = std.atomic.Atomic(bool);
|
||||||
|
|
||||||
paused: AtomicBool = AtomicBool.init(true), // FIXME: can ui_busy and paused be the same?
|
// UI -> Emulator
|
||||||
should_quit: AtomicBool = AtomicBool.init(false),
|
ui_busy: *AtomicBool,
|
||||||
|
paused: *AtomicBool, // FIXME: can ui_busy and paused be the same?
|
||||||
|
should_quit: *AtomicBool,
|
||||||
|
|
||||||
emu_access: std.Thread.Mutex = .{},
|
// Emulator -> UI
|
||||||
|
did_pause: *AtomicBool,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 4 Cycles in 1 dot
|
/// 4 Cycles in 1 dot
|
||||||
|
@ -49,7 +52,7 @@ const RunKind = enum {
|
||||||
LimitedFPS,
|
LimitedFPS,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: *Tracker, sync: *Synchro) void {
|
pub fn run(cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: *Tracker, sync: Synchro) 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", .{});
|
||||||
|
|
||||||
|
@ -60,7 +63,7 @@ pub fn run(cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: *Tracker, sync: *Sync
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: ?*Tracker, sync: *Synchro) void {
|
fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: ?*Tracker, sync: Synchro) 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", .{});
|
||||||
|
@ -68,16 +71,18 @@ fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *S
|
||||||
|
|
||||||
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
||||||
|
|
||||||
// FIXME: audioSync accesses emulator state without any guarantees
|
|
||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
.Unlimited, .UnlimitedFPS => {
|
.Unlimited, .UnlimitedFPS => {
|
||||||
log.info("Emulation w/out video sync", .{});
|
log.info("Emulation w/out video sync", .{});
|
||||||
|
|
||||||
while (!sync.should_quit.load(.Monotonic)) {
|
while (!sync.should_quit.load(.Monotonic)) {
|
||||||
if (sync.paused.load(.Monotonic)) continue;
|
if (sync.ui_busy.load(.Monotonic) or sync.paused.load(.Monotonic)) {
|
||||||
|
sync.did_pause.store(true, .Monotonic);
|
||||||
|
|
||||||
runFrame(sync, scheduler, cpu);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
runFrame(scheduler, cpu);
|
||||||
audioSync(audio_sync, bus_ptr.apu.stream, &bus_ptr.apu.is_buffer_full);
|
audioSync(audio_sync, bus_ptr.apu.stream, &bus_ptr.apu.is_buffer_full);
|
||||||
|
|
||||||
if (kind == .UnlimitedFPS) tracker.?.tick();
|
if (kind == .UnlimitedFPS) tracker.?.tick();
|
||||||
|
@ -89,9 +94,13 @@ fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *S
|
||||||
var wake_time: u64 = frame_period;
|
var wake_time: u64 = frame_period;
|
||||||
|
|
||||||
while (!sync.should_quit.load(.Monotonic)) {
|
while (!sync.should_quit.load(.Monotonic)) {
|
||||||
if (sync.paused.load(.Monotonic)) continue;
|
if (sync.ui_busy.load(.Monotonic) or sync.paused.load(.Monotonic)) {
|
||||||
|
sync.did_pause.store(true, .Release);
|
||||||
|
|
||||||
runFrame(sync, scheduler, cpu);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
runFrame(scheduler, cpu);
|
||||||
const new_wake_time = videoSync(&timer, wake_time);
|
const new_wake_time = videoSync(&timer, wake_time);
|
||||||
|
|
||||||
// Spin to make up the difference of OS scheduler innacuracies
|
// Spin to make up the difference of OS scheduler innacuracies
|
||||||
|
@ -109,10 +118,7 @@ fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *S
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runFrame(sync: *Synchro, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
sync.emu_access.lock();
|
|
||||||
defer sync.emu_access.unlock();
|
|
||||||
|
|
||||||
const frame_end = sched.tick + cycles_per_frame;
|
const frame_end = sched.tick + cycles_per_frame;
|
||||||
|
|
||||||
while (sched.tick < frame_end) {
|
while (sched.tick < frame_end) {
|
||||||
|
|
|
@ -175,7 +175,7 @@ pub fn draw(state: *State, win_dim: Dimensions, tex_id: GLuint, cpu: *Arm7tdmi)
|
||||||
_ = zgui.begin(window_title, .{ .flags = .{ .no_resize = true, .always_auto_resize = true } });
|
_ = zgui.begin(window_title, .{ .flags = .{ .no_resize = true, .always_auto_resize = true } });
|
||||||
defer zgui.end();
|
defer zgui.end();
|
||||||
|
|
||||||
zgui.image(@ptrFromInt(tex_id), .{ .w = w, .h = h });
|
zgui.image(@ptrFromInt(tex_id), .{ .w = w, .h = h, .uv0 = .{ 0, 1 }, .uv1 = .{ 1, 0 } });
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Any other steps to respect the copyright of the libraries I use?
|
// TODO: Any other steps to respect the copyright of the libraries I use?
|
||||||
|
|
19
src/main.zig
19
src/main.zig
|
@ -119,7 +119,16 @@ pub fn main() void {
|
||||||
var gui = Gui.init(allocator, &bus.apu, title_ptr) catch |e| exitln("failed to init gui: {}", .{e});
|
var gui = Gui.init(allocator, &bus.apu, title_ptr) catch |e| exitln("failed to init gui: {}", .{e});
|
||||||
defer gui.deinit();
|
defer gui.deinit();
|
||||||
|
|
||||||
var sync: Synchro = .{};
|
var sync: Synchro = blk: {
|
||||||
|
const AtomicBool = std.atomic.Atomic(bool);
|
||||||
|
|
||||||
|
var ui_busy = AtomicBool.init(false);
|
||||||
|
var paused = AtomicBool.init(true);
|
||||||
|
var should_quit = AtomicBool.init(false);
|
||||||
|
var did_pause = AtomicBool.init(false);
|
||||||
|
|
||||||
|
break :blk .{ .ui_busy = &ui_busy, .paused = &paused, .should_quit = &should_quit, .did_pause = &did_pause };
|
||||||
|
};
|
||||||
|
|
||||||
if (result.args.gdb != 0) {
|
if (result.args.gdb != 0) {
|
||||||
const Server = @import("gdbstub").Server;
|
const Server = @import("gdbstub").Server;
|
||||||
|
@ -136,25 +145,25 @@ pub fn main() void {
|
||||||
|
|
||||||
log.info("Starting GDB Server Thread", .{});
|
log.info("Starting GDB Server Thread", .{});
|
||||||
|
|
||||||
const thread = std.Thread.spawn(.{}, Server.run, .{ &server, allocator, &sync.should_quit }) catch |e| exitln("gdb server thread crashed: {}", .{e});
|
const thread = std.Thread.spawn(.{}, Server.run, .{ &server, allocator, sync.should_quit }) catch |e| exitln("gdb server thread crashed: {}", .{e});
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
|
||||||
gui.run(.{
|
gui.run(.{
|
||||||
.cpu = &cpu,
|
.cpu = &cpu,
|
||||||
.scheduler = &scheduler,
|
.scheduler = &scheduler,
|
||||||
.sync = &sync,
|
.sync = sync,
|
||||||
}) catch |e| exitln("main thread panicked: {}", .{e});
|
}) catch |e| exitln("main thread panicked: {}", .{e});
|
||||||
} else {
|
} else {
|
||||||
var tracker = FpsTracker.init();
|
var tracker = FpsTracker.init();
|
||||||
|
|
||||||
const thread = std.Thread.spawn(.{}, emu.run, .{ &cpu, &scheduler, &tracker, &sync }) catch |e| exitln("emu thread panicked: {}", .{e});
|
const thread = std.Thread.spawn(.{}, emu.run, .{ &cpu, &scheduler, &tracker, sync }) catch |e| exitln("emu thread panicked: {}", .{e});
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
|
||||||
gui.run(.{
|
gui.run(.{
|
||||||
.cpu = &cpu,
|
.cpu = &cpu,
|
||||||
.scheduler = &scheduler,
|
.scheduler = &scheduler,
|
||||||
.tracker = &tracker,
|
.tracker = &tracker,
|
||||||
.sync = &sync,
|
.sync = sync,
|
||||||
}) catch |e| exitln("main thread panicked: {}", .{e});
|
}) catch |e| exitln("main thread panicked: {}", .{e});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
125
src/platform.zig
125
src/platform.zig
|
@ -95,7 +95,7 @@ pub const Gui = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const RunOptions = struct {
|
const RunOptions = struct {
|
||||||
sync: *Synchro,
|
sync: Synchro,
|
||||||
tracker: ?*FpsTracker = null,
|
tracker: ?*FpsTracker = null,
|
||||||
cpu: *Arm7tdmi,
|
cpu: *Arm7tdmi,
|
||||||
scheduler: *Scheduler,
|
scheduler: *Scheduler,
|
||||||
|
@ -108,17 +108,18 @@ pub const Gui = struct {
|
||||||
|
|
||||||
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
||||||
|
|
||||||
const vao_id = opengl_impl.vao();
|
const objects = opengl_impl.createObjects();
|
||||||
defer gl.deleteVertexArrays(1, &[_]GLuint{vao_id});
|
defer gl.deleteBuffers(3, @as(*const [3]GLuint, &.{ objects.vao, objects.vbo, objects.ebo }));
|
||||||
|
|
||||||
const emu_tex = opengl_impl.screenTex(bus_ptr.ppu.framebuf.get(.Renderer));
|
const emu_tex = opengl_impl.createScreenTexture(bus_ptr.ppu.framebuf.get(.Renderer));
|
||||||
const out_tex = opengl_impl.outTex();
|
const out_tex = opengl_impl.createOutputTexture();
|
||||||
defer gl.deleteTextures(2, &[_]GLuint{ emu_tex, out_tex });
|
defer gl.deleteTextures(2, &[_]GLuint{ emu_tex, out_tex });
|
||||||
|
|
||||||
const fbo_id = try opengl_impl.frameBuffer(out_tex);
|
const fbo_id = try opengl_impl.createFrameBuffer(out_tex);
|
||||||
defer gl.deleteFramebuffers(1, &fbo_id);
|
defer gl.deleteFramebuffers(1, &fbo_id);
|
||||||
|
|
||||||
const prog_id = try opengl_impl.program(); // Dynamic Shaders?
|
// TODO: Support dynamically switching shaders?
|
||||||
|
const prog_id = try opengl_impl.compileShaders();
|
||||||
defer gl.deleteProgram(prog_id);
|
defer gl.deleteProgram(prog_id);
|
||||||
|
|
||||||
var win_dim: Dimensions = default_dim;
|
var win_dim: Dimensions = default_dim;
|
||||||
|
@ -126,7 +127,7 @@ pub const Gui = struct {
|
||||||
emu_loop: while (true) {
|
emu_loop: while (true) {
|
||||||
// Outside of `SDL.SDL_QUIT` below, the DearImgui UI might signal that the program
|
// Outside of `SDL.SDL_QUIT` below, the DearImgui UI might signal that the program
|
||||||
// should exit, in which case we should also handle this
|
// should exit, in which case we should also handle this
|
||||||
if (self.state.should_quit or sync.should_quit.load(.Monotonic)) break :emu_loop;
|
if (self.state.should_quit) break :emu_loop;
|
||||||
|
|
||||||
var event: SDL.SDL_Event = undefined;
|
var event: SDL.SDL_Event = undefined;
|
||||||
while (SDL.SDL_PollEvent(&event) != 0) {
|
while (SDL.SDL_PollEvent(&event) != 0) {
|
||||||
|
@ -206,9 +207,21 @@ pub const Gui = struct {
|
||||||
self.state.emulation = .Inactive;
|
self.state.emulation = .Inactive;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.Active => {
|
.Active => blk: {
|
||||||
sync.emu_access.lock();
|
sync.ui_busy.store(true, .Monotonic);
|
||||||
defer sync.emu_access.unlock();
|
defer sync.ui_busy.store(false, .Monotonic);
|
||||||
|
|
||||||
|
// spin until we know the emu is paused :)
|
||||||
|
|
||||||
|
const timeout = 0x100000;
|
||||||
|
wait_loop: for (0..timeout) |i| {
|
||||||
|
const ret = sync.did_pause.compareAndSwap(true, false, .Acquire, .Monotonic);
|
||||||
|
if (ret == null) break :wait_loop;
|
||||||
|
|
||||||
|
if (i == timeout - 1) break :blk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// while (sync.did_pause.compareAndSwap(true, false, .Acquire, .Acquire) != null) std.atomic.spinLoopHint();
|
||||||
|
|
||||||
// Add FPS count to the histogram
|
// Add FPS count to the histogram
|
||||||
if (tracker) |t| self.state.fps_hist.push(t.value()) catch {};
|
if (tracker) |t| self.state.fps_hist.push(t.value()) catch {};
|
||||||
|
@ -218,8 +231,9 @@ pub const Gui = struct {
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo_id);
|
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo_id);
|
||||||
defer gl.bindFramebuffer(gl.FRAMEBUFFER, 0);
|
defer gl.bindFramebuffer(gl.FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
const buf = bus_ptr.ppu.framebuf.get(.Renderer);
|
||||||
gl.viewport(0, 0, gba_width, gba_height);
|
gl.viewport(0, 0, gba_width, gba_height);
|
||||||
opengl_impl.drawScreen(emu_tex, prog_id, vao_id, bus_ptr.ppu.framebuf.get(.Renderer));
|
opengl_impl.drawScreenTexture(emu_tex, prog_id, objects, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: We only really care about locking the audio device (and therefore writing silence)
|
// FIXME: We only really care about locking the audio device (and therefore writing silence)
|
||||||
|
@ -298,24 +312,44 @@ fn panic() noreturn {
|
||||||
}
|
}
|
||||||
|
|
||||||
const opengl_impl = struct {
|
const opengl_impl = struct {
|
||||||
fn drawScreen(tex_id: GLuint, prog_id: GLuint, vao_id: GLuint, buf: []const u8) void {
|
// zig fmt: off
|
||||||
|
const vertices: [32]f32 = [_]f32{
|
||||||
|
// Positions // Colours // Texture Coords
|
||||||
|
1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, // Top Right
|
||||||
|
1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, // Bottom Right
|
||||||
|
-1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, // Bottom Left
|
||||||
|
-1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, // Top Left
|
||||||
|
};
|
||||||
|
|
||||||
|
const indices: [6]u32 = [_]u32{
|
||||||
|
0, 1, 3, // First Triangle
|
||||||
|
1, 2, 3, // Second Triangle
|
||||||
|
};
|
||||||
|
// zig fmt: on
|
||||||
|
|
||||||
|
const Objects = struct { vao: GLuint, vbo: GLuint, ebo: GLuint };
|
||||||
|
|
||||||
|
fn drawScreenTexture(tex_id: GLuint, prog_id: GLuint, ids: Objects, buf: []const u8) void {
|
||||||
gl.bindTexture(gl.TEXTURE_2D, tex_id);
|
gl.bindTexture(gl.TEXTURE_2D, tex_id);
|
||||||
defer gl.bindTexture(gl.TEXTURE_2D, 0);
|
defer gl.bindTexture(gl.TEXTURE_2D, 0);
|
||||||
|
|
||||||
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gba_width, gba_height, gl.RGBA, gl.UNSIGNED_INT_8_8_8_8, buf.ptr);
|
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gba_width, gba_height, gl.RGBA, gl.UNSIGNED_INT_8_8_8_8, buf.ptr);
|
||||||
|
|
||||||
// Bind VAO
|
// Bind VAO, EBO. VBO not bound
|
||||||
gl.bindVertexArray(vao_id);
|
gl.bindVertexArray(ids.vao); // VAO
|
||||||
defer gl.bindVertexArray(0);
|
defer gl.bindVertexArray(0);
|
||||||
|
|
||||||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ids.ebo); // EBO
|
||||||
|
defer gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
// Use compiled frag + vertex shader
|
// Use compiled frag + vertex shader
|
||||||
gl.useProgram(prog_id);
|
gl.useProgram(prog_id);
|
||||||
defer gl.useProgram(0);
|
defer gl.useProgram(0);
|
||||||
|
|
||||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3);
|
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn program() !GLuint {
|
fn compileShaders() !GLuint {
|
||||||
const vert_shader = @embedFile("shader/pixelbuf.vert");
|
const vert_shader = @embedFile("shader/pixelbuf.vert");
|
||||||
const frag_shader = @embedFile("shader/pixelbuf.frag");
|
const frag_shader = @embedFile("shader/pixelbuf.frag");
|
||||||
|
|
||||||
|
@ -335,22 +369,50 @@ const opengl_impl = struct {
|
||||||
|
|
||||||
if (!shader.didCompile(fs)) return error.FragmentCompileError;
|
if (!shader.didCompile(fs)) return error.FragmentCompileError;
|
||||||
|
|
||||||
const prog = gl.createProgram();
|
const program = gl.createProgram();
|
||||||
gl.attachShader(prog, vs);
|
gl.attachShader(program, vs);
|
||||||
gl.attachShader(prog, fs);
|
gl.attachShader(program, fs);
|
||||||
gl.linkProgram(prog);
|
gl.linkProgram(program);
|
||||||
|
|
||||||
return prog;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vao() GLuint {
|
// Returns the VAO ID since it's used in run()
|
||||||
|
fn createObjects() Objects {
|
||||||
var vao_id: GLuint = undefined;
|
var vao_id: GLuint = undefined;
|
||||||
gl.genVertexArrays(1, &vao_id);
|
var vbo_id: GLuint = undefined;
|
||||||
|
var ebo_id: GLuint = undefined;
|
||||||
|
|
||||||
return vao_id;
|
gl.genVertexArrays(1, &vao_id);
|
||||||
|
gl.genBuffers(1, &vbo_id);
|
||||||
|
gl.genBuffers(1, &ebo_id);
|
||||||
|
|
||||||
|
gl.bindVertexArray(vao_id);
|
||||||
|
defer gl.bindVertexArray(0);
|
||||||
|
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, vbo_id);
|
||||||
|
defer gl.bindBuffer(gl.ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo_id);
|
||||||
|
defer gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(@TypeOf(vertices)), &vertices, gl.STATIC_DRAW);
|
||||||
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, @sizeOf(@TypeOf(indices)), &indices, gl.STATIC_DRAW);
|
||||||
|
|
||||||
|
// Position
|
||||||
|
gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), null); // lmao
|
||||||
|
gl.enableVertexAttribArray(0);
|
||||||
|
// Colour
|
||||||
|
gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), @ptrFromInt((3 * @sizeOf(f32))));
|
||||||
|
gl.enableVertexAttribArray(1);
|
||||||
|
// Texture Coord
|
||||||
|
gl.vertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), @ptrFromInt((6 * @sizeOf(f32))));
|
||||||
|
gl.enableVertexAttribArray(2);
|
||||||
|
|
||||||
|
return .{ .vao = vao_id, .vbo = vbo_id, .ebo = ebo_id };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screenTex(buf: []const u8) GLuint {
|
fn createScreenTexture(buf: []const u8) GLuint {
|
||||||
var tex_id: GLuint = undefined;
|
var tex_id: GLuint = undefined;
|
||||||
gl.genTextures(1, &tex_id);
|
gl.genTextures(1, &tex_id);
|
||||||
|
|
||||||
|
@ -365,7 +427,7 @@ const opengl_impl = struct {
|
||||||
return tex_id;
|
return tex_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn outTex() GLuint {
|
fn createOutputTexture() GLuint {
|
||||||
var tex_id: GLuint = undefined;
|
var tex_id: GLuint = undefined;
|
||||||
gl.genTextures(1, &tex_id);
|
gl.genTextures(1, &tex_id);
|
||||||
|
|
||||||
|
@ -380,7 +442,7 @@ const opengl_impl = struct {
|
||||||
return tex_id;
|
return tex_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frameBuffer(tex_id: GLuint) !GLuint {
|
fn createFrameBuffer(tex_id: GLuint) !GLuint {
|
||||||
var fbo_id: GLuint = undefined;
|
var fbo_id: GLuint = undefined;
|
||||||
gl.genFramebuffers(1, &fbo_id);
|
gl.genFramebuffers(1, &fbo_id);
|
||||||
|
|
||||||
|
@ -388,7 +450,9 @@ const opengl_impl = struct {
|
||||||
defer gl.bindFramebuffer(gl.FRAMEBUFFER, 0);
|
defer gl.bindFramebuffer(gl.FRAMEBUFFER, 0);
|
||||||
|
|
||||||
gl.framebufferTexture(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex_id, 0);
|
gl.framebufferTexture(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex_id, 0);
|
||||||
gl.drawBuffers(1, &@as(GLuint, gl.COLOR_ATTACHMENT0));
|
|
||||||
|
const draw_buffers: [1]GLuint = .{gl.COLOR_ATTACHMENT0};
|
||||||
|
gl.drawBuffers(1, &draw_buffers);
|
||||||
|
|
||||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)
|
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)
|
||||||
return error.FrameBufferObejctInitFailed;
|
return error.FrameBufferObejctInitFailed;
|
||||||
|
@ -397,7 +461,8 @@ const opengl_impl = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const shader = struct {
|
const shader = struct {
|
||||||
const log = std.log.scoped(.shader);
|
const Kind = enum { vertex, fragment };
|
||||||
|
const log = std.log.scoped(.Shader);
|
||||||
|
|
||||||
fn didCompile(id: gl.GLuint) bool {
|
fn didCompile(id: gl.GLuint) bool {
|
||||||
var success: gl.GLint = undefined;
|
var success: gl.GLint = undefined;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#version 330 core
|
#version 330 core
|
||||||
out vec4 frag_color;
|
out vec4 frag_color;
|
||||||
|
|
||||||
|
in vec3 color;
|
||||||
in vec2 uv;
|
in vec2 uv;
|
||||||
|
|
||||||
uniform sampler2D screen;
|
uniform sampler2D screen;
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
#version 330 core
|
#version 330 core
|
||||||
|
layout (location = 0) in vec3 pos;
|
||||||
|
layout (location = 1) in vec3 in_color;
|
||||||
|
layout (location = 2) in vec2 in_uv;
|
||||||
|
|
||||||
|
out vec3 color;
|
||||||
out vec2 uv;
|
out vec2 uv;
|
||||||
|
|
||||||
const vec2 pos[3] = vec2[3](vec2(-1.0f, -1.0f), vec2(-1.0f, 3.0f), vec2(3.0f, -1.0f));
|
|
||||||
const vec2 uvs[3] = vec2[3](vec2( 0.0f, 0.0f), vec2( 0.0f, 2.0f), vec2(2.0f, 0.0f));
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
uv = uvs[gl_VertexID];
|
color = in_color;
|
||||||
gl_Position = vec4(pos[gl_VertexID], 0.0, 1.0);
|
uv = in_uv;
|
||||||
}
|
gl_Position = vec4(pos, 1.0);
|
||||||
|
}
|
Loading…
Reference in New Issue