mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
metal: setup the render loop, draw the background color
This commit is contained in:
@ -223,7 +223,10 @@ fn addDeps(
|
||||
_ = try utf8proc.link(b, step);
|
||||
|
||||
// Glfw
|
||||
const glfw_opts: glfw.Options = .{ .metal = false, .opengl = false };
|
||||
const glfw_opts: glfw.Options = .{
|
||||
.metal = step.target.isDarwin(),
|
||||
.opengl = false,
|
||||
};
|
||||
try glfw.link(b, step, glfw_opts);
|
||||
|
||||
// Imgui, we have to do this later since we need some information
|
||||
|
@ -54,7 +54,7 @@ pub const Object = struct {
|
||||
break :getter objc.sel(val);
|
||||
} else objc.sel(n);
|
||||
|
||||
self.msgSend(T, getter, .{});
|
||||
return self.msgSend(T, getter, .{});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2,12 +2,19 @@
|
||||
pub const Metal = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const glfw = @import("glfw");
|
||||
const objc = @import("objc");
|
||||
const font = @import("../font/main.zig");
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
const renderer = @import("../renderer.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// Get native API access on certain platforms so we can do more customization.
|
||||
const glfwNative = glfw.Native(.{
|
||||
.cocoa = builtin.os.tag == .macos,
|
||||
});
|
||||
|
||||
const log = std.log.scoped(.metal);
|
||||
|
||||
/// Current cell dimensions for this grid.
|
||||
@ -19,6 +26,11 @@ foreground: terminal.color.RGB,
|
||||
/// Default background color
|
||||
background: terminal.color.RGB,
|
||||
|
||||
/// Metal objects
|
||||
device: objc.Object, // MTLDevice
|
||||
queue: objc.Object, // MTLCommandQueue
|
||||
swapchain: objc.Object, // CAMetalLayer
|
||||
|
||||
/// Returns the hints that we want for this
|
||||
pub fn windowHints() glfw.Window.Hints {
|
||||
return .{
|
||||
@ -32,9 +44,23 @@ pub fn windowHints() glfw.Window.Hints {
|
||||
/// window surface as necessary.
|
||||
pub fn windowInit(window: glfw.Window) !void {
|
||||
_ = window;
|
||||
|
||||
// We don't do anything else here because we want to set everything
|
||||
// else up during actual initialization.
|
||||
}
|
||||
|
||||
pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
||||
// Initialize our metal stuff
|
||||
const device = objc.Object.fromId(MTLCreateSystemDefaultDevice());
|
||||
const queue = device.msgSend(objc.Object, objc.sel("newCommandQueue"), .{});
|
||||
const swapchain = swapchain: {
|
||||
const CAMetalLayer = objc.Class.getClass("CAMetalLayer").?;
|
||||
const swapchain = CAMetalLayer.msgSend(objc.Object, objc.sel("layer"), .{});
|
||||
swapchain.setProperty("device", device.value);
|
||||
swapchain.setProperty("opaque", true);
|
||||
break :swapchain swapchain;
|
||||
};
|
||||
|
||||
// Get our cell metrics based on a regular font ascii 'M'. Why 'M'?
|
||||
// Doesn't matter, any normal ASCII will do we're just trying to make
|
||||
// sure we use the regular font.
|
||||
@ -49,6 +75,9 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
||||
.cell_size = .{ .width = metrics.cell_width, .height = metrics.cell_height },
|
||||
.background = .{ .r = 0, .g = 0, .b = 0 },
|
||||
.foreground = .{ .r = 255, .g = 255, .b = 255 },
|
||||
.device = device,
|
||||
.queue = queue,
|
||||
.swapchain = swapchain,
|
||||
};
|
||||
}
|
||||
|
||||
@ -59,19 +88,26 @@ pub fn deinit(self: *Metal) void {
|
||||
/// This is called just prior to spinning up the renderer thread for
|
||||
/// final main thread setup requirements.
|
||||
pub fn finalizeInit(self: *const Metal, window: glfw.Window) !void {
|
||||
_ = self;
|
||||
_ = window;
|
||||
// Set our window backing layer to be our swapchain
|
||||
const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(window).?);
|
||||
const contentView = objc.Object.fromId(nswindow.getProperty(?*anyopaque, "contentView").?);
|
||||
contentView.setProperty("layer", self.swapchain.value);
|
||||
contentView.setProperty("wantsLayer", true);
|
||||
}
|
||||
|
||||
/// Callback called by renderer.Thread when it begins.
|
||||
pub fn threadEnter(self: *const Metal, window: glfw.Window) !void {
|
||||
_ = self;
|
||||
_ = window;
|
||||
|
||||
// Metal requires no per-thread state.
|
||||
}
|
||||
|
||||
/// Callback called by renderer.Thread when it exits.
|
||||
pub fn threadExit(self: *const Metal) void {
|
||||
_ = self;
|
||||
|
||||
// Metal requires no per-thread state.
|
||||
}
|
||||
|
||||
/// The primary render callback that is completely thread-safe.
|
||||
@ -80,7 +116,109 @@ pub fn render(
|
||||
window: glfw.Window,
|
||||
state: *renderer.State,
|
||||
) !void {
|
||||
_ = self;
|
||||
_ = window;
|
||||
_ = state;
|
||||
|
||||
// Data we extract out of the critical area.
|
||||
const Critical = struct {
|
||||
bg: terminal.color.RGB,
|
||||
};
|
||||
|
||||
// Update all our data as tightly as possible within the mutex.
|
||||
const critical: Critical = critical: {
|
||||
state.mutex.lock();
|
||||
defer state.mutex.unlock();
|
||||
|
||||
// Swap bg/fg if the terminal is reversed
|
||||
const bg = self.background;
|
||||
const fg = self.foreground;
|
||||
defer {
|
||||
self.background = bg;
|
||||
self.foreground = fg;
|
||||
}
|
||||
if (state.terminal.modes.reverse_colors) {
|
||||
self.background = fg;
|
||||
self.foreground = bg;
|
||||
}
|
||||
|
||||
break :critical .{
|
||||
.bg = self.background,
|
||||
};
|
||||
};
|
||||
|
||||
// @autoreleasepool {}
|
||||
const pool = objc_autoreleasePoolPush();
|
||||
defer objc_autoreleasePoolPop(pool);
|
||||
|
||||
// Get our surface (CAMetalDrawable)
|
||||
const surface = self.swapchain.msgSend(objc.Object, objc.sel("nextDrawable"), .{});
|
||||
|
||||
// MTLRenderPassDescriptor
|
||||
const MTLRenderPassDescriptor = objc.Class.getClass("MTLRenderPassDescriptor").?;
|
||||
const desc = desc: {
|
||||
const desc = MTLRenderPassDescriptor.msgSend(
|
||||
objc.Object,
|
||||
objc.sel("renderPassDescriptor"),
|
||||
.{},
|
||||
);
|
||||
|
||||
// Set our color attachment to be our drawable surface.
|
||||
const attachments = objc.Object.fromId(desc.getProperty(?*anyopaque, "colorAttachments"));
|
||||
{
|
||||
const attachment = attachments.msgSend(
|
||||
objc.Object,
|
||||
objc.sel("objectAtIndexedSubscript:"),
|
||||
.{@as(c_ulong, 0)},
|
||||
);
|
||||
|
||||
attachment.setProperty("loadAction", @enumToInt(MTLLoadAction.clear));
|
||||
attachment.setProperty("storeAction", @enumToInt(MTLStoreAction.store));
|
||||
attachment.setProperty("texture", surface.getProperty(objc.c.id, "texture").?);
|
||||
attachment.setProperty("clearColor", MTLClearColor{
|
||||
.red = @intToFloat(f32, critical.bg.r) / 255,
|
||||
.green = @intToFloat(f32, critical.bg.g) / 255,
|
||||
.blue = @intToFloat(f32, critical.bg.b) / 255,
|
||||
.alpha = 1.0,
|
||||
});
|
||||
}
|
||||
|
||||
break :desc desc;
|
||||
};
|
||||
|
||||
// Command buffer (MTLCommandBuffer)
|
||||
const buffer = self.queue.msgSend(objc.Object, objc.sel("commandBuffer"), .{});
|
||||
|
||||
// MTLRenderCommandEncoder
|
||||
const encoder = buffer.msgSend(
|
||||
objc.Object,
|
||||
objc.sel("renderCommandEncoderWithDescriptor:"),
|
||||
.{desc.value},
|
||||
);
|
||||
encoder.msgSend(void, objc.sel("endEncoding"), .{});
|
||||
|
||||
buffer.msgSend(void, objc.sel("presentDrawable:"), .{surface.value});
|
||||
buffer.msgSend(void, objc.sel("commit"), .{});
|
||||
}
|
||||
|
||||
/// https://developer.apple.com/documentation/metal/mtlloadaction?language=objc
|
||||
const MTLLoadAction = enum(c_ulong) {
|
||||
dont_care = 0,
|
||||
load = 1,
|
||||
clear = 2,
|
||||
};
|
||||
|
||||
/// https://developer.apple.com/documentation/metal/mtlstoreaction?language=objc
|
||||
const MTLStoreAction = enum(c_ulong) {
|
||||
dont_care = 0,
|
||||
store = 1,
|
||||
};
|
||||
|
||||
const MTLClearColor = extern struct {
|
||||
red: f64,
|
||||
green: f64,
|
||||
blue: f64,
|
||||
alpha: f64,
|
||||
};
|
||||
|
||||
extern "c" fn MTLCreateSystemDefaultDevice() ?*anyopaque;
|
||||
extern "c" fn objc_autoreleasePoolPush() ?*anyopaque;
|
||||
extern "c" fn objc_autoreleasePoolPop(?*anyopaque) void;
|
||||
|
Reference in New Issue
Block a user