mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-19 10:16:12 +03:00
202 lines
4.8 KiB
Zig
202 lines
4.8 KiB
Zig
//! Wrapper for handling textures.
|
|
const Self = @This();
|
|
|
|
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const assert = std.debug.assert;
|
|
const builtin = @import("builtin");
|
|
const objc = @import("objc");
|
|
|
|
const mtl = @import("api.zig");
|
|
const Metal = @import("../Metal.zig");
|
|
|
|
const log = std.log.scoped(.metal);
|
|
|
|
/// Options for initializing a texture.
|
|
pub const Options = struct {
|
|
/// MTLDevice
|
|
device: objc.Object,
|
|
pixel_format: mtl.MTLPixelFormat,
|
|
resource_options: mtl.MTLResourceOptions,
|
|
};
|
|
|
|
/// The underlying MTLTexture Object.
|
|
texture: objc.Object,
|
|
|
|
/// The width of this texture.
|
|
width: usize,
|
|
/// The height of this texture.
|
|
height: usize,
|
|
|
|
/// Bytes per pixel for this texture.
|
|
bpp: usize,
|
|
|
|
pub const Error = error{
|
|
/// A Metal API call failed.
|
|
MetalFailed,
|
|
};
|
|
|
|
/// Initialize a texture
|
|
pub fn init(
|
|
opts: Options,
|
|
width: usize,
|
|
height: usize,
|
|
data: ?[]const u8,
|
|
) Error!Self {
|
|
// Create our descriptor
|
|
const desc = init: {
|
|
const Class = objc.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;
|
|
};
|
|
errdefer desc.msgSend(void, objc.sel("release"), .{});
|
|
|
|
// Set our properties
|
|
desc.setProperty("pixelFormat", @intFromEnum(opts.pixel_format));
|
|
desc.setProperty("width", @as(c_ulong, width));
|
|
desc.setProperty("height", @as(c_ulong, height));
|
|
desc.setProperty("resourceOptions", opts.resource_options);
|
|
|
|
// Initialize
|
|
const id = opts.device.msgSend(
|
|
?*anyopaque,
|
|
objc.sel("newTextureWithDescriptor:"),
|
|
.{desc},
|
|
) orelse return error.MetalFailed;
|
|
|
|
const self: Self = .{
|
|
.texture = objc.Object.fromId(id),
|
|
.width = width,
|
|
.height = height,
|
|
.bpp = bppOf(opts.pixel_format),
|
|
};
|
|
|
|
// If we have data, we set it here.
|
|
if (data) |d| {
|
|
assert(d.len == width * height * self.bpp);
|
|
try self.replaceRegion(0, 0, width, height, d);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
pub fn deinit(self: Self) void {
|
|
self.texture.release();
|
|
}
|
|
|
|
/// Replace a region of the texture with the provided data.
|
|
///
|
|
/// Does NOT check the dimensions of the data to ensure correctness.
|
|
pub fn replaceRegion(
|
|
self: Self,
|
|
x: usize,
|
|
y: usize,
|
|
width: usize,
|
|
height: usize,
|
|
data: []const u8,
|
|
) error{}!void {
|
|
self.texture.msgSend(
|
|
void,
|
|
objc.sel("replaceRegion:mipmapLevel:withBytes:bytesPerRow:"),
|
|
.{
|
|
mtl.MTLRegion{
|
|
.origin = .{ .x = x, .y = y, .z = 0 },
|
|
.size = .{
|
|
.width = @intCast(width),
|
|
.height = @intCast(height),
|
|
.depth = 1,
|
|
},
|
|
},
|
|
@as(c_ulong, 0),
|
|
@as(*const anyopaque, data.ptr),
|
|
@as(c_ulong, self.bpp * width),
|
|
},
|
|
);
|
|
}
|
|
|
|
/// Returns the bytes per pixel for the provided pixel format
|
|
fn bppOf(pixel_format: mtl.MTLPixelFormat) usize {
|
|
return switch (pixel_format) {
|
|
// Invalid
|
|
.invalid => @panic("invalid pixel format"),
|
|
|
|
// Weird formats I was too lazy to get the sizes of
|
|
else => @panic("pixel format size unknown (unlikely that this format was actually used, could be memory corruption)"),
|
|
|
|
// 8-bit pixel formats
|
|
.a8unorm,
|
|
.r8unorm,
|
|
.r8unorm_srgb,
|
|
.r8snorm,
|
|
.r8uint,
|
|
.r8sint,
|
|
.rg8unorm,
|
|
.rg8unorm_srgb,
|
|
.rg8snorm,
|
|
.rg8uint,
|
|
.rg8sint,
|
|
.stencil8,
|
|
=> 1,
|
|
|
|
// 16-bit pixel formats
|
|
.r16unorm,
|
|
.r16snorm,
|
|
.r16uint,
|
|
.r16sint,
|
|
.r16float,
|
|
.rg16unorm,
|
|
.rg16snorm,
|
|
.rg16uint,
|
|
.rg16sint,
|
|
.rg16float,
|
|
.b5g6r5unorm,
|
|
.a1bgr5unorm,
|
|
.abgr4unorm,
|
|
.bgr5a1unorm,
|
|
.depth16unorm,
|
|
=> 2,
|
|
|
|
// 32-bit pixel formats
|
|
.rgba8unorm,
|
|
.rgba8unorm_srgb,
|
|
.rgba8snorm,
|
|
.rgba8uint,
|
|
.rgba8sint,
|
|
.bgra8unorm,
|
|
.bgra8unorm_srgb,
|
|
.rgb10a2unorm,
|
|
.rgb10a2uint,
|
|
.rg11b10float,
|
|
.rgb9e5float,
|
|
.bgr10a2unorm,
|
|
.bgr10_xr,
|
|
.bgr10_xr_srgb,
|
|
.r32uint,
|
|
.r32sint,
|
|
.r32float,
|
|
.depth32float,
|
|
.depth24unorm_stencil8,
|
|
=> 4,
|
|
|
|
// 64-bit pixel formats
|
|
.rg32uint,
|
|
.rg32sint,
|
|
.rg32float,
|
|
.rgba16unorm,
|
|
.rgba16snorm,
|
|
.rgba16uint,
|
|
.rgba16sint,
|
|
.rgba16float,
|
|
.bgra10_xr,
|
|
.bgra10_xr_srgb,
|
|
=> 8,
|
|
|
|
// 128-bit pixel formats,
|
|
.rgba32uint,
|
|
.rgba32sint,
|
|
.rgba32float,
|
|
=> 128,
|
|
};
|
|
}
|