From af9a552d19382fa2590c9fcd014ef62b50b0f1cb Mon Sep 17 00:00:00 2001 From: julia Date: Wed, 22 Jan 2025 20:45:09 +1100 Subject: [PATCH 1/5] use main buffer and copy data to fbo texture DO NOT MERGE. --- pkg/opengl/Texture.zig | 22 ++++++++++++++++++++++ src/renderer/OpenGL.zig | 20 ++++++-------------- src/renderer/opengl/custom.zig | 8 ++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/pkg/opengl/Texture.zig b/pkg/opengl/Texture.zig index 4cd1cf9f9b..a9fa5d4fe3 100644 --- a/pkg/opengl/Texture.zig +++ b/pkg/opengl/Texture.zig @@ -162,4 +162,26 @@ pub const Binding = struct { data, ); } + + pub fn copySubImage2D( + b: Binding, + level: c.GLint, + xoffset: c.GLint, + yoffset: c.GLint, + x: c.GLint, + y: c.GLint, + width: c.GLsizei, + height: c.GLsizei, + ) !void { + glad.context.CopyTexSubImage2D.?( + @intFromEnum(b.target), + level, + xoffset, + yoffset, + x, + y, + width, + height + ); + } }; diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 80fc3cab91..9fdd1a547c 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -2363,26 +2363,18 @@ fn drawCustomPrograms(self: *OpenGL, custom_state: *custom.State) !void { // Setup the new frame try custom_state.newFrame(); - // To allow programs to retrieve each other via a texture - // then we must render the next shaders to the screen fbo. - // However, the first shader must be run while the default fbo - // is attached - { - const bind = try custom_state.programs[0].bind(); - defer bind.unbind(); - try bind.draw(); - if (custom_state.programs.len == 1) return; - } - - const fbobind = try custom_state.fbo.bind(.framebuffer); - defer fbobind.unbind(); + // const fbobind = try custom_state.fbo.bind(.framebuffer); + // defer fbobind.unbind(); // Go through each custom shader and draw it. - for (custom_state.programs[1..]) |program| { + for (custom_state.programs) |program| { // Bind our cell program state, buffers const bind = try program.bind(); defer bind.unbind(); try bind.draw(); + + // copy main and custom fbo + try custom_state.copy(); } } diff --git a/src/renderer/opengl/custom.zig b/src/renderer/opengl/custom.zig index 3806921dfa..a69fc8b2b7 100644 --- a/src/renderer/opengl/custom.zig +++ b/src/renderer/opengl/custom.zig @@ -230,6 +230,14 @@ pub const State = struct { }; } + /// copy the fbo's attached texture to the backbuffer + pub fn copy(self: *State) !void { + const texbind = try self.fb_texture.bind(.@"2D"); + errdefer texbind.unbind(); + + try texbind.copySubImage2D(0, 0, 0, 0, 0, @intFromFloat(self.uniforms.resolution[0]), @intFromFloat(self.uniforms.resolution[1])); + } + pub const Binding = struct { vao: gl.VertexArray.Binding, ebo: gl.Buffer.Binding, From 93806df469ebb75c68da047ff1097d852c56dc3a Mon Sep 17 00:00:00 2001 From: julia Date: Thu, 23 Jan 2025 10:41:06 +1100 Subject: [PATCH 2/5] remove fbo entirely rely on a backbuffer copy to sample from while we draw everything to the default framebuffer --- src/renderer/OpenGL.zig | 13 +------- src/renderer/opengl/custom.zig | 56 +++++++++++----------------------- 2 files changed, 19 insertions(+), 50 deletions(-) diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 9fdd1a547c..3a96d67b70 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -2363,9 +2363,6 @@ fn drawCustomPrograms(self: *OpenGL, custom_state: *custom.State) !void { // Setup the new frame try custom_state.newFrame(); - // const fbobind = try custom_state.fbo.bind(.framebuffer); - // defer fbobind.unbind(); - // Go through each custom shader and draw it. for (custom_state.programs) |program| { // Bind our cell program state, buffers @@ -2373,7 +2370,7 @@ fn drawCustomPrograms(self: *OpenGL, custom_state: *custom.State) !void { defer bind.unbind(); try bind.draw(); - // copy main and custom fbo + // copy backbuffers try custom_state.copy(); } } @@ -2387,14 +2384,6 @@ 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( @floatCast(@as(f32, @floatFromInt(self.draw_background.r)) / 255 * self.config.background_opacity), diff --git a/src/renderer/opengl/custom.zig b/src/renderer/opengl/custom.zig index a69fc8b2b7..b726a25120 100644 --- a/src/renderer/opengl/custom.zig +++ b/src/renderer/opengl/custom.zig @@ -27,20 +27,19 @@ pub const Uniforms = extern struct { /// The state associated with custom shaders. This should only be initialized /// if there is at least one custom shader. /// -/// To use this, the main terminal shader should render to the framebuffer -/// specified by "fbo". The resulting "fb_texture" will contain the color -/// attachment. This is then used as the iChannel0 input to the custom -/// shader. +/// To use this, each shader should be rendererd to the screen and then +/// copied to the "backbuffer" texture. This texture, which acts as a +/// mirror of the screen, can then be sampled from iChannel0 in consecutive +/// shaders. pub const State = struct { /// The uniform data uniforms: Uniforms, /// The OpenGL buffers - fbo: gl.Framebuffer, ubo: gl.Buffer, vao: gl.VertexArray, ebo: gl.Buffer, - fb_texture: gl.Texture, + backbuffer: gl.Texture, /// The set of programs for the custom shaders. programs: []const Program, @@ -67,11 +66,11 @@ 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(); + // Create the texture for the backbuffer mirror + const bb_tex = try gl.Texture.create(); + errdefer bb_tex.destroy(); { - const texbind = try fb_tex.bind(.@"2D"); + const texbind = try bb_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); @@ -88,23 +87,6 @@ pub const State = struct { ); } - // 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(); @@ -134,11 +116,10 @@ pub const State = struct { return .{ .programs = try programs.toOwnedSlice(), .uniforms = .{}, - .fbo = fbo, .ubo = ubo, .vao = vao, .ebo = ebo, - .fb_texture = fb_tex, + .backbuffer = bb_tex, .first_frame_time = try std.time.Instant.now(), .last_frame_time = try std.time.Instant.now(), }; @@ -150,8 +131,7 @@ pub const State = struct { self.ubo.destroy(); self.ebo.destroy(); self.vao.destroy(); - self.fb_texture.destroy(); - self.fbo.destroy(); + self.backbuffer.destroy(); } pub fn setScreenSize(self: *State, size: Size) !void { @@ -164,7 +144,7 @@ pub const State = struct { try self.syncUniforms(); // Update our texture - const texbind = try self.fb_texture.bind(.@"2D"); + const texbind = try self.backbuffer.bind(.@"2D"); try texbind.image2D( 0, .rgb, @@ -214,7 +194,7 @@ pub const State = struct { // Bind our texture that is shared amongst all try gl.Texture.active(gl.c.GL_TEXTURE0); - var texbind = try self.fb_texture.bind(.@"2D"); + var texbind = try self.backbuffer.bind(.@"2D"); errdefer texbind.unbind(); const vao = try self.vao.bind(); @@ -226,13 +206,13 @@ pub const State = struct { return .{ .vao = vao, .ebo = ebo, - .fb_texture = texbind, + .backbuffer = texbind, }; } - /// copy the fbo's attached texture to the backbuffer + /// copy the main backbuffer to our backbuffer copy texture pub fn copy(self: *State) !void { - const texbind = try self.fb_texture.bind(.@"2D"); + const texbind = try self.backbuffer.bind(.@"2D"); errdefer texbind.unbind(); try texbind.copySubImage2D(0, 0, 0, 0, 0, @intFromFloat(self.uniforms.resolution[0]), @intFromFloat(self.uniforms.resolution[1])); @@ -241,12 +221,12 @@ pub const State = struct { pub const Binding = struct { vao: gl.VertexArray.Binding, ebo: gl.Buffer.Binding, - fb_texture: gl.Texture.Binding, + backbuffer: gl.Texture.Binding, pub fn unbind(self: Binding) void { self.ebo.unbind(); self.vao.unbind(); - self.fb_texture.unbind(); + self.backbuffer.unbind(); } }; }; From b9b0451c614affd96711cb9497c8e6551686f2e3 Mon Sep 17 00:00:00 2001 From: julia Date: Thu, 23 Jan 2025 19:43:13 +1100 Subject: [PATCH 3/5] Revert "remove fbo entirely" This reverts commit 9b10363f27c2a34bd067254c0eed39448594fc27. --- src/renderer/OpenGL.zig | 13 +++++++- src/renderer/opengl/custom.zig | 56 +++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 3a96d67b70..9fdd1a547c 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -2363,6 +2363,9 @@ fn drawCustomPrograms(self: *OpenGL, custom_state: *custom.State) !void { // Setup the new frame try custom_state.newFrame(); + // const fbobind = try custom_state.fbo.bind(.framebuffer); + // defer fbobind.unbind(); + // Go through each custom shader and draw it. for (custom_state.programs) |program| { // Bind our cell program state, buffers @@ -2370,7 +2373,7 @@ fn drawCustomPrograms(self: *OpenGL, custom_state: *custom.State) !void { defer bind.unbind(); try bind.draw(); - // copy backbuffers + // copy main and custom fbo try custom_state.copy(); } } @@ -2384,6 +2387,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( @floatCast(@as(f32, @floatFromInt(self.draw_background.r)) / 255 * self.config.background_opacity), diff --git a/src/renderer/opengl/custom.zig b/src/renderer/opengl/custom.zig index b726a25120..a69fc8b2b7 100644 --- a/src/renderer/opengl/custom.zig +++ b/src/renderer/opengl/custom.zig @@ -27,19 +27,20 @@ pub const Uniforms = extern struct { /// The state associated with custom shaders. This should only be initialized /// if there is at least one custom shader. /// -/// To use this, each shader should be rendererd to the screen and then -/// copied to the "backbuffer" texture. This texture, which acts as a -/// mirror of the screen, can then be sampled from iChannel0 in consecutive -/// shaders. +/// To use this, the main terminal shader should render to the framebuffer +/// specified by "fbo". The resulting "fb_texture" will contain the color +/// attachment. This is then used as the iChannel0 input to the custom +/// shader. pub const State = struct { /// The uniform data uniforms: Uniforms, /// The OpenGL buffers + fbo: gl.Framebuffer, ubo: gl.Buffer, vao: gl.VertexArray, ebo: gl.Buffer, - backbuffer: gl.Texture, + fb_texture: gl.Texture, /// The set of programs for the custom shaders. programs: []const Program, @@ -66,11 +67,11 @@ pub const State = struct { try programs.append(try Program.init(src)); } - // Create the texture for the backbuffer mirror - const bb_tex = try gl.Texture.create(); - errdefer bb_tex.destroy(); + // Create the texture for the framebuffer + const fb_tex = try gl.Texture.create(); + errdefer fb_tex.destroy(); { - const texbind = try bb_tex.bind(.@"2D"); + 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); @@ -87,6 +88,23 @@ pub const State = struct { ); } + // 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(); @@ -116,10 +134,11 @@ pub const State = struct { return .{ .programs = try programs.toOwnedSlice(), .uniforms = .{}, + .fbo = fbo, .ubo = ubo, .vao = vao, .ebo = ebo, - .backbuffer = bb_tex, + .fb_texture = fb_tex, .first_frame_time = try std.time.Instant.now(), .last_frame_time = try std.time.Instant.now(), }; @@ -131,7 +150,8 @@ pub const State = struct { self.ubo.destroy(); self.ebo.destroy(); self.vao.destroy(); - self.backbuffer.destroy(); + self.fb_texture.destroy(); + self.fbo.destroy(); } pub fn setScreenSize(self: *State, size: Size) !void { @@ -144,7 +164,7 @@ pub const State = struct { try self.syncUniforms(); // Update our texture - const texbind = try self.backbuffer.bind(.@"2D"); + const texbind = try self.fb_texture.bind(.@"2D"); try texbind.image2D( 0, .rgb, @@ -194,7 +214,7 @@ pub const State = struct { // Bind our texture that is shared amongst all try gl.Texture.active(gl.c.GL_TEXTURE0); - var texbind = try self.backbuffer.bind(.@"2D"); + var texbind = try self.fb_texture.bind(.@"2D"); errdefer texbind.unbind(); const vao = try self.vao.bind(); @@ -206,13 +226,13 @@ pub const State = struct { return .{ .vao = vao, .ebo = ebo, - .backbuffer = texbind, + .fb_texture = texbind, }; } - /// copy the main backbuffer to our backbuffer copy texture + /// copy the fbo's attached texture to the backbuffer pub fn copy(self: *State) !void { - const texbind = try self.backbuffer.bind(.@"2D"); + const texbind = try self.fb_texture.bind(.@"2D"); errdefer texbind.unbind(); try texbind.copySubImage2D(0, 0, 0, 0, 0, @intFromFloat(self.uniforms.resolution[0]), @intFromFloat(self.uniforms.resolution[1])); @@ -221,12 +241,12 @@ pub const State = struct { pub const Binding = struct { vao: gl.VertexArray.Binding, ebo: gl.Buffer.Binding, - backbuffer: gl.Texture.Binding, + fb_texture: gl.Texture.Binding, pub fn unbind(self: Binding) void { self.ebo.unbind(); self.vao.unbind(); - self.backbuffer.unbind(); + self.fb_texture.unbind(); } }; }; From be5828b5b6c5a3690dfec2b032b6f8d24d6261fc Mon Sep 17 00:00:00 2001 From: julia Date: Thu, 23 Jan 2025 20:25:57 +1100 Subject: [PATCH 4/5] remove commented lines --- src/renderer/OpenGL.zig | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 9fdd1a547c..b0fcdfc3a3 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -2363,9 +2363,6 @@ fn drawCustomPrograms(self: *OpenGL, custom_state: *custom.State) !void { // Setup the new frame try custom_state.newFrame(); - // const fbobind = try custom_state.fbo.bind(.framebuffer); - // defer fbobind.unbind(); - // Go through each custom shader and draw it. for (custom_state.programs) |program| { // Bind our cell program state, buffers From 480489bdce6f6d5331794a572ef0a7436f9c8a1f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 23 Jan 2025 12:44:25 -0800 Subject: [PATCH 5/5] renderer/opengl: small tweaks --- src/renderer/OpenGL.zig | 5 +---- src/renderer/opengl/custom.zig | 15 +++++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index b0fcdfc3a3..3e674c7155 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -2365,13 +2365,10 @@ fn drawCustomPrograms(self: *OpenGL, custom_state: *custom.State) !void { // Go through each custom shader and draw it. for (custom_state.programs) |program| { - // Bind our cell program state, buffers const bind = try program.bind(); defer bind.unbind(); try bind.draw(); - - // copy main and custom fbo - try custom_state.copy(); + try custom_state.copyFramebuffer(); } } diff --git a/src/renderer/opengl/custom.zig b/src/renderer/opengl/custom.zig index a69fc8b2b7..859277ce51 100644 --- a/src/renderer/opengl/custom.zig +++ b/src/renderer/opengl/custom.zig @@ -230,12 +230,19 @@ pub const State = struct { }; } - /// copy the fbo's attached texture to the backbuffer - pub fn copy(self: *State) !void { + /// Copy the fbo's attached texture to the backbuffer. + pub fn copyFramebuffer(self: *State) !void { const texbind = try self.fb_texture.bind(.@"2D"); errdefer texbind.unbind(); - - try texbind.copySubImage2D(0, 0, 0, 0, 0, @intFromFloat(self.uniforms.resolution[0]), @intFromFloat(self.uniforms.resolution[1])); + try texbind.copySubImage2D( + 0, + 0, + 0, + 0, + 0, + @intFromFloat(self.uniforms.resolution[0]), + @intFromFloat(self.uniforms.resolution[1]), + ); } pub const Binding = struct {