mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-21 19:26:09 +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 macos = @import("macos");
|
||||||
const imgui = @import("imgui");
|
const imgui = @import("imgui");
|
||||||
const glslang = @import("glslang");
|
const glslang = @import("glslang");
|
||||||
|
const xev = @import("xev");
|
||||||
const apprt = @import("../apprt.zig");
|
const apprt = @import("../apprt.zig");
|
||||||
const configpkg = @import("../config.zig");
|
const configpkg = @import("../config.zig");
|
||||||
const font = @import("../font/main.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 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.
|
// Get native API access on certain platforms so we can do more customization.
|
||||||
const glfwNative = glfw.Native(.{
|
const glfwNative = glfw.Native(.{
|
||||||
.cocoa = builtin.os.tag == .macos,
|
.cocoa = builtin.os.tag == .macos,
|
||||||
@ -115,6 +121,11 @@ shaders: Shaders, // Compiled shaders
|
|||||||
/// Metal objects
|
/// Metal objects
|
||||||
layer: objc.Object, // CAMetalLayer
|
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. This is only set if we have custom shaders.
|
||||||
custom_shader_state: ?CustomShaderState = null,
|
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);
|
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{
|
return Metal{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.config = options.config,
|
.config = options.config,
|
||||||
@ -581,6 +598,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
|
|
||||||
// Metal stuff
|
// Metal stuff
|
||||||
.layer = layer,
|
.layer = layer,
|
||||||
|
.display_link = display_link,
|
||||||
.custom_shader_state = custom_shader_state,
|
.custom_shader_state = custom_shader_state,
|
||||||
.gpu_state = gpu_state,
|
.gpu_state = gpu_state,
|
||||||
};
|
};
|
||||||
@ -589,6 +607,13 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
pub fn deinit(self: *Metal) void {
|
pub fn deinit(self: *Metal) void {
|
||||||
self.gpu_state.deinit();
|
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.cells.deinit(self.alloc);
|
||||||
|
|
||||||
self.font_shaper.deinit();
|
self.font_shaper.deinit();
|
||||||
@ -635,6 +660,45 @@ pub fn threadExit(self: *const Metal) void {
|
|||||||
// Metal requires no per-thread state.
|
// 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
|
/// True if our renderer has animations so that a higher frequency
|
||||||
/// timer is used.
|
/// timer is used.
|
||||||
pub fn hasAnimations(self: *const Metal) bool {
|
pub fn hasAnimations(self: *const Metal) bool {
|
||||||
|
@ -146,7 +146,7 @@ pub fn init(
|
|||||||
var mailbox = try Mailbox.create(alloc);
|
var mailbox = try Mailbox.create(alloc);
|
||||||
errdefer mailbox.destroy(alloc);
|
errdefer mailbox.destroy(alloc);
|
||||||
|
|
||||||
return Thread{
|
return .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.config = DerivedConfig.init(config),
|
.config = DerivedConfig.init(config),
|
||||||
.loop = loop,
|
.loop = loop,
|
||||||
@ -191,6 +191,11 @@ pub fn threadMain(self: *Thread) void {
|
|||||||
fn threadMain_(self: *Thread) !void {
|
fn threadMain_(self: *Thread) !void {
|
||||||
defer log.debug("renderer thread exited", .{});
|
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
|
// Run our thread start/end callbacks. This is important because some
|
||||||
// renderers have to do per-thread setup. For example, OpenGL has to set
|
// renderers have to do per-thread setup. For example, OpenGL has to set
|
||||||
// some thread-local state since that is how it works.
|
// some thread-local state since that is how it works.
|
||||||
|
Reference in New Issue
Block a user