mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-21 03:06:15 +03:00
renderer/metal: setup display link
This commit is contained in:
@ -11,6 +11,7 @@ const objc = @import("objc");
|
||||
const macos = @import("macos");
|
||||
const imgui = @import("imgui");
|
||||
const glslang = @import("glslang");
|
||||
const xev = @import("xev");
|
||||
const apprt = @import("../apprt.zig");
|
||||
const configpkg = @import("../config.zig");
|
||||
const font = @import("../font/main.zig");
|
||||
@ -42,6 +43,11 @@ const InstanceBuffer = mtl_buffer.Buffer(u16);
|
||||
|
||||
const ImagePlacementList = std.ArrayListUnmanaged(mtl_image.Placement);
|
||||
|
||||
const DisplayLink = switch (builtin.os.tag) {
|
||||
.macos => *macos.video.DisplayLink,
|
||||
else => void,
|
||||
};
|
||||
|
||||
// Get native API access on certain platforms so we can do more customization.
|
||||
const glfwNative = glfw.Native(.{
|
||||
.cocoa = builtin.os.tag == .macos,
|
||||
@ -115,6 +121,11 @@ shaders: Shaders, // Compiled shaders
|
||||
/// Metal objects
|
||||
layer: objc.Object, // CAMetalLayer
|
||||
|
||||
/// The CVDisplayLink used to drive the rendering loop in sync
|
||||
/// with the display. This is void on platforms that don't support
|
||||
/// a display link.
|
||||
display_link: ?DisplayLink = null,
|
||||
|
||||
/// Custom shader state. This is only set if we have custom shaders.
|
||||
custom_shader_state: ?CustomShaderState = null,
|
||||
|
||||
@ -545,9 +556,15 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
};
|
||||
};
|
||||
|
||||
const cells = try mtl_cell.Contents.init(alloc);
|
||||
var cells = try mtl_cell.Contents.init(alloc);
|
||||
errdefer cells.deinit(alloc);
|
||||
|
||||
const display_link: ?DisplayLink = switch (builtin.os.tag) {
|
||||
.macos => try macos.video.DisplayLink.createWithActiveCGDisplays(),
|
||||
else => null,
|
||||
};
|
||||
errdefer if (display_link) |v| v.release();
|
||||
|
||||
return Metal{
|
||||
.alloc = alloc,
|
||||
.config = options.config,
|
||||
@ -581,6 +598,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
|
||||
// Metal stuff
|
||||
.layer = layer,
|
||||
.display_link = display_link,
|
||||
.custom_shader_state = custom_shader_state,
|
||||
.gpu_state = gpu_state,
|
||||
};
|
||||
@ -589,6 +607,13 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
pub fn deinit(self: *Metal) void {
|
||||
self.gpu_state.deinit();
|
||||
|
||||
if (DisplayLink != void) {
|
||||
if (self.display_link) |display_link| {
|
||||
display_link.stop() catch {};
|
||||
display_link.release();
|
||||
}
|
||||
}
|
||||
|
||||
self.cells.deinit(self.alloc);
|
||||
|
||||
self.font_shaper.deinit();
|
||||
@ -635,6 +660,45 @@ pub fn threadExit(self: *const Metal) void {
|
||||
// Metal requires no per-thread state.
|
||||
}
|
||||
|
||||
/// Called by renderer.Thread when it starts the main loop.
|
||||
pub fn loopEnter(self: *Metal, thr: *renderer.Thread) !void {
|
||||
// If we don't support a display link we have no work to do.
|
||||
if (comptime DisplayLink == void) return;
|
||||
|
||||
// This is when we know our "self" pointer is stable so we can
|
||||
// setup the display link. To setup the display link we set our
|
||||
// callback and we can start it immediately.
|
||||
const display_link = self.display_link orelse return;
|
||||
try display_link.setOutputCallback(
|
||||
xev.Async,
|
||||
&displayLinkCallback,
|
||||
&thr.draw_now,
|
||||
);
|
||||
display_link.start() catch {};
|
||||
}
|
||||
|
||||
/// Called by renderer.Thread when it exits the main loop.
|
||||
pub fn loopExit(self: *const Metal) void {
|
||||
// If we don't support a display link we have no work to do.
|
||||
if (comptime DisplayLink == void) return;
|
||||
|
||||
// Stop our display link. If this fails its okay it just means
|
||||
// that we either never started it or the view its attached to
|
||||
// is gone which is fine.
|
||||
const display_link = self.display_link orelse return;
|
||||
display_link.stop() catch {};
|
||||
}
|
||||
|
||||
fn displayLinkCallback(
|
||||
_: *macos.video.DisplayLink,
|
||||
ud: ?*xev.Async,
|
||||
) void {
|
||||
const draw_now = ud orelse return;
|
||||
draw_now.notify() catch |err| {
|
||||
log.err("error notifying draw_now err={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
/// True if our renderer has animations so that a higher frequency
|
||||
/// timer is used.
|
||||
pub fn hasAnimations(self: *const Metal) bool {
|
||||
|
@ -146,7 +146,7 @@ pub fn init(
|
||||
var mailbox = try Mailbox.create(alloc);
|
||||
errdefer mailbox.destroy(alloc);
|
||||
|
||||
return Thread{
|
||||
return .{
|
||||
.alloc = alloc,
|
||||
.config = DerivedConfig.init(config),
|
||||
.loop = loop,
|
||||
@ -191,6 +191,11 @@ pub fn threadMain(self: *Thread) void {
|
||||
fn threadMain_(self: *Thread) !void {
|
||||
defer log.debug("renderer thread exited", .{});
|
||||
|
||||
// Run our loop start/end callbacks if the renderer cares.
|
||||
const has_loop = @hasDecl(renderer.Renderer, "loopEnter");
|
||||
if (has_loop) try self.renderer.loopEnter(self);
|
||||
defer if (has_loop) self.renderer.loopExit();
|
||||
|
||||
// Run our thread start/end callbacks. This is important because some
|
||||
// renderers have to do per-thread setup. For example, OpenGL has to set
|
||||
// some thread-local state since that is how it works.
|
||||
|
Reference in New Issue
Block a user