Compare commits
	
		
			3 Commits
		
	
	
		
			main
			...
			7d2f988482
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7d2f988482 | |||
| 0a17e0aa1c | |||
| d7d58bf5a7 | 
| @@ -26,6 +26,9 @@ pub fn build(b: *std.build.Builder) void { | |||||||
|     // Argument Parsing Library |     // Argument Parsing Library | ||||||
|     exe.addPackagePath("clap", "lib/zig-clap/clap.zig"); |     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 |     // Zig SDL Bindings: https://github.com/MasterQ32/SDL.zig | ||||||
|     const sdk = Sdk.init(b); |     const sdk = Sdk.init(b); | ||||||
|     sdk.link(exe, .dynamic); |     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
											
										
									
								
							
							
								
								
									
										144
									
								
								src/platform.zig
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								src/platform.zig
									
									
									
									
									
								
							| @@ -1,5 +1,6 @@ | |||||||
| const std = @import("std"); | const std = @import("std"); | ||||||
| const SDL = @import("sdl2"); | const SDL = @import("sdl2"); | ||||||
|  | const gl = @import("gl"); | ||||||
| const emu = @import("core/emu.zig"); | const emu = @import("core/emu.zig"); | ||||||
|  |  | ||||||
| const Apu = @import("core/apu.zig").Apu; | 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 pitch = @import("core/ppu.zig").framebuf_pitch; | ||||||
| const scale = @import("core/emu.zig").win_scale; | 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"; | const default_title: []const u8 = "ZBA"; | ||||||
|  |  | ||||||
| pub const Gui = struct { | pub const Gui = struct { | ||||||
|     const Self = @This(); |     const Self = @This(); | ||||||
|  |     const SDL_GLContext = *anyopaque; // SDL.SDL_GLContext is a ?*anyopaque | ||||||
|     const log = std.log.scoped(.Gui); |     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, |     window: *SDL.SDL_Window, | ||||||
|  |     ctx: SDL_GLContext, | ||||||
|     title: []const u8, |     title: []const u8, | ||||||
|     renderer: *SDL.SDL_Renderer, |  | ||||||
|     texture: *SDL.SDL_Texture, |  | ||||||
|     audio: Audio, |     audio: Audio, | ||||||
|  |  | ||||||
|  |     program_id: gl.GLuint, | ||||||
|  |  | ||||||
|     pub fn init(title: *const [12]u8, apu: *Apu, width: i32, height: i32) Self { |     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 (SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO) < 0) panic(); | ||||||
|         if (ret < 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, 3) < 0) panic(); | ||||||
|  |         if (SDL.SDL_GL_SetAttribute(SDL.SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 0) panic(); | ||||||
|  |  | ||||||
|         const window = SDL.SDL_CreateWindow( |         const window = SDL.SDL_CreateWindow( | ||||||
|             default_title.ptr, |             default_title.ptr, | ||||||
| @@ -34,28 +56,98 @@ pub const Gui = struct { | |||||||
|             SDL.SDL_WINDOWPOS_CENTERED, |             SDL.SDL_WINDOWPOS_CENTERED, | ||||||
|             @as(c_int, width * scale), |             @as(c_int, width * scale), | ||||||
|             @as(c_int, height * scale), |             @as(c_int, height * scale), | ||||||
|             SDL.SDL_WINDOW_SHOWN, |             SDL.SDL_WINDOW_OPENGL | SDL.SDL_WINDOW_SHOWN, | ||||||
|         ) orelse panic(); |         ) 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( |         gl.load(ctx, Self.glGetProcAddress) catch @panic("gl.load failed"); | ||||||
|             renderer, |         if (SDL.SDL_GL_SetSwapInterval(1) < 0) panic(); | ||||||
|             SDL.SDL_PIXELFORMAT_RGBA8888, |  | ||||||
|             SDL.SDL_TEXTUREACCESS_STREAMING, |         const program_id = compileShaders(); | ||||||
|             @as(c_int, width), |  | ||||||
|             @as(c_int, height), |  | ||||||
|         ) orelse panic(); |  | ||||||
|  |  | ||||||
|         return Self{ |         return Self{ | ||||||
|             .window = window, |             .window = window, | ||||||
|             .title = span(title), |             .title = span(title), | ||||||
|             .renderer = renderer, |             .ctx = ctx, | ||||||
|             .texture = texture, |             .program_id = program_id, | ||||||
|             .audio = Audio.init(apu), |             .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), @intToPtr(?*anyopaque, 0)); // lmao | ||||||
|  |         gl.enableVertexAttribArray(0); | ||||||
|  |         // Colour | ||||||
|  |         gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), @intToPtr(?*anyopaque, (3 * @sizeOf(f32)))); | ||||||
|  |         gl.enableVertexAttribArray(1); | ||||||
|  |         // Texture Coord | ||||||
|  |         gl.vertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, 8 * @sizeOf(f32), @intToPtr(?*anyopaque, (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_INT_8_8_8_8, buf.ptr); | ||||||
|  |         // gl.generateMipmap(gl.TEXTURE_2D); // TODO: Remove? | ||||||
|  |  | ||||||
|  |         return tex_id; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub fn run(self: *Self, cpu: *Arm7tdmi, scheduler: *Scheduler) !void { |     pub fn run(self: *Self, cpu: *Arm7tdmi, scheduler: *Scheduler) !void { | ||||||
|         var quit = std.atomic.Atomic(bool).init(false); |         var quit = std.atomic.Atomic(bool).init(false); | ||||||
|         var frame_rate = FpsTracker.init(); |         var frame_rate = FpsTracker.init(); | ||||||
| @@ -65,6 +157,9 @@ pub const Gui = struct { | |||||||
|  |  | ||||||
|         var title_buf: [0x100]u8 = [_]u8{0} ** 0x100; |         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) { |         emu_loop: while (true) { | ||||||
|             var event: SDL.SDL_Event = undefined; |             var event: SDL.SDL_Event = undefined; | ||||||
|             while (SDL.SDL_PollEvent(&event) != 0) { |             while (SDL.SDL_PollEvent(&event) != 0) { | ||||||
| @@ -123,9 +218,12 @@ pub const Gui = struct { | |||||||
|  |  | ||||||
|             // Emulator has an internal Double Buffer |             // Emulator has an internal Double Buffer | ||||||
|             const framebuf = cpu.bus.ppu.framebuf.get(.Renderer); |             const framebuf = cpu.bus.ppu.framebuf.get(.Renderer); | ||||||
|             _ = SDL.SDL_UpdateTexture(self.texture, null, framebuf.ptr, pitch); |             gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gba_width, gba_height, gl.RGBA, gl.UNSIGNED_INT_8_8_8_8, framebuf.ptr); | ||||||
|             _ = SDL.SDL_RenderCopy(self.renderer, self.texture, null, null); |  | ||||||
|             SDL.SDL_RenderPresent(self.renderer); |             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; |             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); |             SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr); | ||||||
| @@ -136,12 +234,18 @@ pub const Gui = struct { | |||||||
|  |  | ||||||
|     pub fn deinit(self: *Self) void { |     pub fn deinit(self: *Self) void { | ||||||
|         self.audio.deinit(); |         self.audio.deinit(); | ||||||
|         SDL.SDL_DestroyTexture(self.texture); |         // TODO: Buffer deletions | ||||||
|         SDL.SDL_DestroyRenderer(self.renderer); |         gl.deleteProgram(self.program_id); | ||||||
|  |         SDL.SDL_GL_DeleteContext(self.ctx); | ||||||
|         SDL.SDL_DestroyWindow(self.window); |         SDL.SDL_DestroyWindow(self.window); | ||||||
|         SDL.SDL_Quit(); |         SDL.SDL_Quit(); | ||||||
|         self.* = undefined; |         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 { | const Audio = struct { | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								src/shader/pixelbuf.frag
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/shader/pixelbuf.frag
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | #version 330 core | ||||||
|  | out vec4 frag_color; | ||||||
|  |  | ||||||
|  | in vec3 color; | ||||||
|  | in vec2 uv; | ||||||
|  |  | ||||||
|  | uniform sampler2D screen; | ||||||
|  |  | ||||||
|  | void main() { | ||||||
|  | 	// https://near.sh/video/color-emulation | ||||||
|  | 	// Thanks to Talarubi + Near for the Colour Correction | ||||||
|  | 	// Thanks to fleur + mattrb for the Shader Impl | ||||||
|  |  | ||||||
|  | 	vec4 color = texture(screen, uv); | ||||||
|  | 	color.rgb = pow(color.rgb, vec3(4.0)); // LCD Gamma | ||||||
|  |    | ||||||
|  | 	frag_color = vec4( | ||||||
|  | 		pow(vec3( | ||||||
|  | 		  	  0 * color.b +  50 * color.g + 255 * color.r, | ||||||
|  | 	     	 30 * color.b + 230 * color.g +  10 * color.r, | ||||||
|  | 			220 * color.b +  10 * color.g +  50 * color.r | ||||||
|  | 		) / 255, vec3(1.0 / 2.2)), // Out Gamma | ||||||
|  | 	1.0);  | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								src/shader/pixelbuf.vert
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/shader/pixelbuf.vert
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +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; | ||||||
|  |  | ||||||
|  | void main() { | ||||||
|  | 	color = in_color; | ||||||
|  | 	uv = in_uv; | ||||||
|  | 	gl_Position = vec4(pos, 1.0); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user