mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
shader modes, draw a jank cursor
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
|
|
||||||
in vec2 glyph_tex_coords;
|
in vec2 glyph_tex_coords;
|
||||||
|
flat in uint mode;
|
||||||
|
|
||||||
// The color for this cell. If this is a background pass this is the
|
// The color for this cell. If this is a background pass this is the
|
||||||
// background color. Otherwise, this is the foreground color.
|
// background color. Otherwise, this is the foreground color.
|
||||||
@ -9,14 +10,19 @@ flat in vec4 color;
|
|||||||
// Font texture
|
// Font texture
|
||||||
uniform sampler2D text;
|
uniform sampler2D text;
|
||||||
|
|
||||||
// Background or foreground pass.
|
// See fragment shader
|
||||||
uniform int background;
|
const uint MODE_BG = 1u;
|
||||||
|
const uint MODE_FG = 2u;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
if (background == 1) {
|
switch (mode) {
|
||||||
|
case MODE_BG:
|
||||||
gl_FragColor = color;
|
gl_FragColor = color;
|
||||||
} else {
|
break;
|
||||||
|
|
||||||
|
case MODE_FG:
|
||||||
float a = texture(text, glyph_tex_coords).r;
|
float a = texture(text, glyph_tex_coords).r;
|
||||||
gl_FragColor = vec4(color.rgb, color.a*a);
|
gl_FragColor = vec4(color.rgb, color.a*a);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
#version 330 core
|
#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
|
// The grid coordinates (x, y) where x < columns and y < rows
|
||||||
layout (location = 0) in vec2 grid_coord;
|
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)
|
// The background color for this cell in RGBA (0 to 1.0)
|
||||||
layout (location = 5) in vec4 bg_color_in;
|
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
|
// The background or foreground color for the fragment, depending on
|
||||||
// whether this is a background or foreground pass.
|
// whether this is a background or foreground pass.
|
||||||
flat out vec4 color;
|
flat out vec4 color;
|
||||||
@ -25,15 +38,37 @@ flat out vec4 color;
|
|||||||
// The x/y coordinate for the glyph representing the font.
|
// The x/y coordinate for the glyph representing the font.
|
||||||
out vec2 glyph_tex_coords;
|
out vec2 glyph_tex_coords;
|
||||||
|
|
||||||
|
// Pass the mode forward to the fragment shader.
|
||||||
|
flat out uint mode;
|
||||||
|
|
||||||
uniform sampler2D text;
|
uniform sampler2D text;
|
||||||
uniform vec2 cell_size;
|
uniform vec2 cell_size;
|
||||||
uniform mat4 projection;
|
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.
|
* Modes
|
||||||
uniform int background;
|
*
|
||||||
|
*-------------------------------------------------------------------
|
||||||
|
* 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() {
|
void main() {
|
||||||
|
// We always forward our mode
|
||||||
|
mode = mode_in;
|
||||||
|
|
||||||
// Top-left cell coordinates converted to world space
|
// Top-left cell coordinates converted to world space
|
||||||
// Example: (1,0) with a 30 wide cell is converted to (30,0)
|
// Example: (1,0) with a 30 wide cell is converted to (30,0)
|
||||||
vec2 cell_pos = cell_size * grid_coord;
|
vec2 cell_pos = cell_size * grid_coord;
|
||||||
@ -52,7 +87,8 @@ void main() {
|
|||||||
position.x = (gl_VertexID == 0 || gl_VertexID == 1) ? 1. : 0.;
|
position.x = (gl_VertexID == 0 || gl_VertexID == 1) ? 1. : 0.;
|
||||||
position.y = (gl_VertexID == 0 || gl_VertexID == 3) ? 0. : 1.;
|
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.
|
// Calculate the final position of our cell in world space.
|
||||||
// We have to add our cell size since our vertices are offset
|
// 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)
|
// 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);
|
gl_Position = projection * vec4(cell_pos, 0.0, 1.0);
|
||||||
color = bg_color_in / 255.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
|
// 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
|
// be based on the offset of our cell. This is equivalent to
|
||||||
// "1 - value" to flip the value.
|
// "1 - value" to flip the value.
|
||||||
@ -80,5 +118,6 @@ void main() {
|
|||||||
|
|
||||||
// Set our foreground color output
|
// Set our foreground color output
|
||||||
color = fg_color_in / 255.;
|
color = fg_color_in / 255.;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
46
src/Grid.zig
46
src/Grid.zig
@ -41,16 +41,16 @@ const GPUCell = struct {
|
|||||||
grid_row: u16,
|
grid_row: u16,
|
||||||
|
|
||||||
/// vec2 glyph_pos
|
/// vec2 glyph_pos
|
||||||
glyph_x: u32,
|
glyph_x: u32 = 0,
|
||||||
glyph_y: u32,
|
glyph_y: u32 = 0,
|
||||||
|
|
||||||
/// vec2 glyph_size
|
/// vec2 glyph_size
|
||||||
glyph_width: u32,
|
glyph_width: u32 = 0,
|
||||||
glyph_height: u32,
|
glyph_height: u32 = 0,
|
||||||
|
|
||||||
/// vec2 glyph_size
|
/// vec2 glyph_size
|
||||||
glyph_offset_x: i32,
|
glyph_offset_x: i32 = 0,
|
||||||
glyph_offset_y: i32,
|
glyph_offset_y: i32 = 0,
|
||||||
|
|
||||||
/// vec4 fg_color_in
|
/// vec4 fg_color_in
|
||||||
fg_r: u8,
|
fg_r: u8,
|
||||||
@ -63,6 +63,9 @@ const GPUCell = struct {
|
|||||||
bg_g: u8,
|
bg_g: u8,
|
||||||
bg_b: u8,
|
bg_b: u8,
|
||||||
bg_a: u8,
|
bg_a: u8,
|
||||||
|
|
||||||
|
/// uint mode
|
||||||
|
mode: u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(alloc: Allocator) !Grid {
|
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);
|
try vbobind.attributeAdvanced(4, 4, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(GPUCell), offset);
|
||||||
offset += 4 * @sizeOf(u8);
|
offset += 4 * @sizeOf(u8);
|
||||||
try vbobind.attributeAdvanced(5, 4, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(GPUCell), offset);
|
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(0);
|
||||||
try vbobind.enableAttribArray(1);
|
try vbobind.enableAttribArray(1);
|
||||||
try vbobind.enableAttribArray(2);
|
try vbobind.enableAttribArray(2);
|
||||||
try vbobind.enableAttribArray(3);
|
try vbobind.enableAttribArray(3);
|
||||||
try vbobind.enableAttribArray(4);
|
try vbobind.enableAttribArray(4);
|
||||||
try vbobind.enableAttribArray(5);
|
try vbobind.enableAttribArray(5);
|
||||||
|
try vbobind.enableAttribArray(6);
|
||||||
try vbobind.attributeDivisor(0, 1);
|
try vbobind.attributeDivisor(0, 1);
|
||||||
try vbobind.attributeDivisor(1, 1);
|
try vbobind.attributeDivisor(1, 1);
|
||||||
try vbobind.attributeDivisor(2, 1);
|
try vbobind.attributeDivisor(2, 1);
|
||||||
try vbobind.attributeDivisor(3, 1);
|
try vbobind.attributeDivisor(3, 1);
|
||||||
try vbobind.attributeDivisor(4, 1);
|
try vbobind.attributeDivisor(4, 1);
|
||||||
try vbobind.attributeDivisor(5, 1);
|
try vbobind.attributeDivisor(5, 1);
|
||||||
|
try vbobind.attributeDivisor(6, 1);
|
||||||
|
|
||||||
// Build our texture
|
// Build our texture
|
||||||
const tex = try gl.Texture.create();
|
const tex = try gl.Texture.create();
|
||||||
@ -239,6 +246,7 @@ pub fn updateCells(self: *Grid, term: Terminal) !void {
|
|||||||
term.screen.items.len * term.cols,
|
term.screen.items.len * term.cols,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Build each cell
|
||||||
for (term.screen.items) |line, y| {
|
for (term.screen.items) |line, y| {
|
||||||
for (line.items) |cell, x| {
|
for (line.items) |cell, x| {
|
||||||
// It can be zero if the cell is empty
|
// 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.
|
// 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);
|
const glyph = try self.font_atlas.addGlyph(self.alloc, cell.char);
|
||||||
|
|
||||||
|
// TODO: for background colors, add another cell with mode = 1
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.grid_col = @intCast(u16, x),
|
.grid_col = @intCast(u16, x),
|
||||||
.grid_row = @intCast(u16, y),
|
.grid_row = @intCast(u16, y),
|
||||||
@ -265,9 +274,25 @@ pub fn updateCells(self: *Grid, term: Terminal) !void {
|
|||||||
.bg_g = 0xA5,
|
.bg_g = 0xA5,
|
||||||
.bg_b = 0,
|
.bg_b = 0,
|
||||||
.bg_a = 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
|
/// 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");
|
var texbind = try self.texture.bind(.@"2D");
|
||||||
defer texbind.unbind();
|
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(
|
try gl.drawElementsInstanced(
|
||||||
gl.c.GL_TRIANGLES,
|
gl.c.GL_TRIANGLES,
|
||||||
6,
|
6,
|
||||||
|
@ -92,7 +92,7 @@ pub fn create(alloc: Allocator) !*Window {
|
|||||||
// Create our terminal
|
// Create our terminal
|
||||||
var term = Terminal.init(grid.size.columns, grid.size.rows);
|
var term = Terminal.init(grid.size.columns, grid.size.rows);
|
||||||
errdefer term.deinit(alloc);
|
errdefer term.deinit(alloc);
|
||||||
try term.append(alloc, "hello!\r\nworld!");
|
try term.append(alloc, "> ");
|
||||||
|
|
||||||
self.* = .{
|
self.* = .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
|
@ -170,6 +170,23 @@ pub const Binding = struct {
|
|||||||
try errors.getError();
|
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 {
|
pub inline fn unbind(b: *Binding) void {
|
||||||
c.glBindBuffer(@enumToInt(b.target), 0);
|
c.glBindBuffer(@enumToInt(b.target), 0);
|
||||||
b.* = undefined;
|
b.* = undefined;
|
||||||
|
Reference in New Issue
Block a user