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_greyscale: objc.Object, // MTLTexture
|
||||||
texture_color: objc.Object, // MTLTexture
|
texture_color: objc.Object, // MTLTexture
|
||||||
|
|
||||||
/// The screen texture. This is only set if we have custom shaders. If
|
/// Custom shader state. This is only set if we have custom shaders.
|
||||||
/// we don't have custom shaders, we render directly to the drawable.
|
custom_shader_state: ?CustomShaderState = null,
|
||||||
texture_screen: ?objc.Object, // MTLTexture
|
|
||||||
|
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
|
/// The configuration for this renderer that is derived from the main
|
||||||
/// configuration. This must be exported so that we don't need to
|
/// 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 &.{};
|
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
|
// Initialize our shaders
|
||||||
var shaders = try Shaders.init(alloc, device, custom_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);
|
errdefer shaders.deinit(alloc);
|
||||||
|
|
||||||
// Font atlas textures
|
// Font atlas textures
|
||||||
@ -336,7 +376,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
.swapchain = swapchain,
|
.swapchain = swapchain,
|
||||||
.texture_greyscale = texture_greyscale,
|
.texture_greyscale = texture_greyscale,
|
||||||
.texture_color = texture_color,
|
.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();
|
self.buf_instance.deinit();
|
||||||
deinitMTLResource(self.texture_greyscale);
|
deinitMTLResource(self.texture_greyscale);
|
||||||
deinitMTLResource(self.texture_color);
|
deinitMTLResource(self.texture_color);
|
||||||
if (self.texture_screen) |tex| deinitMTLResource(tex);
|
|
||||||
self.queue.msgSend(void, objc.sel("release"), .{});
|
self.queue.msgSend(void, objc.sel("release"), .{});
|
||||||
|
|
||||||
|
if (self.custom_shader_state) |*state| state.deinit();
|
||||||
|
|
||||||
self.shaders.deinit(self.alloc);
|
self.shaders.deinit(self.alloc);
|
||||||
|
|
||||||
self.* = undefined;
|
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
|
// Get our screen texture. If we don't have a dedicated screen texture
|
||||||
// then we just use the drawable 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"), .{});
|
const texture = drawable.msgSend(objc.c.id, objc.sel("texture"), .{});
|
||||||
break :tex objc.Object.fromId(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
|
// If we have custom shaders AND we have a screen texture, then we
|
||||||
// render the custom shaders.
|
// render the custom shaders.
|
||||||
if (self.config.custom_shaders.items.len > 0 and
|
if (self.custom_shader_state) |state| {
|
||||||
self.texture_screen != null)
|
|
||||||
{
|
|
||||||
// MTLRenderPassDescriptor
|
// MTLRenderPassDescriptor
|
||||||
const desc = desc: {
|
const desc = desc: {
|
||||||
const MTLRenderPassDescriptor = objc.getClass("MTLRenderPassDescriptor").?;
|
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"), .{});
|
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});
|
buffer.msgSend(void, objc.sel("presentDrawable:"), .{drawable.value});
|
||||||
@ -761,57 +804,42 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
|||||||
fn drawPostShader(
|
fn drawPostShader(
|
||||||
self: *Metal,
|
self: *Metal,
|
||||||
encoder: objc.Object,
|
encoder: objc.Object,
|
||||||
texture: objc.c.id,
|
pipeline: objc.Object,
|
||||||
|
state: *const CustomShaderState,
|
||||||
) !void {
|
) !void {
|
||||||
// Build our sampler for our texture
|
_ = self;
|
||||||
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();
|
|
||||||
|
|
||||||
// Use our custom shader pipeline
|
// Use our custom shader pipeline
|
||||||
encoder.msgSend(
|
encoder.msgSend(
|
||||||
void,
|
void,
|
||||||
objc.sel("setRenderPipelineState:"),
|
objc.sel("setRenderPipelineState:"),
|
||||||
.{self.shaders.post_pipelines[0].value},
|
.{pipeline.value},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set our sampler
|
// Set our sampler
|
||||||
encoder.msgSend(
|
encoder.msgSend(
|
||||||
void,
|
void,
|
||||||
objc.sel("setFragmentSamplerState:atIndex:"),
|
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(
|
encoder.msgSend(
|
||||||
void,
|
void,
|
||||||
objc.sel("setFragmentBuffer:offset:atIndex:"),
|
objc.sel("setFragmentBytes:length:atIndex:"),
|
||||||
.{ buf.buffer.value, @as(c_ulong, 0), @as(c_ulong, 0) },
|
.{
|
||||||
|
@as(*const anyopaque, @ptrCast(&state.uniforms)),
|
||||||
|
@as(c_ulong, @sizeOf(@TypeOf(state.uniforms))),
|
||||||
|
@as(c_ulong, 0),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Screen texture
|
||||||
encoder.msgSend(
|
encoder.msgSend(
|
||||||
void,
|
void,
|
||||||
objc.sel("setFragmentTexture:atIndex:"),
|
objc.sel("setFragmentTexture:atIndex:"),
|
||||||
.{
|
.{
|
||||||
texture,
|
state.screen_texture.value,
|
||||||
@as(c_ulong, 0),
|
@as(c_ulong, 0),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -1248,11 +1276,21 @@ pub fn setScreenSize(
|
|||||||
self.cells.clearAndFree(self.alloc);
|
self.cells.clearAndFree(self.alloc);
|
||||||
self.cells_bg.clearAndFree(self.alloc);
|
self.cells_bg.clearAndFree(self.alloc);
|
||||||
|
|
||||||
// Setup our screen texture
|
// If we have custom shaders then we update the state
|
||||||
const screen_texture: ?objc.Object = screen_texture: {
|
if (self.custom_shader_state) |*state| {
|
||||||
// If we have no custom shaders then we don't need a screen texture.
|
// Only free our previous texture if this isn't our first
|
||||||
if (self.config.custom_shaders.items.len == 0) break :screen_texture null;
|
// 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
|
// 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.
|
// render target AND reading so that the custom shaders can read from it.
|
||||||
const desc = init: {
|
const desc = init: {
|
||||||
@ -1277,15 +1315,11 @@ pub fn setScreenSize(
|
|||||||
?*anyopaque,
|
?*anyopaque,
|
||||||
objc.sel("newTextureWithDescriptor:"),
|
objc.sel("newTextureWithDescriptor:"),
|
||||||
.{desc},
|
.{desc},
|
||||||
) orelse break :screen_texture null;
|
) orelse return error.MetalFailed;
|
||||||
|
|
||||||
break :screen_texture objc.Object.fromId(id);
|
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 });
|
log.debug("screen size screen={} grid={}, cell={}", .{ dim, grid_size, self.cell_size });
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user