From c8a51a2158a8c46b38cf430c5d4ab4753769a6b7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 17 Nov 2023 20:39:20 -0800 Subject: [PATCH] renderer/opengl: create the screen texture --- pkg/opengl/Framebuffer.zig | 15 ++++++++-- pkg/opengl/Texture.zig | 51 ++++++++++++++++---------------- src/renderer/OpenGL.zig | 31 +++++++++++-------- src/renderer/opengl/custom.zig | 54 ++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 40 deletions(-) diff --git a/pkg/opengl/Framebuffer.zig b/pkg/opengl/Framebuffer.zig index 6335fa538..8ab07a238 100644 --- a/pkg/opengl/Framebuffer.zig +++ b/pkg/opengl/Framebuffer.zig @@ -20,8 +20,13 @@ pub fn destroy(v: Framebuffer) void { } pub fn bind(v: Framebuffer, target: Target) !Binding { + // The default framebuffer is documented as being zero but + // on multiple OpenGL drivers its not zero, so we grab it + // at runtime. + var current: c.GLint = undefined; + glad.context.GetIntegerv.?(c.GL_FRAMEBUFFER_BINDING, ¤t); glad.context.BindFramebuffer.?(@intFromEnum(target), v.id); - return .{ .target = target }; + return .{ .target = target, .previous = @intCast(current) }; } /// Enum for possible binding targets. @@ -55,9 +60,13 @@ pub const Status = enum(c_uint) { pub const Binding = struct { target: Target, + previous: c.GLuint, pub fn unbind(self: Binding) void { - glad.context.BindFramebuffer.?(@intFromEnum(self.target), 0); + glad.context.BindFramebuffer.?( + @intFromEnum(self.target), + self.previous, + ); } pub fn texture2D( @@ -78,6 +87,6 @@ pub const Binding = struct { } pub fn checkStatus(self: Binding) Status { - return @enumFromInt(glad.context.CheckFramebufferStatus.?(self.target)); + return @enumFromInt(glad.context.CheckFramebufferStatus.?(@intFromEnum(self.target))); } }; diff --git a/pkg/opengl/Texture.zig b/pkg/opengl/Texture.zig index 91a65b565..afa22e926 100644 --- a/pkg/opengl/Texture.zig +++ b/pkg/opengl/Texture.zig @@ -7,11 +7,29 @@ const glad = @import("glad.zig"); id: c.GLuint, -pub inline fn active(target: c.GLenum) !void { +pub fn active(target: c.GLenum) !void { glad.context.ActiveTexture.?(target); try errors.getError(); } +/// Create a single texture. +pub fn create() !Texture { + var id: c.GLuint = undefined; + glad.context.GenTextures.?(1, &id); + return .{ .id = id }; +} + +/// glBindTexture +pub fn bind(v: Texture, target: Target) !Binding { + glad.context.BindTexture.?(@intFromEnum(target), v.id); + try errors.getError(); + return .{ .target = target }; +} + +pub fn destroy(v: Texture) void { + glad.context.DeleteTextures.?(1, &v.id); +} + /// Enun for possible texture binding targets. pub const Target = enum(c_uint) { @"1D" = c.GL_TEXTURE_1D, @@ -48,8 +66,9 @@ pub const Parameter = enum(c_uint) { /// Internal format enum for texture images. pub const InternalFormat = enum(c_int) { - Red = c.GL_RED, - RGBA = c.GL_RGBA, + red = c.GL_RED, + rgb = c.GL_RGB, + rgba = c.GL_RGBA, // There are so many more that I haven't filled in. _, @@ -57,8 +76,9 @@ pub const InternalFormat = enum(c_int) { /// Format for texture images pub const Format = enum(c_uint) { - Red = c.GL_RED, - BGRA = c.GL_BGRA, + red = c.GL_RED, + rgb = c.GL_RGB, + bgra = c.GL_BGRA, // There are so many more that I haven't filled in. _, @@ -75,9 +95,8 @@ pub const DataType = enum(c_uint) { pub const Binding = struct { target: Target, - pub inline fn unbind(b: *Binding) void { + pub fn unbind(b: *const Binding) void { glad.context.BindTexture.?(@intFromEnum(b.target), 0); - b.* = undefined; } pub fn generateMipmap(b: Binding) void { @@ -143,21 +162,3 @@ pub const Binding = struct { ); } }; - -/// Create a single texture. -pub inline fn create() !Texture { - var id: c.GLuint = undefined; - glad.context.GenTextures.?(1, &id); - return Texture{ .id = id }; -} - -/// glBindTexture -pub inline fn bind(v: Texture, target: Target) !Binding { - glad.context.BindTexture.?(@intFromEnum(target), v.id); - try errors.getError(); - return Binding{ .target = target }; -} - -pub inline fn destroy(v: Texture) void { - glad.context.DeleteTextures.?(1, &v.id); -} diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index f5609fd13..3d3f05d29 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -1346,11 +1346,11 @@ fn flushAtlas(self: *OpenGL) !void { atlas.resized = false; try texbind.image2D( 0, - .Red, + .red, @intCast(atlas.size), @intCast(atlas.size), 0, - .Red, + .red, .UnsignedByte, atlas.data.ptr, ); @@ -1361,7 +1361,7 @@ fn flushAtlas(self: *OpenGL) !void { 0, @intCast(atlas.size), @intCast(atlas.size), - .Red, + .red, .UnsignedByte, atlas.data.ptr, ); @@ -1380,11 +1380,11 @@ fn flushAtlas(self: *OpenGL) !void { atlas.resized = false; try texbind.image2D( 0, - .RGBA, + .rgba, @intCast(atlas.size), @intCast(atlas.size), 0, - .BGRA, + .bgra, .UnsignedByte, atlas.data.ptr, ); @@ -1395,7 +1395,7 @@ fn flushAtlas(self: *OpenGL) !void { 0, @intCast(atlas.size), @intCast(atlas.size), - .BGRA, + .bgra, .UnsignedByte, atlas.data.ptr, ); @@ -1441,8 +1441,7 @@ fn drawCustomPrograms( const custom_bind = try custom_state.bind(); defer custom_bind.unbind(); - // Sync the uniform data. - // TODO: only do this when the data has changed + // Setup the new frame try custom_state.newFrame(); // Go through each custom shader and draw it. @@ -1469,6 +1468,14 @@ fn drawCellProgram( // are changes to the atlas. try self.flushAtlas(); + // If we have custom shaders, then we draw to the custom + // shader framebuffer. + const fbobind: ?gl.Framebuffer.Binding = fbobind: { + const state = gl_state.custom orelse break :fbobind null; + break :fbobind try state.fbo.bind(.framebuffer); + }; + defer if (fbobind) |v| v.unbind(); + // Clear the surface gl.clearColor( @as(f32, @floatFromInt(self.draw_background.r)) / 255, @@ -1615,11 +1622,11 @@ const GLState = struct { try texbind.parameter(.MagFilter, gl.c.GL_LINEAR); try texbind.image2D( 0, - .Red, + .red, @intCast(font_group.atlas_greyscale.size), @intCast(font_group.atlas_greyscale.size), 0, - .Red, + .red, .UnsignedByte, font_group.atlas_greyscale.data.ptr, ); @@ -1636,11 +1643,11 @@ const GLState = struct { try texbind.parameter(.MagFilter, gl.c.GL_LINEAR); try texbind.image2D( 0, - .RGBA, + .rgba, @intCast(font_group.atlas_color.size), @intCast(font_group.atlas_color.size), 0, - .BGRA, + .bgra, .UnsignedByte, font_group.atlas_color.data.ptr, ); diff --git a/src/renderer/opengl/custom.zig b/src/renderer/opengl/custom.zig index 96169be30..cb75cfd54 100644 --- a/src/renderer/opengl/custom.zig +++ b/src/renderer/opengl/custom.zig @@ -3,6 +3,8 @@ const Allocator = std.mem.Allocator; const gl = @import("opengl"); const ScreenSize = @import("../size.zig").ScreenSize; +const log = std.log.scoped(.opengl_custom); + /// The "INDEX" is the index into the global GL state and the /// "BINDING" is the binding location in the shader. const UNIFORM_INDEX: gl.c.GLuint = 0; @@ -27,9 +29,11 @@ pub const State = struct { uniforms: Uniforms, /// The OpenGL buffers + fbo: gl.Framebuffer, ubo: gl.Buffer, vao: gl.VertexArray, ebo: gl.Buffer, + fb_texture: gl.Texture, /// The set of programs for the custom shaders. programs: []const Program, @@ -50,6 +54,44 @@ pub const State = struct { try programs.append(try Program.init(src)); } + // Create the texture for the framebuffer + const fb_tex = try gl.Texture.create(); + errdefer fb_tex.destroy(); + { + const texbind = try fb_tex.bind(.@"2D"); + try texbind.parameter(.WrapS, gl.c.GL_CLAMP_TO_EDGE); + try texbind.parameter(.WrapT, gl.c.GL_CLAMP_TO_EDGE); + try texbind.parameter(.MinFilter, gl.c.GL_LINEAR); + try texbind.parameter(.MagFilter, gl.c.GL_LINEAR); + try texbind.image2D( + 0, + .rgb, + 1, + 1, + 0, + .rgb, + .UnsignedByte, + null, + ); + } + + // Create our framebuffer for rendering off screen. + // The shader prior to custom shaders should use this + // framebuffer. + const fbo = try gl.Framebuffer.create(); + errdefer fbo.destroy(); + const fbbind = try fbo.bind(.framebuffer); + defer fbbind.unbind(); + try fbbind.texture2D(.color0, .@"2D", fb_tex, 0); + const fbstatus = fbbind.checkStatus(); + if (fbstatus != .complete) { + log.warn( + "framebuffer is not complete state={}", + .{fbstatus}, + ); + return error.InvalidFramebuffer; + } + // Create our uniform buffer that is shared across all // custom shaders const ubo = try gl.Buffer.create(); @@ -79,9 +121,11 @@ pub const State = struct { return .{ .programs = try programs.toOwnedSlice(), .uniforms = .{}, + .fbo = fbo, .ubo = ubo, .vao = vao, .ebo = ebo, + .fb_texture = fb_tex, .last_frame_time = try std.time.Instant.now(), }; } @@ -92,6 +136,8 @@ pub const State = struct { self.ubo.destroy(); self.ebo.destroy(); self.vao.destroy(); + self.fb_texture.destroy(); + self.fbo.destroy(); } pub fn setScreenSize(self: *State, size: ScreenSize) !void { @@ -137,6 +183,11 @@ pub const State = struct { // the global state. try self.ubo.bindBase(.uniform, UNIFORM_INDEX); + // Bind our texture that is shared amongst all + try gl.Texture.active(gl.c.GL_TEXTURE0); + var texbind = try self.fb_texture.bind(.@"2D"); + errdefer texbind.unbind(); + const vao = try self.vao.bind(); errdefer vao.unbind(); @@ -146,16 +197,19 @@ pub const State = struct { return .{ .vao = vao, .ebo = ebo, + .fb_texture = texbind, }; } pub const Binding = struct { vao: gl.VertexArray.Binding, ebo: gl.Buffer.Binding, + fb_texture: gl.Texture.Binding, pub fn unbind(self: Binding) void { self.ebo.unbind(); self.vao.unbind(); + self.fb_texture.unbind(); } }; };