color: non-sRGB preview, per-monitor display ICC, pipeline hardening
Preview now follows the project output colorspace end to end: the display chain derives its content space from the project's OutputColorSpec instead of a hardcoded sRGB name, self-managed ICC transforms go through an XYZ D65 interchange stage (OCIO cie_xyz_d65_interchange) for non-sRGB targets, and the platform layer declares the content colorspace (gpui submodule bump). macOS defaults to OS-managed (fixes wide-gamut UI oversaturation); Windows ACM warns once on non-sRGB targets. Multi-monitor: the display ICC is looked up per the window's current screen (macOS display id, Windows per-monitor DC, X11 RandR output profile) with a throttled poll that invalidates frame caches on moves. Pipeline precision: 10-bit+ sources fall back to YUV444P16LE + a Rust matrix conversion when swscale lacks F32 output (no more 8-bit truncation); BT.709/2020 SDR decodes with BT.1886 gamma 2.4 instead of the sRGB EOTF; working-space compositing no longer clamps RGB to [0,1] (alpha still clamped); the output node clamps to the target gamut; frames without colorimetry metadata convert with BT.709 defaults (warned once) instead of passing through; scopes read the output-colorspace signal on both F32 paths. Also: only emit rerun-if-changed for .env when it exists (a missing file made every build fully dirty).
This commit is contained in:
@@ -576,6 +576,10 @@ impl WorkerSession {
|
||||
};
|
||||
match oak_node::serializer::load_with_id_map(&content) {
|
||||
Ok((project, id_map)) => {
|
||||
let (working, output_spec) = {
|
||||
let guard = project.lock().unwrap_or_else(|e| e.into_inner());
|
||||
(guard.working_color_space(), guard.output_color_spec())
|
||||
};
|
||||
let project_uuid = project
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
@@ -588,6 +592,10 @@ impl WorkerSession {
|
||||
id_map,
|
||||
project_copy: 0,
|
||||
});
|
||||
// The project's color pipeline properties drive this
|
||||
// process's input/output transforms (the oakrender
|
||||
// process global read by eval + the output node).
|
||||
oak_render::color::set_pipeline_color_settings(working, output_spec);
|
||||
// A fresh graph snapshot can change what any viewer
|
||||
// identity renders; cached pixels from the previous
|
||||
// graph must not be served (M16 S2 frame cache).
|
||||
@@ -1098,8 +1106,12 @@ impl WorkerSession {
|
||||
// BGRA8: the F32 pipeline frame comes from the cache or the
|
||||
// session scratch, then converts into the slot (the end-of-pipe
|
||||
// format convert is not an extra frame copy, design §3.1).
|
||||
match &cached {
|
||||
// Before quantization the output node runs: working space
|
||||
// (ACEScg) → the project's output colorspace, so the 8-bit
|
||||
// pixels carry gamma-encoded display values, not linear light.
|
||||
match cached.as_mut() {
|
||||
Some(c) => {
|
||||
apply_output_node(c, (w * h) as usize);
|
||||
convert_f32_rgba_to_bgra8(&c[..f32_need], &mut dst[..dst_need]);
|
||||
}
|
||||
None => {
|
||||
@@ -1116,6 +1128,7 @@ impl WorkerSession {
|
||||
)?;
|
||||
self.frame_cache
|
||||
.insert(key, self.f32_scratch[..f32_need].to_vec());
|
||||
apply_output_node(&mut self.f32_scratch, (w * h) as usize);
|
||||
convert_f32_rgba_to_bgra8(&self.f32_scratch[..f32_need], &mut dst[..dst_need]);
|
||||
}
|
||||
}
|
||||
@@ -1313,6 +1326,22 @@ fn convert_f32_rgba_to_bgra8(src: &[u8], dst: &mut [u8]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The output node for the BGRA8 delivery path: convert the first
|
||||
/// `pixels` pixels of an F32 RGBA byte buffer from the pipeline working
|
||||
/// space to the project's output colorspace (in place), so the 8-bit
|
||||
/// quantization encodes display-referred values instead of linear light.
|
||||
/// A no-op in the legacy sRGB working space (content already is
|
||||
/// display-referred sRGB).
|
||||
fn apply_output_node(bytes: &mut [u8], pixels: usize) {
|
||||
if oak_render::color::pipeline_working_space()
|
||||
== oak_common::colormath::WorkingColorSpace::SrgbLegacy
|
||||
{
|
||||
return;
|
||||
}
|
||||
let spec = oak_render::color::pipeline_output_spec();
|
||||
oak_common::colormath::acescg_to_output_bytes(bytes, pixels, spec);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user