mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
renderer/metal: wip for loading custom shader pipelines
This commit is contained in:
@ -257,8 +257,8 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
errdefer buf_instance.deinit();
|
errdefer buf_instance.deinit();
|
||||||
|
|
||||||
// Initialize our shaders
|
// Initialize our shaders
|
||||||
var shaders = try Shaders.init(device);
|
var shaders = try Shaders.init(alloc, device, &.{});
|
||||||
errdefer shaders.deinit();
|
errdefer shaders.deinit(alloc);
|
||||||
|
|
||||||
// Font atlas textures
|
// Font atlas textures
|
||||||
const texture_greyscale = try initAtlasTexture(device, &options.font_group.atlas_greyscale);
|
const texture_greyscale = try initAtlasTexture(device, &options.font_group.atlas_greyscale);
|
||||||
@ -328,7 +328,7 @@ pub fn deinit(self: *Metal) void {
|
|||||||
deinitMTLResource(self.texture_color);
|
deinitMTLResource(self.texture_color);
|
||||||
self.queue.msgSend(void, objc.sel("release"), .{});
|
self.queue.msgSend(void, objc.sel("release"), .{});
|
||||||
|
|
||||||
self.shaders.deinit();
|
self.shaders.deinit(self.alloc);
|
||||||
|
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,31 @@ const log = std.log.scoped(.metal);
|
|||||||
/// This contains the state for the shaders used by the Metal renderer.
|
/// This contains the state for the shaders used by the Metal renderer.
|
||||||
pub const Shaders = struct {
|
pub const Shaders = struct {
|
||||||
library: objc.Object,
|
library: objc.Object,
|
||||||
|
|
||||||
|
/// The cell shader is the shader used to render the terminal cells.
|
||||||
|
/// It is a single shader that is used for both the background and
|
||||||
|
/// foreground.
|
||||||
cell_pipeline: objc.Object,
|
cell_pipeline: objc.Object,
|
||||||
|
|
||||||
|
/// The image shader is the shader used to render images for things
|
||||||
|
/// like the Kitty image protocol.
|
||||||
image_pipeline: objc.Object,
|
image_pipeline: objc.Object,
|
||||||
|
|
||||||
pub fn init(device: objc.Object) !Shaders {
|
/// 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
|
||||||
|
/// against the output of the previous shader.
|
||||||
|
post_pipelines: []const objc.Object,
|
||||||
|
|
||||||
|
/// Initialize our shader set.
|
||||||
|
///
|
||||||
|
/// "post_shaders" is an optional list of postprocess shaders to run
|
||||||
|
/// against the final drawable texture. This is an array of shader source
|
||||||
|
/// code, not file paths.
|
||||||
|
pub fn init(
|
||||||
|
alloc: Allocator,
|
||||||
|
device: objc.Object,
|
||||||
|
post_shaders: []const [:0]const u8,
|
||||||
|
) !Shaders {
|
||||||
const library = try initLibrary(device);
|
const library = try initLibrary(device);
|
||||||
errdefer library.msgSend(void, objc.sel("release"), .{});
|
errdefer library.msgSend(void, objc.sel("release"), .{});
|
||||||
|
|
||||||
@ -25,17 +46,43 @@ pub const Shaders = struct {
|
|||||||
const image_pipeline = try initImagePipeline(device, library);
|
const image_pipeline = try initImagePipeline(device, library);
|
||||||
errdefer image_pipeline.msgSend(void, objc.sel("release"), .{});
|
errdefer image_pipeline.msgSend(void, objc.sel("release"), .{});
|
||||||
|
|
||||||
|
const post_pipelines: []const objc.Object = initPostPipelines(
|
||||||
|
alloc,
|
||||||
|
device,
|
||||||
|
post_shaders,
|
||||||
|
) catch |err| err: {
|
||||||
|
// If an error happens while building postprocess shaders we
|
||||||
|
// want to just not use any postprocess shaders since we don't
|
||||||
|
// want to block Ghostty from working.
|
||||||
|
log.warn("error initializing postprocess shaders err={}", .{err});
|
||||||
|
break :err &.{};
|
||||||
|
};
|
||||||
|
errdefer if (post_pipelines.len > 0) {
|
||||||
|
for (post_pipelines) |pipeline| pipeline.msgSend(void, objc.sel("release"), .{});
|
||||||
|
alloc.free(post_pipelines);
|
||||||
|
};
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.library = library,
|
.library = library,
|
||||||
.cell_pipeline = cell_pipeline,
|
.cell_pipeline = cell_pipeline,
|
||||||
.image_pipeline = image_pipeline,
|
.image_pipeline = image_pipeline,
|
||||||
|
.post_pipelines = post_pipelines,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Shaders) void {
|
pub fn deinit(self: *Shaders, alloc: Allocator) void {
|
||||||
|
// Release our primary shaders
|
||||||
self.cell_pipeline.msgSend(void, objc.sel("release"), .{});
|
self.cell_pipeline.msgSend(void, objc.sel("release"), .{});
|
||||||
self.image_pipeline.msgSend(void, objc.sel("release"), .{});
|
self.image_pipeline.msgSend(void, objc.sel("release"), .{});
|
||||||
self.library.msgSend(void, objc.sel("release"), .{});
|
self.library.msgSend(void, objc.sel("release"), .{});
|
||||||
|
|
||||||
|
// Release our postprocess shaders
|
||||||
|
if (self.post_pipelines.len > 0) {
|
||||||
|
for (self.post_pipelines) |pipeline| {
|
||||||
|
pipeline.msgSend(void, objc.sel("release"), .{});
|
||||||
|
}
|
||||||
|
alloc.free(self.post_pipelines);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -105,6 +152,68 @@ fn initLibrary(device: objc.Object) !objc.Object {
|
|||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize our custom shader pipelines. The shaders argument is a
|
||||||
|
/// set of shader source code, not file paths.
|
||||||
|
fn initPostPipelines(
|
||||||
|
alloc: Allocator,
|
||||||
|
device: objc.Object,
|
||||||
|
shaders: []const [:0]const u8,
|
||||||
|
) ![]const objc.Object {
|
||||||
|
// If we have no shaders, do nothing.
|
||||||
|
if (shaders.len == 0) return &.{};
|
||||||
|
|
||||||
|
// Keeps track of how many shaders we successfully wrote.
|
||||||
|
var i: usize = 0;
|
||||||
|
|
||||||
|
// Initialize our result set. If any error happens, we undo everything.
|
||||||
|
var pipelines = try alloc.alloc(objc.Object, shaders.len);
|
||||||
|
errdefer {
|
||||||
|
for (pipelines[0..i]) |pipeline| {
|
||||||
|
pipeline.msgSend(void, objc.sel("release"), .{});
|
||||||
|
}
|
||||||
|
alloc.free(pipelines);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build each shader. Note we don't use "0.." to build our index
|
||||||
|
// because we need to keep track of our length to clean up above.
|
||||||
|
for (shaders) |source| {
|
||||||
|
pipelines[i] = try initPostPipeline(device, source);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipelines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a single custom shader pipeline from shader source.
|
||||||
|
fn initPostPipeline(device: objc.Object, data: [:0]const u8) !objc.Object {
|
||||||
|
// Create our library which has the shader source
|
||||||
|
const library = library: {
|
||||||
|
const source = try macos.foundation.String.createWithBytes(
|
||||||
|
data,
|
||||||
|
.utf8,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
defer source.release();
|
||||||
|
|
||||||
|
var err: ?*anyopaque = null;
|
||||||
|
const library = device.msgSend(
|
||||||
|
objc.Object,
|
||||||
|
objc.sel("newLibraryWithSource:options:error:"),
|
||||||
|
.{ source, @as(?*anyopaque, null), &err },
|
||||||
|
);
|
||||||
|
try checkError(err);
|
||||||
|
errdefer library.msgSend(void, objc.sel("release"), .{});
|
||||||
|
|
||||||
|
break :library library;
|
||||||
|
};
|
||||||
|
// TODO: need to do this once we set the pipeline
|
||||||
|
//defer library.msgSend(void, objc.sel("release"), .{});
|
||||||
|
|
||||||
|
// TODO: need to implement the actual pipeline
|
||||||
|
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize the cell render pipeline for our shader library.
|
/// Initialize the cell render pipeline for our shader library.
|
||||||
fn initCellPipeline(device: objc.Object, library: objc.Object) !objc.Object {
|
fn initCellPipeline(device: objc.Object, library: objc.Object) !objc.Object {
|
||||||
// Get our vertex and fragment functions
|
// Get our vertex and fragment functions
|
||||||
|
Reference in New Issue
Block a user