feat(macos): port Ghostty's macOS code paths (Metal, CoreText, Darwin)
Ports the macOS-specific Zig code behind cfg(target_os = "macos"): - renderer: a GraphicsApi trait with generic::Renderer<G>; Metal backend (all renderer/metal/* files, shaders.metal verbatim, runtime library compile, IOSurface layer, CVDisplayLink), shadertoy -> MSL via naga; OpenGL remains the backend elsewhere. - font: CoreText face, shaper and discovery with build.rs backend selection (coretext default; coretext-freetype / coretext-harfbuzz / coretext-noshape features), no fontconfig/freetype/harfbuzz on Apple. - input: KeymapDarwin via UCKeyTranslate, option-as-alt, mac keycodes. - os: macos, mach, cf_release_thread, NSProcessInfo args and macOS branches; config: macOS defaults, keybinds, Application Support paths. - termio/core: login(1) wrapping, openpty/execvpe, Darwin child exit handling, os_log logging. Compile-checked for aarch64/x86_64-apple-darwin (clippy -D warnings clean) using the cross setup in docs/rewrite/notes/macos-M1.md; cannot run here. The embedded runtime/C API (embedded.zig) is the next step. Also includes the cross-platform integration edits shared by the crash, verification, Windows and macOS work (Cargo target-gating, thread crash-context hooks, apprt-gtk surface/IME refactor, termio/os cfg gates), a cfg fix so ghostty-config builds on wasm32, and an allow for the intentional recursion in the crash overflow test. Test Plan: - cargo test --workspace (3224 passed) - cargo clippy --workspace --all-targets -- -D warnings - cargo check --target aarch64-apple-darwin -p ghostty-renderer (with the documented cross env)
This commit is contained in:
1 parent
3c8fae1089
commit
452b782149
124 files changed
+14936
-1242
No files matched your search
@@ -0,0 +1,156 @@
|
||||
//! Wrapper for handling textures (`metal/Texture.zig`).
|
||||
use std::{ffi::c_void, ptr::NonNull};
|
||||
|
||||
use objc2::{rc::Retained, runtime::ProtocolObject};
|
||||
use objc2_metal::{
|
||||
MTLDevice as _,
|
||||
MTLOrigin,
|
||||
MTLPixelFormat,
|
||||
MTLRegion,
|
||||
MTLResourceOptions,
|
||||
MTLSize,
|
||||
MTLTexture,
|
||||
MTLTextureDescriptor,
|
||||
MTLTextureUsage,
|
||||
};
|
||||
|
||||
use super::{Device, Error};
|
||||
use crate::{
|
||||
graphics_api::{ApiError, GpuTexture},
|
||||
metal_layout::{PixelFormat, ResourceOptions, TextureUsage},
|
||||
};
|
||||
|
||||
/// Options for initializing a texture.
|
||||
#[derive(Clone)]
|
||||
pub struct Options {
|
||||
/// MTLDevice
|
||||
pub device: Device,
|
||||
pub pixel_format: PixelFormat,
|
||||
pub resource_options: ResourceOptions,
|
||||
pub usage: TextureUsage,
|
||||
}
|
||||
|
||||
/// A 2D texture.
|
||||
pub struct Texture {
|
||||
/// The underlying MTLTexture Object.
|
||||
texture: Retained<ProtocolObject<dyn MTLTexture>>,
|
||||
|
||||
/// The width of this texture.
|
||||
pub width: usize,
|
||||
/// The height of this texture.
|
||||
pub height: usize,
|
||||
|
||||
/// Bytes per pixel for this texture.
|
||||
pub bpp: usize,
|
||||
}
|
||||
|
||||
// SAFETY: MTLTexture objects may be used from any thread (the CPU side only
|
||||
// calls `replaceRegion`, which the renderer serializes through its draw
|
||||
// mutex).
|
||||
unsafe impl Send for Texture {}
|
||||
// SAFETY: see above.
|
||||
unsafe impl Sync for Texture {}
|
||||
|
||||
impl Texture {
|
||||
/// The underlying MTLTexture.
|
||||
pub fn texture(&self) -> &ProtocolObject<dyn MTLTexture> {
|
||||
&self.texture
|
||||
}
|
||||
|
||||
/// Initialize a texture
|
||||
pub fn init(
|
||||
opts: &Options,
|
||||
width: usize,
|
||||
height: usize,
|
||||
data: Option<&[u8]>,
|
||||
) -> Result<Texture, Error> {
|
||||
// Create our descriptor and set our properties
|
||||
let desc = MTLTextureDescriptor::new();
|
||||
desc.setPixelFormat(MTLPixelFormat(opts.pixel_format as usize));
|
||||
// SAFETY: plain numbers, validated when the texture is created.
|
||||
unsafe {
|
||||
desc.setWidth(width);
|
||||
desc.setHeight(height);
|
||||
}
|
||||
desc.setResourceOptions(MTLResourceOptions(opts.resource_options.bits() as usize));
|
||||
desc.setUsage(MTLTextureUsage(opts.usage.bits() as usize));
|
||||
|
||||
// Initialize
|
||||
let texture = opts
|
||||
.device
|
||||
.newTextureWithDescriptor(&desc)
|
||||
.ok_or(Error::MetalFailed)?;
|
||||
|
||||
let this = Texture {
|
||||
texture,
|
||||
width,
|
||||
height,
|
||||
bpp: opts.pixel_format.bytes_per_pixel(),
|
||||
};
|
||||
|
||||
// If we have data, we set it here.
|
||||
if let Some(d) = data {
|
||||
debug_assert_eq!(d.len(), width * height * this.bpp);
|
||||
this.replace_region(0, 0, width, height, d)?;
|
||||
}
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Replace a region of the texture with the provided data.
|
||||
///
|
||||
/// Zig does NOT check the dimensions of the data to ensure correctness;
|
||||
/// since this is a safe function we reject data that is too short.
|
||||
pub fn replace_region(
|
||||
&self,
|
||||
x: usize,
|
||||
y: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
data: &[u8],
|
||||
) -> Result<(), Error> {
|
||||
let bytes_per_row = self.bpp * width;
|
||||
if data.len() < bytes_per_row * height {
|
||||
return Err(Error::MetalFailed);
|
||||
}
|
||||
let Some(ptr) = NonNull::new(data.as_ptr().cast_mut().cast::<c_void>()) else {
|
||||
return Err(Error::MetalFailed);
|
||||
};
|
||||
let region = MTLRegion {
|
||||
origin: MTLOrigin { x, y, z: 0 },
|
||||
size: MTLSize {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
},
|
||||
};
|
||||
// SAFETY: `ptr` points to at least `bytes_per_row * height` readable
|
||||
// bytes (checked above); Metal only reads from it.
|
||||
unsafe {
|
||||
self.texture
|
||||
.replaceRegion_mipmapLevel_withBytes_bytesPerRow(region, 0, ptr, bytes_per_row);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GpuTexture for Texture {
|
||||
fn width(&self) -> usize {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> usize {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn replace_region(
|
||||
&self,
|
||||
x: usize,
|
||||
y: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
data: &[u8],
|
||||
) -> Result<(), ApiError> {
|
||||
Texture::replace_region(self, x, y, width, height, data).map_err(ApiError::new)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user