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:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
mod dispatcher;
|
||||
mod display;
|
||||
mod display_colorspace;
|
||||
mod display_link;
|
||||
mod events;
|
||||
mod haptic_feedback;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user