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)
135 lines
4.3 KiB
Rust
135 lines
4.3 KiB
Rust
//! OpenGL data storage for a certain set of equal types (`opengl/buffer.zig`).
|
|
use std::marker::PhantomData;
|
|
|
|
use bytemuck::Pod;
|
|
|
|
use super::gl::{self, BufferTarget, BufferUsage, Gl, RawBuffer};
|
|
|
|
/// Options for initializing a buffer.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct Options {
|
|
pub target: BufferTarget,
|
|
pub usage: BufferUsage,
|
|
}
|
|
|
|
impl Default for Options {
|
|
fn default() -> Self {
|
|
Options {
|
|
target: BufferTarget::Array,
|
|
usage: BufferUsage::DynamicDraw,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// OpenGL data storage for a certain set of equal types. This is usually
|
|
/// used for vertex buffers, etc. This helpful wrapper makes it easy to
|
|
/// prealloc, shrink, grow, sync, buffers with OpenGL.
|
|
pub struct Buffer<T: Pod> {
|
|
gl: Gl,
|
|
|
|
/// Underlying GL buffer object.
|
|
handle: RawBuffer,
|
|
|
|
/// Options this buffer was allocated with.
|
|
pub opts: Options,
|
|
|
|
/// Current allocated length of the data store.
|
|
/// Note this is the number of `T`s, not the size in bytes.
|
|
pub len: usize,
|
|
|
|
_marker: PhantomData<fn() -> T>,
|
|
}
|
|
|
|
impl<T: Pod> Buffer<T> {
|
|
/// The raw GL buffer handle (for passing to render pass steps).
|
|
pub fn raw(&self) -> RawBuffer {
|
|
self.handle
|
|
}
|
|
|
|
/// Initialize a buffer with the given length pre-allocated.
|
|
pub fn init(gl: &Gl, opts: Options, len: usize) -> gl::Result<Self> {
|
|
let buffer = gl.create_buffer()?;
|
|
let this = Buffer {
|
|
gl: gl.clone(),
|
|
handle: buffer,
|
|
opts,
|
|
len,
|
|
_marker: PhantomData,
|
|
};
|
|
// On error `this` is dropped, which destroys the buffer.
|
|
gl.bind_buffer(opts.target, Some(buffer));
|
|
let r = gl.buffer_data_size(opts.target, len * size_of::<T>(), opts.usage);
|
|
gl.bind_buffer(opts.target, None);
|
|
r?;
|
|
Ok(this)
|
|
}
|
|
|
|
/// Init the buffer filled with the given data.
|
|
pub fn init_fill(gl: &Gl, opts: Options, data: &[T]) -> gl::Result<Self> {
|
|
let buffer = gl.create_buffer()?;
|
|
// NOTE: Zig sets `len = data.len * @sizeOf(T)` here, which is a bug
|
|
// (len is documented as a count of `T`s); we store the element count.
|
|
let this = Buffer {
|
|
gl: gl.clone(),
|
|
handle: buffer,
|
|
opts,
|
|
len: data.len(),
|
|
_marker: PhantomData,
|
|
};
|
|
gl.bind_buffer(opts.target, Some(buffer));
|
|
let r = gl.buffer_data(opts.target, bytemuck::cast_slice(data), opts.usage);
|
|
gl.bind_buffer(opts.target, None);
|
|
r?;
|
|
Ok(this)
|
|
}
|
|
|
|
/// Sync new contents to the buffer. The data is expected to be the
|
|
/// complete contents of the buffer. If the amount of data is larger
|
|
/// than the buffer length, the buffer will be reallocated.
|
|
///
|
|
/// If the amount of data is smaller than the buffer length, the
|
|
/// remaining data in the buffer is left untouched.
|
|
pub fn sync(&mut self, data: &[T]) -> gl::Result<()> {
|
|
self.sync_from_slices(&[data]).map(|_| ())
|
|
}
|
|
|
|
/// Like `Buffer::sync` but takes data from an array of slices, rather
|
|
/// than a single slice. Returns the number of items synced.
|
|
pub fn sync_from_slices(&mut self, lists: &[&[T]]) -> gl::Result<usize> {
|
|
let target = self.opts.target;
|
|
self.gl.bind_buffer(target, Some(self.handle));
|
|
let r = self.sync_bound(lists);
|
|
self.gl.bind_buffer(target, None);
|
|
r
|
|
}
|
|
|
|
fn sync_bound(&mut self, lists: &[&[T]]) -> gl::Result<usize> {
|
|
let target = self.opts.target;
|
|
let total_len: usize = lists.iter().map(|l| l.len()).sum();
|
|
|
|
// If we need more space than our buffer has, we need to reallocate.
|
|
if total_len > self.len {
|
|
// Reallocate the buffer to hold double what we require.
|
|
self.len = total_len * 2;
|
|
self.gl
|
|
.buffer_data_size(target, self.len * size_of::<T>(), self.opts.usage)?;
|
|
}
|
|
|
|
// We can fit within the buffer so we can just replace bytes.
|
|
let mut offset = 0;
|
|
for list in lists {
|
|
self.gl
|
|
.buffer_sub_data(target, offset, bytemuck::cast_slice(list))?;
|
|
offset += size_of_val(*list);
|
|
}
|
|
|
|
Ok(total_len)
|
|
}
|
|
}
|
|
|
|
impl<T: Pod> Drop for Buffer<T> {
|
|
fn drop(&mut self) {
|
|
self.gl.delete_buffer(self.handle);
|
|
}
|
|
}
|