Compare commits
	
		
			3 Commits
		
	
	
		
			apu-things
			...
			7d2f988482
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7d2f988482 | |||
| 0a17e0aa1c | |||
| d7d58bf5a7 | 
| @@ -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
											
										
									
								
							
							
								
								
									
										144
									
								
								src/platform.zig
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								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, 3) < 0) panic(); | ||||
|         if (SDL.SDL_GL_SetAttribute(SDL.SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 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), @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 { | ||||
|         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_INT_8_8_8_8, 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,18 @@ pub const Gui = struct { | ||||
|  | ||||
|     pub fn deinit(self: *Self) void { | ||||
|         self.audio.deinit(); | ||||
|         SDL.SDL_DestroyTexture(self.texture); | ||||
|         SDL.SDL_DestroyRenderer(self.renderer); | ||||
|         // TODO: Buffer deletions | ||||
|         gl.deleteProgram(self.program_id); | ||||
|         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 { | ||||
|   | ||||
							
								
								
									
										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