metal: devmode

This commit is contained in:
Mitchell Hashimoto
2022-10-30 20:24:11 -07:00
parent 666833f12f
commit c1b70cb788
9 changed files with 127 additions and 8 deletions

View File

@ -230,9 +230,12 @@ fn addDeps(
try glfw.link(b, step, glfw_opts);
// Imgui, we have to do this later since we need some information
const imgui_backends = [_][]const u8{ "glfw", "opengl3" };
const imgui_backends = if (step.target.isDarwin())
&[_][]const u8{ "glfw", "opengl3", "metal" }
else
&[_][]const u8{ "glfw", "opengl3" };
var imgui_opts: imgui.Options = .{
.backends = &imgui_backends,
.backends = imgui_backends,
.freetype = .{ .enabled = true },
};

View File

@ -101,11 +101,17 @@ pub fn buildImgui(
lib.addCSourceFiles(srcs, flags.items);
if (opt.backends) |backends| {
for (backends) |backend| {
const ext = if (std.mem.eql(u8, "metal", backend)) ext: {
// Metal requires some extra frameworks
step.linkFramework("QuartzCore");
break :ext "mm";
} else "cpp";
var buf: [4096]u8 = undefined;
const path = try std.fmt.bufPrint(
&buf,
"{s}imgui/backends/imgui_impl_{s}.cpp",
.{ root, backend },
"{s}imgui/backends/imgui_impl_{s}.{s}",
.{ root, backend, ext },
);
lib.addCSourceFile(path, flags.items);

View File

@ -13,6 +13,10 @@ pub const ImplGlfw = struct {
return ImGui_ImplGlfw_InitForOpenGL(win, install_callbacks);
}
pub fn initForOther(win: *GLFWWindow, install_callbacks: bool) bool {
return ImGui_ImplGlfw_InitForOther(win, install_callbacks);
}
pub fn shutdown() void {
return ImGui_ImplGlfw_Shutdown();
}
@ -23,6 +27,7 @@ pub const ImplGlfw = struct {
extern "c" fn glfwGetError(?*const anyopaque) c_int;
extern "c" fn ImGui_ImplGlfw_InitForOpenGL(*GLFWWindow, bool) bool;
extern "c" fn ImGui_ImplGlfw_InitForOther(*GLFWWindow, bool) bool;
extern "c" fn ImGui_ImplGlfw_Shutdown() void;
extern "c" fn ImGui_ImplGlfw_NewFrame() void;
};

31
pkg/imgui/impl_metal.zig Normal file
View File

@ -0,0 +1,31 @@
const std = @import("std");
const c = @import("c.zig");
const imgui = @import("main.zig");
const Allocator = std.mem.Allocator;
pub const ImplMetal = struct {
pub fn init(device: ?*anyopaque) bool {
return ImGui_ImplMetal_Init(device);
}
pub fn shutdown() void {
return ImGui_ImplMetal_Shutdown();
}
pub fn newFrame(render_pass_desc: ?*anyopaque) void {
return ImGui_ImplMetal_NewFrame(render_pass_desc);
}
pub fn renderDrawData(
data: *imgui.DrawData,
command_buffer: ?*anyopaque,
command_encoder: ?*anyopaque,
) void {
ImGui_ImplMetal_RenderDrawData(data, command_buffer, command_encoder);
}
extern "c" fn ImGui_ImplMetal_Init(?*anyopaque) bool;
extern "c" fn ImGui_ImplMetal_Shutdown() void;
extern "c" fn ImGui_ImplMetal_NewFrame(?*anyopaque) void;
extern "c" fn ImGui_ImplMetal_RenderDrawData(*imgui.DrawData, ?*anyopaque, ?*anyopaque) void;
};

View File

@ -7,6 +7,7 @@ pub usingnamespace @import("io.zig");
pub usingnamespace @import("style.zig");
pub usingnamespace @import("impl_glfw.zig");
pub usingnamespace @import("impl_metal.zig");
pub usingnamespace @import("impl_opengl3.zig");
test {

View File

@ -9,6 +9,7 @@ const assert = std.debug.assert;
const Atlas = @import("Atlas.zig");
const Window = @import("Window.zig");
const renderer = @import("renderer.zig");
/// If this is false, the rest of the terminal will be compiled without
/// dev mode support at all.
@ -27,9 +28,10 @@ window: ?*Window = null,
/// Update the state associated with the dev mode. This should generally
/// only be called paired with a render since it otherwise wastes CPU
/// cycles.
///
/// Note: renderers should call their implementation "newFrame" functions
/// prior to this.
pub fn update(self: *const DevMode) !void {
imgui.ImplOpenGL3.newFrame();
imgui.ImplGlfw.newFrame();
imgui.newFrame();
if (imgui.begin("dev mode", null, .{})) {
@ -42,16 +44,27 @@ pub fn update(self: *const DevMode) !void {
helpMarker("The number of glyphs loaded and rendered into a " ++
"font atlas currently.");
const Renderer = @TypeOf(window.renderer);
if (imgui.treeNode("Atlas: Greyscale", .{ .default_open = true })) {
defer imgui.treePop();
const atlas = &window.font_group.atlas_greyscale;
try self.atlasInfo(atlas, @intCast(usize, window.renderer.texture.id));
const tex = switch (Renderer) {
renderer.OpenGL => @intCast(usize, window.renderer.texture.id),
renderer.Metal => @ptrToInt(window.renderer.texture_greyscale.value),
else => @compileError("renderer unsupported, add it!"),
};
try self.atlasInfo(atlas, tex);
}
if (imgui.treeNode("Atlas: Color (Emoji)", .{ .default_open = true })) {
defer imgui.treePop();
const atlas = &window.font_group.atlas_color;
try self.atlasInfo(atlas, @intCast(usize, window.renderer.texture_color.id));
const tex = switch (Renderer) {
renderer.OpenGL => @intCast(usize, window.renderer.texture_color.id),
renderer.Metal => @ptrToInt(window.renderer.texture_color.value),
else => @compileError("renderer unsupported, add it!"),
};
try self.atlasInfo(atlas, tex);
}
}
}

View File

@ -13,11 +13,13 @@ const builtin = @import("builtin");
const glfw = @import("glfw");
const objc = @import("objc");
const macos = @import("macos");
const imgui = @import("imgui");
const Atlas = @import("../Atlas.zig");
const font = @import("../font/main.zig");
const terminal = @import("../terminal/main.zig");
const renderer = @import("../renderer.zig");
const math = @import("../math.zig");
const DevMode = @import("../DevMode.zig");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const Terminal = terminal.Terminal;
@ -240,6 +242,11 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
}
pub fn deinit(self: *Metal) void {
if (DevMode.enabled) {
imgui.ImplMetal.shutdown();
imgui.ImplGlfw.shutdown();
}
self.cells.deinit(self.alloc);
self.font_shaper.deinit();
@ -256,6 +263,15 @@ pub fn finalizeInit(self: *const Metal, window: glfw.Window) !void {
const contentView = objc.Object.fromId(nswindow.getProperty(?*anyopaque, "contentView").?);
contentView.setProperty("layer", self.swapchain.value);
contentView.setProperty("wantsLayer", true);
if (DevMode.enabled) {
// Initialize for our window
assert(imgui.ImplGlfw.initForOther(
@ptrCast(*imgui.ImplGlfw.GLFWWindow, window.handle),
true,
));
assert(imgui.ImplMetal.init(self.device.value));
}
}
/// Callback called by renderer.Thread when it begins.
@ -285,6 +301,7 @@ pub fn render(
const Critical = struct {
bg: terminal.color.RGB,
screen_size: ?renderer.ScreenSize,
devmode: bool,
};
// Update all our data as tightly as possible within the mutex.
@ -323,6 +340,7 @@ pub fn render(
break :critical .{
.bg = self.background,
.screen_size = state.resize_screen,
.devmode = if (state.devmode) |dm| dm.visible else false,
};
};
@ -470,6 +488,27 @@ pub fn render(
@as(c_ulong, self.cells.items.len),
},
);
// Build our devmode draw data. This sucks because it requires we
// lock our state mutex but the metal imgui implementation requires
// access to all this stuff.
if (critical.devmode) {
state.mutex.lock();
defer state.mutex.unlock();
if (state.devmode) |dm| {
if (dm.visible) {
imgui.ImplMetal.newFrame(desc.value);
imgui.ImplGlfw.newFrame();
try dm.update();
imgui.ImplMetal.renderDrawData(
try dm.render(),
buffer.value,
encoder.value,
);
}
}
}
}
buffer.msgSend(void, objc.sel("presentDrawable:"), .{surface.value});

View File

@ -483,6 +483,8 @@ pub fn render(
const devmode_data = devmode_data: {
if (state.devmode) |dm| {
if (dm.visible) {
imgui.ImplOpenGL3.newFrame();
imgui.ImplGlfw.newFrame();
try dm.update();
break :devmode_data try dm.render();
}

19
src/renderer/cursor.zig Normal file
View File

@ -0,0 +1,19 @@
const terminal = @import("../terminal/main.zig");
/// Available cursor styles for drawing that renderers must support.
pub const CursorStyle = enum {
box,
box_hollow,
bar,
/// Create a cursor style from the terminal style request.
pub fn fromTerminal(style: terminal.CursorStyle) ?CursorStyle {
return switch (style) {
.blinking_block, .steady_block => .box,
.blinking_bar, .steady_bar => .bar,
.blinking_underline, .steady_underline => null, // TODO
.default => .box,
else => null,
};
}
};