mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-24 20:56:08 +03:00
draw a triangle
This commit is contained in:
@ -26,9 +26,6 @@ alloc: std.mem.Allocator,
|
|||||||
/// Current cell dimensions for this grid.
|
/// Current cell dimensions for this grid.
|
||||||
cell_size: renderer.CellSize,
|
cell_size: renderer.CellSize,
|
||||||
|
|
||||||
/// The last screen size set.
|
|
||||||
screen_size: renderer.ScreenSize,
|
|
||||||
|
|
||||||
/// Default foreground color
|
/// Default foreground color
|
||||||
foreground: terminal.color.RGB,
|
foreground: terminal.color.RGB,
|
||||||
|
|
||||||
@ -47,8 +44,9 @@ font_shaper: font.Shaper,
|
|||||||
device: objc.Object, // MTLDevice
|
device: objc.Object, // MTLDevice
|
||||||
queue: objc.Object, // MTLCommandQueue
|
queue: objc.Object, // MTLCommandQueue
|
||||||
swapchain: objc.Object, // CAMetalLayer
|
swapchain: objc.Object, // CAMetalLayer
|
||||||
library: objc.Object, // MTLLibrary
|
buf_cells: objc.Object, // MTLBuffer
|
||||||
buf_instance: objc.Object, // MTLBuffer
|
buf_instance: objc.Object, // MTLBuffer
|
||||||
|
pipeline: objc.Object, // MTLRenderPipelineState
|
||||||
|
|
||||||
const GPUCell = extern struct {
|
const GPUCell = extern struct {
|
||||||
foo: f64,
|
foo: f64,
|
||||||
@ -104,7 +102,7 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
|||||||
|
|
||||||
// Initialize our Metal buffers
|
// Initialize our Metal buffers
|
||||||
const buf_instance = buffer: {
|
const buf_instance = buffer: {
|
||||||
const data = [6]u8{
|
const data = [6]u16{
|
||||||
0, 1, 3, // Top-left triangle
|
0, 1, 3, // Top-left triangle
|
||||||
1, 2, 3, // Bottom-right triangle
|
1, 2, 3, // Bottom-right triangle
|
||||||
};
|
};
|
||||||
@ -114,7 +112,25 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
|||||||
objc.sel("newBufferWithBytes:length:options:"),
|
objc.sel("newBufferWithBytes:length:options:"),
|
||||||
.{
|
.{
|
||||||
@ptrCast(*const anyopaque, &data),
|
@ptrCast(*const anyopaque, &data),
|
||||||
@intCast(c_ulong, data.len * @sizeOf(u8)),
|
@intCast(c_ulong, data.len * @sizeOf(u16)),
|
||||||
|
MTLResourceStorageModeShared,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const buf_cells = buffer: {
|
||||||
|
const data = [9]f32{
|
||||||
|
0, 1, 0,
|
||||||
|
-1, -1, 0,
|
||||||
|
1, -1, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
break :buffer device.msgSend(
|
||||||
|
objc.Object,
|
||||||
|
objc.sel("newBufferWithBytes:length:options:"),
|
||||||
|
.{
|
||||||
|
@ptrCast(*const anyopaque, &data),
|
||||||
|
@intCast(c_ulong, data.len * @sizeOf(f32)),
|
||||||
MTLResourceStorageModeShared,
|
MTLResourceStorageModeShared,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -156,11 +172,70 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
|||||||
|
|
||||||
break :library library;
|
break :library library;
|
||||||
};
|
};
|
||||||
|
const func_vert = func_vert: {
|
||||||
|
const str = try macos.foundation.String.createWithBytes(
|
||||||
|
"demo_vertex",
|
||||||
|
.utf8,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
defer str.release();
|
||||||
|
|
||||||
|
const ptr = library.msgSend(?*anyopaque, objc.sel("newFunctionWithName:"), .{str});
|
||||||
|
break :func_vert objc.Object.fromId(ptr.?);
|
||||||
|
};
|
||||||
|
const func_frag = func_frag: {
|
||||||
|
const str = try macos.foundation.String.createWithBytes(
|
||||||
|
"basic_fragment",
|
||||||
|
.utf8,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
defer str.release();
|
||||||
|
|
||||||
|
const ptr = library.msgSend(?*anyopaque, objc.sel("newFunctionWithName:"), .{str});
|
||||||
|
break :func_frag objc.Object.fromId(ptr.?);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pipeline_state = pipeline_state: {
|
||||||
|
// Create our descriptor
|
||||||
|
const desc = init: {
|
||||||
|
const Class = objc.Class.getClass("MTLRenderPipelineDescriptor").?;
|
||||||
|
const id_alloc = Class.msgSend(objc.Object, objc.sel("alloc"), .{});
|
||||||
|
const id_init = id_alloc.msgSend(objc.Object, objc.sel("init"), .{});
|
||||||
|
break :init id_init;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set our properties
|
||||||
|
desc.setProperty("vertexFunction", func_vert);
|
||||||
|
desc.setProperty("fragmentFunction", func_frag);
|
||||||
|
|
||||||
|
// Set our color attachment
|
||||||
|
const attachments = objc.Object.fromId(desc.getProperty(?*anyopaque, "colorAttachments"));
|
||||||
|
{
|
||||||
|
const attachment = attachments.msgSend(
|
||||||
|
objc.Object,
|
||||||
|
objc.sel("objectAtIndexedSubscript:"),
|
||||||
|
.{@as(c_ulong, 0)},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Value is MTLPixelFormatBGRA8Unorm
|
||||||
|
attachment.setProperty("pixelFormat", @as(c_ulong, 80));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make our state
|
||||||
|
var err: ?*anyopaque = null;
|
||||||
|
const pipeline_state = device.msgSend(
|
||||||
|
objc.Object,
|
||||||
|
objc.sel("newRenderPipelineStateWithDescriptor:error:"),
|
||||||
|
.{ desc, &err },
|
||||||
|
);
|
||||||
|
try checkError(err);
|
||||||
|
|
||||||
|
break :pipeline_state pipeline_state;
|
||||||
|
};
|
||||||
|
|
||||||
return Metal{
|
return Metal{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.cell_size = .{ .width = metrics.cell_width, .height = metrics.cell_height },
|
.cell_size = .{ .width = metrics.cell_width, .height = metrics.cell_height },
|
||||||
.screen_size = .{ .width = 0, .height = 0 },
|
|
||||||
.background = .{ .r = 0, .g = 0, .b = 0 },
|
.background = .{ .r = 0, .g = 0, .b = 0 },
|
||||||
.foreground = .{ .r = 255, .g = 255, .b = 255 },
|
.foreground = .{ .r = 255, .g = 255, .b = 255 },
|
||||||
|
|
||||||
@ -175,8 +250,9 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
|||||||
.device = device,
|
.device = device,
|
||||||
.queue = queue,
|
.queue = queue,
|
||||||
.swapchain = swapchain,
|
.swapchain = swapchain,
|
||||||
.library = library,
|
.buf_cells = buf_cells,
|
||||||
.buf_instance = buf_instance,
|
.buf_instance = buf_instance,
|
||||||
|
.pipeline = pipeline_state,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,8 +340,8 @@ pub fn render(
|
|||||||
const surface = self.swapchain.msgSend(objc.Object, objc.sel("nextDrawable"), .{});
|
const surface = self.swapchain.msgSend(objc.Object, objc.sel("nextDrawable"), .{});
|
||||||
|
|
||||||
// MTLRenderPassDescriptor
|
// MTLRenderPassDescriptor
|
||||||
const MTLRenderPassDescriptor = objc.Class.getClass("MTLRenderPassDescriptor").?;
|
|
||||||
const desc = desc: {
|
const desc = desc: {
|
||||||
|
const MTLRenderPassDescriptor = objc.Class.getClass("MTLRenderPassDescriptor").?;
|
||||||
const desc = MTLRenderPassDescriptor.msgSend(
|
const desc = MTLRenderPassDescriptor.msgSend(
|
||||||
objc.Object,
|
objc.Object,
|
||||||
objc.sel("renderPassDescriptor"),
|
objc.sel("renderPassDescriptor"),
|
||||||
@ -298,36 +374,59 @@ pub fn render(
|
|||||||
// Command buffer (MTLCommandBuffer)
|
// Command buffer (MTLCommandBuffer)
|
||||||
const buffer = self.queue.msgSend(objc.Object, objc.sel("commandBuffer"), .{});
|
const buffer = self.queue.msgSend(objc.Object, objc.sel("commandBuffer"), .{});
|
||||||
|
|
||||||
// MTLRenderCommandEncoder
|
{
|
||||||
const encoder = buffer.msgSend(
|
// MTLRenderCommandEncoder
|
||||||
objc.Object,
|
const encoder = buffer.msgSend(
|
||||||
objc.sel("renderCommandEncoderWithDescriptor:"),
|
objc.Object,
|
||||||
.{desc.value},
|
objc.sel("renderCommandEncoderWithDescriptor:"),
|
||||||
);
|
.{desc.value},
|
||||||
|
);
|
||||||
|
defer encoder.msgSend(void, objc.sel("endEncoding"), .{});
|
||||||
|
|
||||||
// If we are resizing we need to update the viewport
|
// Use our shader pipeline
|
||||||
encoder.msgSend(void, objc.sel("setViewport:"), .{MTLViewport{
|
encoder.msgSend(void, objc.sel("setRenderPipelineState:"), .{self.pipeline.value});
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
// Set our buffers
|
||||||
.width = @intToFloat(f64, self.screen_size.width),
|
encoder.msgSend(
|
||||||
.height = @intToFloat(f64, self.screen_size.height),
|
void,
|
||||||
.znear = 0,
|
objc.sel("setVertexBuffer:offset:atIndex:"),
|
||||||
.zfar = 1,
|
.{ self.buf_cells.value, @as(c_ulong, 0), @as(c_ulong, 0) },
|
||||||
}});
|
);
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
encoder.msgSend(
|
||||||
|
void,
|
||||||
|
objc.sel("drawPrimitives:vertexStart:vertexCount:instanceCount:"),
|
||||||
|
.{
|
||||||
|
@enumToInt(MTLPrimitiveType.triangle),
|
||||||
|
@as(c_ulong, 0),
|
||||||
|
@as(c_ulong, 3),
|
||||||
|
@as(c_ulong, 1),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// encoder.msgSend(
|
||||||
|
// void,
|
||||||
|
// objc.sel("drawIndexedPrimitives:indexCount:indexType:indexBuffer:indexBufferOffset:instanceCount:"),
|
||||||
|
// .{
|
||||||
|
// @enumToInt(MTLPrimitiveType.triangle),
|
||||||
|
// @as(c_ulong, 6),
|
||||||
|
// @enumToInt(MTLIndexType.uint16),
|
||||||
|
// self.buf_instance.value,
|
||||||
|
// @as(c_ulong, 0),
|
||||||
|
// @as(c_ulong, 1),
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
// End our rendering and draw
|
|
||||||
encoder.msgSend(void, objc.sel("endEncoding"), .{});
|
|
||||||
buffer.msgSend(void, objc.sel("presentDrawable:"), .{surface.value});
|
buffer.msgSend(void, objc.sel("presentDrawable:"), .{surface.value});
|
||||||
buffer.msgSend(void, objc.sel("commit"), .{});
|
buffer.msgSend(void, objc.sel("commit"), .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resize the screen.
|
/// Resize the screen.
|
||||||
fn setScreenSize(self: *Metal, dim: renderer.ScreenSize) !void {
|
fn setScreenSize(self: *Metal, dim: renderer.ScreenSize) !void {
|
||||||
// Update our screen size
|
|
||||||
self.screen_size = dim;
|
|
||||||
|
|
||||||
// Recalculate the rows/columns.
|
// Recalculate the rows/columns.
|
||||||
const grid_size = renderer.GridSize.init(self.screen_size, self.cell_size);
|
const grid_size = renderer.GridSize.init(dim, self.cell_size);
|
||||||
|
|
||||||
// Update our shaper
|
// Update our shaper
|
||||||
// TODO: don't reallocate if it is close enough (but bigger)
|
// TODO: don't reallocate if it is close enough (but bigger)
|
||||||
@ -335,6 +434,8 @@ fn setScreenSize(self: *Metal, dim: renderer.ScreenSize) !void {
|
|||||||
errdefer self.alloc.free(shape_buf);
|
errdefer self.alloc.free(shape_buf);
|
||||||
self.alloc.free(self.font_shaper.cell_buf);
|
self.alloc.free(self.font_shaper.cell_buf);
|
||||||
self.font_shaper.cell_buf = shape_buf;
|
self.font_shaper.cell_buf = shape_buf;
|
||||||
|
|
||||||
|
log.debug("screen size screen={} grid={}, cell={}", .{ dim, grid_size, self.cell_size });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sync all the CPU cells with the GPU state (but still on the CPU here).
|
/// Sync all the CPU cells with the GPU state (but still on the CPU here).
|
||||||
@ -462,6 +563,19 @@ pub fn updateCell(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn checkError(err_: ?*anyopaque) !void {
|
||||||
|
if (err_) |err| {
|
||||||
|
const nserr = objc.Object.fromId(err);
|
||||||
|
const str = @ptrCast(
|
||||||
|
*macos.foundation.String,
|
||||||
|
nserr.getProperty(?*anyopaque, "localizedDescription").?,
|
||||||
|
);
|
||||||
|
|
||||||
|
log.err("metal error={s}", .{str.cstringPtr(.ascii).?});
|
||||||
|
return error.MetalFailed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// https://developer.apple.com/documentation/metal/mtlloadaction?language=objc
|
/// https://developer.apple.com/documentation/metal/mtlloadaction?language=objc
|
||||||
const MTLLoadAction = enum(c_ulong) {
|
const MTLLoadAction = enum(c_ulong) {
|
||||||
dont_care = 0,
|
dont_care = 0,
|
||||||
@ -483,6 +597,21 @@ const MTLStorageMode = enum(c_ulong) {
|
|||||||
memoryless = 3,
|
memoryless = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// https://developer.apple.com/documentation/metal/mtlprimitivetype?language=objc
|
||||||
|
const MTLPrimitiveType = enum(c_ulong) {
|
||||||
|
point = 0,
|
||||||
|
line = 1,
|
||||||
|
line_strip = 2,
|
||||||
|
triangle = 3,
|
||||||
|
triangle_strip = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// https://developer.apple.com/documentation/metal/mtlindextype?language=objc
|
||||||
|
const MTLIndexType = enum(c_ulong) {
|
||||||
|
uint16 = 0,
|
||||||
|
uint32 = 1,
|
||||||
|
};
|
||||||
|
|
||||||
/// https://developer.apple.com/documentation/metal/mtlresourceoptions?language=objc
|
/// https://developer.apple.com/documentation/metal/mtlresourceoptions?language=objc
|
||||||
/// (incomplete, we only use this mode so we just hardcode it)
|
/// (incomplete, we only use this mode so we just hardcode it)
|
||||||
const MTLResourceStorageModeShared: c_ulong = @enumToInt(MTLStorageMode.shared) << 4;
|
const MTLResourceStorageModeShared: c_ulong = @enumToInt(MTLStorageMode.shared) << 4;
|
||||||
|
@ -3,7 +3,8 @@ vertex float4 basic_vertex(unsigned int vid [[ vertex_id ]]) {
|
|||||||
float2 grid_coord = float2(0.0f, 0.0f);
|
float2 grid_coord = float2(0.0f, 0.0f);
|
||||||
|
|
||||||
// The size of a single cell in pixels
|
// The size of a single cell in pixels
|
||||||
float2 cell_size = float2(75.0f, 100.0f);
|
//float2 cell_size = float2(75.0f, 100.0f);
|
||||||
|
float2 cell_size = float2(1.0f, 1.0f);
|
||||||
|
|
||||||
// Convert the grid x,y into world space x, y by accounting for cell size
|
// Convert the grid x,y into world space x, y by accounting for cell size
|
||||||
float2 cell_pos = cell_size * grid_coord;
|
float2 cell_pos = cell_size * grid_coord;
|
||||||
@ -25,11 +26,17 @@ vertex float4 basic_vertex(unsigned int vid [[ vertex_id ]]) {
|
|||||||
// Calculate the final position of our cell in world space.
|
// Calculate the final position of our cell in world space.
|
||||||
// We have to add our cell size since our vertices are offset
|
// We have to add our cell size since our vertices are offset
|
||||||
// one cell up and to the left. (Do the math to verify yourself)
|
// one cell up and to the left. (Do the math to verify yourself)
|
||||||
cell_pos = cell_pos + cell_size * position;
|
cell_pos = cell_size * position;
|
||||||
|
|
||||||
return float4(cell_pos.x, cell_pos.y, 0.5f, 1.0f);
|
return float4(cell_pos.x, cell_pos.y, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex float4 demo_vertex(
|
||||||
|
const device packed_float3* vertex_array [[ buffer(0) ]],
|
||||||
|
unsigned int vid [[ vertex_id ]]) {
|
||||||
|
return float4(vertex_array[vid], 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment half4 basic_fragment() {
|
fragment half4 basic_fragment() {
|
||||||
return half4(1.0);
|
return half4(1.0, 0.0, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user