Initial metal abstraction (noop)

This commit is contained in:
Mitchell Hashimoto
2022-10-28 14:48:36 -07:00
parent 19f003d7d0
commit 07271a6cfd
4 changed files with 101 additions and 21 deletions

View File

@ -36,7 +36,7 @@ const log = std.log.scoped(.window);
const WRITE_REQ_PREALLOC = std.math.pow(usize, 2, 5);
// The renderer implementation to use.
const Renderer = renderer.OpenGL;
const Renderer = renderer.Renderer;
/// Allocator
alloc: Allocator,

View File

@ -7,11 +7,21 @@
//! APIs. The renderers in this package assume that the renderer is already
//! setup (OpenGL has a context, Vulkan has a surface, etc.)
const builtin = @import("builtin");
pub usingnamespace @import("renderer/size.zig");
pub const Metal = @import("renderer/Metal.zig");
pub const OpenGL = @import("renderer/OpenGL.zig");
pub const Thread = @import("renderer/Thread.zig");
pub const State = @import("renderer/State.zig");
/// The implementation to use for the renderer. This is comptime chosen
/// so that every build has exactly one renderer implementation.
pub const Renderer = switch (builtin.os.tag) {
.macos => Metal,
else => OpenGL,
};
test {
@import("std").testing.refAllDecls(@This());
}

86
src/renderer/Metal.zig Normal file
View File

@ -0,0 +1,86 @@
//! Renderer implementation for Metal.
pub const Metal = @This();
const std = @import("std");
const glfw = @import("glfw");
const font = @import("../font/main.zig");
const terminal = @import("../terminal/main.zig");
const renderer = @import("../renderer.zig");
const Allocator = std.mem.Allocator;
const log = std.log.scoped(.metal);
/// Current cell dimensions for this grid.
cell_size: renderer.CellSize,
/// Default foreground color
foreground: terminal.color.RGB,
/// Default background color
background: terminal.color.RGB,
/// Returns the hints that we want for this
pub fn windowHints() glfw.Window.Hints {
return .{
.client_api = .no_api,
// .cocoa_graphics_switching = builtin.os.tag == .macos,
// .cocoa_retina_framebuffer = true,
};
}
/// This is called early right after window creation to setup our
/// window surface as necessary.
pub fn windowInit(window: glfw.Window) !void {
_ = window;
}
pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
// 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.
const metrics = metrics: {
const index = (try font_group.indexForCodepoint(alloc, 'M', .regular, .text)).?;
const face = try font_group.group.faceFromIndex(index);
break :metrics face.metrics;
};
log.debug("cell dimensions={}", .{metrics});
return 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 },
};
}
pub fn deinit(self: *Metal) void {
self.* = undefined;
}
/// 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;
}
/// Callback called by renderer.Thread when it begins.
pub fn threadEnter(self: *const Metal, window: glfw.Window) !void {
_ = self;
_ = window;
}
/// Callback called by renderer.Thread when it exits.
pub fn threadExit(self: *const Metal) void {
_ = self;
}
/// The primary render callback that is completely thread-safe.
pub fn render(
self: *Metal,
window: glfw.Window,
state: *renderer.State,
) !void {
_ = self;
_ = window;
_ = state;
}

View File

@ -31,7 +31,7 @@ render_h: libuv.Timer,
window: glfw.Window,
/// The underlying renderer implementation.
renderer: *renderer.OpenGL,
renderer: *renderer.Renderer,
/// Pointer to the shared state that is used to generate the final render.
state: *renderer.State,
@ -42,7 +42,7 @@ state: *renderer.State,
pub fn init(
alloc: Allocator,
window: glfw.Window,
renderer_impl: *renderer.OpenGL,
renderer_impl: *renderer.Renderer,
state: *renderer.State,
) !Thread {
// We always store allocator pointer on the loop data so that
@ -143,16 +143,11 @@ pub fn threadMain(self: *Thread) void {
}
fn threadMain_(self: *Thread) !void {
// Get a copy to our allocator
// const alloc_ptr = self.loop.getData(Allocator).?;
// const alloc = alloc_ptr.*;
// 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.
const Renderer = RendererType();
if (@hasDecl(Renderer, "threadEnter")) try self.renderer.threadEnter(self.window);
defer if (@hasDecl(Renderer, "threadExit")) self.renderer.threadExit();
try self.renderer.threadEnter(self.window);
defer self.renderer.threadExit();
// Set up our async handler to support rendering
self.wakeup.setData(self);
@ -199,14 +194,3 @@ fn renderCallback(h: *libuv.Timer) void {
fn stopCallback(h: *libuv.Async) void {
h.loop().stop();
}
// This is unnecessary right now but is logic we'll need for when we
// abstract renderers out.
fn RendererType() type {
const self: Thread = undefined;
return switch (@typeInfo(@TypeOf(self.renderer))) {
.Pointer => |p| p.child,
.Struct => |s| s,
else => unreachable,
};
}