mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
renderer/metal: don't recreate custom shader resources per frame
This commit is contained in:
@ -116,9 +116,21 @@ swapchain: objc.Object, // CAMetalLayer
|
||||
texture_greyscale: objc.Object, // MTLTexture
|
||||
texture_color: objc.Object, // MTLTexture
|
||||
|
||||
/// The screen texture. This is only set if we have custom shaders. If
|
||||
/// we don't have custom shaders, we render directly to the drawable.
|
||||
texture_screen: ?objc.Object, // MTLTexture
|
||||
/// Custom shader state. This is only set if we have custom shaders.
|
||||
custom_shader_state: ?CustomShaderState = null,
|
||||
|
||||
pub const CustomShaderState = struct {
|
||||
/// The screen texture that we render the terminal to. If we don't have
|
||||
/// custom shaders, we render directly to the drawable.
|
||||
screen_texture: objc.Object, // MTLTexture
|
||||
sampler: mtl_sampler.Sampler,
|
||||
uniforms: mtl_shaders.PostUniforms,
|
||||
|
||||
pub fn deinit(self: *CustomShaderState) void {
|
||||
deinitMTLResource(self.screen_texture);
|
||||
self.sampler.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
/// The configuration for this renderer that is derived from the main
|
||||
/// configuration. This must be exported so that we don't need to
|
||||
@ -288,9 +300,37 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
break :err &.{};
|
||||
};
|
||||
|
||||
// If we have custom shaders then setup our state
|
||||
var custom_shader_state: ?CustomShaderState = state: {
|
||||
if (custom_shaders.len == 0) break :state null;
|
||||
|
||||
// Build our sampler for our texture
|
||||
var sampler = try mtl_sampler.Sampler.init(device);
|
||||
errdefer sampler.deinit();
|
||||
|
||||
break :state .{
|
||||
// Resolution and screen texture will be fixed up by first
|
||||
// call to setScreenSize. This happens before any draw call.
|
||||
.screen_texture = undefined,
|
||||
.sampler = sampler,
|
||||
.uniforms = .{
|
||||
.resolution = .{ 0, 0, 1 },
|
||||
.time = 1,
|
||||
.time_delta = 1,
|
||||
.frame_rate = 1,
|
||||
.frame = 1,
|
||||
.channel_time = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4,
|
||||
.channel_resolution = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4,
|
||||
.mouse = .{ 0, 0, 0, 0 },
|
||||
.date = .{ 0, 0, 0, 0 },
|
||||
.sample_rate = 1,
|
||||
},
|
||||
};
|
||||
};
|
||||
errdefer if (custom_shader_state) |*state| state.deinit();
|
||||
|
||||
// Initialize our shaders
|
||||
var shaders = try Shaders.init(alloc, device, custom_shaders);
|
||||
//var shaders = try Shaders.init(alloc, device, &.{@embedFile("shaders/temp3.metal")});
|
||||
errdefer shaders.deinit(alloc);
|
||||
|
||||
// Font atlas textures
|
||||
@ -336,7 +376,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
.swapchain = swapchain,
|
||||
.texture_greyscale = texture_greyscale,
|
||||
.texture_color = texture_color,
|
||||
.texture_screen = null,
|
||||
.custom_shader_state = custom_shader_state,
|
||||
};
|
||||
}
|
||||
|
||||
@ -360,9 +400,10 @@ pub fn deinit(self: *Metal) void {
|
||||
self.buf_instance.deinit();
|
||||
deinitMTLResource(self.texture_greyscale);
|
||||
deinitMTLResource(self.texture_color);
|
||||
if (self.texture_screen) |tex| deinitMTLResource(tex);
|
||||
self.queue.msgSend(void, objc.sel("release"), .{});
|
||||
|
||||
if (self.custom_shader_state) |*state| state.deinit();
|
||||
|
||||
self.shaders.deinit(self.alloc);
|
||||
|
||||
self.* = undefined;
|
||||
@ -621,7 +662,9 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
||||
|
||||
// Get our screen texture. If we don't have a dedicated screen texture
|
||||
// then we just use the drawable texture.
|
||||
const screen_texture = self.texture_screen orelse tex: {
|
||||
const screen_texture = if (self.custom_shader_state) |state|
|
||||
state.screen_texture
|
||||
else tex: {
|
||||
const texture = drawable.msgSend(objc.c.id, objc.sel("texture"), .{});
|
||||
break :tex objc.Object.fromId(texture);
|
||||
};
|
||||
@ -703,9 +746,7 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
||||
|
||||
// If we have custom shaders AND we have a screen texture, then we
|
||||
// render the custom shaders.
|
||||
if (self.config.custom_shaders.items.len > 0 and
|
||||
self.texture_screen != null)
|
||||
{
|
||||
if (self.custom_shader_state) |state| {
|
||||
// MTLRenderPassDescriptor
|
||||
const desc = desc: {
|
||||
const MTLRenderPassDescriptor = objc.getClass("MTLRenderPassDescriptor").?;
|
||||
@ -751,7 +792,9 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
||||
);
|
||||
defer encoder.msgSend(void, objc.sel("endEncoding"), .{});
|
||||
|
||||
try self.drawPostShader(encoder, screen_texture.value);
|
||||
for (self.shaders.post_pipelines) |pipeline| {
|
||||
try self.drawPostShader(encoder, pipeline, &state);
|
||||
}
|
||||
}
|
||||
|
||||
buffer.msgSend(void, objc.sel("presentDrawable:"), .{drawable.value});
|
||||
@ -761,57 +804,42 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
||||
fn drawPostShader(
|
||||
self: *Metal,
|
||||
encoder: objc.Object,
|
||||
texture: objc.c.id,
|
||||
pipeline: objc.Object,
|
||||
state: *const CustomShaderState,
|
||||
) !void {
|
||||
// Build our sampler for our texture
|
||||
var sampler = try mtl_sampler.Sampler.init(self.device);
|
||||
defer sampler.deinit();
|
||||
|
||||
const Buffer = mtl_buffer.Buffer(mtl_shaders.PostUniforms);
|
||||
var buf = try Buffer.initFill(self.device, &.{.{
|
||||
.resolution = .{
|
||||
@floatFromInt(self.screen_size.?.width),
|
||||
@floatFromInt(self.screen_size.?.height),
|
||||
1,
|
||||
},
|
||||
.time = 1,
|
||||
.time_delta = 1,
|
||||
.frame_rate = 1,
|
||||
.frame = 1,
|
||||
.channel_time = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4,
|
||||
.channel_resolution = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4,
|
||||
.mouse = .{ 0, 0, 0, 0 },
|
||||
.date = .{ 0, 0, 0, 0 },
|
||||
.sample_rate = 1,
|
||||
}});
|
||||
defer buf.deinit();
|
||||
_ = self;
|
||||
|
||||
// Use our custom shader pipeline
|
||||
encoder.msgSend(
|
||||
void,
|
||||
objc.sel("setRenderPipelineState:"),
|
||||
.{self.shaders.post_pipelines[0].value},
|
||||
.{pipeline.value},
|
||||
);
|
||||
|
||||
// Set our sampler
|
||||
encoder.msgSend(
|
||||
void,
|
||||
objc.sel("setFragmentSamplerState:atIndex:"),
|
||||
.{ sampler.sampler.value, @as(c_ulong, 0) },
|
||||
.{ state.sampler.sampler.value, @as(c_ulong, 0) },
|
||||
);
|
||||
|
||||
// Set our buffer
|
||||
// Set our uniforms
|
||||
encoder.msgSend(
|
||||
void,
|
||||
objc.sel("setFragmentBuffer:offset:atIndex:"),
|
||||
.{ buf.buffer.value, @as(c_ulong, 0), @as(c_ulong, 0) },
|
||||
objc.sel("setFragmentBytes:length:atIndex:"),
|
||||
.{
|
||||
@as(*const anyopaque, @ptrCast(&state.uniforms)),
|
||||
@as(c_ulong, @sizeOf(@TypeOf(state.uniforms))),
|
||||
@as(c_ulong, 0),
|
||||
},
|
||||
);
|
||||
|
||||
// Screen texture
|
||||
encoder.msgSend(
|
||||
void,
|
||||
objc.sel("setFragmentTexture:atIndex:"),
|
||||
.{
|
||||
texture,
|
||||
state.screen_texture.value,
|
||||
@as(c_ulong, 0),
|
||||
},
|
||||
);
|
||||
@ -1248,11 +1276,21 @@ pub fn setScreenSize(
|
||||
self.cells.clearAndFree(self.alloc);
|
||||
self.cells_bg.clearAndFree(self.alloc);
|
||||
|
||||
// Setup our screen texture
|
||||
const screen_texture: ?objc.Object = screen_texture: {
|
||||
// If we have no custom shaders then we don't need a screen texture.
|
||||
if (self.config.custom_shaders.items.len == 0) break :screen_texture null;
|
||||
// If we have custom shaders then we update the state
|
||||
if (self.custom_shader_state) |*state| {
|
||||
// Only free our previous texture if this isn't our first
|
||||
// time setting the custom shader state.
|
||||
if (state.uniforms.resolution[0] > 0) {
|
||||
deinitMTLResource(state.screen_texture);
|
||||
}
|
||||
|
||||
state.uniforms.resolution = .{
|
||||
@floatFromInt(dim.width),
|
||||
@floatFromInt(dim.height),
|
||||
1,
|
||||
};
|
||||
|
||||
state.screen_texture = screen_texture: {
|
||||
// This texture is the size of our drawable but supports being a
|
||||
// render target AND reading so that the custom shaders can read from it.
|
||||
const desc = init: {
|
||||
@ -1277,15 +1315,11 @@ pub fn setScreenSize(
|
||||
?*anyopaque,
|
||||
objc.sel("newTextureWithDescriptor:"),
|
||||
.{desc},
|
||||
) orelse break :screen_texture null;
|
||||
) orelse return error.MetalFailed;
|
||||
|
||||
break :screen_texture objc.Object.fromId(id);
|
||||
};
|
||||
errdefer if (screen_texture) |tex| tex.msgSend(void, objc.sel("release"), .{});
|
||||
|
||||
// Set our new screen texture
|
||||
if (self.texture_screen) |tex| tex.msgSend(void, objc.sel("release"), .{});
|
||||
self.texture_screen = screen_texture;
|
||||
}
|
||||
|
||||
log.debug("screen size screen={} grid={}, cell={}", .{ dim, grid_size, self.cell_size });
|
||||
}
|
||||
|
Reference in New Issue
Block a user