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)
166 lines
5.0 KiB
Rust
166 lines
5.0 KiB
Rust
//! Wrapper for handling render passes (`opengl/RenderPass.zig`).
|
|
use super::{
|
|
OpenGL,
|
|
gl::{Attachment as GlAttachment, BufferTarget, FramebufferTarget, Gl},
|
|
};
|
|
|
|
/// What a color attachment renders into.
|
|
pub type AttachmentTarget<'a> = crate::graphics_api::AttachmentTarget<'a, OpenGL>;
|
|
|
|
/// Describes a color attachment.
|
|
pub type Attachment<'a> = crate::graphics_api::Attachment<'a, OpenGL>;
|
|
|
|
/// Options for beginning a render pass.
|
|
#[derive(Clone, Copy)]
|
|
pub struct Options<'a> {
|
|
/// Color attachments for this render pass.
|
|
pub attachments: &'a [Attachment<'a>],
|
|
}
|
|
|
|
/// Describes the draw call for a step.
|
|
pub use crate::graphics_api::Draw;
|
|
|
|
/// Describes a step in a render pass.
|
|
pub type Step<'a> = crate::graphics_api::Step<'a, OpenGL>;
|
|
|
|
pub struct RenderPass<'a> {
|
|
gl: Gl,
|
|
attachments: &'a [Attachment<'a>],
|
|
step_number: usize,
|
|
}
|
|
|
|
impl<'a> RenderPass<'a> {
|
|
/// Begin a render pass.
|
|
pub fn begin(gl: &Gl, opts: Options<'a>) -> RenderPass<'a> {
|
|
RenderPass {
|
|
gl: gl.clone(),
|
|
attachments: opts.attachments,
|
|
step_number: 0,
|
|
}
|
|
}
|
|
|
|
/// Add a step to this render pass.
|
|
///
|
|
/// TODO: Errors are silently ignored in this function, maybe they shouldn't be?
|
|
/// (Same as upstream; failures are logged at debug level here.)
|
|
pub fn step(&mut self, s: &Step<'_>) {
|
|
if s.draw.instance_count == 0 {
|
|
return;
|
|
}
|
|
if self.step_(s).is_none() {
|
|
log::debug!("render pass step failed");
|
|
}
|
|
self.step_number += 1;
|
|
}
|
|
|
|
fn step_(&mut self, s: &Step<'_>) -> Option<()> {
|
|
let gl = self.gl.clone();
|
|
|
|
gl.use_program(Some(s.pipeline.program)).ok()?;
|
|
// Unbinding on all exit paths.
|
|
let r = (|| {
|
|
gl.bind_vertex_array(Some(s.pipeline.vao)).ok()?;
|
|
let r = self.step_with_vao(s);
|
|
let _ = gl.bind_vertex_array(None);
|
|
r
|
|
})();
|
|
let _ = gl.use_program(None);
|
|
r
|
|
}
|
|
|
|
fn step_with_vao(&mut self, s: &Step<'_>) -> Option<()> {
|
|
let gl = self.gl.clone();
|
|
|
|
let fbobind = match &self.attachments[0].target {
|
|
AttachmentTarget::Target(t) => {
|
|
gl.bind_framebuffer(t.framebuffer(), FramebufferTarget::Framebuffer)
|
|
}
|
|
AttachmentTarget::Texture(t) => {
|
|
let fbobind = gl.bind_framebuffer(s.pipeline.fbo, FramebufferTarget::Framebuffer);
|
|
if gl
|
|
.framebuffer_texture_2d(&fbobind, GlAttachment::Color0, t.target, t.raw(), 0)
|
|
.is_err()
|
|
{
|
|
gl.unbind_framebuffer(fbobind);
|
|
return None;
|
|
}
|
|
fbobind
|
|
}
|
|
};
|
|
|
|
let r = self.step_with_fbo(s);
|
|
self.gl.unbind_framebuffer(fbobind);
|
|
r
|
|
}
|
|
|
|
fn step_with_fbo(&mut self, s: &Step<'_>) -> Option<()> {
|
|
let gl = &self.gl;
|
|
|
|
// If we have a clear color and this is the
|
|
// first step in the pass, go ahead and clear.
|
|
if self.step_number == 0
|
|
&& let Some(c) = self.attachments[0].clear_color
|
|
{
|
|
gl.clear_color(c[0], c[1], c[2], c[3]);
|
|
gl.clear(glow::COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
// Bind the uniform buffer we bind at index 1 to align with Metal.
|
|
if let Some(ubo) = s.uniforms {
|
|
gl.bind_buffer_base(BufferTarget::Uniform, 1, ubo).ok()?;
|
|
}
|
|
|
|
// Bind relevant texture units.
|
|
for (i, t) in s.textures.iter().enumerate() {
|
|
if let Some(tex) = t {
|
|
gl.active_texture(i as u32).ok()?;
|
|
gl.bind_texture(tex.target, Some(tex.raw())).ok()?;
|
|
}
|
|
}
|
|
|
|
// Bind relevant samplers.
|
|
for (i, sampler) in s.samplers.iter().enumerate() {
|
|
if let Some(sampler) = sampler {
|
|
gl.bind_sampler(i as u32, sampler.raw()).ok()?;
|
|
}
|
|
}
|
|
|
|
// Bind 0th buffer as the vertex buffer,
|
|
// and bind the rest as storage buffers.
|
|
if !s.buffers.is_empty() {
|
|
if let Some(vbo) = s.buffers[0] {
|
|
gl.bind_vertex_buffer(0, vbo, 0, s.pipeline.stride as i32)
|
|
.ok()?;
|
|
}
|
|
|
|
for (i, b) in s.buffers.iter().enumerate().skip(1) {
|
|
if let Some(buf) = b {
|
|
gl.bind_buffer_base(BufferTarget::Storage, i as u32, *buf)
|
|
.ok()?;
|
|
}
|
|
}
|
|
}
|
|
|
|
if s.pipeline.blending_enabled {
|
|
gl.enable(glow::BLEND).ok()?;
|
|
gl.blend_func(glow::ONE, glow::ONE_MINUS_SRC_ALPHA).ok()?;
|
|
} else {
|
|
gl.disable(glow::BLEND).ok()?;
|
|
}
|
|
|
|
gl.draw_arrays_instanced(
|
|
s.draw.ty,
|
|
0,
|
|
s.draw.vertex_count as i32,
|
|
s.draw.instance_count as i32,
|
|
)
|
|
.ok()
|
|
}
|
|
|
|
/// Complete this render pass.
|
|
/// This struct can no longer be used after calling this.
|
|
pub fn complete(self) {
|
|
self.gl.flush();
|
|
}
|
|
}
|