# How Ghostty's Rendering Pipeline Uses Metal on macOS: Architecture and Implementation

> Explore how Ghostty's rendering pipeline leverages Metal on macOS for efficient GPU rendering. Learn about its architecture and implementation details.

- Repository: [Ghostty/ghostty](https://github.com/ghostty-org/ghostty)
- Tags: architecture
- Published: 2026-05-01

---

**Ghostty delegates all GPU rendering to Metal through a thin Swift UI layer that provides a `CAMetalLayer` backing, while the heavy lifting of vertex buffer construction, glyph atlas sampling, and draw call encoding happens entirely within the libghostty C library.**

Ghostty is a modern terminal emulator that leverages native GPU acceleration for high-performance text rendering. According to the ghostty-org/ghostty repository source code, Ghostty's rendering pipeline on macOS uses a hybrid architecture where SwiftUI handles window composition while Metal handles the actual pixel processing. This design keeps the UI layer declarative and platform-agnostic while maximizing rendering performance through low-level GPU access.

## Architecture Overview

The rendering pipeline follows a strict separation of concerns between the platform-specific UI layer and the core rendering engine. The Swift side is responsible only for creating the view hierarchy and forwarding geometry changes, while **libghostty** performs all actual graphics operations. This architecture allows Ghostty to use Metal on macOS, OpenGL on Linux, and maintain a consistent API across platforms.

The Swift layer creates a `CAMetalLayer`-backed view that acts as a rendering target, registers this surface with the C library, and forwards size and scale changes. Once initialized, libghostty takes full control of the Metal device, command queues, shaders, and draw loops.

## The Three-Stage Rendering Flow

Ghostty's rendering pipeline follows a precise initialization sequence that bridges SwiftUI with the C-based graphics engine.

### Stage 1: Metal Layer Initialization

The process begins when SwiftUI creates a view hierarchy that requires terminal rendering. In `macos/Sources/Ghostty/Surface View/SurfaceView_UIKit.swift`, the `SurfaceView` class (a subclass of `OSSurfaceView`) overrides the `layerClass` property to return `CAMetalLayer.self`【/macos/Sources/Ghostty/Surface%20View/SurfaceView_UIKit.swift#L76-L78】. This ensures the backing layer is a Metal-compatible surface rather than a standard CALayer.

For SwiftUI integration, Ghostty provides the `MetalView` helper in [`macos/Sources/Helpers/MetalView.swift`](https://github.com/ghostty-org/ghostty/blob/main/macos/Sources/Helpers/MetalView.swift). This wrapper hosts an `MTKView` that serves as the rendering target bridge【/macos/Sources/Helpers/MetalView.swift#L4-L10】. The same pattern appears in [`SurfaceView_AppKit.swift`](https://github.com/ghostty-org/ghostty/blob/main/SurfaceView_AppKit.swift) for AppKit-based applications, ensuring consistent behavior across UI frameworks.

### Stage 2: Surface Registration

Once the view creates its Metal layer, it instantiates a ghostty surface by calling the C API `ghostty_surface_new`. The constructor in [`SurfaceView_UIKit.swift`](https://github.com/ghostty-org/ghostty/blob/main/SurfaceView_UIKit.swift) builds the surface configuration and registers the view, storing the returned `ghostty_surface_t` pointer in the `_surface` property【/macos/Sources/Ghostty/Surface%20View/SurfaceView_UIKit.swift#L28-L31】.

This registration establishes the connection between the Swift view instance and the C rendering context. After this call, libghostty holds a reference to the `CAMetalLayer` and can access its drawable resources directly.

### Stage 3: Dynamic Geometry Synchronization

Terminal windows must handle resize events, fullscreen transitions, and DPI changes without dropping frames. Ghostty accomplishes this through the `sizeDidChange(_:)` method in [`SurfaceView_UIKit.swift`](https://github.com/ghostty-org/ghostty/blob/main/SurfaceView_UIKit.swift), which captures the view size and screen `contentScaleFactor`, then forwards these values to libghostty via `ghostty_surface_set_content_scale` and `ghostty_surface_set_size`【/macos/Sources/Ghostty/Surface%20View/SurfaceView_UIKit.swift#L61-L71】.

The corresponding AppKit implementation in [`SurfaceView_AppKit.swift`](https://github.com/ghostty-org/ghostty/blob/main/SurfaceView_AppKit.swift) performs identical forwarding, ensuring consistent behavior across macOS UI frameworks. These calls update the framebuffer dimensions and pixel density used by the Metal shaders to maintain crisp text rendering on Retina displays.

## Metal Rendering Inside libghostty

Once the surface is registered, libghostty drives the Metal pipeline independently of the Swift runtime. The C library manages the complete GPU resource lifecycle:

- **Device and Command Queue**: libghostty obtains an `MTLDevice` reference from the `CAMetalLayer` and creates a dedicated `MTLCommandQueue` for submitting rendering commands.
- **Render Pipeline State**: The engine builds a `MTLRenderPipelineState` encoding a vertex shader that maps terminal cell coordinates to clip-space positions, paired with a fragment shader that samples from a pre-rendered glyph bitmap atlas.
- **Draw Loop**: On each frame, the engine updates a texture with the latest cell contents, encodes draw calls for changed rows only (damage tracking), and presents the drawable obtained from `CAMetalLayer`. The loop triggers either through an internal timer or explicit `ghostty_surface_render` calls following new input.
- **Synchronization**: A semaphore ensures GPU work completes before the next update, preventing tearing while maintaining UI responsiveness.

## Implementation Examples

The following code illustrates the integration points between SwiftUI and libghostty's Metal backend:

```swift
// Create a Metal-backed view in SwiftUI
struct TerminalView: View {
    var body: some View {
        // MetalView hosts an MTKView that libghostty draws into
        MetalView<MTKView>()
            .frame(minWidth: 400, minHeight: 300)
    }
}

```

```swift
// The underlying UIView/NSView that provides the CAMetalLayer
class SurfaceView: OSSurfaceView {
    // ... init registers the C surface ...
    override class var layerClass: AnyClass { CAMetalLayer.self }

    override func sizeDidChange(_ size: CGSize) {
        // Forward geometry to libghostty
        ghostty_surface_set_content_scale(surface, scale, scale)
        ghostty_surface_set_size(surface,
                                 UInt32(size.width * scale),
                                 UInt32(size.height * scale))
    }
}

```

```c
// libghostty snippet (simplified) – creates the Metal pipeline
static void create_metal_renderer(ghostty_surface_t *surf) {
    id<MTLDevice> device = surf->metal_layer.device;
    surf->commandQueue = [device newCommandQueue];
    // Build render pipeline, load glyph atlas texture, etc.
}

```

```c
// libghostty draw loop – called on every frame
static void render_surface(ghostty_surface_t *surf) {
    id<CAMetalDrawable> drawable = [surf->metal_layer nextDrawable];
    id<MTLCommandBuffer> cmd = [surf->commandQueue commandBuffer];
    // Encode draw calls for changed terminal rows
    // ...
    [cmd presentDrawable:drawable];
    [cmd commit];
}

```

## Key Source Files

Understanding Ghostty's rendering pipeline requires familiarity with these specific files in the ghostty-org/ghostty repository:

- **[`macos/Sources/Helpers/MetalView.swift`](https://github.com/ghostty-org/ghostty/blob/main/macos/Sources/Helpers/MetalView.swift)**: SwiftUI wrapper that hosts an `MTKView` used as the rendering target.
- **`macos/Sources/Ghostty/Surface View/SurfaceView_UIKit.swift`**: UIKit subclass providing `CAMetalLayer` and forwarding size/focus updates to libghostty.
- **`macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift`**: AppKit counterpart for the macOS app bundle.
- **`macos/Sources/Ghostty/Surface View/SurfaceView.swift`**: SwiftUI-compatible `OSViewRepresentable` tying the view hierarchy to the C surface.
- **[`include/ghostty/ghostty_surface.h`](https://github.com/ghostty-org/ghostty/blob/main/include/ghostty/ghostty_surface.h)**: C header defining the `ghostty_surface_*` API used by Swift to configure the Metal renderer.

## Summary

Ghostty's Metal rendering pipeline achieves high-performance terminal graphics through a clean separation between UI and rendering concerns:

- The Swift layer creates a `CAMetalLayer`-backed view and forwards geometry changes, but performs no drawing itself.
- Surface registration via `ghostty_surface_new` establishes the bridge to libghostty's C rendering engine.
- libghostty manages the complete Metal pipeline including device selection, shader execution, glyph atlas sampling, and frame synchronization.
- This architecture enables platform-specific optimizations while sharing the core rendering logic across macOS, Linux, and iOS builds.

## Frequently Asked Questions

### Why does Ghostty use Metal instead of Core Graphics for terminal rendering?

**Metal provides significantly lower overhead and higher throughput for high-frequency text updates compared to Core Graphics.** According to the source code in `ghostty-org/ghostty`, the Metal pipeline allows libghostty to submit draw calls directly to the GPU using pre-computed glyph atlases and efficient vertex buffers, whereas Core Graphics would require software rasterization for each frame. This approach achieves consistent 60fps+ performance even with rapid terminal scrollback and complex Unicode glyph rendering.

### How does Ghostty handle Retina (high-DPI) display scaling?

**Ghostty forwards the screen's `contentScaleFactor` to libghostty via `ghostty_surface_set_content_scale`.** When `sizeDidChange(_:)` executes in [`SurfaceView_UIKit.swift`](https://github.com/ghostty-org/ghostty/blob/main/SurfaceView_UIKit.swift), it multiplies the view dimensions by the scale factor before calling `ghostty_surface_set_size`, ensuring the Metal framebuffer matches the physical pixel resolution. The fragment shader then samples from high-resolution glyph atlases that respect the display's native DPI, producing crisp text on Retina displays.

### Is the Metal rendering code shared with the Linux version of Ghostty?

**No, the Metal-specific implementation is macOS-only, but the architecture is shared.** The libghostty C library abstracts the graphics API behind the `ghostty_surface_*` interface. While macOS uses the Metal backend described here, the Linux build links against OpenGL or Vulkan implementations using the same C API surface registration calls. Only the `CAMetalLayer` setup code in the Swift files is platform-specific to macOS.

### What triggers a redraw in Ghostty's Metal pipeline?

**Redraws trigger via explicit `ghostty_surface_render` calls or internal surface invalidations.** When terminal content changes (new output, cursor movement, or scrollback), libghostty marks affected regions as "damaged" and schedules a render pass. The draw loop acquires the next drawable from `CAMetalLayer`, encodes commands for dirty rectangles only, and presents the frame. This partial redraw optimization minimizes GPU workload by avoiding full-screen updates when only a few cells change.