feat(macos): tag the Metal layer with the display colorspace on demand

When the app self-manages the display transform (OAK_MACOS_LAYER_
COLORSPACE=display set at startup), the CAMetalLayer is tagged with the
main display's colorspace so ColorSync passes the already-transformed
pixels through — without the tag the OS would re-correct them (double
correction). The CoreGraphics FFI lives in its own module because the
build script forwards metal_renderer.rs FFI declarations into the
Metal shader header.
This commit is contained in:
2026-08-21 02:17:16 +08:00
parent 3325521a85
commit 4067918c67
3 changed files with 54 additions and 0 deletions
@@ -0,0 +1,39 @@
// GPUI macOS platform - GPUI is licensed under the Apache License, Version 2.0
// (see the gpui submodule's license).
//! The main display's colorspace as a raw `CGColorSpaceRef`, for the
//! color-management coordination in `metal_renderer` (when the app
//! self-manages the display transform, the CAMetalLayer is tagged with the
//! display's colorspace so ColorSync passes the pixels through).
//!
//! This lives in its own module because the `metal_renderer` build script
//! scans `metal_renderer.rs` for FFI declarations and forwards them into
//! the Metal shader header — raw extern blocks must not be added there.
use std::ffi::c_void;
#[link(name = "CoreGraphics", kind = "framework")]
unsafe extern "C" {
fn CGMainDisplayID() -> u32;
fn CGDisplayCopyColorSpace(display: u32) -> *mut c_void;
}
#[link(name = "CoreFoundation", kind = "framework")]
unsafe extern "C" {
fn CFRelease(obj: *const c_void);
}
/// The main display's colorspace as a raw pointer (caller's
/// responsibility to keep it alive for the msg_send call; released here
/// after the layer call returns — the layer retains it). `None` when the
/// display has no colorspace (headless).
pub fn with_main_display_colorspace(f: impl FnOnce(*mut c_void)) {
unsafe {
let space = CGDisplayCopyColorSpace(CGMainDisplayID());
if space.is_null() {
return;
}
f(space);
CFRelease(space);
}
}
+1
View File
@@ -6,6 +6,7 @@
mod dispatcher;
mod display;
mod display_colorspace;
mod display_link;
mod events;
mod haptic_feedback;
+14
View File
@@ -251,6 +251,20 @@ impl MetalRenderer {
];
}
// Color management coordination: when the app self-manages the
// display transform (OAK_MACOS_LAYER_COLORSPACE=display, set at
// startup when display-ICC color management is active), tag the
// layer with the DISPLAY's colorspace so ColorSync's mapping
// becomes a pass-through — otherwise the OS would re-correct our
// already-corrected pixels (double correction).
if std::env::var_os("OAK_MACOS_LAYER_COLORSPACE").as_deref()
== Some(std::ffi::OsStr::new("display"))
{
crate::display_colorspace::with_main_display_colorspace(|space| unsafe {
let _: () = msg_send![&*layer, setColorspace: space];
});
}
Self::new_internal(device, Some(layer), !transparent, instance_buffer_pool)
}