From e2ed1ed745cdeab2ad79b281fd60b169b86239e1 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 19 Apr 2022 13:54:50 -0700 Subject: [PATCH] shader modes, draw a jank cursor --- shaders/cell.f.glsl | 14 +++++++++---- shaders/cell.v.glsl | 49 ++++++++++++++++++++++++++++++++++++++----- src/Grid.zig | 46 +++++++++++++++++++++++++++------------- src/Window.zig | 2 +- src/opengl/Buffer.zig | 17 +++++++++++++++ 5 files changed, 103 insertions(+), 25 deletions(-) diff --git a/shaders/cell.f.glsl b/shaders/cell.f.glsl index c62e89ddb..b7b7f3914 100644 --- a/shaders/cell.f.glsl +++ b/shaders/cell.f.glsl @@ -1,6 +1,7 @@ #version 330 core in vec2 glyph_tex_coords; +flat in uint mode; // The color for this cell. If this is a background pass this is the // background color. Otherwise, this is the foreground color. @@ -9,14 +10,19 @@ flat in vec4 color; // Font texture uniform sampler2D text; -// Background or foreground pass. -uniform int background; +// See fragment shader +const uint MODE_BG = 1u; +const uint MODE_FG = 2u; void main() { - if (background == 1) { + switch (mode) { + case MODE_BG: gl_FragColor = color; - } else { + break; + + case MODE_FG: float a = texture(text, glyph_tex_coords).r; gl_FragColor = vec4(color.rgb, color.a*a); + break; } } diff --git a/shaders/cell.v.glsl b/shaders/cell.v.glsl index d6eb693d9..8dd30dfe7 100644 --- a/shaders/cell.v.glsl +++ b/shaders/cell.v.glsl @@ -1,5 +1,12 @@ #version 330 core +// These are the possible modes that "mode" can be set to. This is +// used to multiplex multiple render modes into a single shader. +// +// NOTE: this must be kept in sync with the fragment shader +const uint MODE_BG = 1u; +const uint MODE_FG = 2u; + // The grid coordinates (x, y) where x < columns and y < rows layout (location = 0) in vec2 grid_coord; @@ -18,6 +25,12 @@ layout (location = 4) in vec4 fg_color_in; // The background color for this cell in RGBA (0 to 1.0) layout (location = 5) in vec4 bg_color_in; +// The mode of this shader. The mode determines what fields are used, +// what the output will be, etc. This shader is capable of executing in +// multiple "modes" so that we can share some logic and so that we can draw +// the entire terminal grid in a single GPU pass. +layout (location = 6) in uint mode_in; + // The background or foreground color for the fragment, depending on // whether this is a background or foreground pass. flat out vec4 color; @@ -25,15 +38,37 @@ flat out vec4 color; // The x/y coordinate for the glyph representing the font. out vec2 glyph_tex_coords; +// Pass the mode forward to the fragment shader. +flat out uint mode; + uniform sampler2D text; uniform vec2 cell_size; uniform mat4 projection; -// non-zero if this is a background pass where we draw the background -// of the cell. We do a background pass followed by a foreground pass. -uniform int background; +/******************************************************************** + * Modes + * + *------------------------------------------------------------------- + * MODE_BG + * + * In MODE_BG, this shader renders only the background color for the + * cell. This is a simple mode where we generate a simple rectangle + * made up of 4 vertices and then it is filled. In this mode, the output + * "color" is the fill color for the bg. + * + *------------------------------------------------------------------- + * MODE_FG + * + * In MODE_FG, the shader renders the glyph onto this cell and utilizes + * the glyph texture "text". In this mode, the output "color" is the + * fg color to use for the glyph. + * + */ void main() { + // We always forward our mode + mode = mode_in; + // Top-left cell coordinates converted to world space // Example: (1,0) with a 30 wide cell is converted to (30,0) vec2 cell_pos = cell_size * grid_coord; @@ -52,7 +87,8 @@ void main() { position.x = (gl_VertexID == 0 || gl_VertexID == 1) ? 1. : 0.; position.y = (gl_VertexID == 0 || gl_VertexID == 3) ? 0. : 1.; - if (background == 1) { + switch (mode_in) { + case MODE_BG: // Calculate the final position of our cell in world space. // We have to add our cell size since our vertices are offset // one cell up and to the left. (Do the math to verify yourself) @@ -60,7 +96,9 @@ void main() { gl_Position = projection * vec4(cell_pos, 0.0, 1.0); color = bg_color_in / 255.0; - } else { + break; + + case MODE_FG: // The glyph offset is upside down so we need to reverse it to // be based on the offset of our cell. This is equivalent to // "1 - value" to flip the value. @@ -80,5 +118,6 @@ void main() { // Set our foreground color output color = fg_color_in / 255.; + break; } } diff --git a/src/Grid.zig b/src/Grid.zig index 93a677633..f45e8ebcf 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -41,16 +41,16 @@ const GPUCell = struct { grid_row: u16, /// vec2 glyph_pos - glyph_x: u32, - glyph_y: u32, + glyph_x: u32 = 0, + glyph_y: u32 = 0, /// vec2 glyph_size - glyph_width: u32, - glyph_height: u32, + glyph_width: u32 = 0, + glyph_height: u32 = 0, /// vec2 glyph_size - glyph_offset_x: i32, - glyph_offset_y: i32, + glyph_offset_x: i32 = 0, + glyph_offset_y: i32 = 0, /// vec4 fg_color_in fg_r: u8, @@ -63,6 +63,9 @@ const GPUCell = struct { bg_g: u8, bg_b: u8, bg_a: u8, + + /// uint mode + mode: u8, }; pub fn init(alloc: Allocator) !Grid { @@ -148,18 +151,22 @@ pub fn init(alloc: Allocator) !Grid { try vbobind.attributeAdvanced(4, 4, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(GPUCell), offset); offset += 4 * @sizeOf(u8); try vbobind.attributeAdvanced(5, 4, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(GPUCell), offset); + offset += 4 * @sizeOf(u8); + try vbobind.attributeIAdvanced(6, 1, gl.c.GL_UNSIGNED_BYTE, @sizeOf(GPUCell), offset); try vbobind.enableAttribArray(0); try vbobind.enableAttribArray(1); try vbobind.enableAttribArray(2); try vbobind.enableAttribArray(3); try vbobind.enableAttribArray(4); try vbobind.enableAttribArray(5); + try vbobind.enableAttribArray(6); try vbobind.attributeDivisor(0, 1); try vbobind.attributeDivisor(1, 1); try vbobind.attributeDivisor(2, 1); try vbobind.attributeDivisor(3, 1); try vbobind.attributeDivisor(4, 1); try vbobind.attributeDivisor(5, 1); + try vbobind.attributeDivisor(6, 1); // Build our texture const tex = try gl.Texture.create(); @@ -239,6 +246,7 @@ pub fn updateCells(self: *Grid, term: Terminal) !void { term.screen.items.len * term.cols, ); + // Build each cell for (term.screen.items) |line, y| { for (line.items) |cell, x| { // It can be zero if the cell is empty @@ -248,6 +256,7 @@ pub fn updateCells(self: *Grid, term: Terminal) !void { // TODO: if we add a glyph, I think we need to rerender the texture. const glyph = try self.font_atlas.addGlyph(self.alloc, cell.char); + // TODO: for background colors, add another cell with mode = 1 self.cells.appendAssumeCapacity(.{ .grid_col = @intCast(u16, x), .grid_row = @intCast(u16, y), @@ -265,9 +274,25 @@ pub fn updateCells(self: *Grid, term: Terminal) !void { .bg_g = 0xA5, .bg_b = 0, .bg_a = 0, + .mode = 2, }); } } + + // Draw the cursor + self.cells.appendAssumeCapacity(.{ + .grid_col = @intCast(u16, term.cursor.x), + .grid_row = @intCast(u16, term.cursor.y), + .fg_r = 0, + .fg_g = 0, + .fg_b = 0, + .fg_a = 0, + .bg_r = 0xFF, + .bg_g = 0xFF, + .bg_b = 0xFF, + .bg_a = 255, + .mode = 1, + }); } /// Set the screen size for rendering. This will update the projection @@ -319,15 +344,6 @@ pub fn render(self: Grid) !void { var texbind = try self.texture.bind(.@"2D"); defer texbind.unbind(); - try self.program.setUniform("background", 1); - try gl.drawElementsInstanced( - gl.c.GL_TRIANGLES, - 6, - gl.c.GL_UNSIGNED_BYTE, - self.cells.items.len, - ); - - try self.program.setUniform("background", 0); try gl.drawElementsInstanced( gl.c.GL_TRIANGLES, 6, diff --git a/src/Window.zig b/src/Window.zig index f6e7a5ca0..bed111279 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -92,7 +92,7 @@ pub fn create(alloc: Allocator) !*Window { // Create our terminal var term = Terminal.init(grid.size.columns, grid.size.rows); errdefer term.deinit(alloc); - try term.append(alloc, "hello!\r\nworld!"); + try term.append(alloc, "> "); self.* = .{ .alloc = alloc, diff --git a/src/opengl/Buffer.zig b/src/opengl/Buffer.zig index 7fa3a41f9..3c29b91d2 100644 --- a/src/opengl/Buffer.zig +++ b/src/opengl/Buffer.zig @@ -170,6 +170,23 @@ pub const Binding = struct { try errors.getError(); } + pub inline fn attributeIAdvanced( + _: Binding, + idx: c.GLuint, + size: c.GLint, + typ: c.GLenum, + stride: c.GLsizei, + offset: usize, + ) !void { + const offsetPtr = if (offset > 0) + @intToPtr(*const anyopaque, offset) + else + null; + + c.glVertexAttribIPointer(idx, size, typ, stride, offsetPtr); + try errors.getError(); + } + pub inline fn unbind(b: *Binding) void { c.glBindBuffer(@enumToInt(b.target), 0); b.* = undefined;