mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-10-05 17:36:32 +00:00
metal: populate the greyscale texture, prep ubershader
This commit is contained in:
@@ -6,6 +6,7 @@ const builtin = @import("builtin");
|
|||||||
const glfw = @import("glfw");
|
const glfw = @import("glfw");
|
||||||
const objc = @import("objc");
|
const objc = @import("objc");
|
||||||
const macos = @import("macos");
|
const macos = @import("macos");
|
||||||
|
const Atlas = @import("../Atlas.zig");
|
||||||
const font = @import("../font/main.zig");
|
const font = @import("../font/main.zig");
|
||||||
const terminal = @import("../terminal/main.zig");
|
const terminal = @import("../terminal/main.zig");
|
||||||
const renderer = @import("../renderer.zig");
|
const renderer = @import("../renderer.zig");
|
||||||
@@ -51,9 +52,14 @@ swapchain: objc.Object, // CAMetalLayer
|
|||||||
buf_cells: objc.Object, // MTLBuffer
|
buf_cells: objc.Object, // MTLBuffer
|
||||||
buf_instance: objc.Object, // MTLBuffer
|
buf_instance: objc.Object, // MTLBuffer
|
||||||
pipeline: objc.Object, // MTLRenderPipelineState
|
pipeline: objc.Object, // MTLRenderPipelineState
|
||||||
|
texture_greyscale: objc.Object, // MTLTexture
|
||||||
|
|
||||||
const GPUCell = extern struct {
|
const GPUCell = extern struct {
|
||||||
|
mode: GPUCellMode,
|
||||||
grid_pos: [2]f32,
|
grid_pos: [2]f32,
|
||||||
|
glyph_pos: [2]u32 = .{ 0, 0 },
|
||||||
|
glyph_size: [2]u32 = .{ 0, 0 },
|
||||||
|
glyph_offset: [2]i32 = .{ 0, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const GPUUniforms = extern struct {
|
const GPUUniforms = extern struct {
|
||||||
@@ -61,6 +67,17 @@ const GPUUniforms = extern struct {
|
|||||||
cell_size: [2]f32,
|
cell_size: [2]f32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const GPUCellMode = enum(u8) {
|
||||||
|
bg = 1,
|
||||||
|
fg = 2,
|
||||||
|
fg_color = 7,
|
||||||
|
cursor_rect = 3,
|
||||||
|
cursor_rect_hollow = 4,
|
||||||
|
cursor_bar = 5,
|
||||||
|
underline = 6,
|
||||||
|
strikethrough = 8,
|
||||||
|
};
|
||||||
|
|
||||||
/// Returns the hints that we want for this
|
/// Returns the hints that we want for this
|
||||||
pub fn windowHints() glfw.Window.Hints {
|
pub fn windowHints() glfw.Window.Hints {
|
||||||
return .{
|
return .{
|
||||||
@@ -145,6 +162,7 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
|||||||
// Initialize our shader (MTLLibrary)
|
// Initialize our shader (MTLLibrary)
|
||||||
const library = try initLibrary(device, @embedFile("../shaders/cell.metal"));
|
const library = try initLibrary(device, @embedFile("../shaders/cell.metal"));
|
||||||
const pipeline_state = try initPipelineState(device, library);
|
const pipeline_state = try initPipelineState(device, library);
|
||||||
|
const texture_greyscale = try initAtlasTexture(device, &font_group.atlas_greyscale);
|
||||||
|
|
||||||
return Metal{
|
return Metal{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
@@ -167,6 +185,7 @@ pub fn init(alloc: Allocator, font_group: *font.GroupCache) !Metal {
|
|||||||
.buf_cells = buf_cells,
|
.buf_cells = buf_cells,
|
||||||
.buf_instance = buf_instance,
|
.buf_instance = buf_instance,
|
||||||
.pipeline = pipeline_state,
|
.pipeline = pipeline_state,
|
||||||
|
.texture_greyscale = texture_greyscale,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,9 +302,15 @@ pub fn render(
|
|||||||
// Get our surface (CAMetalDrawable)
|
// Get our surface (CAMetalDrawable)
|
||||||
const surface = self.swapchain.msgSend(objc.Object, objc.sel("nextDrawable"), .{});
|
const surface = self.swapchain.msgSend(objc.Object, objc.sel("nextDrawable"), .{});
|
||||||
|
|
||||||
// Setup our buffer
|
// Setup our buffers
|
||||||
try self.syncCells();
|
try self.syncCells();
|
||||||
|
|
||||||
|
// If our font atlas changed, sync the texture data
|
||||||
|
if (self.font_group.atlas_greyscale.modified) {
|
||||||
|
try syncAtlasTexture(&self.font_group.atlas_greyscale, &self.texture_greyscale);
|
||||||
|
self.font_group.atlas_greyscale.modified = false;
|
||||||
|
}
|
||||||
|
|
||||||
// MTLRenderPassDescriptor
|
// MTLRenderPassDescriptor
|
||||||
const desc = desc: {
|
const desc = desc: {
|
||||||
const MTLRenderPassDescriptor = objc.Class.getClass("MTLRenderPassDescriptor").?;
|
const MTLRenderPassDescriptor = objc.Class.getClass("MTLRenderPassDescriptor").?;
|
||||||
@@ -351,6 +376,14 @@ pub fn render(
|
|||||||
@as(c_ulong, 1),
|
@as(c_ulong, 1),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
encoder.msgSend(
|
||||||
|
void,
|
||||||
|
objc.sel("setFragmentTexture:atIndex:"),
|
||||||
|
.{
|
||||||
|
self.texture_greyscale.value,
|
||||||
|
@as(c_ulong, 0),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
encoder.msgSend(
|
encoder.msgSend(
|
||||||
void,
|
void,
|
||||||
@@ -489,6 +522,7 @@ pub fn updateCell(
|
|||||||
if (colors.bg) |rgb| {
|
if (colors.bg) |rgb| {
|
||||||
_ = rgb;
|
_ = rgb;
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
|
.mode = .bg,
|
||||||
.grid_pos = .{ @intToFloat(f32, x), @intToFloat(f32, y) },
|
.grid_pos = .{ @intToFloat(f32, x), @intToFloat(f32, y) },
|
||||||
// .grid_col = @intCast(u16, x),
|
// .grid_col = @intCast(u16, x),
|
||||||
// .grid_row = @intCast(u16, y),
|
// .grid_row = @intCast(u16, y),
|
||||||
@@ -516,20 +550,16 @@ pub fn updateCell(
|
|||||||
shaper_cell.glyph_index,
|
shaper_cell.glyph_index,
|
||||||
@floatToInt(u16, @ceil(self.cell_size.height)),
|
@floatToInt(u16, @ceil(self.cell_size.height)),
|
||||||
);
|
);
|
||||||
_ = glyph;
|
|
||||||
|
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
|
.mode = .fg,
|
||||||
.grid_pos = .{ @intToFloat(f32, x), @intToFloat(f32, y) },
|
.grid_pos = .{ @intToFloat(f32, x), @intToFloat(f32, y) },
|
||||||
|
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
|
||||||
|
.glyph_size = .{ glyph.width, glyph.height },
|
||||||
|
.glyph_offset = .{ glyph.offset_x, glyph.offset_y },
|
||||||
|
|
||||||
// .mode = mode,
|
// .mode = mode,
|
||||||
// .grid_col = @intCast(u16, x),
|
|
||||||
// .grid_row = @intCast(u16, y),
|
|
||||||
// .grid_width = cell.widthLegacy(),
|
// .grid_width = cell.widthLegacy(),
|
||||||
// .glyph_x = glyph.atlas_x,
|
|
||||||
// .glyph_y = glyph.atlas_y,
|
|
||||||
// .glyph_width = glyph.width,
|
|
||||||
// .glyph_height = glyph.height,
|
|
||||||
// .glyph_offset_x = glyph.offset_x,
|
|
||||||
// .glyph_offset_y = glyph.offset_y,
|
|
||||||
// .fg_r = colors.fg.r,
|
// .fg_r = colors.fg.r,
|
||||||
// .fg_g = colors.fg.g,
|
// .fg_g = colors.fg.g,
|
||||||
// .fg_b = colors.fg.b,
|
// .fg_b = colors.fg.b,
|
||||||
@@ -565,6 +595,34 @@ fn syncCells(self: *Metal) !void {
|
|||||||
@memcpy(ptr, @ptrCast([*]const u8, self.cells.items.ptr), req_bytes);
|
@memcpy(ptr, @ptrCast([*]const u8, self.cells.items.ptr), req_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sync the atlas data to the given texture. This copies the bytes
|
||||||
|
/// associated with the atlas to the given texture. If the atlas no longer
|
||||||
|
/// fits into the texture, the texture will be resized.
|
||||||
|
fn syncAtlasTexture(atlas: *const Atlas, texture: *objc.Object) !void {
|
||||||
|
const width = texture.getProperty(c_ulong, "width");
|
||||||
|
if (atlas.size > width) {
|
||||||
|
@panic("TODO: reallocate texture");
|
||||||
|
}
|
||||||
|
|
||||||
|
texture.msgSend(
|
||||||
|
void,
|
||||||
|
objc.sel("replaceRegion:mipmapLevel:withBytes:bytesPerRow:"),
|
||||||
|
.{
|
||||||
|
MTLRegion{
|
||||||
|
.origin = .{ .x = 0, .y = 0, .z = 0 },
|
||||||
|
.size = .{
|
||||||
|
.width = @intCast(c_ulong, atlas.size),
|
||||||
|
.height = @intCast(c_ulong, atlas.size),
|
||||||
|
.depth = 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
@as(c_ulong, 0),
|
||||||
|
atlas.data.ptr,
|
||||||
|
@as(c_ulong, atlas.format.depth() * atlas.size),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize the shader library.
|
/// Initialize the shader library.
|
||||||
fn initLibrary(device: objc.Object, data: []const u8) !objc.Object {
|
fn initLibrary(device: objc.Object, data: []const u8) !objc.Object {
|
||||||
const source = try macos.foundation.String.createWithBytes(
|
const source = try macos.foundation.String.createWithBytes(
|
||||||
@@ -594,7 +652,7 @@ fn initPipelineState(device: objc.Object, library: objc.Object) !objc.Object {
|
|||||||
// Get our vertex and fragment functions
|
// Get our vertex and fragment functions
|
||||||
const func_vert = func_vert: {
|
const func_vert = func_vert: {
|
||||||
const str = try macos.foundation.String.createWithBytes(
|
const str = try macos.foundation.String.createWithBytes(
|
||||||
"basic_vertex",
|
"uber_vertex",
|
||||||
.utf8,
|
.utf8,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
@@ -605,7 +663,7 @@ fn initPipelineState(device: objc.Object, library: objc.Object) !objc.Object {
|
|||||||
};
|
};
|
||||||
const func_frag = func_frag: {
|
const func_frag = func_frag: {
|
||||||
const str = try macos.foundation.String.createWithBytes(
|
const str = try macos.foundation.String.createWithBytes(
|
||||||
"basic_fragment",
|
"uber_fragment",
|
||||||
.utf8,
|
.utf8,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
@@ -636,8 +694,52 @@ fn initPipelineState(device: objc.Object, library: objc.Object) !objc.Object {
|
|||||||
.{@as(c_ulong, 0)},
|
.{@as(c_ulong, 0)},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
attr.setProperty("format", @enumToInt(MTLVertexFormat.uchar));
|
||||||
|
attr.setProperty("offset", @as(c_ulong, @offsetOf(GPUCell, "mode")));
|
||||||
|
attr.setProperty("bufferIndex", @as(c_ulong, 0));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const attr = attrs.msgSend(
|
||||||
|
objc.Object,
|
||||||
|
objc.sel("objectAtIndexedSubscript:"),
|
||||||
|
.{@as(c_ulong, 1)},
|
||||||
|
);
|
||||||
|
|
||||||
attr.setProperty("format", @enumToInt(MTLVertexFormat.float2));
|
attr.setProperty("format", @enumToInt(MTLVertexFormat.float2));
|
||||||
attr.setProperty("offset", @as(c_ulong, 0));
|
attr.setProperty("offset", @as(c_ulong, @offsetOf(GPUCell, "grid_pos")));
|
||||||
|
attr.setProperty("bufferIndex", @as(c_ulong, 0));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const attr = attrs.msgSend(
|
||||||
|
objc.Object,
|
||||||
|
objc.sel("objectAtIndexedSubscript:"),
|
||||||
|
.{@as(c_ulong, 2)},
|
||||||
|
);
|
||||||
|
|
||||||
|
attr.setProperty("format", @enumToInt(MTLVertexFormat.uint2));
|
||||||
|
attr.setProperty("offset", @as(c_ulong, @offsetOf(GPUCell, "glyph_pos")));
|
||||||
|
attr.setProperty("bufferIndex", @as(c_ulong, 0));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const attr = attrs.msgSend(
|
||||||
|
objc.Object,
|
||||||
|
objc.sel("objectAtIndexedSubscript:"),
|
||||||
|
.{@as(c_ulong, 3)},
|
||||||
|
);
|
||||||
|
|
||||||
|
attr.setProperty("format", @enumToInt(MTLVertexFormat.uint2));
|
||||||
|
attr.setProperty("offset", @as(c_ulong, @offsetOf(GPUCell, "glyph_size")));
|
||||||
|
attr.setProperty("bufferIndex", @as(c_ulong, 0));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const attr = attrs.msgSend(
|
||||||
|
objc.Object,
|
||||||
|
objc.sel("objectAtIndexedSubscript:"),
|
||||||
|
.{@as(c_ulong, 4)},
|
||||||
|
);
|
||||||
|
|
||||||
|
attr.setProperty("format", @enumToInt(MTLVertexFormat.int2));
|
||||||
|
attr.setProperty("offset", @as(c_ulong, @offsetOf(GPUCell, "glyph_offset")));
|
||||||
attr.setProperty("bufferIndex", @as(c_ulong, 0));
|
attr.setProperty("bufferIndex", @as(c_ulong, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -696,6 +798,44 @@ fn initPipelineState(device: objc.Object, library: objc.Object) !objc.Object {
|
|||||||
return pipeline_state;
|
return pipeline_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize a MTLTexture object for the given atlas.
|
||||||
|
fn initAtlasTexture(device: objc.Object, atlas: *const Atlas) !objc.Object {
|
||||||
|
// Determine our pixel format
|
||||||
|
const pixel_format: MTLPixelFormat = switch (atlas.format) {
|
||||||
|
.greyscale => .r8unorm,
|
||||||
|
else => @panic("unsupported atlas format for Metal texture"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create our descriptor
|
||||||
|
const desc = init: {
|
||||||
|
const Class = objc.Class.getClass("MTLTextureDescriptor").?;
|
||||||
|
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("pixelFormat", @enumToInt(pixel_format));
|
||||||
|
desc.setProperty("width", @intCast(c_ulong, atlas.size));
|
||||||
|
desc.setProperty("height", @intCast(c_ulong, atlas.size));
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
const id = device.msgSend(
|
||||||
|
?*anyopaque,
|
||||||
|
objc.sel("newTextureWithDescriptor:"),
|
||||||
|
.{desc},
|
||||||
|
) orelse return error.MetalFailed;
|
||||||
|
|
||||||
|
return objc.Object.fromId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deinitialize a metal resource (buffer, texture, etc.) and free the
|
||||||
|
/// memory associated with it.
|
||||||
|
fn deinitMTLResource(obj: objc.Object) void {
|
||||||
|
obj.msgSend(void, objc.sel("setPurgeableState:"), .{@enumToInt(MTLPurgeableState.empty)});
|
||||||
|
obj.msgSend(void, objc.sel("release"), .{});
|
||||||
|
}
|
||||||
|
|
||||||
fn checkError(err_: ?*anyopaque) !void {
|
fn checkError(err_: ?*anyopaque) !void {
|
||||||
if (err_) |err| {
|
if (err_) |err| {
|
||||||
const nserr = objc.Object.fromId(err);
|
const nserr = objc.Object.fromId(err);
|
||||||
@@ -748,6 +888,9 @@ const MTLIndexType = enum(c_ulong) {
|
|||||||
/// https://developer.apple.com/documentation/metal/mtlvertexformat?language=objc
|
/// https://developer.apple.com/documentation/metal/mtlvertexformat?language=objc
|
||||||
const MTLVertexFormat = enum(c_ulong) {
|
const MTLVertexFormat = enum(c_ulong) {
|
||||||
float2 = 29,
|
float2 = 29,
|
||||||
|
int2 = 33,
|
||||||
|
uint2 = 37,
|
||||||
|
uchar = 45,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// https://developer.apple.com/documentation/metal/mtlvertexstepfunction?language=objc
|
/// https://developer.apple.com/documentation/metal/mtlvertexstepfunction?language=objc
|
||||||
@@ -757,6 +900,16 @@ const MTLVertexStepFunction = enum(c_ulong) {
|
|||||||
per_instance = 2,
|
per_instance = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// https://developer.apple.com/documentation/metal/mtlpixelformat?language=objc
|
||||||
|
const MTLPixelFormat = enum(c_ulong) {
|
||||||
|
r8unorm = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// https://developer.apple.com/documentation/metal/mtlpurgeablestate?language=objc
|
||||||
|
const MTLPurgeableState = enum(c_ulong) {
|
||||||
|
empty = 4,
|
||||||
|
};
|
||||||
|
|
||||||
/// 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;
|
||||||
@@ -777,6 +930,23 @@ const MTLViewport = extern struct {
|
|||||||
zfar: f64,
|
zfar: f64,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MTLRegion = extern struct {
|
||||||
|
origin: MTLOrigin,
|
||||||
|
size: MTLSize,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MTLOrigin = extern struct {
|
||||||
|
x: c_ulong,
|
||||||
|
y: c_ulong,
|
||||||
|
z: c_ulong,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MTLSize = extern struct {
|
||||||
|
width: c_ulong,
|
||||||
|
height: c_ulong,
|
||||||
|
depth: c_ulong,
|
||||||
|
};
|
||||||
|
|
||||||
extern "c" fn MTLCreateSystemDefaultDevice() ?*anyopaque;
|
extern "c" fn MTLCreateSystemDefaultDevice() ?*anyopaque;
|
||||||
extern "c" fn objc_autoreleasePoolPush() ?*anyopaque;
|
extern "c" fn objc_autoreleasePoolPush() ?*anyopaque;
|
||||||
extern "c" fn objc_autoreleasePoolPop(?*anyopaque) void;
|
extern "c" fn objc_autoreleasePoolPop(?*anyopaque) void;
|
||||||
|
@@ -1,26 +1,46 @@
|
|||||||
using namespace metal;
|
using namespace metal;
|
||||||
|
|
||||||
|
// The possible modes that a shader can take.
|
||||||
|
enum Mode : uint8_t {
|
||||||
|
MODE_BG = 1u,
|
||||||
|
MODE_FG = 2u,
|
||||||
|
};
|
||||||
|
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
float4x4 projection_matrix;
|
float4x4 projection_matrix;
|
||||||
float2 cell_size;
|
float2 cell_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexIn {
|
struct VertexIn {
|
||||||
|
// The mode for this cell.
|
||||||
|
uint8_t mode [[ attribute(0) ]];
|
||||||
|
|
||||||
// The grid coordinates (x, y) where x < columns and y < rows
|
// The grid coordinates (x, y) where x < columns and y < rows
|
||||||
float2 grid_pos [[ attribute(0) ]];
|
float2 grid_pos [[ attribute(1) ]];
|
||||||
|
|
||||||
|
// The fields below are present only when rendering text.
|
||||||
|
|
||||||
|
// The position of the glyph in the texture (x,y)
|
||||||
|
uint2 glyph_pos [[ attribute(2) ]];
|
||||||
|
|
||||||
|
// The size of the glyph in the texture (w,h)
|
||||||
|
uint2 glyph_size [[ attribute(3) ]];
|
||||||
|
|
||||||
|
// The left and top bearings for the glyph (x,y)
|
||||||
|
int2 glyph_offset [[ attribute(4) ]];
|
||||||
};
|
};
|
||||||
|
|
||||||
vertex float4 basic_vertex(
|
struct VertexOut {
|
||||||
|
float4 position [[ position ]];
|
||||||
|
};
|
||||||
|
|
||||||
|
vertex VertexOut uber_vertex(
|
||||||
unsigned int vid [[ vertex_id ]],
|
unsigned int vid [[ vertex_id ]],
|
||||||
VertexIn input [[ stage_in ]],
|
VertexIn input [[ stage_in ]],
|
||||||
constant Uniforms &uniforms [[ buffer(1) ]]
|
constant Uniforms &uniforms [[ buffer(1) ]]
|
||||||
) {
|
) {
|
||||||
// Where we are in the grid (x, y) where top-left is origin
|
|
||||||
// float2 grid_coord = float2(5.0f, 0.0f);
|
|
||||||
float2 grid_coord = input.grid_pos;
|
|
||||||
|
|
||||||
// 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 = uniforms.cell_size * grid_coord;
|
float2 cell_pos = uniforms.cell_size * input.grid_pos;
|
||||||
|
|
||||||
// Turn the cell position into a vertex point depending on the
|
// Turn the cell position into a vertex point depending on the
|
||||||
// vertex ID. Since we use instanced drawing, we have 4 vertices
|
// vertex ID. Since we use instanced drawing, we have 4 vertices
|
||||||
@@ -36,14 +56,43 @@ vertex float4 basic_vertex(
|
|||||||
position.x = (vid == 0 || vid == 1) ? 1.0f : 0.0f;
|
position.x = (vid == 0 || vid == 1) ? 1.0f : 0.0f;
|
||||||
position.y = (vid == 0 || vid == 3) ? 0.0f : 1.0f;
|
position.y = (vid == 0 || vid == 3) ? 0.0f : 1.0f;
|
||||||
|
|
||||||
|
// TODO: scale
|
||||||
|
float2 cell_size = uniforms.cell_size;
|
||||||
|
|
||||||
|
VertexOut out;
|
||||||
|
switch (input.mode) {
|
||||||
|
case MODE_BG:
|
||||||
// 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 + uniforms.cell_size * position;
|
cell_pos = cell_pos + uniforms.cell_size * position;
|
||||||
|
|
||||||
return uniforms.projection_matrix * float4(cell_pos.x, cell_pos.y, 0.0f, 1.0f);
|
out.position = uniforms.projection_matrix * float4(cell_pos.x, cell_pos.y, 0.0f, 1.0f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MODE_FG:
|
||||||
|
float2 glyph_size = float2(input.glyph_size);
|
||||||
|
float2 glyph_offset = float2(input.glyph_offset);
|
||||||
|
|
||||||
|
// TODO: downsampling
|
||||||
|
|
||||||
|
// The glyph_offset.y is the y bearing, a y value that when added
|
||||||
|
// to the baseline is the offset (+y is up). Our grid goes down.
|
||||||
|
// So we flip it with `cell_size.y - glyph_offset.y`.
|
||||||
|
glyph_offset.y = cell_size.y - glyph_offset.y;
|
||||||
|
|
||||||
|
// Calculate the final position of the cell.
|
||||||
|
cell_pos = cell_pos + glyph_size * position + glyph_offset;
|
||||||
|
|
||||||
|
out.position = uniforms.projection_matrix * float4(cell_pos.x, cell_pos.y, 0.0f, 1.0f);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment half4 basic_fragment() {
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment half4 uber_fragment(
|
||||||
|
VertexOut in [[ stage_in ]]
|
||||||
|
) {
|
||||||
return half4(1.0, 0.0, 0.0, 1.0);
|
return half4(1.0, 0.0, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user