feat: use opengl
TODO: - Texture isn't scaling properly - I need to reverse the colours in the frag shader
This commit is contained in:
		@@ -26,6 +26,9 @@ pub fn build(b: *std.build.Builder) void {
 | 
			
		||||
    // Argument Parsing Library
 | 
			
		||||
    exe.addPackagePath("clap", "lib/zig-clap/clap.zig");
 | 
			
		||||
 | 
			
		||||
    // OpenGL Bindings
 | 
			
		||||
    exe.addPackagePath("gl", "lib/gl_4v5.zig");
 | 
			
		||||
 | 
			
		||||
    // Zig SDL Bindings: https://github.com/MasterQ32/SDL.zig
 | 
			
		||||
    const sdk = Sdk.init(b);
 | 
			
		||||
    sdk.link(exe, .dynamic);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9263
									
								
								lib/gl_4v5.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9263
									
								
								lib/gl_4v5.zig
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										143
									
								
								src/platform.zig
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								src/platform.zig
									
									
									
									
									
								
							@@ -1,5 +1,6 @@
 | 
			
		||||
const std = @import("std");
 | 
			
		||||
const SDL = @import("sdl2");
 | 
			
		||||
const gl = @import("gl");
 | 
			
		||||
const emu = @import("core/emu.zig");
 | 
			
		||||
 | 
			
		||||
const Apu = @import("core/apu.zig").Apu;
 | 
			
		||||
@@ -11,22 +12,43 @@ const span = @import("util.zig").span;
 | 
			
		||||
 | 
			
		||||
const pitch = @import("core/ppu.zig").framebuf_pitch;
 | 
			
		||||
const scale = @import("core/emu.zig").win_scale;
 | 
			
		||||
const gba_width = @import("core/ppu.zig").width;
 | 
			
		||||
const gba_height = @import("core/ppu.zig").height;
 | 
			
		||||
 | 
			
		||||
const default_title: []const u8 = "ZBA";
 | 
			
		||||
 | 
			
		||||
pub const Gui = struct {
 | 
			
		||||
    const Self = @This();
 | 
			
		||||
    const SDL_GLContext = *anyopaque; // SDL.SDL_GLContext is a ?*anyopaque
 | 
			
		||||
    const log = std.log.scoped(.Gui);
 | 
			
		||||
 | 
			
		||||
    // 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
 | 
			
		||||
 | 
			
		||||
    window: *SDL.SDL_Window,
 | 
			
		||||
    ctx: SDL_GLContext,
 | 
			
		||||
    title: []const u8,
 | 
			
		||||
    renderer: *SDL.SDL_Renderer,
 | 
			
		||||
    texture: *SDL.SDL_Texture,
 | 
			
		||||
    audio: Audio,
 | 
			
		||||
 | 
			
		||||
    program_id: gl.GLuint,
 | 
			
		||||
 | 
			
		||||
    pub fn init(title: *const [12]u8, apu: *Apu, width: i32, height: i32) Self {
 | 
			
		||||
        const ret = SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO | SDL.SDL_INIT_GAMECONTROLLER);
 | 
			
		||||
        if (ret < 0) panic();
 | 
			
		||||
        if (SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO) < 0) panic();
 | 
			
		||||
        if (SDL.SDL_GL_SetAttribute(SDL.SDL_GL_CONTEXT_PROFILE_MASK, SDL.SDL_GL_CONTEXT_PROFILE_CORE) < 0) panic();
 | 
			
		||||
        if (SDL.SDL_GL_SetAttribute(SDL.SDL_GL_CONTEXT_MAJOR_VERSION, 4) < 0) panic();
 | 
			
		||||
        if (SDL.SDL_GL_SetAttribute(SDL.SDL_GL_CONTEXT_MAJOR_VERSION, 4) < 0) panic();
 | 
			
		||||
 | 
			
		||||
        const window = SDL.SDL_CreateWindow(
 | 
			
		||||
            default_title.ptr,
 | 
			
		||||
@@ -34,28 +56,98 @@ pub const Gui = struct {
 | 
			
		||||
            SDL.SDL_WINDOWPOS_CENTERED,
 | 
			
		||||
            @as(c_int, width * scale),
 | 
			
		||||
            @as(c_int, height * scale),
 | 
			
		||||
            SDL.SDL_WINDOW_SHOWN,
 | 
			
		||||
            SDL.SDL_WINDOW_OPENGL | SDL.SDL_WINDOW_SHOWN,
 | 
			
		||||
        ) orelse panic();
 | 
			
		||||
 | 
			
		||||
        const renderer = SDL.SDL_CreateRenderer(window, -1, SDL.SDL_RENDERER_ACCELERATED | SDL.SDL_RENDERER_PRESENTVSYNC) orelse panic();
 | 
			
		||||
        const ctx = SDL.SDL_GL_CreateContext(window) orelse panic();
 | 
			
		||||
        if (SDL.SDL_GL_MakeCurrent(window, ctx) < 0) panic();
 | 
			
		||||
 | 
			
		||||
        const texture = SDL.SDL_CreateTexture(
 | 
			
		||||
            renderer,
 | 
			
		||||
            SDL.SDL_PIXELFORMAT_RGBA8888,
 | 
			
		||||
            SDL.SDL_TEXTUREACCESS_STREAMING,
 | 
			
		||||
            @as(c_int, width),
 | 
			
		||||
            @as(c_int, height),
 | 
			
		||||
        ) orelse panic();
 | 
			
		||||
        gl.load(ctx, Self.glGetProcAddress) catch @panic("gl.load failed");
 | 
			
		||||
        if (SDL.SDL_GL_SetSwapInterval(1) < 0) panic();
 | 
			
		||||
 | 
			
		||||
        const program_id = compileShaders();
 | 
			
		||||
 | 
			
		||||
        return Self{
 | 
			
		||||
            .window = window,
 | 
			
		||||
            .title = span(title),
 | 
			
		||||
            .renderer = renderer,
 | 
			
		||||
            .texture = texture,
 | 
			
		||||
            .ctx = ctx,
 | 
			
		||||
            .program_id = program_id,
 | 
			
		||||
            .audio = Audio.init(apu),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn compileShaders() gl.GLuint {
 | 
			
		||||
        // TODO: Panic on Shader Compiler Failure + Error Message
 | 
			
		||||
        const vert_shader = @embedFile("shader/pixelbuf.vert");
 | 
			
		||||
        const frag_shader = @embedFile("shader/pixelbuf.frag");
 | 
			
		||||
 | 
			
		||||
        const vs = gl.createShader(gl.VERTEX_SHADER);
 | 
			
		||||
        defer gl.deleteShader(vs);
 | 
			
		||||
 | 
			
		||||
        gl.shaderSource(vs, 1, &[_][*c]const u8{vert_shader}, 0);
 | 
			
		||||
        gl.compileShader(vs);
 | 
			
		||||
 | 
			
		||||
        const fs = gl.createShader(gl.FRAGMENT_SHADER);
 | 
			
		||||
        defer gl.deleteShader(fs);
 | 
			
		||||
 | 
			
		||||
        gl.shaderSource(fs, 1, &[_][*c]const u8{frag_shader}, 0);
 | 
			
		||||
        gl.compileShader(fs);
 | 
			
		||||
 | 
			
		||||
        const program = gl.createProgram();
 | 
			
		||||
        gl.attachShader(program, vs);
 | 
			
		||||
        gl.attachShader(program, fs);
 | 
			
		||||
        gl.linkProgram(program);
 | 
			
		||||
 | 
			
		||||
        return program;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Returns the VAO ID since it's used in run()
 | 
			
		||||
    fn generateBuffers() [3]c_uint {
 | 
			
		||||
        var vao_id: c_uint = undefined;
 | 
			
		||||
        var vbo_id: c_uint = undefined;
 | 
			
		||||
        var ebo_id: c_uint = undefined;
 | 
			
		||||
        gl.genVertexArrays(1, &vao_id);
 | 
			
		||||
        gl.genBuffers(1, &vbo_id);
 | 
			
		||||
        gl.genBuffers(1, &ebo_id);
 | 
			
		||||
 | 
			
		||||
        gl.bindVertexArray(vao_id);
 | 
			
		||||
 | 
			
		||||
        gl.bindBuffer(gl.ARRAY_BUFFER, vbo_id);
 | 
			
		||||
        gl.bufferData(gl.ARRAY_BUFFER, @sizeOf(@TypeOf(vertices)), &vertices, gl.STATIC_DRAW);
 | 
			
		||||
 | 
			
		||||
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo_id);
 | 
			
		||||
        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), &0);
 | 
			
		||||
        gl.enableVertexAttribArray(0);
 | 
			
		||||
        // Colour
 | 
			
		||||
        gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), &(3 * @sizeOf(f32)));
 | 
			
		||||
        gl.enableVertexAttribArray(1);
 | 
			
		||||
        // Texture Coord
 | 
			
		||||
        gl.vertexAttribPointer(2, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), &(6 * @sizeOf(f32)));
 | 
			
		||||
        gl.enableVertexAttribArray(2);
 | 
			
		||||
 | 
			
		||||
        return .{ vao_id, vbo_id, ebo_id };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn generateTexture(buf: []const u8) c_uint {
 | 
			
		||||
        var tex_id: c_uint = undefined;
 | 
			
		||||
        gl.genTextures(1, &tex_id);
 | 
			
		||||
        gl.bindTexture(gl.TEXTURE_2D, tex_id);
 | 
			
		||||
 | 
			
		||||
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
 | 
			
		||||
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 | 
			
		||||
 | 
			
		||||
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
 | 
			
		||||
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
 | 
			
		||||
 | 
			
		||||
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gba_width, gba_height, 0, gl.RGBA, gl.UNSIGNED_BYTE, buf.ptr);
 | 
			
		||||
        // gl.generateMipmap(gl.TEXTURE_2D); // TODO: Remove?
 | 
			
		||||
 | 
			
		||||
        return tex_id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn run(self: *Self, cpu: *Arm7tdmi, scheduler: *Scheduler) !void {
 | 
			
		||||
        var quit = std.atomic.Atomic(bool).init(false);
 | 
			
		||||
        var frame_rate = FpsTracker.init();
 | 
			
		||||
@@ -65,6 +157,9 @@ pub const Gui = struct {
 | 
			
		||||
 | 
			
		||||
        var title_buf: [0x100]u8 = [_]u8{0} ** 0x100;
 | 
			
		||||
 | 
			
		||||
        const vao_id = Self.generateBuffers()[0];
 | 
			
		||||
        _ = Self.generateTexture(cpu.bus.ppu.framebuf.get(.Renderer));
 | 
			
		||||
 | 
			
		||||
        emu_loop: while (true) {
 | 
			
		||||
            var event: SDL.SDL_Event = undefined;
 | 
			
		||||
            while (SDL.SDL_PollEvent(&event) != 0) {
 | 
			
		||||
@@ -123,9 +218,12 @@ pub const Gui = struct {
 | 
			
		||||
 | 
			
		||||
            // Emulator has an internal Double Buffer
 | 
			
		||||
            const framebuf = cpu.bus.ppu.framebuf.get(.Renderer);
 | 
			
		||||
            _ = SDL.SDL_UpdateTexture(self.texture, null, framebuf.ptr, pitch);
 | 
			
		||||
            _ = SDL.SDL_RenderCopy(self.renderer, self.texture, null, null);
 | 
			
		||||
            SDL.SDL_RenderPresent(self.renderer);
 | 
			
		||||
            gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gba_width, gba_height, gl.RGBA, gl.UNSIGNED_BYTE, framebuf.ptr);
 | 
			
		||||
 | 
			
		||||
            gl.useProgram(self.program_id);
 | 
			
		||||
            gl.bindVertexArray(vao_id);
 | 
			
		||||
            gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, null);
 | 
			
		||||
            SDL.SDL_GL_SwapWindow(self.window);
 | 
			
		||||
 | 
			
		||||
            const dyn_title = std.fmt.bufPrint(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, frame_rate.value() }) catch unreachable;
 | 
			
		||||
            SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr);
 | 
			
		||||
@@ -136,12 +234,17 @@ pub const Gui = struct {
 | 
			
		||||
 | 
			
		||||
    pub fn deinit(self: *Self) void {
 | 
			
		||||
        self.audio.deinit();
 | 
			
		||||
        SDL.SDL_DestroyTexture(self.texture);
 | 
			
		||||
        SDL.SDL_DestroyRenderer(self.renderer);
 | 
			
		||||
 | 
			
		||||
        SDL.SDL_GL_DeleteContext(self.ctx);
 | 
			
		||||
        SDL.SDL_DestroyWindow(self.window);
 | 
			
		||||
        SDL.SDL_Quit();
 | 
			
		||||
        self.* = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn glGetProcAddress(ctx: SDL.SDL_GLContext, proc: [:0]const u8) ?*anyopaque {
 | 
			
		||||
        _ = ctx;
 | 
			
		||||
        return SDL.SDL_GL_GetProcAddress(@ptrCast([*c]const u8, proc));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Audio = struct {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								src/shader/pixelbuf.frag
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/shader/pixelbuf.frag
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#version 450 core
 | 
			
		||||
out vec4 FragColor;
 | 
			
		||||
 | 
			
		||||
in vec3 ourColor;
 | 
			
		||||
in vec2 TexCoord;
 | 
			
		||||
 | 
			
		||||
// texture sampler
 | 
			
		||||
uniform sampler2D texture1;
 | 
			
		||||
 | 
			
		||||
void main()
 | 
			
		||||
{
 | 
			
		||||
	FragColor = texture(texture1, TexCoord).wzyx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										14
									
								
								src/shader/pixelbuf.vert
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/shader/pixelbuf.vert
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#version 450 core
 | 
			
		||||
layout (location = 0) in vec3 aPos;
 | 
			
		||||
layout (location = 1) in vec3 aColor;
 | 
			
		||||
layout (location = 2) in vec2 aTexCoord;
 | 
			
		||||
 | 
			
		||||
out vec3 ourColor;
 | 
			
		||||
out vec2 TexCoord;
 | 
			
		||||
 | 
			
		||||
void main()
 | 
			
		||||
{
 | 
			
		||||
	gl_Position = vec4(aPos, 1.0);
 | 
			
		||||
	ourColor = aColor;
 | 
			
		||||
	TexCoord = vec2(aTexCoord.x, aTexCoord.y);
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user