mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-09-05 19:08:17 +00:00
renderer: make shader pipeline prep code DRYer
In this format it will be a lot easier to iterate on this since adding and removing pipelines only has to be done in a single place. This commit also separates out the main background color from individual cell background color drawing, because sometimes kitty images need to be between the main background and individual cell backgrounds (kitty image z-index seems to be entirely broken at the moment, I intend to fix it in future commits).
This commit is contained in:
@@ -75,7 +75,7 @@ fn initTarget(
|
||||
self.metallib = .create(b, .{
|
||||
.name = "Ghostty",
|
||||
.target = target,
|
||||
.sources = &.{b.path("src/renderer/shaders/cell.metal")},
|
||||
.sources = &.{b.path("src/renderer/shaders/shaders.metal")},
|
||||
});
|
||||
|
||||
// Change our config
|
||||
|
@@ -1391,23 +1391,43 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
}});
|
||||
defer pass.complete();
|
||||
|
||||
// bg images
|
||||
try self.drawImagePlacements(&pass, self.image_placements.items[0..self.image_bg_end]);
|
||||
// bg
|
||||
// First we draw the background color.
|
||||
//
|
||||
// NOTE: We don't use the clear_color for this because that
|
||||
// would require us to do color space conversion on the
|
||||
// CPU-side. In the future when we have utilities for
|
||||
// that we should remove this step and use clear_color.
|
||||
pass.step(.{
|
||||
.pipeline = self.shaders.cell_bg_pipeline,
|
||||
.pipeline = self.shaders.pipelines.bg_color,
|
||||
.uniforms = frame.uniforms.buffer,
|
||||
.buffers = &.{ null, frame.cells_bg.buffer },
|
||||
.draw = .{
|
||||
.type = .triangle,
|
||||
.vertex_count = 3,
|
||||
},
|
||||
.draw = .{ .type = .triangle, .vertex_count = 3 },
|
||||
});
|
||||
// mg images
|
||||
try self.drawImagePlacements(&pass, self.image_placements.items[self.image_bg_end..self.image_text_end]);
|
||||
// text
|
||||
|
||||
// Then we draw any kitty images that need
|
||||
// to be behind text AND cell backgrounds.
|
||||
try self.drawImagePlacements(
|
||||
&pass,
|
||||
self.image_placements.items[0..self.image_bg_end],
|
||||
);
|
||||
|
||||
// Then we draw any opaque cell backgrounds.
|
||||
pass.step(.{
|
||||
.pipeline = self.shaders.cell_text_pipeline,
|
||||
.pipeline = self.shaders.pipelines.cell_bg,
|
||||
.uniforms = frame.uniforms.buffer,
|
||||
.buffers = &.{ null, frame.cells_bg.buffer },
|
||||
.draw = .{ .type = .triangle, .vertex_count = 3 },
|
||||
});
|
||||
|
||||
// Kitty images between cell backgrounds and text.
|
||||
try self.drawImagePlacements(
|
||||
&pass,
|
||||
self.image_placements.items[self.image_bg_end..self.image_text_end],
|
||||
);
|
||||
|
||||
// Text.
|
||||
pass.step(.{
|
||||
.pipeline = self.shaders.pipelines.cell_text,
|
||||
.uniforms = frame.uniforms.buffer,
|
||||
.buffers = &.{
|
||||
frame.cells.buffer,
|
||||
@@ -1423,8 +1443,12 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
.instance_count = fg_count,
|
||||
},
|
||||
});
|
||||
// fg images
|
||||
try self.drawImagePlacements(&pass, self.image_placements.items[self.image_text_end..]);
|
||||
|
||||
// Kitty images in front of text.
|
||||
try self.drawImagePlacements(
|
||||
&pass,
|
||||
self.image_placements.items[self.image_text_end..],
|
||||
);
|
||||
}
|
||||
|
||||
// If we have custom shaders, then we render them.
|
||||
@@ -1539,7 +1563,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
defer buf.deinit();
|
||||
|
||||
pass.step(.{
|
||||
.pipeline = self.shaders.image_pipeline,
|
||||
.pipeline = self.shaders.pipelines.image,
|
||||
.buffers = &.{buf.buffer},
|
||||
.textures = &.{texture},
|
||||
.draw = .{
|
||||
@@ -2378,8 +2402,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
const bg_alpha: u8 = bg_alpha: {
|
||||
const default: u8 = 255;
|
||||
|
||||
if (self.config.background_opacity >= 1) break :bg_alpha default;
|
||||
|
||||
// Cells that are selected should be fully opaque.
|
||||
if (selected) break :bg_alpha default;
|
||||
|
||||
@@ -2387,12 +2409,12 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
if (style.flags.inverse) break :bg_alpha default;
|
||||
|
||||
// Cells that have an explicit bg color should be fully opaque.
|
||||
if (bg_style != null) {
|
||||
break :bg_alpha default;
|
||||
}
|
||||
if (bg_style != null) break :bg_alpha default;
|
||||
|
||||
// Otherwise, we use the configured background opacity.
|
||||
break :bg_alpha @intFromFloat(@round(self.config.background_opacity * 255.0));
|
||||
// Otherwise, we won't draw the bg for this cell,
|
||||
// we'll let the already-drawn background color
|
||||
// show through.
|
||||
break :bg_alpha 0;
|
||||
};
|
||||
|
||||
self.cells.bgCell(y, x).* = .{
|
||||
|
@@ -10,20 +10,90 @@ const Pipeline = @import("Pipeline.zig");
|
||||
|
||||
const log = std.log.scoped(.metal);
|
||||
|
||||
const pipeline_descs: []const struct { [:0]const u8, PipelineDescription } =
|
||||
&.{
|
||||
.{ "bg_color", .{
|
||||
.vertex_fn = "full_screen_vertex",
|
||||
.fragment_fn = "bg_color_fragment",
|
||||
.blending_enabled = false,
|
||||
} },
|
||||
.{ "cell_bg", .{
|
||||
.vertex_fn = "full_screen_vertex",
|
||||
.fragment_fn = "cell_bg_fragment",
|
||||
.blending_enabled = true,
|
||||
} },
|
||||
.{ "cell_text", .{
|
||||
.vertex_attributes = CellText,
|
||||
.vertex_fn = "cell_text_vertex",
|
||||
.fragment_fn = "cell_text_fragment",
|
||||
.step_fn = .per_instance,
|
||||
.blending_enabled = true,
|
||||
} },
|
||||
.{ "image", .{
|
||||
.vertex_attributes = Image,
|
||||
.vertex_fn = "image_vertex",
|
||||
.fragment_fn = "image_fragment",
|
||||
.step_fn = .per_instance,
|
||||
.blending_enabled = true,
|
||||
} },
|
||||
};
|
||||
|
||||
/// All the comptime-known info about a pipeline, so that
|
||||
/// we can define them ahead-of-time in an ergonomic way.
|
||||
const PipelineDescription = struct {
|
||||
vertex_attributes: ?type = null,
|
||||
vertex_fn: []const u8,
|
||||
fragment_fn: []const u8,
|
||||
step_fn: mtl.MTLVertexStepFunction = .per_vertex,
|
||||
blending_enabled: bool,
|
||||
|
||||
fn initPipeline(
|
||||
self: PipelineDescription,
|
||||
device: objc.Object,
|
||||
library: objc.Object,
|
||||
pixel_format: mtl.MTLPixelFormat,
|
||||
) !Pipeline {
|
||||
return try .init(self.vertex_attributes, .{
|
||||
.device = device,
|
||||
.vertex_fn = self.vertex_fn,
|
||||
.fragment_fn = self.fragment_fn,
|
||||
.vertex_library = library,
|
||||
.fragment_library = library,
|
||||
.step_fn = self.step_fn,
|
||||
.attachments = &.{.{
|
||||
.pixel_format = pixel_format,
|
||||
.blending_enabled = self.blending_enabled,
|
||||
}},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/// We create a type for the pipeline collection based on our desc array.
|
||||
const PipelineCollection = t: {
|
||||
var fields: [pipeline_descs.len]std.builtin.Type.StructField = undefined;
|
||||
for (pipeline_descs, 0..) |pipeline, i| {
|
||||
fields[i] = .{
|
||||
.name = pipeline[0],
|
||||
.type = Pipeline,
|
||||
.default_value_ptr = null,
|
||||
.is_comptime = false,
|
||||
.alignment = @alignOf(Pipeline),
|
||||
};
|
||||
}
|
||||
break :t @Type(.{ .@"struct" = .{
|
||||
.layout = .auto,
|
||||
.fields = &fields,
|
||||
.decls = &.{},
|
||||
.is_tuple = false,
|
||||
} });
|
||||
};
|
||||
|
||||
/// This contains the state for the shaders used by the Metal renderer.
|
||||
pub const Shaders = struct {
|
||||
library: objc.Object,
|
||||
|
||||
/// Renders cell foreground elements (text, decorations).
|
||||
cell_text_pipeline: Pipeline,
|
||||
|
||||
/// The cell background shader is the shader used to render the
|
||||
/// background of terminal cells.
|
||||
cell_bg_pipeline: Pipeline,
|
||||
|
||||
/// The image shader is the shader used to render images for things
|
||||
/// like the Kitty image protocol.
|
||||
image_pipeline: Pipeline,
|
||||
/// Collection of available render pipelines.
|
||||
pipelines: PipelineCollection,
|
||||
|
||||
/// Custom shaders to run against the final drawable texture. This
|
||||
/// can be used to apply a lot of effects. Each shader is run in sequence
|
||||
@@ -48,14 +118,24 @@ pub const Shaders = struct {
|
||||
const library = try initLibrary(device);
|
||||
errdefer library.msgSend(void, objc.sel("release"), .{});
|
||||
|
||||
const cell_text_pipeline = try initCellTextPipeline(device, library, pixel_format);
|
||||
errdefer cell_text_pipeline.deinit();
|
||||
var pipelines: PipelineCollection = undefined;
|
||||
|
||||
const cell_bg_pipeline = try initCellBgPipeline(device, library, pixel_format);
|
||||
errdefer cell_bg_pipeline.deinit();
|
||||
var initialized_pipelines: usize = 0;
|
||||
|
||||
const image_pipeline = try initImagePipeline(device, library, pixel_format);
|
||||
errdefer image_pipeline.deinit();
|
||||
errdefer inline for (pipeline_descs, 0..) |pipeline, i| {
|
||||
if (i < initialized_pipelines) {
|
||||
@field(pipelines, pipeline[0]).deinit();
|
||||
}
|
||||
};
|
||||
|
||||
inline for (pipeline_descs) |pipeline| {
|
||||
@field(pipelines, pipeline[0]) = try pipeline[1].initPipeline(
|
||||
device,
|
||||
library,
|
||||
pixel_format,
|
||||
);
|
||||
initialized_pipelines += 1;
|
||||
}
|
||||
|
||||
const post_pipelines: []const Pipeline = initPostPipelines(
|
||||
alloc,
|
||||
@@ -77,9 +157,7 @@ pub const Shaders = struct {
|
||||
|
||||
return .{
|
||||
.library = library,
|
||||
.cell_text_pipeline = cell_text_pipeline,
|
||||
.cell_bg_pipeline = cell_bg_pipeline,
|
||||
.image_pipeline = image_pipeline,
|
||||
.pipelines = pipelines,
|
||||
.post_pipelines = post_pipelines,
|
||||
};
|
||||
}
|
||||
@@ -89,9 +167,9 @@ pub const Shaders = struct {
|
||||
self.defunct = true;
|
||||
|
||||
// Release our primary shaders
|
||||
self.cell_text_pipeline.deinit();
|
||||
self.cell_bg_pipeline.deinit();
|
||||
self.image_pipeline.deinit();
|
||||
inline for (pipeline_descs) |pipeline| {
|
||||
@field(self.pipelines, pipeline[0]).deinit();
|
||||
}
|
||||
self.library.msgSend(void, objc.sel("release"), .{});
|
||||
|
||||
// Release our postprocess shaders
|
||||
@@ -104,15 +182,7 @@ pub const Shaders = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// Single parameter for the image shader. See shader for field details.
|
||||
pub const Image = extern struct {
|
||||
grid_pos: [2]f32,
|
||||
cell_offset: [2]f32,
|
||||
source_rect: [4]f32,
|
||||
dest_size: [2]f32,
|
||||
};
|
||||
|
||||
/// The uniforms that are passed to the terminal cell shader.
|
||||
/// The uniforms that are passed to our shaders.
|
||||
pub const Uniforms = extern struct {
|
||||
// Note: all of the explicit alignments are copied from the
|
||||
// MSL developer reference just so that we can be sure that we got
|
||||
@@ -182,6 +252,42 @@ pub const Uniforms = extern struct {
|
||||
};
|
||||
};
|
||||
|
||||
/// This is a single parameter for the terminal cell shader.
|
||||
pub const CellText = extern struct {
|
||||
glyph_pos: [2]u32 align(8) = .{ 0, 0 },
|
||||
glyph_size: [2]u32 align(8) = .{ 0, 0 },
|
||||
bearings: [2]i16 align(4) = .{ 0, 0 },
|
||||
grid_pos: [2]u16 align(4),
|
||||
color: [4]u8 align(4),
|
||||
mode: Mode align(1),
|
||||
constraint_width: u8 align(1) = 0,
|
||||
|
||||
pub const Mode = enum(u8) {
|
||||
fg = 1,
|
||||
fg_constrained = 2,
|
||||
fg_color = 3,
|
||||
cursor = 4,
|
||||
fg_powerline = 5,
|
||||
};
|
||||
|
||||
test {
|
||||
// Minimizing the size of this struct is important,
|
||||
// so we test it in order to be aware of any changes.
|
||||
try std.testing.expectEqual(32, @sizeOf(CellText));
|
||||
}
|
||||
};
|
||||
|
||||
/// This is a single parameter for the cell bg shader.
|
||||
pub const CellBg = [4]u8;
|
||||
|
||||
/// Single parameter for the image shader. See shader for field details.
|
||||
pub const Image = extern struct {
|
||||
grid_pos: [2]f32,
|
||||
cell_offset: [2]f32,
|
||||
source_rect: [4]f32,
|
||||
dest_size: [2]f32,
|
||||
};
|
||||
|
||||
/// Initialize the MTLLibrary. A MTLLibrary is a collection of shaders.
|
||||
fn initLibrary(device: objc.Object) !objc.Object {
|
||||
const start = try std.time.Instant.now();
|
||||
@@ -294,99 +400,6 @@ fn initPostPipeline(
|
||||
});
|
||||
}
|
||||
|
||||
/// This is a single parameter for the terminal cell shader.
|
||||
pub const CellText = extern struct {
|
||||
glyph_pos: [2]u32 align(8) = .{ 0, 0 },
|
||||
glyph_size: [2]u32 align(8) = .{ 0, 0 },
|
||||
bearings: [2]i16 align(4) = .{ 0, 0 },
|
||||
grid_pos: [2]u16 align(4),
|
||||
color: [4]u8 align(4),
|
||||
mode: Mode align(1),
|
||||
constraint_width: u8 align(1) = 0,
|
||||
|
||||
pub const Mode = enum(u8) {
|
||||
fg = 1,
|
||||
fg_constrained = 2,
|
||||
fg_color = 3,
|
||||
cursor = 4,
|
||||
fg_powerline = 5,
|
||||
};
|
||||
|
||||
test {
|
||||
// Minimizing the size of this struct is important,
|
||||
// so we test it in order to be aware of any changes.
|
||||
try std.testing.expectEqual(32, @sizeOf(CellText));
|
||||
}
|
||||
};
|
||||
|
||||
/// Initialize the cell render pipeline for our shader library.
|
||||
fn initCellTextPipeline(
|
||||
device: objc.Object,
|
||||
library: objc.Object,
|
||||
pixel_format: mtl.MTLPixelFormat,
|
||||
) !Pipeline {
|
||||
return try Pipeline.init(CellText, .{
|
||||
.device = device,
|
||||
.vertex_fn = "cell_text_vertex",
|
||||
.fragment_fn = "cell_text_fragment",
|
||||
.vertex_library = library,
|
||||
.fragment_library = library,
|
||||
.step_fn = .per_instance,
|
||||
.attachments = &.{
|
||||
.{
|
||||
.pixel_format = pixel_format,
|
||||
.blending_enabled = true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/// This is a single parameter for the cell bg shader.
|
||||
pub const CellBg = [4]u8;
|
||||
|
||||
/// Initialize the cell background render pipeline for our shader library.
|
||||
fn initCellBgPipeline(
|
||||
device: objc.Object,
|
||||
library: objc.Object,
|
||||
pixel_format: mtl.MTLPixelFormat,
|
||||
) !Pipeline {
|
||||
return try Pipeline.init(null, .{
|
||||
.device = device,
|
||||
.vertex_fn = "cell_bg_vertex",
|
||||
.fragment_fn = "cell_bg_fragment",
|
||||
.vertex_library = library,
|
||||
.fragment_library = library,
|
||||
.attachments = &.{
|
||||
.{
|
||||
.pixel_format = pixel_format,
|
||||
.blending_enabled = false,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/// Initialize the image render pipeline for our shader library.
|
||||
fn initImagePipeline(
|
||||
device: objc.Object,
|
||||
library: objc.Object,
|
||||
pixel_format: mtl.MTLPixelFormat,
|
||||
) !Pipeline {
|
||||
return try Pipeline.init(Image, .{
|
||||
.device = device,
|
||||
.vertex_fn = "image_vertex",
|
||||
.fragment_fn = "image_fragment",
|
||||
.vertex_library = library,
|
||||
.fragment_library = library,
|
||||
.step_fn = .per_instance,
|
||||
.attachments = &.{
|
||||
.{
|
||||
.pixel_format = pixel_format,
|
||||
.blending_enabled = true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn checkError(err_: ?*anyopaque) !void {
|
||||
const nserr = objc.Object.fromId(err_ orelse return);
|
||||
const str = @as(
|
||||
|
@@ -7,18 +7,77 @@ const Pipeline = @import("Pipeline.zig");
|
||||
|
||||
const log = std.log.scoped(.opengl);
|
||||
|
||||
const pipeline_descs: []const struct { [:0]const u8, PipelineDescription } =
|
||||
&.{
|
||||
.{ "bg_color", .{
|
||||
.vertex_fn = loadShaderCode("../shaders/glsl/full_screen.v.glsl"),
|
||||
.fragment_fn = loadShaderCode("../shaders/glsl/bg_color.f.glsl"),
|
||||
.blending_enabled = false,
|
||||
} },
|
||||
.{ "cell_bg", .{
|
||||
.vertex_fn = loadShaderCode("../shaders/glsl/full_screen.v.glsl"),
|
||||
.fragment_fn = loadShaderCode("../shaders/glsl/cell_bg.f.glsl"),
|
||||
.blending_enabled = true,
|
||||
} },
|
||||
.{ "cell_text", .{
|
||||
.vertex_attributes = CellText,
|
||||
.vertex_fn = loadShaderCode("../shaders/glsl/cell_text.v.glsl"),
|
||||
.fragment_fn = loadShaderCode("../shaders/glsl/cell_text.f.glsl"),
|
||||
.step_fn = .per_instance,
|
||||
.blending_enabled = true,
|
||||
} },
|
||||
.{ "image", .{
|
||||
.vertex_attributes = Image,
|
||||
.vertex_fn = loadShaderCode("../shaders/glsl/image.v.glsl"),
|
||||
.fragment_fn = loadShaderCode("../shaders/glsl/image.f.glsl"),
|
||||
.step_fn = .per_instance,
|
||||
.blending_enabled = true,
|
||||
} },
|
||||
};
|
||||
|
||||
/// All the comptime-known info about a pipeline, so that
|
||||
/// we can define them ahead-of-time in an ergonomic way.
|
||||
const PipelineDescription = struct {
|
||||
vertex_attributes: ?type = null,
|
||||
vertex_fn: [:0]const u8,
|
||||
fragment_fn: [:0]const u8,
|
||||
step_fn: Pipeline.Options.StepFunction = .per_vertex,
|
||||
blending_enabled: bool = true,
|
||||
|
||||
fn initPipeline(self: PipelineDescription) !Pipeline {
|
||||
return try .init(self.vertex_attributes, .{
|
||||
.vertex_fn = self.vertex_fn,
|
||||
.fragment_fn = self.fragment_fn,
|
||||
.step_fn = self.step_fn,
|
||||
.blending_enabled = self.blending_enabled,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/// We create a type for the pipeline collection based on our desc array.
|
||||
const PipelineCollection = t: {
|
||||
var fields: [pipeline_descs.len]std.builtin.Type.StructField = undefined;
|
||||
for (pipeline_descs, 0..) |pipeline, i| {
|
||||
fields[i] = .{
|
||||
.name = pipeline[0],
|
||||
.type = Pipeline,
|
||||
.default_value_ptr = null,
|
||||
.is_comptime = false,
|
||||
.alignment = @alignOf(Pipeline),
|
||||
};
|
||||
}
|
||||
break :t @Type(.{ .@"struct" = .{
|
||||
.layout = .auto,
|
||||
.fields = &fields,
|
||||
.decls = &.{},
|
||||
.is_tuple = false,
|
||||
} });
|
||||
};
|
||||
|
||||
/// This contains the state for the shaders used by the Metal renderer.
|
||||
pub const Shaders = struct {
|
||||
/// Renders cell foreground elements (text, decorations).
|
||||
cell_text_pipeline: Pipeline,
|
||||
|
||||
/// The cell background shader is the shader used to render the
|
||||
/// background of terminal cells.
|
||||
cell_bg_pipeline: Pipeline,
|
||||
|
||||
/// The image shader is the shader used to render images for things
|
||||
/// like the Kitty image protocol.
|
||||
image_pipeline: Pipeline,
|
||||
/// Collection of available render pipelines.
|
||||
pipelines: PipelineCollection,
|
||||
|
||||
/// Custom shaders to run against the final drawable texture. This
|
||||
/// can be used to apply a lot of effects. Each shader is run in sequence
|
||||
@@ -38,14 +97,20 @@ pub const Shaders = struct {
|
||||
alloc: Allocator,
|
||||
post_shaders: []const [:0]const u8,
|
||||
) !Shaders {
|
||||
const cell_text_pipeline = try initCellTextPipeline();
|
||||
errdefer cell_text_pipeline.deinit();
|
||||
var pipelines: PipelineCollection = undefined;
|
||||
|
||||
const cell_bg_pipeline = try initCellBgPipeline();
|
||||
errdefer cell_bg_pipeline.deinit();
|
||||
var initialized_pipelines: usize = 0;
|
||||
|
||||
const image_pipeline = try initImagePipeline();
|
||||
errdefer image_pipeline.deinit();
|
||||
errdefer inline for (pipeline_descs, 0..) |pipeline, i| {
|
||||
if (i < initialized_pipelines) {
|
||||
@field(pipelines, pipeline[0]).deinit();
|
||||
}
|
||||
};
|
||||
|
||||
inline for (pipeline_descs) |pipeline| {
|
||||
@field(pipelines, pipeline[0]) = try pipeline[1].initPipeline();
|
||||
initialized_pipelines += 1;
|
||||
}
|
||||
|
||||
const post_pipelines: []const Pipeline = initPostPipelines(
|
||||
alloc,
|
||||
@@ -63,9 +128,7 @@ pub const Shaders = struct {
|
||||
};
|
||||
|
||||
return .{
|
||||
.cell_text_pipeline = cell_text_pipeline,
|
||||
.cell_bg_pipeline = cell_bg_pipeline,
|
||||
.image_pipeline = image_pipeline,
|
||||
.pipelines = pipelines,
|
||||
.post_pipelines = post_pipelines,
|
||||
};
|
||||
}
|
||||
@@ -75,9 +138,9 @@ pub const Shaders = struct {
|
||||
self.defunct = true;
|
||||
|
||||
// Release our primary shaders
|
||||
self.cell_text_pipeline.deinit();
|
||||
self.cell_bg_pipeline.deinit();
|
||||
self.image_pipeline.deinit();
|
||||
inline for (pipeline_descs) |pipeline| {
|
||||
@field(self.pipelines, pipeline[0]).deinit();
|
||||
}
|
||||
|
||||
// Release our postprocess shaders
|
||||
if (self.post_pipelines.len > 0) {
|
||||
@@ -89,15 +152,7 @@ pub const Shaders = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// Single parameter for the image shader. See shader for field details.
|
||||
pub const Image = extern struct {
|
||||
grid_pos: [2]f32 align(8),
|
||||
cell_offset: [2]f32 align(8),
|
||||
source_rect: [4]f32 align(16),
|
||||
dest_size: [2]f32 align(8),
|
||||
};
|
||||
|
||||
/// The uniforms that are passed to the terminal cell shader.
|
||||
/// The uniforms that are passed to our shaders.
|
||||
pub const Uniforms = extern struct {
|
||||
/// The projection matrix for turning world coordinates to normalized.
|
||||
/// This is calculated based on the size of the screen.
|
||||
@@ -165,6 +220,42 @@ pub const Uniforms = extern struct {
|
||||
};
|
||||
};
|
||||
|
||||
/// This is a single parameter for the terminal cell shader.
|
||||
pub const CellText = extern struct {
|
||||
glyph_pos: [2]u32 align(8) = .{ 0, 0 },
|
||||
glyph_size: [2]u32 align(8) = .{ 0, 0 },
|
||||
bearings: [2]i16 align(4) = .{ 0, 0 },
|
||||
grid_pos: [2]u16 align(4),
|
||||
color: [4]u8 align(4),
|
||||
mode: Mode align(4),
|
||||
constraint_width: u32 align(4) = 0,
|
||||
|
||||
pub const Mode = enum(u32) {
|
||||
fg = 1,
|
||||
fg_constrained = 2,
|
||||
fg_color = 3,
|
||||
cursor = 4,
|
||||
fg_powerline = 5,
|
||||
};
|
||||
|
||||
// test {
|
||||
// // Minimizing the size of this struct is important,
|
||||
// // so we test it in order to be aware of any changes.
|
||||
// try std.testing.expectEqual(32, @sizeOf(CellText));
|
||||
// }
|
||||
};
|
||||
|
||||
/// This is a single parameter for the cell bg shader.
|
||||
pub const CellBg = [4]u8;
|
||||
|
||||
/// Single parameter for the image shader. See shader for field details.
|
||||
pub const Image = extern struct {
|
||||
grid_pos: [2]f32 align(8),
|
||||
cell_offset: [2]f32 align(8),
|
||||
source_rect: [4]f32 align(16),
|
||||
dest_size: [2]f32 align(8),
|
||||
};
|
||||
|
||||
/// Initialize our custom shader pipelines. The shaders argument is a
|
||||
/// set of shader source code, not file paths.
|
||||
fn initPostPipelines(
|
||||
@@ -204,60 +295,6 @@ fn initPostPipeline(data: [:0]const u8) !Pipeline {
|
||||
});
|
||||
}
|
||||
|
||||
/// This is a single parameter for the terminal cell shader.
|
||||
pub const CellText = extern struct {
|
||||
glyph_pos: [2]u32 align(8) = .{ 0, 0 },
|
||||
glyph_size: [2]u32 align(8) = .{ 0, 0 },
|
||||
bearings: [2]i16 align(4) = .{ 0, 0 },
|
||||
grid_pos: [2]u16 align(4),
|
||||
color: [4]u8 align(4),
|
||||
mode: Mode align(4),
|
||||
constraint_width: u32 align(4) = 0,
|
||||
|
||||
pub const Mode = enum(u32) {
|
||||
fg = 1,
|
||||
fg_constrained = 2,
|
||||
fg_color = 3,
|
||||
cursor = 4,
|
||||
fg_powerline = 5,
|
||||
};
|
||||
|
||||
// test {
|
||||
// // Minimizing the size of this struct is important,
|
||||
// // so we test it in order to be aware of any changes.
|
||||
// try std.testing.expectEqual(32, @sizeOf(CellText));
|
||||
// }
|
||||
};
|
||||
|
||||
/// Initialize the cell render pipeline.
|
||||
fn initCellTextPipeline() !Pipeline {
|
||||
return try Pipeline.init(CellText, .{
|
||||
.vertex_fn = loadShaderCode("../shaders/glsl/cell_text.v.glsl"),
|
||||
.fragment_fn = loadShaderCode("../shaders/glsl/cell_text.f.glsl"),
|
||||
.step_fn = .per_instance,
|
||||
});
|
||||
}
|
||||
|
||||
/// This is a single parameter for the cell bg shader.
|
||||
pub const CellBg = [4]u8;
|
||||
|
||||
/// Initialize the cell background render pipeline.
|
||||
fn initCellBgPipeline() !Pipeline {
|
||||
return try Pipeline.init(null, .{
|
||||
.vertex_fn = loadShaderCode("../shaders/glsl/full_screen.v.glsl"),
|
||||
.fragment_fn = loadShaderCode("../shaders/glsl/cell_bg.f.glsl"),
|
||||
});
|
||||
}
|
||||
|
||||
/// Initialize the image render pipeline.
|
||||
fn initImagePipeline() !Pipeline {
|
||||
return try Pipeline.init(Image, .{
|
||||
.vertex_fn = loadShaderCode("../shaders/glsl/image.v.glsl"),
|
||||
.fragment_fn = loadShaderCode("../shaders/glsl/image.f.glsl"),
|
||||
.step_fn = .per_instance,
|
||||
});
|
||||
}
|
||||
|
||||
/// Load shader code from the target path, processing `#include` directives.
|
||||
///
|
||||
/// Comptime only for now, this code is really sloppy and makes a bunch of
|
||||
|
13
src/renderer/shaders/glsl/bg_color.f.glsl
Normal file
13
src/renderer/shaders/glsl/bg_color.f.glsl
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "common.glsl"
|
||||
|
||||
// Must declare this output for some versions of OpenGL.
|
||||
layout(location = 0) out vec4 out_FragColor;
|
||||
|
||||
void main() {
|
||||
bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0;
|
||||
|
||||
out_FragColor = load_color(
|
||||
unpack4u8(bg_color_packed_4u8),
|
||||
use_linear_blending
|
||||
);
|
||||
}
|
@@ -15,7 +15,7 @@ vec4 cell_bg() {
|
||||
ivec2 grid_pos = ivec2(floor((gl_FragCoord.xy - grid_padding.wx) / cell_size));
|
||||
bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0;
|
||||
|
||||
vec4 bg = load_color(unpack4u8(bg_color_packed_4u8), use_linear_blending);
|
||||
vec4 bg = vec4(0.0);
|
||||
|
||||
// Clamp x position, extends edge bg colors in to padding on sides.
|
||||
if (grid_pos.x < 0) {
|
||||
|
@@ -139,6 +139,12 @@ void main() {
|
||||
unpack4u8(bg_colors[grid_pos.y * grid_size.x + grid_pos.x]),
|
||||
true
|
||||
);
|
||||
// Blend it with the global bg color
|
||||
vec4 global_bg = load_color(
|
||||
unpack4u8(bg_color_packed_4u8),
|
||||
true
|
||||
);
|
||||
out_data.bg_color += global_bg * vec4(1.0 - out_data.bg_color.a);
|
||||
|
||||
// If we have a minimum contrast, we need to check if we need to
|
||||
// change the color of the text to ensure it has enough contrast
|
||||
|
@@ -216,45 +216,34 @@ vertex FullScreenVertexOut full_screen_vertex(
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Cell Background Shader
|
||||
// Background Color Shader
|
||||
//-------------------------------------------------------------------
|
||||
#pragma mark - Cell BG Shader
|
||||
#pragma mark - BG Color Shader
|
||||
|
||||
struct CellBgVertexOut {
|
||||
float4 position [[position]];
|
||||
float4 bg_color;
|
||||
};
|
||||
|
||||
vertex CellBgVertexOut cell_bg_vertex(
|
||||
uint vid [[vertex_id]],
|
||||
fragment float4 bg_color_fragment(
|
||||
FullScreenVertexOut in [[stage_in]],
|
||||
constant Uniforms& uniforms [[buffer(1)]]
|
||||
) {
|
||||
CellBgVertexOut out;
|
||||
|
||||
float4 position;
|
||||
position.x = (vid == 2) ? 3.0 : -1.0;
|
||||
position.y = (vid == 0) ? -3.0 : 1.0;
|
||||
position.zw = 1.0;
|
||||
out.position = position;
|
||||
|
||||
// Convert the background color to Display P3
|
||||
out.bg_color = load_color(
|
||||
return load_color(
|
||||
uniforms.bg_color,
|
||||
uniforms.use_display_p3,
|
||||
uniforms.use_linear_blending
|
||||
);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Cell Background Shader
|
||||
//-------------------------------------------------------------------
|
||||
#pragma mark - Cell BG Shader
|
||||
|
||||
fragment float4 cell_bg_fragment(
|
||||
CellBgVertexOut in [[stage_in]],
|
||||
FullScreenVertexOut in [[stage_in]],
|
||||
constant Uniforms& uniforms [[buffer(1)]],
|
||||
constant uchar4 *cells [[buffer(2)]]
|
||||
) {
|
||||
int2 grid_pos = int2(floor((in.position.xy - uniforms.grid_padding.wx) / uniforms.cell_size));
|
||||
|
||||
float4 bg = in.bg_color;
|
||||
float4 bg = float4(0.0);
|
||||
|
||||
// Clamp x position, extends edge bg colors in to padding on sides.
|
||||
if (grid_pos.x < 0) {
|
||||
@@ -289,17 +278,8 @@ fragment float4 cell_bg_fragment(
|
||||
// Load the color for the cell.
|
||||
uchar4 cell_color = cells[grid_pos.y * uniforms.grid_size.x + grid_pos.x];
|
||||
|
||||
// We have special case handling for when the cell color matches the bg color.
|
||||
if (all(cell_color == uniforms.bg_color)) {
|
||||
return bg;
|
||||
}
|
||||
|
||||
// Convert the color and return it.
|
||||
//
|
||||
// TODO: We may want to blend the color with the background
|
||||
// color, rather than purely replacing it, this needs
|
||||
// some consideration about config options though.
|
||||
//
|
||||
// TODO: It might be a good idea to do a pass before this
|
||||
// to convert all of the bg colors, so we don't waste
|
||||
// a bunch of work converting the cell color in every
|
||||
@@ -462,6 +442,13 @@ vertex CellTextVertexOut cell_text_vertex(
|
||||
uniforms.use_display_p3,
|
||||
true
|
||||
);
|
||||
// Blend it with the global bg color
|
||||
float4 global_bg = load_color(
|
||||
uniforms.bg_color,
|
||||
uniforms.use_display_p3,
|
||||
true
|
||||
);
|
||||
out.bg_color += global_bg * (1.0 - out.bg_color.a);
|
||||
|
||||
// If we have a minimum contrast, we need to check if we need to
|
||||
// change the color of the text to ensure it has enough contrast
|
Reference in New Issue
Block a user