Compare commits
No commits in common. "9183e6850de553adc3ab53f3768f1fa6314c9c26" and "78b849b6ff13f6eaaaffc986d94daeedec983065" have entirely different histories.
9183e6850d
...
78b849b6ff
|
@ -4,6 +4,3 @@
|
|||
[submodule "lib/zgui"]
|
||||
path = lib/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 zgui = @import("lib/zgui/build.zig");
|
||||
const gdbstub = @import("lib/zba-gdbstub/build.zig");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
// 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("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("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("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("gdbstub", gdbstub.module(b)); // https://git.musuka.dev/paoda/gdbstub
|
||||
|
||||
// https://github.com/fabioarnold/nfd-zig
|
||||
const nfd_dep = b.dependency("nfd", .{ .target = target, .optimize = optimize });
|
||||
exe.linkLibrary(nfd_dep.artifact("nfd"));
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
.url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz",
|
||||
.hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2",
|
||||
},
|
||||
.@"zba-gdbstub" = .{
|
||||
.url = "https://git.musuka.dev/paoda/zba-gdbstub/archive/d3655e8f61a4943edfce40629dd2a82f9c7bf21c.tar.gz",
|
||||
.hash = "122076f8e22bb55b555c9812d72d908419eda1bf07b6bb7a1f6d9ecb2bb2a49c56d0",
|
||||
},
|
||||
.@"zba-util" = .{
|
||||
.url = "https://git.musuka.dev/paoda/zba-util/archive/322c798e384a0d24cc84ffcfa2e4a3ca807798a0.tar.gz",
|
||||
.hash = "12209ce0e729460b997706e47a53a32f1842672cd120189e612f4871731780a30ed0",
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 479319e7cad78c3fd38b6865c56e9fe9e78d495c
|
|
@ -17,10 +17,13 @@ const Timer = std.time.Timer;
|
|||
pub const Synchro = struct {
|
||||
const AtomicBool = std.atomic.Atomic(bool);
|
||||
|
||||
paused: AtomicBool = AtomicBool.init(true), // FIXME: can ui_busy and paused be the same?
|
||||
should_quit: AtomicBool = AtomicBool.init(false),
|
||||
// UI -> Emulator
|
||||
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
|
||||
|
@ -49,7 +52,7 @@ const RunKind = enum {
|
|||
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;
|
||||
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) {
|
||||
std.debug.assert(tracker != null);
|
||||
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));
|
||||
|
||||
// FIXME: audioSync accesses emulator state without any guarantees
|
||||
|
||||
switch (kind) {
|
||||
.Unlimited, .UnlimitedFPS => {
|
||||
log.info("Emulation w/out video sync", .{});
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
// 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 {
|
||||
sync.emu_access.lock();
|
||||
defer sync.emu_access.unlock();
|
||||
|
||||
pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||
const frame_end = sched.tick + cycles_per_frame;
|
||||
|
||||
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 } });
|
||||
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?
|
||||
|
|
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});
|
||||
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) {
|
||||
const Server = @import("gdbstub").Server;
|
||||
|
@ -136,25 +145,25 @@ pub fn main() void {
|
|||
|
||||
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();
|
||||
|
||||
gui.run(.{
|
||||
.cpu = &cpu,
|
||||
.scheduler = &scheduler,
|
||||
.sync = &sync,
|
||||
.sync = sync,
|
||||
}) catch |e| exitln("main thread panicked: {}", .{e});
|
||||
} else {
|
||||
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();
|
||||
|
||||
gui.run(.{
|
||||
.cpu = &cpu,
|
||||
.scheduler = &scheduler,
|
||||
.tracker = &tracker,
|
||||
.sync = &sync,
|
||||
.sync = sync,
|
||||
}) 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 {
|
||||
sync: *Synchro,
|
||||
sync: Synchro,
|
||||
tracker: ?*FpsTracker = null,
|
||||
cpu: *Arm7tdmi,
|
||||
scheduler: *Scheduler,
|
||||
|
@ -108,17 +108,18 @@ pub const Gui = struct {
|
|||
|
||||
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
||||
|
||||
const vao_id = opengl_impl.vao();
|
||||
defer gl.deleteVertexArrays(1, &[_]GLuint{vao_id});
|
||||
const objects = opengl_impl.createObjects();
|
||||
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 out_tex = opengl_impl.outTex();
|
||||
const emu_tex = opengl_impl.createScreenTexture(bus_ptr.ppu.framebuf.get(.Renderer));
|
||||
const out_tex = opengl_impl.createOutputTexture();
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
var win_dim: Dimensions = default_dim;
|
||||
|
@ -126,7 +127,7 @@ pub const Gui = struct {
|
|||
emu_loop: while (true) {
|
||||
// Outside of `SDL.SDL_QUIT` below, the DearImgui UI might signal that the program
|
||||
// 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;
|
||||
while (SDL.SDL_PollEvent(&event) != 0) {
|
||||
|
@ -206,9 +207,21 @@ pub const Gui = struct {
|
|||
self.state.emulation = .Inactive;
|
||||
},
|
||||
},
|
||||
.Active => {
|
||||
sync.emu_access.lock();
|
||||
defer sync.emu_access.unlock();
|
||||
.Active => blk: {
|
||||
sync.ui_busy.store(true, .Monotonic);
|
||||
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
|
||||
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);
|
||||
defer gl.bindFramebuffer(gl.FRAMEBUFFER, 0);
|
||||
|
||||
const buf = bus_ptr.ppu.framebuf.get(.Renderer);
|
||||
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)
|
||||
|
@ -298,24 +312,44 @@ fn panic() noreturn {
|
|||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
// Bind VAO
|
||||
gl.bindVertexArray(vao_id);
|
||||
// Bind VAO, EBO. VBO not bound
|
||||
gl.bindVertexArray(ids.vao); // VAO
|
||||
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
|
||||
gl.useProgram(prog_id);
|
||||
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 frag_shader = @embedFile("shader/pixelbuf.frag");
|
||||
|
||||
|
@ -335,22 +369,50 @@ const opengl_impl = struct {
|
|||
|
||||
if (!shader.didCompile(fs)) return error.FragmentCompileError;
|
||||
|
||||
const prog = gl.createProgram();
|
||||
gl.attachShader(prog, vs);
|
||||
gl.attachShader(prog, fs);
|
||||
gl.linkProgram(prog);
|
||||
const program = gl.createProgram();
|
||||
gl.attachShader(program, vs);
|
||||
gl.attachShader(program, fs);
|
||||
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;
|
||||
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;
|
||||
gl.genTextures(1, &tex_id);
|
||||
|
||||
|
@ -365,7 +427,7 @@ const opengl_impl = struct {
|
|||
return tex_id;
|
||||
}
|
||||
|
||||
fn outTex() GLuint {
|
||||
fn createOutputTexture() GLuint {
|
||||
var tex_id: GLuint = undefined;
|
||||
gl.genTextures(1, &tex_id);
|
||||
|
||||
|
@ -380,7 +442,7 @@ const opengl_impl = struct {
|
|||
return tex_id;
|
||||
}
|
||||
|
||||
fn frameBuffer(tex_id: GLuint) !GLuint {
|
||||
fn createFrameBuffer(tex_id: GLuint) !GLuint {
|
||||
var fbo_id: GLuint = undefined;
|
||||
gl.genFramebuffers(1, &fbo_id);
|
||||
|
||||
|
@ -388,7 +450,9 @@ const opengl_impl = struct {
|
|||
defer gl.bindFramebuffer(gl.FRAMEBUFFER, 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)
|
||||
return error.FrameBufferObejctInitFailed;
|
||||
|
@ -397,7 +461,8 @@ const opengl_impl = 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 {
|
||||
var success: gl.GLint = undefined;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#version 330 core
|
||||
out vec4 frag_color;
|
||||
|
||||
in vec3 color;
|
||||
in vec2 uv;
|
||||
|
||||
uniform sampler2D screen;
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#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;
|
||||
|
||||
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() {
|
||||
uv = uvs[gl_VertexID];
|
||||
gl_Position = vec4(pos[gl_VertexID], 0.0, 1.0);
|
||||
color = in_color;
|
||||
uv = in_uv;
|
||||
gl_Position = vec4(pos, 1.0);
|
||||
}
|
Loading…
Reference in New Issue