Merge pull request #399 from mahkoh/jorth/cm-4
color-management: add more capabilities
This commit is contained in:
commit
9e20e32338
63 changed files with 2509 additions and 330 deletions
|
|
@ -451,7 +451,9 @@ impl MetalConnector {
|
|||
};
|
||||
self.state.present_hardware_cursor(node, &mut c);
|
||||
if c.cursor_swap_buffer {
|
||||
c.sync_file = c.cursor_buffer.copy_to_dev(c.sync_file)?;
|
||||
c.sync_file = c
|
||||
.cursor_buffer
|
||||
.copy_to_dev(&self.state.color_manager, c.sync_file)?;
|
||||
}
|
||||
self.cursor_swap_buffer.set(c.cursor_swap_buffer);
|
||||
if c.sync_file.is_some() {
|
||||
|
|
@ -558,6 +560,10 @@ impl MetalConnector {
|
|||
}
|
||||
return None;
|
||||
};
|
||||
if ct.cd.id != self.state.color_manager.srgb_srgb().id {
|
||||
// Direct scanout requires identical color descriptions.
|
||||
return None;
|
||||
}
|
||||
if ct.alpha.is_some() {
|
||||
// Direct scanout with alpha factor is not supported.
|
||||
return None;
|
||||
|
|
@ -747,12 +753,14 @@ impl MetalConnector {
|
|||
.perform_render_pass(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::Explicit,
|
||||
self.state.color_manager.srgb_srgb(),
|
||||
&latched.pass,
|
||||
&latched.damage,
|
||||
buffer.blend_buffer.as_ref(),
|
||||
self.state.color_manager.srgb_linear(),
|
||||
)
|
||||
.map_err(MetalError::RenderFrame)?;
|
||||
sync_file = buffer.copy_to_dev(sf)?;
|
||||
sync_file = buffer.copy_to_dev(&self.state.color_manager, sf)?;
|
||||
fb = buffer.drm.clone();
|
||||
tex = buffer.render_tex.clone();
|
||||
}
|
||||
|
|
@ -792,6 +800,7 @@ impl MetalConnector {
|
|||
None => {
|
||||
output.perform_screencopies(
|
||||
&fb.tex,
|
||||
self.state.color_manager.srgb_srgb(),
|
||||
None,
|
||||
&AcquireSync::Unnecessary,
|
||||
ReleaseSync::None,
|
||||
|
|
@ -804,6 +813,7 @@ impl MetalConnector {
|
|||
Some(dsd) => {
|
||||
output.perform_screencopies(
|
||||
&dsd.tex,
|
||||
self.state.color_manager.srgb_srgb(),
|
||||
dsd.resv.as_ref(),
|
||||
&dsd.acquire_sync,
|
||||
dsd.release_sync,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use {
|
|||
POST_COMMIT_MARGIN_DELTA, PresentFb,
|
||||
},
|
||||
},
|
||||
cmm::cmm_manager::ColorManager,
|
||||
drm_feedback::DrmFeedback,
|
||||
edid::{CtaDataBlock, Descriptor, EdidExtension},
|
||||
format::{ARGB8888, Format, XRGB8888},
|
||||
|
|
@ -2704,7 +2705,11 @@ impl MetalBackend {
|
|||
Err(e) => return Err(MetalError::ImportFb(e)),
|
||||
};
|
||||
dev_fb
|
||||
.clear(AcquireSync::Unnecessary, ReleaseSync::None)
|
||||
.clear(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::None,
|
||||
self.state.color_manager.srgb_srgb(),
|
||||
)
|
||||
.map_err(MetalError::Clear)?;
|
||||
let (dev_tex, render_tex, render_fb, render_bo) = if dev.id == render_ctx.dev_id {
|
||||
let render_tex = match dev_img.to_texture() {
|
||||
|
|
@ -2758,7 +2763,11 @@ impl MetalBackend {
|
|||
Err(e) => return Err(MetalError::ImportFb(e)),
|
||||
};
|
||||
render_fb
|
||||
.clear(AcquireSync::Unnecessary, ReleaseSync::None)
|
||||
.clear(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::None,
|
||||
self.state.color_manager.srgb_srgb(),
|
||||
)
|
||||
.map_err(MetalError::Clear)?;
|
||||
let render_tex = match render_img.to_texture() {
|
||||
Ok(fb) => fb,
|
||||
|
|
@ -3030,7 +3039,11 @@ impl RenderBuffer {
|
|||
.unwrap_or_else(|| self.dev_fb.clone())
|
||||
}
|
||||
|
||||
pub fn copy_to_dev(&self, sync_file: Option<SyncFile>) -> Result<Option<SyncFile>, MetalError> {
|
||||
pub fn copy_to_dev(
|
||||
&self,
|
||||
cm: &ColorManager,
|
||||
sync_file: Option<SyncFile>,
|
||||
) -> Result<Option<SyncFile>, MetalError> {
|
||||
let Some(tex) = &self.dev_tex else {
|
||||
return Ok(sync_file);
|
||||
};
|
||||
|
|
@ -3038,7 +3051,9 @@ impl RenderBuffer {
|
|||
.copy_texture(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::Explicit,
|
||||
cm.srgb_srgb(),
|
||||
tex,
|
||||
cm.srgb_srgb(),
|
||||
None,
|
||||
AcquireSync::from_sync_file(sync_file),
|
||||
ReleaseSync::None,
|
||||
|
|
|
|||
|
|
@ -751,11 +751,13 @@ impl XBackend {
|
|||
let res = self.state.present_output(
|
||||
&node,
|
||||
&image.fb.get(),
|
||||
self.state.color_manager.srgb_srgb(),
|
||||
AcquireSync::Implicit,
|
||||
ReleaseSync::Implicit,
|
||||
&image.tex.get(),
|
||||
true,
|
||||
None,
|
||||
self.state.color_manager.srgb_linear(),
|
||||
);
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not render screen: {}", ErrorFmt(e));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::{GlobalArgs, color::parse_color, duration::parse_duration},
|
||||
theme::TransferFunction,
|
||||
cmm::cmm_transfer_function::TransferFunction,
|
||||
tools::tool_client::{ToolClient, with_tool_client},
|
||||
wire::jay_damage_tracking::{SetVisualizerColor, SetVisualizerDecay, SetVisualizerEnabled},
|
||||
},
|
||||
|
|
|
|||
8
src/cmm.rs
Normal file
8
src/cmm.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
pub mod cmm_description;
|
||||
pub mod cmm_luminance;
|
||||
pub mod cmm_manager;
|
||||
pub mod cmm_primaries;
|
||||
#[cfg(test)]
|
||||
mod cmm_tests;
|
||||
pub mod cmm_transfer_function;
|
||||
pub mod cmm_transform;
|
||||
78
src/cmm/cmm_description.rs
Normal file
78
src/cmm/cmm_description.rs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::{
|
||||
cmm_luminance::{Luminance, white_balance},
|
||||
cmm_manager::Shared,
|
||||
cmm_primaries::{NamedPrimaries, Primaries},
|
||||
cmm_transfer_function::TransferFunction,
|
||||
cmm_transform::{ColorMatrix, Local, Xyz, bradford_adjustment},
|
||||
},
|
||||
utils::free_list::FreeList,
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
linear_ids!(LinearColorDescriptionIds, LinearColorDescriptionId, u64);
|
||||
|
||||
pub type ColorDescriptionIds = FreeList<ColorDescriptionId, 3>;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct ColorDescriptionId(u32);
|
||||
|
||||
impl From<u32> for ColorDescriptionId {
|
||||
fn from(value: u32) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ColorDescriptionId> for u32 {
|
||||
fn from(value: ColorDescriptionId) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LinearColorDescription {
|
||||
pub id: LinearColorDescriptionId,
|
||||
pub primaries: Primaries,
|
||||
pub xyz_from_local: ColorMatrix<Xyz, Local>,
|
||||
pub local_from_xyz: ColorMatrix<Local, Xyz>,
|
||||
pub luminance: Luminance,
|
||||
pub(super) shared: Rc<Shared>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ColorDescription {
|
||||
pub id: ColorDescriptionId,
|
||||
pub linear: Rc<LinearColorDescription>,
|
||||
#[expect(dead_code)]
|
||||
pub named_primaries: Option<NamedPrimaries>,
|
||||
pub transfer_function: TransferFunction,
|
||||
pub(super) shared: Rc<Shared>,
|
||||
}
|
||||
|
||||
impl LinearColorDescription {
|
||||
pub fn color_transform(&self, target: &Self) -> ColorMatrix {
|
||||
let mut mat = target.local_from_xyz;
|
||||
if self.luminance != target.luminance {
|
||||
mat *= white_balance(&self.luminance, &target.luminance, target.primaries.wp);
|
||||
}
|
||||
if self.primaries.wp != target.primaries.wp {
|
||||
mat *= bradford_adjustment(self.primaries.wp, target.primaries.wp);
|
||||
}
|
||||
mat * self.xyz_from_local
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LinearColorDescription {
|
||||
fn drop(&mut self) {
|
||||
self.shared.dead_linear.fetch_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ColorDescription {
|
||||
fn drop(&mut self) {
|
||||
self.shared.dead_complete.fetch_add(1);
|
||||
self.shared.complete_ids.release(self.id);
|
||||
}
|
||||
}
|
||||
71
src/cmm/cmm_luminance.rs
Normal file
71
src/cmm/cmm_luminance.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
use crate::{
|
||||
cmm::cmm_transform::{ColorMatrix, Xyz},
|
||||
utils::ordered_float::F64,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct Luminance {
|
||||
pub min: F64,
|
||||
pub max: F64,
|
||||
pub white: F64,
|
||||
}
|
||||
|
||||
impl Luminance {
|
||||
pub const SRGB: Self = Self {
|
||||
min: F64(0.2),
|
||||
max: F64(80.0),
|
||||
white: F64(80.0),
|
||||
};
|
||||
|
||||
pub const BT1886: Self = Self {
|
||||
min: F64(0.01),
|
||||
max: F64(100.0),
|
||||
white: F64(100.0),
|
||||
};
|
||||
|
||||
pub const ST2084_PQ: Self = Self {
|
||||
min: F64(0.0),
|
||||
max: F64(10000.0),
|
||||
white: F64(203.0),
|
||||
};
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub const HLG: Self = Self {
|
||||
min: F64(0.005),
|
||||
max: F64(1000.0),
|
||||
white: F64(203.0),
|
||||
};
|
||||
|
||||
pub const WINDOWS_SCRGB: Self = Self {
|
||||
min: Self::ST2084_PQ.min,
|
||||
max: Self::ST2084_PQ.max,
|
||||
// This causes the white balance formula (with target ST2084_PQ) to simplify to
|
||||
// `Y * 80 / 10000`, meaning that sRGB pure white maps to a luminance of
|
||||
// 80 cd/m^2.
|
||||
white: F64(Self::ST2084_PQ.white.0 / 80.0 * Self::ST2084_PQ.max.0),
|
||||
};
|
||||
}
|
||||
|
||||
impl Default for Luminance {
|
||||
fn default() -> Self {
|
||||
Self::SRGB
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(non_snake_case)]
|
||||
pub fn white_balance(from: &Luminance, to: &Luminance, w_to: (F64, F64)) -> ColorMatrix<Xyz, Xyz> {
|
||||
let a = ((from.max - from.min) / (to.max - to.min) * (to.white - from.min)
|
||||
/ (from.white - from.min))
|
||||
.0;
|
||||
let d = ((from.min - to.min) / (to.max - to.min)).0.max(0.0);
|
||||
let s = a - d;
|
||||
let (F64(x_to), F64(y_to)) = w_to;
|
||||
let X_to = x_to / y_to;
|
||||
let Y_to = 1.0;
|
||||
let Z_to = (1.0 - x_to - y_to) / y_to;
|
||||
ColorMatrix::new([
|
||||
[s, 0.0, 0.0, d * X_to],
|
||||
[0.0, s, 0.0, d * Y_to],
|
||||
[0.0, 0.0, s, d * Z_to],
|
||||
])
|
||||
}
|
||||
200
src/cmm/cmm_manager.rs
Normal file
200
src/cmm/cmm_manager.rs
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::{
|
||||
cmm_description::{
|
||||
ColorDescription, ColorDescriptionIds, LinearColorDescription,
|
||||
LinearColorDescriptionId, LinearColorDescriptionIds,
|
||||
},
|
||||
cmm_luminance::Luminance,
|
||||
cmm_primaries::{NamedPrimaries, Primaries},
|
||||
cmm_transfer_function::TransferFunction,
|
||||
},
|
||||
utils::{copyhashmap::CopyHashMap, numcell::NumCell},
|
||||
},
|
||||
std::rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
pub struct ColorManager {
|
||||
linear_ids: LinearColorDescriptionIds,
|
||||
linear_descriptions: CopyHashMap<LinearDescriptionKey, Weak<LinearColorDescription>>,
|
||||
complete_descriptions: CopyHashMap<CompleteDescriptionKey, Weak<ColorDescription>>,
|
||||
shared: Rc<Shared>,
|
||||
srgb_srgb: Rc<ColorDescription>,
|
||||
srgb_linear: Rc<ColorDescription>,
|
||||
windows_scrgb: Rc<ColorDescription>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct Shared {
|
||||
pub(super) dead_linear: NumCell<usize>,
|
||||
pub(super) dead_complete: NumCell<usize>,
|
||||
pub(super) complete_ids: ColorDescriptionIds,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct LinearDescriptionKey {
|
||||
primaries: Primaries,
|
||||
luminance: Luminance,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct CompleteDescriptionKey {
|
||||
linear: LinearColorDescriptionId,
|
||||
named_primaries: Option<NamedPrimaries>,
|
||||
transfer_function: TransferFunction,
|
||||
}
|
||||
|
||||
impl ColorManager {
|
||||
pub fn new() -> Rc<Self> {
|
||||
let linear_ids = LinearColorDescriptionIds::default();
|
||||
let linear_descriptions = CopyHashMap::default();
|
||||
let complete_descriptions = CopyHashMap::default();
|
||||
let shared = Rc::new(Shared::default());
|
||||
let _ = shared.complete_ids.acquire();
|
||||
let srgb_srgb = get_description(
|
||||
&shared,
|
||||
&linear_descriptions,
|
||||
&complete_descriptions,
|
||||
&linear_ids,
|
||||
Some(NamedPrimaries::Srgb),
|
||||
Primaries::SRGB,
|
||||
Luminance::SRGB,
|
||||
TransferFunction::Srgb,
|
||||
);
|
||||
let srgb_linear = get_description(
|
||||
&shared,
|
||||
&linear_descriptions,
|
||||
&complete_descriptions,
|
||||
&linear_ids,
|
||||
Some(NamedPrimaries::Srgb),
|
||||
Primaries::SRGB,
|
||||
Luminance::SRGB,
|
||||
TransferFunction::Linear,
|
||||
);
|
||||
let windows_scrgb = get_description(
|
||||
&shared,
|
||||
&linear_descriptions,
|
||||
&complete_descriptions,
|
||||
&linear_ids,
|
||||
Some(NamedPrimaries::Srgb),
|
||||
Primaries::SRGB,
|
||||
Luminance::WINDOWS_SCRGB,
|
||||
TransferFunction::Linear,
|
||||
);
|
||||
Rc::new(Self {
|
||||
linear_ids,
|
||||
linear_descriptions,
|
||||
complete_descriptions,
|
||||
shared,
|
||||
srgb_srgb,
|
||||
srgb_linear,
|
||||
windows_scrgb,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn srgb_srgb(&self) -> &Rc<ColorDescription> {
|
||||
&self.srgb_srgb
|
||||
}
|
||||
|
||||
pub fn srgb_linear(&self) -> &Rc<ColorDescription> {
|
||||
&self.srgb_linear
|
||||
}
|
||||
|
||||
pub fn windows_scrgb(&self) -> &Rc<ColorDescription> {
|
||||
&self.windows_scrgb
|
||||
}
|
||||
|
||||
pub fn get_description(
|
||||
self: &Rc<Self>,
|
||||
named_primaries: Option<NamedPrimaries>,
|
||||
primaries: Primaries,
|
||||
luminance: Luminance,
|
||||
transfer_function: TransferFunction,
|
||||
) -> Rc<ColorDescription> {
|
||||
get_description(
|
||||
&self.shared,
|
||||
&self.linear_descriptions,
|
||||
&self.complete_descriptions,
|
||||
&self.linear_ids,
|
||||
named_primaries,
|
||||
primaries,
|
||||
luminance,
|
||||
transfer_function,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_description(
|
||||
shared: &Rc<Shared>,
|
||||
linear_descriptions: &CopyHashMap<LinearDescriptionKey, Weak<LinearColorDescription>>,
|
||||
complete_descriptions: &CopyHashMap<CompleteDescriptionKey, Weak<ColorDescription>>,
|
||||
linear_ids: &LinearColorDescriptionIds,
|
||||
named_primaries: Option<NamedPrimaries>,
|
||||
primaries: Primaries,
|
||||
luminance: Luminance,
|
||||
transfer_function: TransferFunction,
|
||||
) -> Rc<ColorDescription> {
|
||||
macro_rules! gc {
|
||||
($d:ident, $i:expr) => {
|
||||
if $d.len() > 16 && $i.get() * 2 > $d.len() {
|
||||
$d.lock().retain(|_, d| d.strong_count() > 0);
|
||||
$i.set(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
gc!(linear_descriptions, &shared.dead_linear);
|
||||
gc!(complete_descriptions, &shared.dead_complete);
|
||||
let key = LinearDescriptionKey {
|
||||
primaries,
|
||||
luminance,
|
||||
};
|
||||
if let Some(d) = linear_descriptions.get(&key) {
|
||||
if let Some(d) = d.upgrade() {
|
||||
let key = CompleteDescriptionKey {
|
||||
linear: d.id,
|
||||
named_primaries,
|
||||
transfer_function,
|
||||
};
|
||||
if let Some(d) = complete_descriptions.get(&key) {
|
||||
if let Some(d) = d.upgrade() {
|
||||
return d;
|
||||
}
|
||||
shared.dead_complete.fetch_sub(1);
|
||||
}
|
||||
let d = Rc::new(ColorDescription {
|
||||
id: shared.complete_ids.acquire(),
|
||||
linear: d,
|
||||
named_primaries,
|
||||
transfer_function,
|
||||
shared: shared.clone(),
|
||||
});
|
||||
complete_descriptions.set(key, Rc::downgrade(&d));
|
||||
return d;
|
||||
}
|
||||
shared.dead_linear.fetch_sub(1);
|
||||
}
|
||||
let (xyz_from_local, local_from_xyz) = primaries.matrices();
|
||||
let d = Rc::new(LinearColorDescription {
|
||||
id: linear_ids.next(),
|
||||
primaries,
|
||||
xyz_from_local,
|
||||
local_from_xyz,
|
||||
luminance,
|
||||
shared: shared.clone(),
|
||||
});
|
||||
linear_descriptions.set(key, Rc::downgrade(&d));
|
||||
let key = CompleteDescriptionKey {
|
||||
linear: d.id,
|
||||
named_primaries,
|
||||
transfer_function,
|
||||
};
|
||||
let d = Rc::new(ColorDescription {
|
||||
id: shared.complete_ids.acquire(),
|
||||
linear: d,
|
||||
named_primaries,
|
||||
transfer_function,
|
||||
shared: shared.clone(),
|
||||
});
|
||||
complete_descriptions.set(key, Rc::downgrade(&d));
|
||||
d
|
||||
}
|
||||
111
src/cmm/cmm_primaries.rs
Normal file
111
src/cmm/cmm_primaries.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
use {crate::utils::ordered_float::F64, std::hash::Hash};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum NamedPrimaries {
|
||||
Srgb,
|
||||
PalM,
|
||||
Pal,
|
||||
Ntsc,
|
||||
GenericFilm,
|
||||
Bt2020,
|
||||
Cie1931Xyz,
|
||||
DciP3,
|
||||
DisplayP3,
|
||||
AdobeRgb,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct Primaries {
|
||||
pub r: (F64, F64),
|
||||
pub g: (F64, F64),
|
||||
pub b: (F64, F64),
|
||||
pub wp: (F64, F64),
|
||||
}
|
||||
|
||||
impl Primaries {
|
||||
pub const SRGB: Self = Self {
|
||||
r: (F64(0.64), F64(0.33)),
|
||||
g: (F64(0.3), F64(0.6)),
|
||||
b: (F64(0.15), F64(0.06)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
|
||||
pub const PAL_M: Self = Self {
|
||||
r: (F64(0.67), F64(0.33)),
|
||||
g: (F64(0.21), F64(0.71)),
|
||||
b: (F64(0.14), F64(0.08)),
|
||||
wp: (F64(0.310), F64(0.316)),
|
||||
};
|
||||
|
||||
pub const PAL: Self = Self {
|
||||
r: (F64(0.64), F64(0.33)),
|
||||
g: (F64(0.29), F64(0.60)),
|
||||
b: (F64(0.15), F64(0.06)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
|
||||
pub const NTSC: Self = Self {
|
||||
r: (F64(0.630), F64(0.340)),
|
||||
g: (F64(0.310), F64(0.595)),
|
||||
b: (F64(0.155), F64(0.070)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
|
||||
pub const GENERIC_FILM: Self = Self {
|
||||
r: (F64(0.681), F64(0.319)),
|
||||
g: (F64(0.243), F64(0.692)),
|
||||
b: (F64(0.145), F64(0.049)),
|
||||
wp: (F64(0.310), F64(0.316)),
|
||||
};
|
||||
|
||||
pub const BT2020: Self = Self {
|
||||
r: (F64(0.708), F64(0.292)),
|
||||
g: (F64(0.170), F64(0.797)),
|
||||
b: (F64(0.131), F64(0.046)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
|
||||
pub const CIE1931_XYZ: Self = Self {
|
||||
r: (F64(1.0), F64(0.0)),
|
||||
g: (F64(0.0), F64(1.0)),
|
||||
b: (F64(0.0), F64(0.0)),
|
||||
wp: (F64(1.0 / 3.0), F64(1.0 / 3.0)),
|
||||
};
|
||||
|
||||
pub const DCI_P3: Self = Self {
|
||||
r: (F64(0.680), F64(0.320)),
|
||||
g: (F64(0.265), F64(0.690)),
|
||||
b: (F64(0.150), F64(0.060)),
|
||||
wp: (F64(0.314), F64(0.351)),
|
||||
};
|
||||
|
||||
pub const DISPLAY_P3: Self = Self {
|
||||
r: (F64(0.680), F64(0.320)),
|
||||
g: (F64(0.265), F64(0.690)),
|
||||
b: (F64(0.150), F64(0.060)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
|
||||
pub const ADOBE_RGB: Self = Self {
|
||||
r: (F64(0.64), F64(0.33)),
|
||||
g: (F64(0.21), F64(0.71)),
|
||||
b: (F64(0.15), F64(0.06)),
|
||||
wp: (F64(0.3127), F64(0.3290)),
|
||||
};
|
||||
}
|
||||
impl NamedPrimaries {
|
||||
pub const fn primaries(self) -> Primaries {
|
||||
match self {
|
||||
NamedPrimaries::Srgb => Primaries::SRGB,
|
||||
NamedPrimaries::PalM => Primaries::PAL_M,
|
||||
NamedPrimaries::Pal => Primaries::PAL,
|
||||
NamedPrimaries::Ntsc => Primaries::NTSC,
|
||||
NamedPrimaries::GenericFilm => Primaries::GENERIC_FILM,
|
||||
NamedPrimaries::Bt2020 => Primaries::BT2020,
|
||||
NamedPrimaries::Cie1931Xyz => Primaries::CIE1931_XYZ,
|
||||
NamedPrimaries::DciP3 => Primaries::DCI_P3,
|
||||
NamedPrimaries::DisplayP3 => Primaries::DISPLAY_P3,
|
||||
NamedPrimaries::AdobeRgb => Primaries::ADOBE_RGB,
|
||||
}
|
||||
}
|
||||
}
|
||||
201
src/cmm/cmm_tests.rs
Normal file
201
src/cmm/cmm_tests.rs
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
mod matrices {
|
||||
use crate::{cmm::cmm_primaries::Primaries, utils::ordered_float::F64};
|
||||
|
||||
fn check(primaries: Primaries, expected: [[f64; 4]; 3]) {
|
||||
let (ltg, gtl) = primaries.matrices();
|
||||
println!("{:#?}", ltg);
|
||||
assert!((ltg.0[0][0].0 - expected[0][0]).abs() < 0.001);
|
||||
assert!((ltg.0[0][1].0 - expected[0][1]).abs() < 0.001);
|
||||
assert!((ltg.0[0][2].0 - expected[0][2]).abs() < 0.001);
|
||||
assert!((ltg.0[0][3].0 - expected[0][3]).abs() < 0.001);
|
||||
assert!((ltg.0[1][0].0 - expected[1][0]).abs() < 0.001);
|
||||
assert!((ltg.0[1][1].0 - expected[1][1]).abs() < 0.001);
|
||||
assert!((ltg.0[1][2].0 - expected[1][2]).abs() < 0.001);
|
||||
assert!((ltg.0[1][3].0 - expected[1][3]).abs() < 0.001);
|
||||
assert!((ltg.0[2][0].0 - expected[2][0]).abs() < 0.001);
|
||||
assert!((ltg.0[2][1].0 - expected[2][1]).abs() < 0.001);
|
||||
assert!((ltg.0[2][2].0 - expected[2][2]).abs() < 0.001);
|
||||
assert!((ltg.0[2][3].0 - expected[2][3]).abs() < 0.001);
|
||||
let roundtrip = gtl * ltg;
|
||||
assert!((roundtrip.0[0][0].0 - 1.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[0][1].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[0][2].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[0][3].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[1][0].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[1][1].0 - 1.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[1][2].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[1][3].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[2][0].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[2][1].0 - 0.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[2][2].0 - 1.0).abs() < 0.001);
|
||||
assert!((roundtrip.0[2][3].0 - 0.0).abs() < 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srgb() {
|
||||
check(
|
||||
Primaries::SRGB,
|
||||
[
|
||||
[0.4124564, 0.3575761, 0.1804375, 0.0],
|
||||
[0.2126729, 0.7151522, 0.0721750, 0.0],
|
||||
[0.0193339, 0.1191920, 0.9503041, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cie1931_xyz() {
|
||||
check(
|
||||
Primaries::CIE1931_XYZ,
|
||||
[
|
||||
[1.0, 0.0, 0.0, 0.0],
|
||||
[0.0, 1.0, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adobe_rgb() {
|
||||
check(
|
||||
Primaries::ADOBE_RGB,
|
||||
[
|
||||
[0.5767309, 0.1855540, 0.1881852, 0.0],
|
||||
[0.2973769, 0.6273491, 0.0752741, 0.0],
|
||||
[0.0270343, 0.0706872, 0.9911085, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apple_rgb() {
|
||||
check(
|
||||
Primaries {
|
||||
r: (F64(0.625), F64(0.34)),
|
||||
g: (F64(0.28), F64(0.595)),
|
||||
b: (F64(0.155), F64(0.07)),
|
||||
wp: (F64(0.31271), F64(0.32902)),
|
||||
},
|
||||
[
|
||||
[0.4497288, 0.3162486, 0.1844926, 0.0],
|
||||
[0.2446525, 0.6720283, 0.0833192, 0.0],
|
||||
[0.0251848, 0.1411824, 0.9224628, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bt2020() {
|
||||
check(
|
||||
Primaries::BT2020,
|
||||
[
|
||||
[0.636958, 0.144617, 0.168881, 0.0],
|
||||
[0.262700, 0.677998, 0.059302, 0.0],
|
||||
[0.000000, 0.028073, 1.060985, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pal() {
|
||||
check(
|
||||
Primaries::PAL,
|
||||
[
|
||||
[0.4306190, 0.3415419, 0.1783091, 0.0],
|
||||
[0.2220379, 0.7066384, 0.0713236, 0.0],
|
||||
[0.0201853, 0.1295504, 0.9390944, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dci_p3() {
|
||||
check(
|
||||
Primaries::DCI_P3,
|
||||
[
|
||||
[0.445170, 0.277134, 0.172283, 0.0],
|
||||
[0.209492, 0.721595, 0.068913, 0.0],
|
||||
[-0.000000, 0.047061, 0.907355, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_p3() {
|
||||
check(
|
||||
Primaries::DISPLAY_P3,
|
||||
[
|
||||
[0.486571, 0.265668, 0.198217, 0.0],
|
||||
[0.228975, 0.691739, 0.079287, 0.0],
|
||||
[-0.000000, 0.045113, 1.043944, 0.0],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod transforms {
|
||||
use crate::cmm::{
|
||||
cmm_luminance::Luminance, cmm_manager::ColorManager, cmm_primaries::Primaries,
|
||||
cmm_transfer_function::TransferFunction,
|
||||
};
|
||||
|
||||
fn check(p1: Primaries, p2: Primaries, expected: [[f64; 4]; 3]) {
|
||||
let manager = ColorManager::new();
|
||||
let d = |p| manager.get_description(None, p, Luminance::SRGB, TransferFunction::Linear);
|
||||
let d1 = d(p1);
|
||||
let d2 = d(p2);
|
||||
let m = d1.linear.color_transform(&d2.linear);
|
||||
println!("{:#?}", m);
|
||||
assert!((m.0[0][0].0 - expected[0][0]).abs() < 0.001);
|
||||
assert!((m.0[0][1].0 - expected[0][1]).abs() < 0.001);
|
||||
assert!((m.0[0][2].0 - expected[0][2]).abs() < 0.001);
|
||||
assert!((m.0[0][3].0 - expected[0][3]).abs() < 0.001);
|
||||
assert!((m.0[1][0].0 - expected[1][0]).abs() < 0.001);
|
||||
assert!((m.0[1][1].0 - expected[1][1]).abs() < 0.001);
|
||||
assert!((m.0[1][2].0 - expected[1][2]).abs() < 0.001);
|
||||
assert!((m.0[1][3].0 - expected[1][3]).abs() < 0.001);
|
||||
assert!((m.0[2][0].0 - expected[2][0]).abs() < 0.001);
|
||||
assert!((m.0[2][1].0 - expected[2][1]).abs() < 0.001);
|
||||
assert!((m.0[2][2].0 - expected[2][2]).abs() < 0.001);
|
||||
assert!((m.0[2][3].0 - expected[2][3]).abs() < 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srgb_to_bt2020() {
|
||||
check(
|
||||
Primaries::SRGB,
|
||||
Primaries::BT2020,
|
||||
[
|
||||
[0.627404, 0.329283, 0.043313, 0.0],
|
||||
[0.069097, 0.919540, 0.011362, 0.0],
|
||||
[0.016391, 0.088013, 0.895595, 0.0],
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bt2020_to_srgb() {
|
||||
check(
|
||||
Primaries::BT2020,
|
||||
Primaries::SRGB,
|
||||
[
|
||||
[1.660491, -0.587641, -0.072850, 0.0],
|
||||
[-0.124550, 1.132900, -0.008349, 0.0],
|
||||
[-0.018151, -0.100579, 1.118730, 0.0],
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn srgb_to_dci_p3() {
|
||||
check(
|
||||
Primaries::SRGB,
|
||||
Primaries::DCI_P3,
|
||||
[
|
||||
[0.868580, 0.128919, 0.002501, 0.0],
|
||||
[0.034540, 0.961811, 0.003648, 0.0],
|
||||
[0.016771, 0.071040, 0.912189, 0.0],
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
16
src/cmm/cmm_transfer_function.rs
Normal file
16
src/cmm/cmm_transfer_function.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use linearize::Linearize;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Linearize)]
|
||||
pub enum TransferFunction {
|
||||
Srgb,
|
||||
Linear,
|
||||
St2084Pq,
|
||||
Bt1886,
|
||||
Gamma22,
|
||||
Gamma28,
|
||||
St240,
|
||||
ExtSrgb,
|
||||
Log100,
|
||||
Log316,
|
||||
St428,
|
||||
}
|
||||
257
src/cmm/cmm_transform.rs
Normal file
257
src/cmm/cmm_transform.rs
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::{cmm_primaries::Primaries, cmm_transfer_function::TransferFunction},
|
||||
theme::Color,
|
||||
utils::{debug_fn::debug_fn, ordered_float::F64},
|
||||
},
|
||||
std::{
|
||||
fmt::{Debug, Formatter},
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
ops::{Mul, MulAssign},
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ColorMatrix<To = Local, From = Local>(pub [[F64; 4]; 3], PhantomData<(To, From)>);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Local;
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Xyz;
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Bradford;
|
||||
|
||||
impl<T, U> Copy for ColorMatrix<T, U> {}
|
||||
|
||||
impl<T, U> Clone for ColorMatrix<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> PartialEq<Self> for ColorMatrix<T, U> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Eq for ColorMatrix<T, U> {}
|
||||
|
||||
impl<T, U> Hash for ColorMatrix<T, U> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Debug for ColorMatrix<T, U> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("ColorMatrix")
|
||||
.field(&format_matrix(&self.0))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn format_matrix<'a>(m: &'a [[F64; 4]; 3]) -> impl Debug + use<'a> {
|
||||
debug_fn(move |f| {
|
||||
let iter = m
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(Some([F64(0.0), F64(0.0), F64(0.0), F64(1.0)]))
|
||||
.enumerate();
|
||||
if f.alternate() {
|
||||
for (idx, row) in iter {
|
||||
if idx > 0 {
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"{:7.4} {:7.4} {:7.4} {:7.4}",
|
||||
row[0], row[1], row[2], row[3]
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
f.write_str("[")?;
|
||||
for (idx, row) in iter {
|
||||
if idx > 0 {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"[{:.4}, {:.4}, {:.4}, {:.4}]",
|
||||
row[0], row[1], row[2], row[3]
|
||||
)?;
|
||||
}
|
||||
f.write_str("]")?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
impl<T, U, V> Mul<ColorMatrix<U, T>> for ColorMatrix<V, U> {
|
||||
type Output = ColorMatrix<V, T>;
|
||||
|
||||
fn mul(self, rhs: ColorMatrix<U, T>) -> Self::Output {
|
||||
let a = &self.0;
|
||||
let b = &rhs.0;
|
||||
macro_rules! mul {
|
||||
($ar:expr, $bc:expr) => {
|
||||
a[$ar][0] * b[0][$bc] + a[$ar][1] * b[1][$bc] + a[$ar][2] * b[2][$bc]
|
||||
};
|
||||
}
|
||||
let m = [
|
||||
[mul!(0, 0), mul!(0, 1), mul!(0, 2), mul!(0, 3) + a[0][3]],
|
||||
[mul!(1, 0), mul!(1, 1), mul!(1, 2), mul!(1, 3) + a[1][3]],
|
||||
[mul!(2, 0), mul!(2, 1), mul!(2, 2), mul!(2, 3) + a[2][3]],
|
||||
];
|
||||
ColorMatrix(m, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<U, V> MulAssign<ColorMatrix<U, U>> for ColorMatrix<V, U> {
|
||||
fn mul_assign(&mut self, rhs: ColorMatrix<U, U>) {
|
||||
*self = *self * rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Mul<[f64; 3]> for ColorMatrix<T, U> {
|
||||
type Output = [f64; 3];
|
||||
|
||||
fn mul(self, rhs: [f64; 3]) -> Self::Output {
|
||||
let a = &self.0;
|
||||
macro_rules! mul {
|
||||
($ar:expr) => {
|
||||
a[$ar][0].0 * rhs[0] + a[$ar][1].0 * rhs[1] + a[$ar][2].0 * rhs[2]
|
||||
};
|
||||
}
|
||||
[mul!(0), mul!(1), mul!(2)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Mul<Color> for ColorMatrix<T, U> {
|
||||
type Output = Color;
|
||||
|
||||
fn mul(self, rhs: Color) -> Self::Output {
|
||||
let mut rgba = rhs.to_array(TransferFunction::Linear);
|
||||
let a = rgba[3];
|
||||
if a < 1.0 && a > 0.0 {
|
||||
for c in &mut rgba[..3] {
|
||||
*c /= a;
|
||||
}
|
||||
}
|
||||
let [r, g, b] = self * [rgba[0] as f64, rgba[1] as f64, rgba[2] as f64];
|
||||
let mut color = Color::new(TransferFunction::Linear, r as f32, g as f32, b as f32);
|
||||
if a < 1.0 {
|
||||
color = color * a;
|
||||
}
|
||||
color
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ColorMatrix<T, U> {
|
||||
pub const fn new(m: [[f64; 4]; 3]) -> Self {
|
||||
let m = [
|
||||
[F64(m[0][0]), F64(m[0][1]), F64(m[0][2]), F64(m[0][3])],
|
||||
[F64(m[1][0]), F64(m[1][1]), F64(m[1][2]), F64(m[1][3])],
|
||||
[F64(m[2][0]), F64(m[2][1]), F64(m[2][2]), F64(m[2][3])],
|
||||
];
|
||||
Self(m, PhantomData)
|
||||
}
|
||||
|
||||
pub const fn to_f32(&self) -> [[f32; 4]; 4] {
|
||||
let m = &self.0;
|
||||
macro_rules! map {
|
||||
($r:expr, $c:expr) => {
|
||||
m[$r][$c].0 as f32
|
||||
};
|
||||
}
|
||||
[
|
||||
[map!(0, 0), map!(0, 1), map!(0, 2), map!(0, 3)],
|
||||
[map!(1, 0), map!(1, 1), map!(1, 2), map!(1, 3)],
|
||||
[map!(2, 0), map!(2, 1), map!(2, 2), map!(2, 3)],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorMatrix<Bradford, Xyz> {
|
||||
const BFD: Self = Self::new([
|
||||
[0.8951, 0.2664, -0.1614, 0.0],
|
||||
[-0.7502, 1.7135, 0.0367, 0.0],
|
||||
[0.0389, -0.0685, 1.0296, 0.0],
|
||||
]);
|
||||
}
|
||||
|
||||
impl ColorMatrix<Xyz, Bradford> {
|
||||
const BFD_INV: Self = Self::new([
|
||||
[0.9870, -0.1471, 0.1600, 0.0],
|
||||
[0.4323, 0.5184, 0.0493, 0.0],
|
||||
[-0.0085, 0.04, 0.9685, 0.0],
|
||||
]);
|
||||
}
|
||||
|
||||
#[expect(non_snake_case)]
|
||||
pub fn bradford_adjustment(w_from: (F64, F64), w_to: (F64, F64)) -> ColorMatrix<Xyz, Xyz> {
|
||||
let (F64(x_from), F64(y_from)) = w_from;
|
||||
let (F64(x_to), F64(y_to)) = w_to;
|
||||
let X_from = x_from / y_from;
|
||||
let Z_from = (1.0 - x_from - y_from) / y_from;
|
||||
let X_to = x_to / y_to;
|
||||
let Z_to = (1.0 - x_to - y_to) / y_to;
|
||||
let [R_from, G_from, B_from] = ColorMatrix::BFD * [X_from, 1.0, Z_from];
|
||||
let [R_to, G_to, B_to] = ColorMatrix::BFD * [X_to, 1.0, Z_to];
|
||||
let adj = ColorMatrix::new([
|
||||
[R_to / R_from, 0.0, 0.0, 0.0],
|
||||
[0.0, G_to / G_from, 0.0, 0.0],
|
||||
[0.0, 0.0, B_to / B_from, 0.0],
|
||||
]);
|
||||
ColorMatrix::BFD_INV * adj * ColorMatrix::BFD
|
||||
}
|
||||
|
||||
impl Primaries {
|
||||
#[expect(non_snake_case)]
|
||||
pub const fn matrices(&self) -> (ColorMatrix<Xyz, Local>, ColorMatrix<Local, Xyz>) {
|
||||
let (F64(xw), F64(yw)) = self.wp;
|
||||
let Xw = xw / yw;
|
||||
let Zw = (1.0 - xw - yw) / yw;
|
||||
let (F64(xr), F64(yr)) = self.r;
|
||||
let (F64(xg), F64(yg)) = self.g;
|
||||
let (F64(xb), F64(yb)) = self.b;
|
||||
let zr = 1.0 - xr - yr;
|
||||
let zg = 1.0 - xg - yg;
|
||||
let zb = 1.0 - xb - yb;
|
||||
let srx = yg * zb - zg * yb;
|
||||
let sry = zg * xb - xg * zb;
|
||||
let srz = xg * yb - yg * xb;
|
||||
let sgx = zr * yb - yr * zb;
|
||||
let sgz = yr * xb - xr * yb;
|
||||
let sgy = xr * zb - zr * xb;
|
||||
let sbx = yr * zg - zr * yg;
|
||||
let sby = zr * xg - xr * zg;
|
||||
let sbz = xr * yg - yr * xg;
|
||||
let det = srz + sgz + sbz;
|
||||
let sr = srx * Xw + sry + srz * Zw;
|
||||
let sg = sgx * Xw + sgy + sgz * Zw;
|
||||
let sb = sbx * Xw + sby + sbz * Zw;
|
||||
let det_inv = 1.0 / det;
|
||||
let sr_inv = 1.0 / sr;
|
||||
let sg_inv = 1.0 / sg;
|
||||
let sb_inv = 1.0 / sb;
|
||||
let srp = sr * det_inv;
|
||||
let sgp = sg * det_inv;
|
||||
let sbp = sb * det_inv;
|
||||
let XYZ_from_local = [
|
||||
[srp * xr, sgp * xg, sbp * xb, 0.0],
|
||||
[srp * yr, sgp * yg, sbp * yb, 0.0],
|
||||
[srp * zr, sgp * zg, sbp * zb, 0.0],
|
||||
];
|
||||
let local_from_XYZ = [
|
||||
[srx * sr_inv, sry * sr_inv, srz * sr_inv, 0.0],
|
||||
[sgx * sg_inv, sgy * sg_inv, sgz * sg_inv, 0.0],
|
||||
[sbx * sb_inv, sby * sb_inv, sbz * sb_inv, 0.0],
|
||||
];
|
||||
(
|
||||
ColorMatrix::new(XYZ_from_local),
|
||||
ColorMatrix::new(local_from_XYZ),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ use {
|
|||
cli::{CliBackend, GlobalArgs, RunArgs},
|
||||
client::{ClientId, Clients},
|
||||
clientmem::{self, ClientMemError},
|
||||
cmm::cmm_manager::ColorManager,
|
||||
config::ConfigProxy,
|
||||
cpu_worker::{CpuWorker, CpuWorkerError},
|
||||
damage::{DamageVisualizer, visualize_damage},
|
||||
|
|
@ -152,6 +153,7 @@ fn start_compositor2(
|
|||
let scales = RefCounted::default();
|
||||
scales.add(Scale::from_int(1));
|
||||
let cpu_worker = Rc::new(CpuWorker::new(&ring, &engine)?);
|
||||
let color_manager = ColorManager::new();
|
||||
let state = Rc::new(State {
|
||||
kb_ctx,
|
||||
backend: CloneCell::new(Rc::new(DummyBackend)),
|
||||
|
|
@ -266,7 +268,7 @@ fn start_compositor2(
|
|||
tablet_ids: Default::default(),
|
||||
tablet_tool_ids: Default::default(),
|
||||
tablet_pad_ids: Default::default(),
|
||||
damage_visualizer: DamageVisualizer::new(&engine),
|
||||
damage_visualizer: DamageVisualizer::new(&engine, &color_manager),
|
||||
default_vrr_mode: Cell::new(VrrMode::NEVER),
|
||||
default_vrr_cursor_hz: Cell::new(None),
|
||||
default_tearing_mode: Cell::new(TearingMode::VARIANT_3),
|
||||
|
|
@ -284,6 +286,7 @@ fn start_compositor2(
|
|||
data_control_device_ids: Default::default(),
|
||||
workspace_managers: Default::default(),
|
||||
color_management_enabled: Cell::new(false),
|
||||
color_manager,
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use {
|
|||
self, ConnectorId, DrmDeviceId, InputDeviceAccelProfile, InputDeviceCapability,
|
||||
InputDeviceId,
|
||||
},
|
||||
cmm::cmm_transfer_function::TransferFunction,
|
||||
compositor::MAX_EXTENTS,
|
||||
config::ConfigProxy,
|
||||
format::config_formats,
|
||||
|
|
@ -14,7 +15,7 @@ use {
|
|||
output_schedule::map_cursor_hz,
|
||||
scale::Scale,
|
||||
state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State},
|
||||
theme::{Color, ThemeSized, TransferFunction},
|
||||
theme::{Color, ThemeSized},
|
||||
tree::{
|
||||
ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase, OutputNode,
|
||||
TearingMode, VrrMode, WsMoveConfig, move_ws_to_output,
|
||||
|
|
|
|||
|
|
@ -398,6 +398,7 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed
|
|||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
renderer.state.color_manager.srgb_srgb(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -422,6 +423,7 @@ impl Cursor for StaticCursor {
|
|||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
renderer.state.color_manager.srgb_srgb(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -464,6 +466,7 @@ impl Cursor for AnimatedCursor {
|
|||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
renderer.state.color_manager.srgb_srgb(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -507,6 +507,7 @@ impl CursorUser {
|
|||
&self.group.state,
|
||||
scale,
|
||||
transform,
|
||||
self.group.state.color_manager.srgb_srgb(),
|
||||
);
|
||||
match res {
|
||||
Ok(sync_file) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::AsyncEngine,
|
||||
cmm::cmm_manager::ColorManager,
|
||||
fixed::Fixed,
|
||||
gfx_api::GfxApiOpt,
|
||||
ifs::wl_output::WlOutputGlobal,
|
||||
|
|
@ -77,6 +78,7 @@ pub struct DamageVisualizer {
|
|||
enabled: Cell<bool>,
|
||||
decay: Cell<Duration>,
|
||||
color: Cell<Color>,
|
||||
color_manager: Rc<ColorManager>,
|
||||
}
|
||||
|
||||
const MAX_RECTS: usize = 100_000;
|
||||
|
|
@ -87,7 +89,7 @@ struct Damage {
|
|||
}
|
||||
|
||||
impl DamageVisualizer {
|
||||
pub fn new(eng: &Rc<AsyncEngine>) -> Self {
|
||||
pub fn new(eng: &Rc<AsyncEngine>, color_manager: &Rc<ColorManager>) -> Self {
|
||||
Self {
|
||||
eng: eng.clone(),
|
||||
entries: Default::default(),
|
||||
|
|
@ -95,6 +97,7 @@ impl DamageVisualizer {
|
|||
enabled: Default::default(),
|
||||
decay: Cell::new(Duration::from_secs(2)),
|
||||
color: Cell::new(Color::from_srgba_straight(255, 0, 0, 128)),
|
||||
color_manager: color_manager.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -162,13 +165,14 @@ impl DamageVisualizer {
|
|||
let dy = -cursor_rect.y1();
|
||||
let decay_millis = decay.as_millis() as u64 as f32;
|
||||
renderer.ops.push(GfxApiOpt::Sync);
|
||||
let srgb = &self.color_manager.srgb_srgb().linear;
|
||||
for entry in entries.iter().rev() {
|
||||
let region = Region::new(entry.rect);
|
||||
let region = region.subtract(&used);
|
||||
if region.is_not_empty() {
|
||||
let age = (now - entry.time).as_millis() as u64 as f32 / decay_millis;
|
||||
let color = base_color * (1.0 - age);
|
||||
renderer.fill_boxes2(region.rects(), &color, dx, dy);
|
||||
renderer.fill_boxes2(region.rects(), &color, srgb, dx, dy);
|
||||
used = used.union(®ion);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::Allocator,
|
||||
cmm::cmm_description::{ColorDescription, LinearColorDescription},
|
||||
cpu_worker::CpuWorker,
|
||||
cursor::Cursor,
|
||||
damage::DamageVisualizer,
|
||||
|
|
@ -41,6 +42,7 @@ pub enum GfxApiOpt {
|
|||
pub struct GfxRenderPass {
|
||||
pub ops: Vec<GfxApiOpt>,
|
||||
pub clear: Option<Color>,
|
||||
pub clear_cd: Rc<LinearColorDescription>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
||||
|
|
@ -194,6 +196,7 @@ pub struct FillRect {
|
|||
pub rect: FramebufferRect,
|
||||
pub color: Color,
|
||||
pub alpha: Option<f32>,
|
||||
pub cd: Rc<LinearColorDescription>,
|
||||
}
|
||||
|
||||
impl FillRect {
|
||||
|
|
@ -215,6 +218,7 @@ pub struct CopyTexture {
|
|||
pub release_sync: ReleaseSync,
|
||||
pub alpha: Option<f32>,
|
||||
pub opaque: bool,
|
||||
pub cd: Rc<ColorDescription>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -299,10 +303,13 @@ pub trait GfxFramebuffer: Debug {
|
|||
self: Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
cd: &Rc<ColorDescription>,
|
||||
ops: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
clear_cd: &Rc<LinearColorDescription>,
|
||||
region: &Region,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
blend_cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError>;
|
||||
|
||||
fn format(&self) -> &'static Format;
|
||||
|
|
@ -333,17 +340,23 @@ impl dyn GfxFramebuffer {
|
|||
self: &Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
cd: &Rc<ColorDescription>,
|
||||
ops: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
clear_cd: &Rc<LinearColorDescription>,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
blend_cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.clone().render_with_region(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
ops,
|
||||
clear,
|
||||
clear_cd,
|
||||
&self.full_region(),
|
||||
blend_buffer,
|
||||
blend_cd,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -351,17 +364,35 @@ impl dyn GfxFramebuffer {
|
|||
self: &Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.clear_with(acquire_sync, release_sync, &Color::TRANSPARENT)
|
||||
self.clear_with(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
&Color::TRANSPARENT,
|
||||
&cd.linear,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn clear_with(
|
||||
self: &Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
cd: &Rc<ColorDescription>,
|
||||
color: &Color,
|
||||
color_cd: &Rc<LinearColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.render(acquire_sync, release_sync, &[], Some(color), None)
|
||||
self.render(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
&[],
|
||||
Some(color),
|
||||
color_cd,
|
||||
None,
|
||||
cd,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn logical_size(&self, transform: Transform) -> (i32, i32) {
|
||||
|
|
@ -381,7 +412,9 @@ impl dyn GfxFramebuffer {
|
|||
self: &Rc<Self>,
|
||||
fb_acquire_sync: AcquireSync,
|
||||
fb_release_sync: ReleaseSync,
|
||||
fb_cd: &Rc<ColorDescription>,
|
||||
texture: &Rc<dyn GfxTexture>,
|
||||
texture_cd: &Rc<ColorDescription>,
|
||||
resv: Option<&Rc<dyn BufferResv>>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
|
|
@ -404,24 +437,46 @@ impl dyn GfxFramebuffer {
|
|||
acquire_sync,
|
||||
release_sync,
|
||||
false,
|
||||
texture_cd,
|
||||
);
|
||||
let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT);
|
||||
self.render(fb_acquire_sync, fb_release_sync, &ops, clear, None)
|
||||
self.render(
|
||||
fb_acquire_sync,
|
||||
fb_release_sync,
|
||||
fb_cd,
|
||||
&ops,
|
||||
clear,
|
||||
&fb_cd.linear,
|
||||
None,
|
||||
fb_cd,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render_custom(
|
||||
self: &Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
cd: &Rc<ColorDescription>,
|
||||
scale: Scale,
|
||||
clear: Option<&Color>,
|
||||
clear_cd: &Rc<LinearColorDescription>,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
blend_cd: &Rc<ColorDescription>,
|
||||
f: &mut dyn FnMut(&mut RendererBase),
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let mut ops = vec![];
|
||||
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
||||
f(&mut renderer);
|
||||
self.render(acquire_sync, release_sync, &ops, clear, blend_buffer)
|
||||
self.render(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
&ops,
|
||||
clear,
|
||||
clear_cd,
|
||||
blend_buffer,
|
||||
blend_cd,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_render_pass(
|
||||
|
|
@ -456,17 +511,22 @@ impl dyn GfxFramebuffer {
|
|||
self: &Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
cd: &Rc<ColorDescription>,
|
||||
pass: &GfxRenderPass,
|
||||
region: &Region,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
blend_cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.clone().render_with_region(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
&pass.ops,
|
||||
pass.clear.as_ref(),
|
||||
&pass.clear_cd,
|
||||
region,
|
||||
blend_buffer,
|
||||
blend_cd,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -474,6 +534,7 @@ impl dyn GfxFramebuffer {
|
|||
self: &Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
cd: &Rc<ColorDescription>,
|
||||
node: &OutputNode,
|
||||
state: &State,
|
||||
cursor_rect: Option<Rect>,
|
||||
|
|
@ -481,10 +542,12 @@ impl dyn GfxFramebuffer {
|
|||
render_hardware_cursor: bool,
|
||||
fill_black_in_grace_period: bool,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
blend_cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
self.render_node(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
node,
|
||||
state,
|
||||
cursor_rect,
|
||||
|
|
@ -495,6 +558,7 @@ impl dyn GfxFramebuffer {
|
|||
fill_black_in_grace_period,
|
||||
node.global.persistent.transform.get(),
|
||||
blend_buffer,
|
||||
blend_cd,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -502,6 +566,7 @@ impl dyn GfxFramebuffer {
|
|||
self: &Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
cd: &Rc<ColorDescription>,
|
||||
node: &dyn Node,
|
||||
state: &State,
|
||||
cursor_rect: Option<Rect>,
|
||||
|
|
@ -512,6 +577,7 @@ impl dyn GfxFramebuffer {
|
|||
fill_black_in_grace_period: bool,
|
||||
transform: Transform,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
blend_cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let pass = self.create_render_pass(
|
||||
node,
|
||||
|
|
@ -528,9 +594,11 @@ impl dyn GfxFramebuffer {
|
|||
self.perform_render_pass(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
&pass,
|
||||
&self.full_region(),
|
||||
blend_buffer,
|
||||
blend_cd,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -542,6 +610,7 @@ impl dyn GfxFramebuffer {
|
|||
state: &State,
|
||||
scale: Scale,
|
||||
transform: Transform,
|
||||
cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let mut ops = vec![];
|
||||
let mut renderer = Renderer {
|
||||
|
|
@ -557,9 +626,12 @@ impl dyn GfxFramebuffer {
|
|||
self.render(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
&ops,
|
||||
Some(&Color::TRANSPARENT),
|
||||
&cd.linear,
|
||||
None,
|
||||
cd,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -832,6 +904,7 @@ pub fn create_render_pass(
|
|||
return GfxRenderPass {
|
||||
ops: vec![],
|
||||
clear: Some(Color::SOLID_BLACK),
|
||||
clear_cd: state.color_manager.srgb_srgb().linear.clone(),
|
||||
};
|
||||
}
|
||||
let mut ops = vec![];
|
||||
|
|
@ -898,6 +971,7 @@ pub fn create_render_pass(
|
|||
GfxRenderPass {
|
||||
ops,
|
||||
clear: Some(c),
|
||||
clear_cd: state.color_manager.srgb_srgb().linear.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,8 +67,9 @@ macro_rules! dynload {
|
|||
|
||||
use {
|
||||
crate::{
|
||||
cmm::cmm_transfer_function::TransferFunction,
|
||||
gfx_api::{
|
||||
AcquireSync, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
||||
AcquireSync, CopyTexture, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
||||
ReleaseSync, SyncFile,
|
||||
},
|
||||
gfx_apis::gl::{
|
||||
|
|
@ -84,7 +85,7 @@ use {
|
|||
GL_TRIANGLE_STRIP, GL_TRIANGLES,
|
||||
},
|
||||
},
|
||||
theme::{Color, TransferFunction},
|
||||
theme::Color,
|
||||
utils::{errorfmt::ErrorFmt, rc_eq::rc_eq, vecstorage::VecStorage},
|
||||
video::{
|
||||
dmabuf::DMA_BUF_SYNC_READ,
|
||||
|
|
@ -204,10 +205,15 @@ enum RenderError {
|
|||
#[derive(Default)]
|
||||
struct GfxGlState {
|
||||
triangles: RefCell<Vec<[f32; 2]>>,
|
||||
fill_rect: VecStorage<FillRect>,
|
||||
fill_rect: VecStorage<GlFillRect>,
|
||||
copy_tex: VecStorage<&'static CopyTexture>,
|
||||
}
|
||||
|
||||
struct GlFillRect {
|
||||
pub rect: FramebufferRect,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
|
||||
let mut state = fb.ctx.gl_state.borrow_mut();
|
||||
let state = &mut *state;
|
||||
|
|
@ -235,10 +241,9 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
|
|||
}
|
||||
}
|
||||
GfxApiOpt::FillRect(f) => {
|
||||
fill_rect.push(FillRect {
|
||||
fill_rect.push(GlFillRect {
|
||||
rect: f.rect,
|
||||
color: f.effective_color(),
|
||||
alpha: None,
|
||||
});
|
||||
i += 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::{
|
||||
cmm_description::{ColorDescription, LinearColorDescription},
|
||||
cmm_transfer_function::TransferFunction,
|
||||
},
|
||||
format::Format,
|
||||
gfx_api::{
|
||||
AcquireSync, AsyncShmGfxTextureCallback, GfxApiOpt, GfxBlendBuffer, GfxError,
|
||||
|
|
@ -18,7 +22,7 @@ use {
|
|||
sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
|
||||
},
|
||||
rect::Region,
|
||||
theme::{Color, TransferFunction},
|
||||
theme::Color,
|
||||
},
|
||||
std::{
|
||||
cell::Cell,
|
||||
|
|
@ -104,10 +108,13 @@ impl GfxFramebuffer for Framebuffer {
|
|||
self: Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
_release_sync: ReleaseSync,
|
||||
_cd: &Rc<ColorDescription>,
|
||||
ops: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
_clear_cd: &Rc<LinearColorDescription>,
|
||||
_region: &Region,
|
||||
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
_blend_cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
(*self)
|
||||
.render(acquire_sync, ops, clear)
|
||||
|
|
|
|||
|
|
@ -106,8 +106,6 @@ impl VulkanRenderer {
|
|||
ty: VulkanImageMemory::Blend(allocation),
|
||||
bridge: None,
|
||||
sampled_image_descriptor: self.sampled_image_descriptor(view),
|
||||
descriptor_buffer_version: Default::default(),
|
||||
descriptor_buffer_offset: Default::default(),
|
||||
execution_version: Default::default(),
|
||||
});
|
||||
cached.insert_entry(Rc::downgrade(&img));
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ use {
|
|||
DeviceSize,
|
||||
},
|
||||
gpu_alloc::UsageFlags,
|
||||
std::{cell::RefCell, mem::ManuallyDrop, rc::Rc},
|
||||
std::{cell::RefCell, mem::ManuallyDrop, ops::Deref, rc::Rc},
|
||||
uapi::Packed,
|
||||
};
|
||||
|
||||
pub struct VulkanBufferCache {
|
||||
|
|
@ -20,6 +21,7 @@ pub struct VulkanBufferCache {
|
|||
allocator: Rc<VulkanAllocator>,
|
||||
buffers: RefCell<Vec<VulkanBufferUnused>>,
|
||||
usage: BufferUsageFlags,
|
||||
min_alignment: DeviceSize,
|
||||
}
|
||||
|
||||
pub struct VulkanBuffer {
|
||||
|
|
@ -40,12 +42,14 @@ impl VulkanBufferCache {
|
|||
device: &Rc<VulkanDevice>,
|
||||
allocator: &Rc<VulkanAllocator>,
|
||||
usage: BufferUsageFlags,
|
||||
min_alignment: DeviceSize,
|
||||
) -> Rc<Self> {
|
||||
Rc::new(Self {
|
||||
device: device.clone(),
|
||||
allocator: allocator.clone(),
|
||||
buffers: Default::default(),
|
||||
usage,
|
||||
min_alignment,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -55,25 +59,27 @@ impl VulkanBufferCache {
|
|||
for_sampler: bool,
|
||||
) -> Rc<Self> {
|
||||
let mut usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS;
|
||||
let mut min_alignment = 1;
|
||||
if for_sampler {
|
||||
usage |= BufferUsageFlags::SAMPLER_DESCRIPTOR_BUFFER_EXT;
|
||||
} else {
|
||||
usage |= BufferUsageFlags::RESOURCE_DESCRIPTOR_BUFFER_EXT;
|
||||
if device.is_anv {
|
||||
// https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33903
|
||||
min_alignment = 4096;
|
||||
}
|
||||
}
|
||||
Self::new(device, allocator, usage)
|
||||
Self::new(device, allocator, usage, min_alignment)
|
||||
}
|
||||
|
||||
pub fn usage(&self) -> BufferUsageFlags {
|
||||
self.usage
|
||||
}
|
||||
|
||||
pub fn allocate(
|
||||
self: &Rc<Self>,
|
||||
capacity: DeviceSize,
|
||||
align: DeviceSize,
|
||||
) -> Result<VulkanBuffer, VulkanError> {
|
||||
pub fn allocate(self: &Rc<Self>, capacity: DeviceSize) -> Result<VulkanBuffer, VulkanError> {
|
||||
const MIN_ALLOCATION: DeviceSize = 1024;
|
||||
let capacity = (capacity.max(MIN_ALLOCATION) + align - 1) & !(align - 1);
|
||||
let align_mask = self.min_alignment - 1;
|
||||
let capacity = (capacity.max(MIN_ALLOCATION) + align_mask) & !align_mask;
|
||||
let mut smallest = None;
|
||||
let mut smallest_size = DeviceSize::MAX;
|
||||
let mut fitting = None;
|
||||
|
|
@ -116,7 +122,7 @@ impl VulkanBufferCache {
|
|||
let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) });
|
||||
let mut memory_requirements =
|
||||
unsafe { self.device.device.get_buffer_memory_requirements(buffer) };
|
||||
memory_requirements.alignment = memory_requirements.alignment.max(align);
|
||||
memory_requirements.alignment = memory_requirements.alignment.max(self.min_alignment);
|
||||
let allocation = {
|
||||
let flags = UsageFlags::UPLOAD
|
||||
| UsageFlags::FAST_DEVICE_ACCESS
|
||||
|
|
@ -162,3 +168,31 @@ impl Drop for VulkanBufferUnused {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GenericBufferWriter {
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl GenericBufferWriter {
|
||||
pub fn clear(&mut self) {
|
||||
self.buf.clear();
|
||||
}
|
||||
|
||||
pub fn write(&mut self, offset_mask: DeviceSize, data: &(impl Packed + ?Sized)) -> DeviceSize {
|
||||
let mut offset = self.buf.len() as DeviceSize;
|
||||
let mask = offset_mask | (align_of_val(data) as DeviceSize - 1);
|
||||
offset = (offset + mask) & !mask;
|
||||
self.buf.resize(offset as usize, 0);
|
||||
self.buf.extend_from_slice(uapi::as_bytes(data));
|
||||
offset
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for GenericBufferWriter {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.buf
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub(super) struct VulkanDescriptorSetLayout {
|
|||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) layout: DescriptorSetLayout,
|
||||
pub(super) size: DeviceSize,
|
||||
pub(super) offsets: ArrayVec<DeviceSize, 1>,
|
||||
pub(super) offsets: ArrayVec<DeviceSize, 2>,
|
||||
pub(super) _sampler: Option<Rc<VulkanSampler>>,
|
||||
}
|
||||
|
||||
|
|
@ -87,12 +87,20 @@ impl VulkanDevice {
|
|||
pub(super) fn create_tex_resource_descriptor_set_layout(
|
||||
self: &Rc<Self>,
|
||||
) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> {
|
||||
let binding = DescriptorSetLayoutBinding::default()
|
||||
.stage_flags(ShaderStageFlags::FRAGMENT)
|
||||
.descriptor_count(1)
|
||||
.descriptor_type(DescriptorType::SAMPLED_IMAGE);
|
||||
let bindings = [
|
||||
DescriptorSetLayoutBinding::default()
|
||||
.binding(0)
|
||||
.stage_flags(ShaderStageFlags::FRAGMENT)
|
||||
.descriptor_count(1)
|
||||
.descriptor_type(DescriptorType::SAMPLED_IMAGE),
|
||||
DescriptorSetLayoutBinding::default()
|
||||
.binding(1)
|
||||
.stage_flags(ShaderStageFlags::FRAGMENT)
|
||||
.descriptor_count(1)
|
||||
.descriptor_type(DescriptorType::UNIFORM_BUFFER),
|
||||
];
|
||||
let create_info = DescriptorSetLayoutCreateInfo::default()
|
||||
.bindings(slice::from_ref(&binding))
|
||||
.bindings(&bindings)
|
||||
.flags(DescriptorSetLayoutCreateFlags::DESCRIPTOR_BUFFER_EXT);
|
||||
let layout = unsafe { self.device.create_descriptor_set_layout(&create_info, None) };
|
||||
let layout = layout.map_err(VulkanError::CreateDescriptorSetLayout)?;
|
||||
|
|
@ -101,6 +109,7 @@ impl VulkanDevice {
|
|||
let mut offsets = ArrayVec::new();
|
||||
unsafe {
|
||||
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 0));
|
||||
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 1));
|
||||
}
|
||||
Ok(Rc::new(VulkanDescriptorSetLayout {
|
||||
device: self.clone(),
|
||||
|
|
|
|||
|
|
@ -37,8 +37,9 @@ use {
|
|||
PhysicalDeviceDrmPropertiesEXT, PhysicalDeviceDynamicRenderingFeatures,
|
||||
PhysicalDeviceExternalSemaphoreInfo, PhysicalDeviceProperties,
|
||||
PhysicalDeviceProperties2, PhysicalDeviceSynchronization2Features,
|
||||
PhysicalDeviceTimelineSemaphoreFeatures, PhysicalDeviceVulkan12Properties, Queue,
|
||||
QueueFlags,
|
||||
PhysicalDeviceTimelineSemaphoreFeatures,
|
||||
PhysicalDeviceUniformBufferStandardLayoutFeatures, PhysicalDeviceVulkan12Properties,
|
||||
Queue, QueueFlags,
|
||||
},
|
||||
},
|
||||
isnt::std_1::collections::IsntHashMap2Ext,
|
||||
|
|
@ -75,6 +76,8 @@ pub struct VulkanDevice {
|
|||
pub(super) sampler_descriptor_size: usize,
|
||||
pub(super) sampled_image_descriptor_size: usize,
|
||||
pub(super) is_anv: bool,
|
||||
pub(super) uniform_buffer_offset_mask: DeviceSize,
|
||||
pub(super) uniform_buffer_descriptor_size: usize,
|
||||
}
|
||||
|
||||
impl Drop for VulkanDevice {
|
||||
|
|
@ -308,6 +311,9 @@ impl VulkanInstance {
|
|||
PhysicalDeviceDescriptorBufferFeaturesEXT::default().descriptor_buffer(true);
|
||||
let mut buffer_device_address_features =
|
||||
PhysicalDeviceBufferDeviceAddressFeatures::default().buffer_device_address(true);
|
||||
let mut uniform_buffer_standard_layout_features =
|
||||
PhysicalDeviceUniformBufferStandardLayoutFeatures::default()
|
||||
.uniform_buffer_standard_layout(true);
|
||||
let mut queue_create_infos = ArrayVec::<_, 2>::new();
|
||||
queue_create_infos.push(
|
||||
DeviceQueueCreateInfo::default()
|
||||
|
|
@ -325,6 +331,7 @@ impl VulkanInstance {
|
|||
.push_next(&mut semaphore_features)
|
||||
.push_next(&mut synchronization2_features)
|
||||
.push_next(&mut dynamic_rendering_features)
|
||||
.push_next(&mut uniform_buffer_standard_layout_features)
|
||||
.queue_create_infos(&queue_create_infos)
|
||||
.enabled_extension_names(&enabled_extensions);
|
||||
if supports_descriptor_buffer {
|
||||
|
|
@ -382,6 +389,14 @@ impl VulkanInstance {
|
|||
let mut descriptor_buffer_offset_mask = 0;
|
||||
let mut sampler_descriptor_size = 0;
|
||||
let mut sampled_image_descriptor_size = 0;
|
||||
let mut uniform_buffer_descriptor_size = 0;
|
||||
let uniform_buffer_offset_mask = physical_device_properties2
|
||||
.properties
|
||||
.limits
|
||||
.min_uniform_buffer_offset_alignment
|
||||
.checked_next_power_of_two()
|
||||
.unwrap()
|
||||
- 1;
|
||||
if supports_descriptor_buffer {
|
||||
descriptor_buffer_offset_mask = descriptor_buffer_props
|
||||
.descriptor_buffer_offset_alignment
|
||||
|
|
@ -390,6 +405,7 @@ impl VulkanInstance {
|
|||
- 1;
|
||||
sampler_descriptor_size = descriptor_buffer_props.sampler_descriptor_size;
|
||||
sampled_image_descriptor_size = descriptor_buffer_props.sampled_image_descriptor_size;
|
||||
uniform_buffer_descriptor_size = descriptor_buffer_props.uniform_buffer_descriptor_size;
|
||||
}
|
||||
let memory_properties =
|
||||
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
|
||||
|
|
@ -432,6 +448,8 @@ impl VulkanInstance {
|
|||
blend_limits,
|
||||
is_anv: physical_device_vulkan12_properties.driver_id
|
||||
== DriverId::INTEL_OPEN_SOURCE_MESA,
|
||||
uniform_buffer_offset_mask,
|
||||
uniform_buffer_descriptor_size,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::cmm_description::{ColorDescription, LinearColorDescription},
|
||||
format::Format,
|
||||
gfx_api::{
|
||||
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
|
||||
|
|
@ -20,8 +21,8 @@ use {
|
|||
ash::vk::{
|
||||
BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle,
|
||||
DescriptorDataEXT, DescriptorGetInfoEXT, DescriptorImageInfo, DescriptorType, DeviceMemory,
|
||||
DeviceSize, Extent3D, ExternalMemoryHandleTypeFlags, ExternalMemoryImageCreateInfo,
|
||||
FormatFeatureFlags, Image, ImageAspectFlags, ImageCreateFlags, ImageCreateInfo,
|
||||
Extent3D, ExternalMemoryHandleTypeFlags, ExternalMemoryImageCreateInfo, FormatFeatureFlags,
|
||||
Image, ImageAspectFlags, ImageCreateFlags, ImageCreateInfo,
|
||||
ImageDrmFormatModifierExplicitCreateInfoEXT, ImageLayout, ImageMemoryRequirementsInfo2,
|
||||
ImagePlaneMemoryRequirementsInfo, ImageSubresourceRange, ImageTiling, ImageType,
|
||||
ImageUsageFlags, ImageView, ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR,
|
||||
|
|
@ -65,8 +66,6 @@ pub struct VulkanImage {
|
|||
pub(super) ty: VulkanImageMemory,
|
||||
pub(super) bridge: Option<VulkanFramebufferBridge>,
|
||||
pub(super) sampled_image_descriptor: Box<[u8]>,
|
||||
pub(super) descriptor_buffer_version: Cell<u64>,
|
||||
pub(super) descriptor_buffer_offset: Cell<DeviceSize>,
|
||||
pub(super) execution_version: Cell<u64>,
|
||||
}
|
||||
|
||||
|
|
@ -467,8 +466,6 @@ impl VulkanDmaBufImageTemplate {
|
|||
}),
|
||||
bridge,
|
||||
sampled_image_descriptor: self.renderer.sampled_image_descriptor(texture_view),
|
||||
descriptor_buffer_version: Cell::new(0),
|
||||
descriptor_buffer_offset: Cell::new(0),
|
||||
execution_version: Cell::new(0),
|
||||
}))
|
||||
}
|
||||
|
|
@ -552,10 +549,13 @@ impl GfxFramebuffer for VulkanImage {
|
|||
self: Rc<Self>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
cd: &Rc<ColorDescription>,
|
||||
ops: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
clear_cd: &Rc<LinearColorDescription>,
|
||||
region: &Region,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
blend_cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let mut blend_buffer =
|
||||
blend_buffer.map(|b| b.clone().into_vk(&self.renderer.device.device));
|
||||
|
|
@ -574,10 +574,13 @@ impl GfxFramebuffer for VulkanImage {
|
|||
&self,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
ops,
|
||||
clear,
|
||||
clear_cd,
|
||||
region,
|
||||
blend_buffer,
|
||||
blend_cd,
|
||||
)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ pub(super) struct PipelineCreateInfo {
|
|||
pub(super) eotf: u32,
|
||||
pub(super) oetf: u32,
|
||||
pub(super) descriptor_set_layouts: ArrayVec<Rc<VulkanDescriptorSetLayout>, 2>,
|
||||
pub(super) has_color_management_data: bool,
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
|
|
@ -77,8 +78,8 @@ impl VulkanDevice {
|
|||
};
|
||||
let destroy_layout =
|
||||
OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) });
|
||||
let mut frag_spec_data = ArrayVec::<_, { 4 * 4 }>::new();
|
||||
let mut frag_spec_entries = ArrayVec::<_, 4>::new();
|
||||
let mut frag_spec_data = ArrayVec::<_, { 5 * 4 }>::new();
|
||||
let mut frag_spec_entries = ArrayVec::<_, 5>::new();
|
||||
let mut frag_spec_entry = |data: &[u8]| {
|
||||
let entry = SpecializationMapEntry::default()
|
||||
.constant_id(frag_spec_entries.len() as _)
|
||||
|
|
@ -91,6 +92,7 @@ impl VulkanDevice {
|
|||
frag_spec_entry(&(info.has_alpha_mult as u32).to_ne_bytes());
|
||||
frag_spec_entry(&info.eotf.to_ne_bytes());
|
||||
frag_spec_entry(&info.oetf.to_ne_bytes());
|
||||
frag_spec_entry(&(info.has_color_management_data as u32).to_ne_bytes());
|
||||
let frag_spec = SpecializationInfo::default()
|
||||
.map_entries(&frag_spec_entries)
|
||||
.data(&frag_spec_data);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
cmm::{
|
||||
cmm_description::{ColorDescription, LinearColorDescription, LinearColorDescriptionId},
|
||||
cmm_transfer_function::TransferFunction,
|
||||
cmm_transform::ColorMatrix,
|
||||
},
|
||||
cpu_worker::PendingJob,
|
||||
format::XRGB8888,
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxBlendBuffer, GfxFormat,
|
||||
GfxTexture, GfxWriteModifier, ReleaseSync, SyncFile,
|
||||
|
|
@ -10,7 +14,7 @@ use {
|
|||
gfx_apis::vulkan::{
|
||||
VulkanError,
|
||||
allocator::{VulkanAllocator, VulkanThreadedAllocator},
|
||||
buffer_cache::{VulkanBuffer, VulkanBufferCache},
|
||||
buffer_cache::{GenericBufferWriter, VulkanBuffer, VulkanBufferCache},
|
||||
command::{VulkanCommandBuffer, VulkanCommandPool},
|
||||
descriptor::VulkanDescriptorSetLayout,
|
||||
descriptor_buffer::VulkanDescriptorBufferWriter,
|
||||
|
|
@ -23,14 +27,14 @@ use {
|
|||
shaders::{
|
||||
FILL_FRAG, FILL_VERT, FillPushConstants, LEGACY_FILL_FRAG, LEGACY_FILL_VERT,
|
||||
LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants, LegacyTexPushConstants,
|
||||
OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants,
|
||||
TexVertex, VulkanShader,
|
||||
OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, TEX_VERT, TexColorManagementData,
|
||||
TexPushConstants, TexVertex, VulkanShader,
|
||||
},
|
||||
transfer_functions::{TF_LINEAR, TF_SRGB},
|
||||
transfer_functions::{TF_LINEAR, TransferFunctionExt},
|
||||
},
|
||||
io_uring::IoUring,
|
||||
rect::{Rect, Region},
|
||||
theme::{Color, TransferFunction},
|
||||
theme::Color,
|
||||
utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack},
|
||||
video::dmabuf::{DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, dma_buf_export_sync_file},
|
||||
},
|
||||
|
|
@ -42,9 +46,10 @@ use {
|
|||
self, AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferUsageFlags,
|
||||
ClearAttachment, ClearColorValue, ClearRect, ClearValue, CommandBuffer,
|
||||
CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags,
|
||||
CopyImageInfo2, DependencyInfoKHR, DescriptorBufferBindingInfoEXT, DescriptorImageInfo,
|
||||
DescriptorType, DeviceAddress, DeviceSize, Extent2D, Extent3D, ImageAspectFlags,
|
||||
ImageCopy2, ImageLayout, ImageMemoryBarrier2, ImageSubresourceLayers,
|
||||
CopyImageInfo2, DependencyInfoKHR, DescriptorAddressInfoEXT,
|
||||
DescriptorBufferBindingInfoEXT, DescriptorDataEXT, DescriptorGetInfoEXT,
|
||||
DescriptorImageInfo, DescriptorType, DeviceAddress, DeviceSize, Extent2D, Extent3D,
|
||||
ImageAspectFlags, ImageCopy2, ImageLayout, ImageMemoryBarrier2, ImageSubresourceLayers,
|
||||
ImageSubresourceRange, Offset2D, Offset3D, PipelineBindPoint, PipelineStageFlags2,
|
||||
QUEUE_FAMILY_FOREIGN_EXT, Rect2D, RenderingAttachmentInfo, RenderingInfo,
|
||||
SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport,
|
||||
|
|
@ -71,8 +76,11 @@ use {
|
|||
pub struct VulkanRenderer {
|
||||
pub(super) formats: Rc<AHashMap<u32, GfxFormat>>,
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) pipelines: StaticMap<RenderPass, CopyHashMap<vk::Format, Rc<VulkanFormatPipelines>>>,
|
||||
pub(super) out_pipelines: RefCell<AHashMap<vk::Format, Rc<VulkanPipeline>>>,
|
||||
pub(super) fill_pipelines: CopyHashMap<vk::Format, FillPipelines>,
|
||||
pub(super) tex_pipelines:
|
||||
StaticMap<TransferFunction, CopyHashMap<vk::Format, Rc<TexPipelines>>>,
|
||||
pub(super) out_pipelines:
|
||||
StaticMap<TransferFunction, CopyHashMap<OutPipelineKey, Rc<VulkanPipeline>>>,
|
||||
pub(super) gfx_command_buffers: CachedCommandBuffers,
|
||||
pub(super) transfer_command_buffers: Option<CachedCommandBuffers>,
|
||||
pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>,
|
||||
|
|
@ -101,6 +109,7 @@ pub struct VulkanRenderer {
|
|||
pub(super) resource_descriptor_buffer_cache: Rc<VulkanBufferCache>,
|
||||
pub(super) blend_buffers: RefCell<AHashMap<(u32, u32), Weak<VulkanImage>>>,
|
||||
pub(super) shader_buffer_cache: Rc<VulkanBufferCache>,
|
||||
pub(super) uniform_buffer_cache: Rc<VulkanBufferCache>,
|
||||
}
|
||||
|
||||
pub(super) struct CachedCommandBuffers {
|
||||
|
|
@ -130,13 +139,13 @@ pub(super) struct UsedTexture {
|
|||
release_sync: ReleaseSync,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Linearize)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Linearize)]
|
||||
pub(super) enum TexCopyType {
|
||||
Identity,
|
||||
Multiply,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Linearize)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Linearize)]
|
||||
pub(super) enum TexSourceType {
|
||||
Opaque,
|
||||
HasAlpha,
|
||||
|
|
@ -152,7 +161,7 @@ pub(super) struct Memory {
|
|||
wait_semaphore_infos: Vec<SemaphoreSubmitInfo<'static>>,
|
||||
release_fence: Option<Rc<VulkanFence>>,
|
||||
release_sync_file: Option<SyncFile>,
|
||||
used_buffers: ArrayVec<VulkanBuffer, 3>,
|
||||
used_buffers: ArrayVec<VulkanBuffer, 4>,
|
||||
paint_bounds: StaticMap<RenderPass, Option<PaintRegion>>,
|
||||
paint_regions: StaticMap<RenderPass, Vec<PaintRegion>>,
|
||||
clear_rects: StaticMap<RenderPass, Vec<ClearRect>>,
|
||||
|
|
@ -167,6 +176,10 @@ pub(super) struct Memory {
|
|||
tex_targets: Vec<[Point; 2]>,
|
||||
data_buffer: Vec<u8>,
|
||||
out_address: DeviceAddress,
|
||||
color_transforms: ColorTransforms,
|
||||
uniform_buffer_writer: GenericBufferWriter,
|
||||
uniform_buffer_descriptor_cache: Option<Box<[u8]>>,
|
||||
blend_buffer_descriptor_buffer_offset: DeviceAddress,
|
||||
}
|
||||
|
||||
type Point = [[f32; 2]; 4];
|
||||
|
|
@ -187,6 +200,9 @@ struct VulkanTexOp {
|
|||
copy_type: TexCopyType,
|
||||
range_address: DeviceAddress,
|
||||
instances: u32,
|
||||
tex_cd: Rc<ColorDescription>,
|
||||
color_management_data_address: Option<DeviceAddress>,
|
||||
resource_descriptor_buffer_offset: DeviceAddress,
|
||||
}
|
||||
|
||||
struct VulkanFillOp {
|
||||
|
|
@ -221,12 +237,29 @@ pub(super) struct PendingFrame {
|
|||
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
|
||||
waiter: Cell<Option<SpawnedFuture<()>>>,
|
||||
_release_fence: Option<Rc<VulkanFence>>,
|
||||
_used_buffers: ArrayVec<VulkanBuffer, 3>,
|
||||
_used_buffers: ArrayVec<VulkanBuffer, 4>,
|
||||
}
|
||||
|
||||
pub(super) struct VulkanFormatPipelines {
|
||||
pub(super) fill: StaticMap<TexSourceType, Rc<VulkanPipeline>>,
|
||||
pub(super) tex: StaticMap<TexCopyType, StaticMap<TexSourceType, Rc<VulkanPipeline>>>,
|
||||
type FillPipelines = Rc<StaticMap<TexSourceType, Rc<VulkanPipeline>>>;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct TexPipelineKey {
|
||||
tex_copy_type: TexCopyType,
|
||||
tex_source_type: TexSourceType,
|
||||
eotf: TransferFunction,
|
||||
has_color_management_data: bool,
|
||||
}
|
||||
|
||||
pub(super) struct TexPipelines {
|
||||
format: vk::Format,
|
||||
oetf: TransferFunction,
|
||||
pipelines: CopyHashMap<TexPipelineKey, Rc<VulkanPipeline>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub(super) struct OutPipelineKey {
|
||||
format: vk::Format,
|
||||
eotf: TransferFunction,
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
|
|
@ -313,12 +346,19 @@ impl VulkanDevice {
|
|||
let shader_buffer_cache = {
|
||||
// TODO: https://github.com/KhronosGroup/Vulkan-Samples/issues/1286
|
||||
let usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS | BufferUsageFlags::STORAGE_BUFFER;
|
||||
VulkanBufferCache::new(self, &allocator, usage)
|
||||
let align = 8;
|
||||
VulkanBufferCache::new(self, &allocator, usage, align)
|
||||
};
|
||||
let uniform_buffer_cache = {
|
||||
let usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS | BufferUsageFlags::UNIFORM_BUFFER;
|
||||
let align = align_of::<TexColorManagementData>() as DeviceSize;
|
||||
VulkanBufferCache::new(self, &allocator, usage, align)
|
||||
};
|
||||
let render = Rc::new(VulkanRenderer {
|
||||
formats: Rc::new(formats),
|
||||
device: self.clone(),
|
||||
pipelines: Default::default(),
|
||||
fill_pipelines: Default::default(),
|
||||
tex_pipelines: Default::default(),
|
||||
out_pipelines: Default::default(),
|
||||
gfx_command_buffers,
|
||||
transfer_command_buffers,
|
||||
|
|
@ -348,24 +388,18 @@ impl VulkanDevice {
|
|||
resource_descriptor_buffer_cache,
|
||||
blend_buffers: Default::default(),
|
||||
shader_buffer_cache,
|
||||
uniform_buffer_cache,
|
||||
});
|
||||
render.get_or_create_pipelines(XRGB8888.vk_format, RenderPass::FrameBuffer)?;
|
||||
Ok(render)
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanRenderer {
|
||||
fn get_or_create_pipelines(
|
||||
fn get_or_create_fill_pipelines(
|
||||
&self,
|
||||
format: vk::Format,
|
||||
pass: RenderPass,
|
||||
) -> Result<Rc<VulkanFormatPipelines>, VulkanError> {
|
||||
let (eotf, oetf) = match pass {
|
||||
RenderPass::BlendBuffer => (TF_SRGB, TF_LINEAR),
|
||||
RenderPass::FrameBuffer => (TF_SRGB, TF_SRGB),
|
||||
};
|
||||
let pipelines = &self.pipelines[pass];
|
||||
if let Some(pl) = pipelines.get(&format) {
|
||||
) -> Result<FillPipelines, VulkanError> {
|
||||
if let Some(pl) = self.fill_pipelines.get(&format) {
|
||||
return Ok(pl);
|
||||
}
|
||||
let create_fill_pipeline = |src_has_alpha| {
|
||||
|
|
@ -381,55 +415,121 @@ impl VulkanRenderer {
|
|||
blend: src_has_alpha,
|
||||
src_has_alpha,
|
||||
has_alpha_mult: false,
|
||||
eotf,
|
||||
oetf,
|
||||
// all transformations are applied in the compositor
|
||||
eotf: TF_LINEAR,
|
||||
oetf: TF_LINEAR,
|
||||
descriptor_set_layouts: Default::default(),
|
||||
has_color_management_data: false,
|
||||
};
|
||||
self.device.create_pipeline2(info, push_size)
|
||||
};
|
||||
let fill_opaque = create_fill_pipeline(false)?;
|
||||
let fill_alpha = create_fill_pipeline(true)?;
|
||||
let create_tex_pipeline = |src_has_alpha, has_alpha_mult| {
|
||||
let push_size = if self.device.descriptor_buffer.is_some() {
|
||||
size_of::<TexPushConstants>()
|
||||
} else {
|
||||
size_of::<LegacyTexPushConstants>()
|
||||
};
|
||||
let info = PipelineCreateInfo {
|
||||
format,
|
||||
vert: self.tex_vert_shader.clone(),
|
||||
frag: self.tex_frag_shader.clone(),
|
||||
blend: src_has_alpha || has_alpha_mult,
|
||||
src_has_alpha,
|
||||
has_alpha_mult,
|
||||
eotf,
|
||||
oetf,
|
||||
descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(),
|
||||
};
|
||||
self.device.create_pipeline2(info, push_size)
|
||||
};
|
||||
let tex_opaque = create_tex_pipeline(false, false)?;
|
||||
let tex_alpha = create_tex_pipeline(true, false)?;
|
||||
let tex_mult_opaque = create_tex_pipeline(false, true)?;
|
||||
let tex_mult_alpha = create_tex_pipeline(true, true)?;
|
||||
let format_pipelines = Rc::new(VulkanFormatPipelines {
|
||||
fill: static_map! {
|
||||
TexSourceType::HasAlpha => fill_alpha.clone(),
|
||||
TexSourceType::Opaque => fill_opaque.clone(),
|
||||
},
|
||||
tex: static_map! {
|
||||
TexCopyType::Identity => static_map! {
|
||||
TexSourceType::HasAlpha => tex_alpha.clone(),
|
||||
TexSourceType::Opaque => tex_opaque.clone(),
|
||||
},
|
||||
TexCopyType::Multiply => static_map! {
|
||||
TexSourceType::HasAlpha => tex_mult_alpha.clone(),
|
||||
TexSourceType::Opaque => tex_mult_opaque.clone(),
|
||||
},
|
||||
},
|
||||
let fill_pipelines = Rc::new(static_map! {
|
||||
TexSourceType::HasAlpha => create_fill_pipeline(true)?,
|
||||
TexSourceType::Opaque => create_fill_pipeline(false)?,
|
||||
});
|
||||
pipelines.set(format, format_pipelines.clone());
|
||||
Ok(format_pipelines)
|
||||
self.fill_pipelines.set(format, fill_pipelines.clone());
|
||||
Ok(fill_pipelines)
|
||||
}
|
||||
|
||||
fn get_or_create_tex_pipelines(
|
||||
&self,
|
||||
format: vk::Format,
|
||||
target_cd: &ColorDescription,
|
||||
) -> Rc<TexPipelines> {
|
||||
let pipelines = &self.tex_pipelines[target_cd.transfer_function];
|
||||
match pipelines.get(&format) {
|
||||
Some(pl) => pl,
|
||||
_ => {
|
||||
let pl = Rc::new(TexPipelines {
|
||||
format,
|
||||
oetf: target_cd.transfer_function,
|
||||
pipelines: Default::default(),
|
||||
});
|
||||
pipelines.set(format, pl.clone());
|
||||
pl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_create_tex_pipeline(
|
||||
&self,
|
||||
pipelines: &TexPipelines,
|
||||
tex_cd: &ColorDescription,
|
||||
tex_copy_type: TexCopyType,
|
||||
tex_source_type: TexSourceType,
|
||||
has_color_management_data: bool,
|
||||
) -> Result<Rc<VulkanPipeline>, VulkanError> {
|
||||
let key = TexPipelineKey {
|
||||
tex_copy_type,
|
||||
tex_source_type,
|
||||
eotf: tex_cd.transfer_function,
|
||||
has_color_management_data,
|
||||
};
|
||||
if let Some(pl) = pipelines.pipelines.get(&key) {
|
||||
return Ok(pl);
|
||||
}
|
||||
let src_has_alpha = match tex_source_type {
|
||||
TexSourceType::Opaque => false,
|
||||
TexSourceType::HasAlpha => true,
|
||||
};
|
||||
let has_alpha_mult = match tex_copy_type {
|
||||
TexCopyType::Identity => false,
|
||||
TexCopyType::Multiply => true,
|
||||
};
|
||||
let push_size = if self.device.descriptor_buffer.is_some() {
|
||||
size_of::<TexPushConstants>()
|
||||
} else {
|
||||
size_of::<LegacyTexPushConstants>()
|
||||
};
|
||||
let info = PipelineCreateInfo {
|
||||
format: pipelines.format,
|
||||
vert: self.tex_vert_shader.clone(),
|
||||
frag: self.tex_frag_shader.clone(),
|
||||
blend: src_has_alpha || has_alpha_mult,
|
||||
src_has_alpha,
|
||||
has_alpha_mult,
|
||||
eotf: key.eotf.to_vulkan(),
|
||||
oetf: pipelines.oetf.to_vulkan(),
|
||||
descriptor_set_layouts: self.tex_descriptor_set_layouts.clone(),
|
||||
has_color_management_data,
|
||||
};
|
||||
let pl = self.device.create_pipeline2(info, push_size)?;
|
||||
pipelines.pipelines.set(key, pl.clone());
|
||||
Ok(pl)
|
||||
}
|
||||
|
||||
fn get_or_create_out_pipeline(
|
||||
&self,
|
||||
format: vk::Format,
|
||||
bb_cd: &ColorDescription,
|
||||
fb_cd: &ColorDescription,
|
||||
) -> Result<Rc<VulkanPipeline>, VulkanError> {
|
||||
let key = OutPipelineKey {
|
||||
format,
|
||||
eotf: bb_cd.transfer_function,
|
||||
};
|
||||
let pipelines = &self.out_pipelines[fb_cd.transfer_function];
|
||||
if let Some(pl) = pipelines.get(&key) {
|
||||
return Ok(pl);
|
||||
}
|
||||
let mut descriptor_set_layouts = ArrayVec::new();
|
||||
descriptor_set_layouts.push(self.out_descriptor_set_layout.clone().unwrap());
|
||||
let out = self
|
||||
.device
|
||||
.create_pipeline::<OutPushConstants>(PipelineCreateInfo {
|
||||
format: key.format,
|
||||
vert: self.out_vert_shader.clone().unwrap(),
|
||||
frag: self.out_frag_shader.clone().unwrap(),
|
||||
blend: false,
|
||||
src_has_alpha: true,
|
||||
has_alpha_mult: false,
|
||||
eotf: key.eotf.to_vulkan(),
|
||||
oetf: fb_cd.transfer_function.to_vulkan(),
|
||||
descriptor_set_layouts,
|
||||
has_color_management_data: false,
|
||||
})?;
|
||||
pipelines.set(key, out.clone());
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub(super) fn allocate_point(&self) -> u64 {
|
||||
|
|
@ -445,7 +545,6 @@ impl VulkanRenderer {
|
|||
return Ok(());
|
||||
};
|
||||
zone!("create_descriptor_buffers");
|
||||
let version = self.allocate_point();
|
||||
let memory = &mut *self.memory.borrow_mut();
|
||||
let sampler_writer = &mut memory.sampler_descriptor_buffer_writer;
|
||||
sampler_writer.clear();
|
||||
|
|
@ -458,30 +557,47 @@ impl VulkanRenderer {
|
|||
}
|
||||
let resource_writer = &mut memory.resource_descriptor_buffer_writer;
|
||||
resource_writer.clear();
|
||||
let uniform_buffer_descriptor_cache = memory
|
||||
.uniform_buffer_descriptor_cache
|
||||
.get_or_insert_with(|| {
|
||||
vec![0u8; self.device.uniform_buffer_descriptor_size].into_boxed_slice()
|
||||
});
|
||||
if let Some(bb) = bb {
|
||||
let layout = self.out_descriptor_set_layout.as_ref().unwrap();
|
||||
let offset = resource_writer.next_offset();
|
||||
bb.descriptor_buffer_offset.set(offset);
|
||||
memory.blend_buffer_descriptor_buffer_offset = resource_writer.next_offset();
|
||||
let mut writer = resource_writer.add_set(layout);
|
||||
writer.write(layout.offsets[0], &bb.sampled_image_descriptor);
|
||||
}
|
||||
let tex_descriptor_set_layout = &self.tex_descriptor_set_layouts[1];
|
||||
for pass in RenderPass::variants() {
|
||||
for cmd in &memory.ops[pass] {
|
||||
for cmd in &mut memory.ops[pass] {
|
||||
let VulkanOp::Tex(c) = cmd else {
|
||||
continue;
|
||||
};
|
||||
let tex = &c.tex;
|
||||
if tex.descriptor_buffer_version.replace(version) == version {
|
||||
continue;
|
||||
}
|
||||
let offset = resource_writer.next_offset();
|
||||
tex.descriptor_buffer_offset.set(offset);
|
||||
c.resource_descriptor_buffer_offset = resource_writer.next_offset();
|
||||
let mut writer = resource_writer.add_set(tex_descriptor_set_layout);
|
||||
writer.write(
|
||||
tex_descriptor_set_layout.offsets[0],
|
||||
&tex.sampled_image_descriptor,
|
||||
);
|
||||
if let Some(addr) = c.color_management_data_address {
|
||||
let uniform_buffer = DescriptorAddressInfoEXT::default()
|
||||
.address(addr)
|
||||
.range(size_of::<TexColorManagementData>() as _);
|
||||
let info = DescriptorGetInfoEXT::default()
|
||||
.ty(DescriptorType::UNIFORM_BUFFER)
|
||||
.data(DescriptorDataEXT {
|
||||
p_uniform_buffer: &uniform_buffer,
|
||||
});
|
||||
unsafe {
|
||||
db.get_descriptor(&info, uniform_buffer_descriptor_cache);
|
||||
}
|
||||
writer.write(
|
||||
tex_descriptor_set_layout.offsets[1],
|
||||
uniform_buffer_descriptor_cache,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut infos = ArrayVec::<_, 2>::new();
|
||||
|
|
@ -489,12 +605,7 @@ impl VulkanRenderer {
|
|||
(&sampler_writer, &self.sampler_descriptor_buffer_cache),
|
||||
(&resource_writer, &self.resource_descriptor_buffer_cache),
|
||||
] {
|
||||
let mut min_alignment = 1;
|
||||
if self.device.is_anv {
|
||||
// https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33903
|
||||
min_alignment = 4096;
|
||||
}
|
||||
let buffer = cache.allocate(writer.len() as DeviceSize, min_alignment)?;
|
||||
let buffer = cache.allocate(writer.len() as DeviceSize)?;
|
||||
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
||||
ptr::copy_nonoverlapping(writer.as_ptr(), ptr, writer.len())
|
||||
})?;
|
||||
|
|
@ -510,7 +621,12 @@ impl VulkanRenderer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn convert_ops(&self, opts: &[GfxApiOpt]) {
|
||||
fn convert_ops(
|
||||
&self,
|
||||
opts: &[GfxApiOpt],
|
||||
blend_cd: &ColorDescription,
|
||||
fb_cd: &ColorDescription,
|
||||
) {
|
||||
zone!("convert_ops");
|
||||
let memory = &mut *self.memory.borrow_mut();
|
||||
for ops in memory.ops.values_mut() {
|
||||
|
|
@ -522,6 +638,8 @@ impl VulkanRenderer {
|
|||
memory.tex_targets.clear();
|
||||
memory.fill_targets.clear();
|
||||
memory.data_buffer.clear();
|
||||
memory.uniform_buffer_writer.clear();
|
||||
memory.color_transforms.map.clear();
|
||||
let sync = |memory: &mut Memory| {
|
||||
for pass in RenderPass::variants() {
|
||||
let ops = &mut memory.ops_tmp[pass];
|
||||
|
|
@ -588,10 +706,6 @@ impl VulkanRenderer {
|
|||
if !bounds.intersects(&target) {
|
||||
continue;
|
||||
}
|
||||
let tf = match pass {
|
||||
RenderPass::BlendBuffer => TransferFunction::Linear,
|
||||
RenderPass::FrameBuffer => TransferFunction::Srgb,
|
||||
};
|
||||
let ops = &mut memory.ops_tmp[pass];
|
||||
let lo = memory.fill_targets.len();
|
||||
for region in &memory.paint_regions[pass] {
|
||||
|
|
@ -605,7 +719,15 @@ impl VulkanRenderer {
|
|||
if lo == hi {
|
||||
continue;
|
||||
}
|
||||
let color = fr.color.to_array2(tf, fr.alpha);
|
||||
let target_cd = match pass {
|
||||
RenderPass::BlendBuffer => blend_cd,
|
||||
RenderPass::FrameBuffer => fb_cd,
|
||||
};
|
||||
let tf = target_cd.transfer_function;
|
||||
let color = memory
|
||||
.color_transforms
|
||||
.apply_to_color(&fr.cd, target_cd, fr.color);
|
||||
let color = color.to_array2(tf, fr.alpha);
|
||||
let source_type = match color[3] < 1.0 {
|
||||
false => TexSourceType::Opaque,
|
||||
true => TexSourceType::HasAlpha,
|
||||
|
|
@ -661,6 +783,16 @@ impl VulkanRenderer {
|
|||
true => TexSourceType::HasAlpha,
|
||||
false => TexSourceType::Opaque,
|
||||
};
|
||||
let target_cd = match pass {
|
||||
RenderPass::BlendBuffer => blend_cd,
|
||||
RenderPass::FrameBuffer => fb_cd,
|
||||
};
|
||||
let color_management_data_address = memory.color_transforms.get_offset(
|
||||
&ct.cd.linear,
|
||||
target_cd,
|
||||
self.device.uniform_buffer_offset_mask,
|
||||
&mut memory.uniform_buffer_writer,
|
||||
);
|
||||
ops.push(VulkanOp::Tex(VulkanTexOp {
|
||||
tex: tex.clone(),
|
||||
range: lo..hi,
|
||||
|
|
@ -672,6 +804,9 @@ impl VulkanRenderer {
|
|||
copy_type,
|
||||
range_address: 0,
|
||||
instances: 0,
|
||||
tex_cd: ct.cd.clone(),
|
||||
color_management_data_address,
|
||||
resource_descriptor_buffer_offset: 0,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -701,7 +836,7 @@ impl VulkanRenderer {
|
|||
if buf.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let buffer = self.shader_buffer_cache.allocate(buf.len() as _, 8)?;
|
||||
let buffer = self.shader_buffer_cache.allocate(buf.len() as _)?;
|
||||
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
||||
ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len());
|
||||
})?;
|
||||
|
|
@ -722,6 +857,33 @@ impl VulkanRenderer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn create_uniform_buffer(&self) -> Result<(), VulkanError> {
|
||||
if self.device.descriptor_buffer.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
zone!("create_uniform_buffer");
|
||||
let memory = &mut *self.memory.borrow_mut();
|
||||
let buf = &memory.uniform_buffer_writer;
|
||||
if buf.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let buffer = self.uniform_buffer_cache.allocate(buf.len() as _)?;
|
||||
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
||||
ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len());
|
||||
})?;
|
||||
for ops in memory.ops.values_mut() {
|
||||
for op in ops {
|
||||
if let VulkanOp::Tex(c) = op {
|
||||
if let Some(addr) = &mut c.color_management_data_address {
|
||||
*addr += buffer.buffer.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
memory.used_buffers.push(buffer);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collect_memory(&self) {
|
||||
zone!("collect_memory");
|
||||
let memory = &mut *self.memory.borrow_mut();
|
||||
|
|
@ -865,7 +1027,9 @@ impl VulkanRenderer {
|
|||
buf: CommandBuffer,
|
||||
target: &VulkanImage,
|
||||
clear: Option<&Color>,
|
||||
clear_cd: &LinearColorDescription,
|
||||
pass: RenderPass,
|
||||
target_cd: &ColorDescription,
|
||||
) {
|
||||
zone!("begin_rendering");
|
||||
let memory = &mut *self.memory.borrow_mut();
|
||||
|
|
@ -874,12 +1038,12 @@ impl VulkanRenderer {
|
|||
let clear_rects = &memory.clear_rects[pass];
|
||||
if let Some(clear) = clear {
|
||||
if clear_rects.is_not_empty() {
|
||||
let color = memory
|
||||
.color_transforms
|
||||
.apply_to_color(clear_cd, target_cd, *clear);
|
||||
let clear_value = ClearValue {
|
||||
color: ClearColorValue {
|
||||
float32: match pass {
|
||||
RenderPass::BlendBuffer => clear.to_array(TransferFunction::Linear),
|
||||
RenderPass::FrameBuffer => clear.to_array(TransferFunction::Srgb),
|
||||
},
|
||||
float32: color.to_array(target_cd.transfer_function),
|
||||
},
|
||||
};
|
||||
let use_load_clear = clear_rects.len() == 1 && {
|
||||
|
|
@ -969,10 +1133,12 @@ impl VulkanRenderer {
|
|||
buf: CommandBuffer,
|
||||
target: &VulkanImage,
|
||||
pass: RenderPass,
|
||||
target_cd: &ColorDescription,
|
||||
) -> Result<(), VulkanError> {
|
||||
zone!("record_draws");
|
||||
let memory = &*self.memory.borrow();
|
||||
let pipelines = self.get_or_create_pipelines(target.format.vk_format, pass)?;
|
||||
let fill_pl = self.get_or_create_fill_pipelines(target.format.vk_format)?;
|
||||
let tex_pl = self.get_or_create_tex_pipelines(target.format.vk_format, target_cd);
|
||||
let dev = &self.device.device;
|
||||
let mut current_pipeline = None;
|
||||
let mut bind = |pipeline: &VulkanPipeline| {
|
||||
|
|
@ -986,7 +1152,7 @@ impl VulkanRenderer {
|
|||
for opt in &memory.ops[pass] {
|
||||
match opt {
|
||||
VulkanOp::Fill(r) => {
|
||||
let pipeline = &pipelines.fill[r.source_type];
|
||||
let pipeline = &fill_pl[r.source_type];
|
||||
bind(pipeline);
|
||||
if self.device.descriptor_buffer.is_some() {
|
||||
let push = FillPushConstants {
|
||||
|
|
@ -1026,8 +1192,14 @@ impl VulkanRenderer {
|
|||
}
|
||||
VulkanOp::Tex(c) => {
|
||||
let tex = &c.tex;
|
||||
let pipeline = &pipelines.tex[c.copy_type][c.source_type];
|
||||
bind(pipeline);
|
||||
let pipeline = self.get_or_create_tex_pipeline(
|
||||
&tex_pl,
|
||||
&c.tex_cd,
|
||||
c.copy_type,
|
||||
c.source_type,
|
||||
c.color_management_data_address.is_some(),
|
||||
)?;
|
||||
bind(&pipeline);
|
||||
let image_info = DescriptorImageInfo::default()
|
||||
.image_view(tex.texture_view)
|
||||
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
|
||||
|
|
@ -1043,7 +1215,7 @@ impl VulkanRenderer {
|
|||
pipeline.pipeline_layout,
|
||||
0,
|
||||
&[0, 1],
|
||||
&[0, tex.descriptor_buffer_offset.get()],
|
||||
&[0, c.resource_descriptor_buffer_offset],
|
||||
);
|
||||
dev.cmd_push_constants(
|
||||
buf,
|
||||
|
|
@ -1118,33 +1290,13 @@ impl VulkanRenderer {
|
|||
&self,
|
||||
buf: CommandBuffer,
|
||||
fb: &VulkanImage,
|
||||
bb: &VulkanImage,
|
||||
fb_cd: &ColorDescription,
|
||||
bb_cd: &ColorDescription,
|
||||
) -> Result<(), VulkanError> {
|
||||
zone!("blend_buffer_copy");
|
||||
let memory = &*self.memory.borrow();
|
||||
let db = self.device.descriptor_buffer.as_ref().unwrap();
|
||||
let pipeline = match self.out_pipelines.borrow_mut().entry(fb.format.vk_format) {
|
||||
Entry::Occupied(pipeline) => pipeline.get().clone(),
|
||||
Entry::Vacant(e) => {
|
||||
let mut descriptor_set_layouts = ArrayVec::new();
|
||||
descriptor_set_layouts.push(self.out_descriptor_set_layout.clone().unwrap());
|
||||
let out = self
|
||||
.device
|
||||
.create_pipeline::<OutPushConstants>(PipelineCreateInfo {
|
||||
format: fb.format.vk_format,
|
||||
vert: self.out_vert_shader.clone().unwrap(),
|
||||
frag: self.out_frag_shader.clone().unwrap(),
|
||||
blend: false,
|
||||
src_has_alpha: true,
|
||||
has_alpha_mult: false,
|
||||
eotf: TF_LINEAR,
|
||||
oetf: TF_SRGB,
|
||||
descriptor_set_layouts,
|
||||
})?;
|
||||
e.insert(out.clone());
|
||||
out
|
||||
}
|
||||
};
|
||||
let pipeline = self.get_or_create_out_pipeline(fb.format.vk_format, bb_cd, fb_cd)?;
|
||||
let push = OutPushConstants {
|
||||
vertices: memory.out_address,
|
||||
};
|
||||
|
|
@ -1158,7 +1310,7 @@ impl VulkanRenderer {
|
|||
pipeline.pipeline_layout,
|
||||
0,
|
||||
&[1],
|
||||
&[bb.descriptor_buffer_offset.get()],
|
||||
&[memory.blend_buffer_descriptor_buffer_offset],
|
||||
);
|
||||
dev.cmd_push_constants(
|
||||
buf,
|
||||
|
|
@ -1530,20 +1682,26 @@ impl VulkanRenderer {
|
|||
fb: &Rc<VulkanImage>,
|
||||
fb_acquire_sync: AcquireSync,
|
||||
fb_release_sync: ReleaseSync,
|
||||
fb_cd: &Rc<ColorDescription>,
|
||||
opts: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
clear_cd: &Rc<LinearColorDescription>,
|
||||
region: &Region,
|
||||
blend_buffer: Option<Rc<VulkanImage>>,
|
||||
blend_cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, VulkanError> {
|
||||
zone!("execute");
|
||||
let res = self.try_execute(
|
||||
fb,
|
||||
fb_acquire_sync,
|
||||
fb_release_sync,
|
||||
fb_cd,
|
||||
opts,
|
||||
clear,
|
||||
clear_cd,
|
||||
region,
|
||||
blend_buffer,
|
||||
blend_cd,
|
||||
);
|
||||
let sync_file = {
|
||||
let mut memory = self.memory.borrow_mut();
|
||||
|
|
@ -1706,18 +1864,22 @@ impl VulkanRenderer {
|
|||
fb: &Rc<VulkanImage>,
|
||||
fb_acquire_sync: AcquireSync,
|
||||
fb_release_sync: ReleaseSync,
|
||||
fb_cd: &Rc<ColorDescription>,
|
||||
opts: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
clear_cd: &Rc<LinearColorDescription>,
|
||||
region: &Region,
|
||||
mut blend_buffer: Option<Rc<VulkanImage>>,
|
||||
bb_cd: &Rc<ColorDescription>,
|
||||
) -> Result<(), VulkanError> {
|
||||
self.check_defunct()?;
|
||||
self.create_regions(fb, opts, clear, region, blend_buffer.as_deref());
|
||||
self.elide_blend_buffer(&mut blend_buffer);
|
||||
let bb = blend_buffer.as_deref();
|
||||
let buf = self.gfx_command_buffers.allocate()?;
|
||||
self.convert_ops(opts);
|
||||
self.convert_ops(opts, bb_cd, fb_cd);
|
||||
self.create_data_buffer()?;
|
||||
self.create_uniform_buffer()?;
|
||||
self.collect_memory();
|
||||
self.begin_command_buffer(buf.buffer)?;
|
||||
self.create_descriptor_buffers(buf.buffer, bb)?;
|
||||
|
|
@ -1725,18 +1887,20 @@ impl VulkanRenderer {
|
|||
self.set_viewport(buf.buffer, fb);
|
||||
if let Some(bb) = bb {
|
||||
zone!("blend buffer pass");
|
||||
let rp = RenderPass::BlendBuffer;
|
||||
self.blend_buffer_initial_barrier(buf.buffer, bb);
|
||||
self.begin_rendering(buf.buffer, bb, clear, RenderPass::BlendBuffer);
|
||||
self.record_draws(buf.buffer, bb, RenderPass::BlendBuffer)?;
|
||||
self.begin_rendering(buf.buffer, bb, clear, clear_cd, rp, bb_cd);
|
||||
self.record_draws(buf.buffer, bb, rp, bb_cd)?;
|
||||
self.end_rendering(buf.buffer);
|
||||
self.blend_buffer_final_barrier(buf.buffer, bb);
|
||||
}
|
||||
{
|
||||
zone!("frame buffer pass");
|
||||
self.begin_rendering(buf.buffer, fb, clear, RenderPass::FrameBuffer);
|
||||
self.record_draws(buf.buffer, fb, RenderPass::FrameBuffer)?;
|
||||
if let Some(bb) = bb {
|
||||
self.blend_buffer_copy(buf.buffer, fb, bb)?;
|
||||
let rp = RenderPass::FrameBuffer;
|
||||
self.begin_rendering(buf.buffer, fb, clear, clear_cd, rp, fb_cd);
|
||||
self.record_draws(buf.buffer, fb, rp, fb_cd)?;
|
||||
if bb.is_some() {
|
||||
self.blend_buffer_copy(buf.buffer, fb, fb_cd, bb_cd)?;
|
||||
}
|
||||
self.end_rendering(buf.buffer);
|
||||
}
|
||||
|
|
@ -1955,3 +2119,67 @@ where
|
|||
let y2 = y2.min(fb.height as i32);
|
||||
Some([x1, y1, x2, y2])
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ColorTransforms {
|
||||
map: AHashMap<[LinearColorDescriptionId; 2], ColorTransform>,
|
||||
}
|
||||
|
||||
struct ColorTransform {
|
||||
matrix: ColorMatrix,
|
||||
offset: Option<DeviceSize>,
|
||||
}
|
||||
|
||||
impl ColorTransforms {
|
||||
fn get_or_create(
|
||||
&mut self,
|
||||
src: &LinearColorDescription,
|
||||
dst: &ColorDescription,
|
||||
) -> Option<&mut ColorTransform> {
|
||||
if src.id == dst.linear.id {
|
||||
return None;
|
||||
}
|
||||
let ct = match self.map.entry([src.id, dst.linear.id]) {
|
||||
Entry::Occupied(o) => o.into_mut(),
|
||||
Entry::Vacant(e) => {
|
||||
let matrix = src.color_transform(&dst.linear);
|
||||
let ct = ColorTransform {
|
||||
matrix,
|
||||
offset: None,
|
||||
};
|
||||
e.insert(ct)
|
||||
}
|
||||
};
|
||||
Some(ct)
|
||||
}
|
||||
|
||||
fn apply_to_color(
|
||||
&mut self,
|
||||
src: &LinearColorDescription,
|
||||
dst: &ColorDescription,
|
||||
mut color: Color,
|
||||
) -> Color {
|
||||
if let Some(ct) = self.get_or_create(src, dst) {
|
||||
color = ct.matrix * color;
|
||||
};
|
||||
color
|
||||
}
|
||||
|
||||
fn get_offset(
|
||||
&mut self,
|
||||
src: &LinearColorDescription,
|
||||
dst: &ColorDescription,
|
||||
uniform_buffer_offset_mask: DeviceSize,
|
||||
writer: &mut GenericBufferWriter,
|
||||
) -> Option<DeviceSize> {
|
||||
let ct = self.get_or_create(src, dst)?;
|
||||
if ct.offset.is_none() {
|
||||
let data = TexColorManagementData {
|
||||
matrix: ct.matrix.to_f32(),
|
||||
};
|
||||
let offset = writer.write(uniform_buffer_offset_mask, &data);
|
||||
ct.offset = Some(offset);
|
||||
}
|
||||
ct.offset
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,14 @@ pub struct TexPushConstants {
|
|||
|
||||
unsafe impl Packed for TexPushConstants {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C, align(16))]
|
||||
pub struct TexColorManagementData {
|
||||
pub matrix: [[f32; 4]; 4],
|
||||
}
|
||||
|
||||
unsafe impl Packed for TexColorManagementData {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LegacyTexPushConstants {
|
||||
|
|
|
|||
|
|
@ -5,5 +5,6 @@ layout(constant_id = 0) const bool src_has_alpha = false;
|
|||
layout(constant_id = 1) const bool has_alpha_multiplier = false;
|
||||
layout(constant_id = 2) const uint eotf = 0;
|
||||
layout(constant_id = 3) const uint oetf = 0;
|
||||
layout(constant_id = 4) const bool has_matrix = false;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,25 +1,35 @@
|
|||
#version 450
|
||||
|
||||
#extension GL_EXT_scalar_block_layout : require
|
||||
|
||||
#include "frag_spec_const.glsl"
|
||||
#include "transfer_functions.glsl"
|
||||
#include "tex.common.glsl"
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler sam;
|
||||
layout(set = 1, binding = 0) uniform texture2D tex;
|
||||
layout(set = 1, binding = 1, row_major, std430) uniform ColorManagementData {
|
||||
mat4x4 matrix;
|
||||
} cm_data;
|
||||
layout(location = 0) in vec2 tex_pos;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main() {
|
||||
vec4 c = textureLod(sampler2D(tex, sam), tex_pos, 0);
|
||||
if (eotf != oetf) {
|
||||
if (eotf != oetf || has_matrix) {
|
||||
vec3 rgb = c.rgb;
|
||||
if (src_has_alpha) {
|
||||
c.rgb /= mix(c.a, 1.0, c.a == 0.0);
|
||||
rgb /= mix(c.a, 1.0, c.a == 0.0);
|
||||
}
|
||||
c.rgb = apply_eotf(c.rgb);
|
||||
c.rgb = apply_oetf(c.rgb);
|
||||
rgb = apply_eotf(rgb);
|
||||
if (has_matrix) {
|
||||
rgb = (cm_data.matrix * vec4(rgb, 1.0)).rgb;
|
||||
}
|
||||
rgb = apply_oetf(rgb);
|
||||
if (src_has_alpha) {
|
||||
c.rgb *= c.a;
|
||||
rgb *= c.a;
|
||||
}
|
||||
c.rgb = rgb;
|
||||
}
|
||||
if (has_alpha_multiplier) {
|
||||
if (src_has_alpha) {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,17 @@
|
|||
|
||||
#include "frag_spec_const.glsl"
|
||||
|
||||
#define SRGB 0
|
||||
#define LINEAR 1
|
||||
#define TF_SRGB 0
|
||||
#define TF_LINEAR 1
|
||||
#define TF_ST2084_PQ 2
|
||||
#define TF_BT1886 3
|
||||
#define TF_GAMMA22 4
|
||||
#define TF_GAMMA28 5
|
||||
#define TF_ST240 6
|
||||
#define TF_EXT_SRGB 7
|
||||
#define TF_LOG100 8
|
||||
#define TF_LOG316 9
|
||||
#define TF_ST428 10
|
||||
|
||||
vec3 eotf_srgb(vec3 c) {
|
||||
return mix(
|
||||
|
|
@ -23,18 +32,141 @@ vec3 oetf_srgb(vec3 c) {
|
|||
);
|
||||
}
|
||||
|
||||
vec3 eotf_ext_srgb(vec3 c) {
|
||||
return mix(
|
||||
-pow((c - vec3(0.055)) / vec3(-1.055), vec3(2.4)),
|
||||
mix(
|
||||
c * vec3(1.0 / 12.92),
|
||||
pow((c + vec3(0.055)) / vec3(1.055), vec3(2.4)),
|
||||
greaterThan(c, vec3(0.04045))
|
||||
),
|
||||
greaterThan(c, vec3(-0.04045))
|
||||
);
|
||||
}
|
||||
|
||||
vec3 oetf_ext_srgb(vec3 c) {
|
||||
c = clamp(c, -0.6038, 7.5913);
|
||||
return mix(
|
||||
vec3(-1.055) * pow(-c, vec3(1/2.4)) + vec3(0.055),
|
||||
mix(
|
||||
c * vec3(12.92),
|
||||
vec3(1.055) * pow(c, vec3(1/2.4)) - vec3(0.055),
|
||||
greaterThan(c, vec3(0.0031308))
|
||||
),
|
||||
greaterThan(c, vec3(-0.0031308))
|
||||
);
|
||||
}
|
||||
|
||||
vec3 eotf_st2084_pq(vec3 c) {
|
||||
vec3 cp = pow(c, vec3(1.0 / 78.84375));
|
||||
vec3 num = max(cp - vec3(0.8359375), 0.0);
|
||||
vec3 den = vec3(18.8515625) - vec3(18.6875) * cp;
|
||||
return pow(num / den, vec3(1.0 / 0.1593017578125));
|
||||
}
|
||||
|
||||
vec3 oetf_st2084_pq(vec3 c) {
|
||||
c = clamp(c, 0.0, 1.0);
|
||||
vec3 num = vec3(0.8359375) + vec3(18.8515625) * pow(c, vec3(0.1593017578125));
|
||||
vec3 den = vec3(1.0) + vec3(18.6875) * pow(c, vec3(0.1593017578125));
|
||||
return pow(num / den, vec3(78.84375));
|
||||
}
|
||||
|
||||
vec3 eotf_bt1886(vec3 c) {
|
||||
return mix(
|
||||
c * vec3(1.0 / 4.5),
|
||||
pow((c + vec3(0.099)) * vec3(1.0 / 1.099), vec3(1.0 / 0.45)),
|
||||
greaterThanEqual(c, vec3(0.081))
|
||||
);
|
||||
}
|
||||
|
||||
vec3 oetf_bt1886(vec3 c) {
|
||||
return mix(
|
||||
vec3(4.5) * c,
|
||||
vec3(1.099) * pow(c, vec3(0.45)) - vec3(0.099),
|
||||
greaterThanEqual(c, vec3(0.018))
|
||||
);
|
||||
}
|
||||
|
||||
vec3 eotf_st240(vec3 c) {
|
||||
return mix(
|
||||
c * vec3(1.0 / 4.0),
|
||||
pow((c + vec3(0.1115)) * vec3(1.0 / 1.1115), vec3(1.0 / 0.45)),
|
||||
greaterThanEqual(c, vec3(0.0913))
|
||||
);
|
||||
}
|
||||
|
||||
vec3 oetf_st240(vec3 c) {
|
||||
return mix(
|
||||
vec3(4.0) * c,
|
||||
vec3(1.1115) * pow(c, vec3(0.45)) - vec3(0.1115),
|
||||
greaterThanEqual(c, vec3(0.0228))
|
||||
);
|
||||
}
|
||||
|
||||
vec3 eotf_log100(vec3 c) {
|
||||
return pow(vec3(10), vec3(2.0) * (c - vec3(1.0)));
|
||||
}
|
||||
|
||||
vec3 oetf_log100(vec3 c) {
|
||||
c = clamp(c, 0.0, 1.0);
|
||||
return mix(
|
||||
vec3(0.0),
|
||||
vec3(1.0) + log2(c) / vec3(log2(10)) / vec3(2.0),
|
||||
greaterThanEqual(c, vec3(0.01))
|
||||
);
|
||||
}
|
||||
|
||||
vec3 eotf_log316(vec3 c) {
|
||||
return pow(vec3(10), vec3(2.5) * (c - vec3(1.0)));
|
||||
}
|
||||
|
||||
vec3 oetf_log316(vec3 c) {
|
||||
c = clamp(c, 0.0, 1.0);
|
||||
return mix(
|
||||
vec3(0.0),
|
||||
vec3(1.0) + log2(c) / vec3(log2(10)) / vec3(2.5),
|
||||
greaterThanEqual(c, vec3(sqrt(10) / 1000.0))
|
||||
);
|
||||
}
|
||||
|
||||
vec3 eotf_st428(vec3 c) {
|
||||
return pow(c, vec3(2.6)) * vec3(52.37 / 48.0);
|
||||
}
|
||||
|
||||
vec3 oetf_st428(vec3 c) {
|
||||
return pow(vec3(48.0) * c / vec3(52.37), vec3(1.0 / 2.6));
|
||||
}
|
||||
|
||||
vec3 apply_eotf(vec3 c) {
|
||||
switch (eotf) {
|
||||
case SRGB: return eotf_srgb(c);
|
||||
case LINEAR: return c;
|
||||
case TF_SRGB: return eotf_srgb(c);
|
||||
case TF_LINEAR: return c;
|
||||
case TF_ST2084_PQ: return eotf_st2084_pq(c);
|
||||
case TF_BT1886: return eotf_bt1886(c);
|
||||
case TF_GAMMA22: return pow(c, vec3(2.2));
|
||||
case TF_GAMMA28: return pow(c, vec3(2.8));
|
||||
case TF_ST240: return eotf_st240(c);
|
||||
case TF_EXT_SRGB: return eotf_ext_srgb(c);
|
||||
case TF_LOG100: return eotf_log100(c);
|
||||
case TF_LOG316: return eotf_log316(c);
|
||||
case TF_ST428: return eotf_st428(c);
|
||||
default: return c;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 apply_oetf(vec3 c) {
|
||||
switch (oetf) {
|
||||
case SRGB: return oetf_srgb(c);
|
||||
case LINEAR: return c;
|
||||
case TF_SRGB: return oetf_srgb(c);
|
||||
case TF_LINEAR: return c;
|
||||
case TF_ST2084_PQ: return oetf_st2084_pq(c);
|
||||
case TF_BT1886: return oetf_bt1886(c);
|
||||
case TF_GAMMA22: return pow(c, vec3(1.0 / 2.2));
|
||||
case TF_GAMMA28: return pow(c, vec3(1.0 / 2.8));
|
||||
case TF_ST240: return oetf_st240(c);
|
||||
case TF_EXT_SRGB: return oetf_ext_srgb(c);
|
||||
case TF_LOG100: return oetf_log100(c);
|
||||
case TF_LOG316: return oetf_log316(c);
|
||||
case TF_ST428: return oetf_st428(c);
|
||||
default: return c;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -460,8 +460,6 @@ impl VulkanRenderer {
|
|||
ty: VulkanImageMemory::Internal(shm),
|
||||
bridge: None,
|
||||
sampled_image_descriptor: self.sampled_image_descriptor(view),
|
||||
descriptor_buffer_version: Cell::new(0),
|
||||
descriptor_buffer_offset: Cell::new(0),
|
||||
execution_version: Cell::new(0),
|
||||
});
|
||||
let shm = match &img.ty {
|
||||
|
|
|
|||
|
|
@ -1,2 +1,35 @@
|
|||
use crate::cmm::cmm_transfer_function::TransferFunction;
|
||||
|
||||
pub const TF_SRGB: u32 = 0;
|
||||
pub const TF_LINEAR: u32 = 1;
|
||||
pub const TF_ST2084_PQ: u32 = 2;
|
||||
pub const TF_BT1887: u32 = 3;
|
||||
pub const TF_GAMMA22: u32 = 4;
|
||||
pub const TF_GAMMA28: u32 = 5;
|
||||
pub const TF_ST240: u32 = 6;
|
||||
pub const TF_EXT_SRGB: u32 = 7;
|
||||
pub const TF_LOG100: u32 = 8;
|
||||
pub const TF_LOG316: u32 = 9;
|
||||
pub const TF_ST428: u32 = 10;
|
||||
|
||||
pub trait TransferFunctionExt: Sized {
|
||||
fn to_vulkan(self) -> u32;
|
||||
}
|
||||
|
||||
impl TransferFunctionExt for TransferFunction {
|
||||
fn to_vulkan(self) -> u32 {
|
||||
match self {
|
||||
TransferFunction::Srgb => TF_SRGB,
|
||||
TransferFunction::Linear => TF_LINEAR,
|
||||
TransferFunction::St2084Pq => TF_ST2084_PQ,
|
||||
TransferFunction::Bt1886 => TF_BT1887,
|
||||
TransferFunction::Gamma22 => TF_GAMMA22,
|
||||
TransferFunction::Gamma28 => TF_GAMMA28,
|
||||
TransferFunction::St240 => TF_ST240,
|
||||
TransferFunction::ExtSrgb => TF_EXT_SRGB,
|
||||
TransferFunction::Log100 => TF_LOG100,
|
||||
TransferFunction::Log316 => TF_LOG316,
|
||||
TransferFunction::St428 => TF_ST428,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,56 +1,63 @@
|
|||
pub use consts::*;
|
||||
|
||||
pub mod wp_color_management_output_v1;
|
||||
pub mod wp_color_management_surface_feedback_v1;
|
||||
pub mod wp_color_management_surface_v1;
|
||||
pub mod wp_color_manager_v1;
|
||||
pub mod wp_image_description_creator_icc_v1;
|
||||
pub mod wp_image_description_creator_params_v1;
|
||||
pub mod wp_image_description_info_v1;
|
||||
pub mod wp_image_description_v1;
|
||||
|
||||
const PRIMARIES_MUL: f64 = 1_000_000.0;
|
||||
const PRIMARIES_MUL_INV: f64 = 1.0 / PRIMARIES_MUL;
|
||||
|
||||
const MIN_LUM_MUL: f64 = 10_000.0;
|
||||
const MIN_LUM_MUL_INV: f64 = 1.0 / MIN_LUM_MUL;
|
||||
|
||||
#[expect(dead_code)]
|
||||
mod consts {
|
||||
pub(super) const RENDER_INTENT_PERCEPTUAL: u32 = 0;
|
||||
pub(super) const RENDER_INTENT_RELATIVE: u32 = 1;
|
||||
pub(super) const RENDER_INTENT_SATURATION: u32 = 2;
|
||||
pub(super) const RENDER_INTENT_ABSOLUTE: u32 = 3;
|
||||
pub(super) const RENDER_INTENT_RELATIVE_BPC: u32 = 4;
|
||||
pub const RENDER_INTENT_PERCEPTUAL: u32 = 0;
|
||||
pub const RENDER_INTENT_RELATIVE: u32 = 1;
|
||||
pub const RENDER_INTENT_SATURATION: u32 = 2;
|
||||
pub const RENDER_INTENT_ABSOLUTE: u32 = 3;
|
||||
pub const RENDER_INTENT_RELATIVE_BPC: u32 = 4;
|
||||
|
||||
pub(super) const FEATURE_ICC_V2_V4: u32 = 0;
|
||||
pub(super) const FEATURE_PARAMETRIC: u32 = 1;
|
||||
pub(super) const FEATURE_SET_PRIMARIES: u32 = 2;
|
||||
pub(super) const FEATURE_SET_TF_POWER: u32 = 3;
|
||||
pub(super) const FEATURE_SET_LUMINANCES: u32 = 4;
|
||||
pub(super) const FEATURE_SET_MASTERING_DISPLAY_PRIMARIES: u32 = 5;
|
||||
pub(super) const FEATURE_EXTENDED_TARGET_VOLUME: u32 = 6;
|
||||
pub(super) const FEATURE_WINDOWS_SCRGB: u32 = 7;
|
||||
pub const FEATURE_ICC_V2_V4: u32 = 0;
|
||||
pub const FEATURE_PARAMETRIC: u32 = 1;
|
||||
pub const FEATURE_SET_PRIMARIES: u32 = 2;
|
||||
pub const FEATURE_SET_TF_POWER: u32 = 3;
|
||||
pub const FEATURE_SET_LUMINANCES: u32 = 4;
|
||||
pub const FEATURE_SET_MASTERING_DISPLAY_PRIMARIES: u32 = 5;
|
||||
pub const FEATURE_EXTENDED_TARGET_VOLUME: u32 = 6;
|
||||
pub const FEATURE_WINDOWS_SCRGB: u32 = 7;
|
||||
|
||||
pub(super) const PRIMARIES_SRGB: u32 = 1;
|
||||
pub(super) const PRIMARIES_PAL_M: u32 = 2;
|
||||
pub(super) const PRIMARIES_PAL: u32 = 3;
|
||||
pub(super) const PRIMARIES_NTSC: u32 = 4;
|
||||
pub(super) const PRIMARIES_GENERIC_FILM: u32 = 5;
|
||||
pub(super) const PRIMARIES_BT2020: u32 = 6;
|
||||
pub(super) const PRIMARIES_CIE1931_XYZ: u32 = 7;
|
||||
pub(super) const PRIMARIES_DCI_P3: u32 = 8;
|
||||
pub(super) const PRIMARIES_DISPLAY_P3: u32 = 9;
|
||||
pub(super) const PRIMARIES_ADOBE_RGB: u32 = 10;
|
||||
pub const PRIMARIES_SRGB: u32 = 1;
|
||||
pub const PRIMARIES_PAL_M: u32 = 2;
|
||||
pub const PRIMARIES_PAL: u32 = 3;
|
||||
pub const PRIMARIES_NTSC: u32 = 4;
|
||||
pub const PRIMARIES_GENERIC_FILM: u32 = 5;
|
||||
pub const PRIMARIES_BT2020: u32 = 6;
|
||||
pub const PRIMARIES_CIE1931_XYZ: u32 = 7;
|
||||
pub const PRIMARIES_DCI_P3: u32 = 8;
|
||||
pub const PRIMARIES_DISPLAY_P3: u32 = 9;
|
||||
pub const PRIMARIES_ADOBE_RGB: u32 = 10;
|
||||
|
||||
pub(super) const TRANSFER_FUNCTION_BT1886: u32 = 1;
|
||||
pub(super) const TRANSFER_FUNCTION_GAMMA22: u32 = 2;
|
||||
pub(super) const TRANSFER_FUNCTION_GAMMA28: u32 = 3;
|
||||
pub(super) const TRANSFER_FUNCTION_ST240: u32 = 4;
|
||||
pub(super) const TRANSFER_FUNCTION_EXT_LINEAR: u32 = 5;
|
||||
pub(super) const TRANSFER_FUNCTION_LOG_100: u32 = 6;
|
||||
pub(super) const TRANSFER_FUNCTION_LOG_316: u32 = 7;
|
||||
pub(super) const TRANSFER_FUNCTION_XVYCC: u32 = 8;
|
||||
pub(super) const TRANSFER_FUNCTION_SRGB: u32 = 9;
|
||||
pub(super) const TRANSFER_FUNCTION_EXT_SRGB: u32 = 10;
|
||||
pub(super) const TRANSFER_FUNCTION_ST2084_PQ: u32 = 11;
|
||||
pub(super) const TRANSFER_FUNCTION_ST428: u32 = 12;
|
||||
pub(super) const TRANSFER_FUNCTION_HLG: u32 = 13;
|
||||
pub const TRANSFER_FUNCTION_BT1886: u32 = 1;
|
||||
pub const TRANSFER_FUNCTION_GAMMA22: u32 = 2;
|
||||
pub const TRANSFER_FUNCTION_GAMMA28: u32 = 3;
|
||||
pub const TRANSFER_FUNCTION_ST240: u32 = 4;
|
||||
pub const TRANSFER_FUNCTION_EXT_LINEAR: u32 = 5;
|
||||
pub const TRANSFER_FUNCTION_LOG_100: u32 = 6;
|
||||
pub const TRANSFER_FUNCTION_LOG_316: u32 = 7;
|
||||
pub const TRANSFER_FUNCTION_XVYCC: u32 = 8;
|
||||
pub const TRANSFER_FUNCTION_SRGB: u32 = 9;
|
||||
pub const TRANSFER_FUNCTION_EXT_SRGB: u32 = 10;
|
||||
pub const TRANSFER_FUNCTION_ST2084_PQ: u32 = 11;
|
||||
pub const TRANSFER_FUNCTION_ST428: u32 = 12;
|
||||
pub const TRANSFER_FUNCTION_HLG: u32 = 13;
|
||||
|
||||
pub(super) const CAUSE_LOW_VERSION: u32 = 0;
|
||||
pub(super) const CAUSE_UNSUPPORTED: u32 = 1;
|
||||
pub(super) const CAUSE_OPERATING_SYSTEM: u32 = 2;
|
||||
pub(super) const CAUSE_NO_OUTPUT: u32 = 3;
|
||||
pub const CAUSE_LOW_VERSION: u32 = 0;
|
||||
pub const CAUSE_UNSUPPORTED: u32 = 1;
|
||||
pub const CAUSE_OPERATING_SYSTEM: u32 = 2;
|
||||
pub const CAUSE_NO_OUTPUT: u32 = 3;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,10 +43,11 @@ impl WpColorManagementOutputV1RequestHandler for WpColorManagementOutputV1 {
|
|||
client: self.client.clone(),
|
||||
version: self.version,
|
||||
tracker: Default::default(),
|
||||
description: self.client.state.color_manager.srgb_srgb().clone(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
obj.send_ready(0);
|
||||
obj.send_ready();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,10 +30,11 @@ impl WpColorManagementSurfaceFeedbackV1 {
|
|||
client: self.client.clone(),
|
||||
version: self.version,
|
||||
tracker: Default::default(),
|
||||
description: self.client.state.color_manager.srgb_srgb().clone(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
obj.send_ready(0);
|
||||
obj.send_ready();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,27 @@ use {
|
|||
crate::{
|
||||
client::{Client, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::color_management::{
|
||||
consts::{
|
||||
FEATURE_PARAMETRIC, PRIMARIES_SRGB, RENDER_INTENT_PERCEPTUAL,
|
||||
TRANSFER_FUNCTION_SRGB,
|
||||
ifs::{
|
||||
color_management::{
|
||||
consts::{
|
||||
FEATURE_PARAMETRIC, FEATURE_SET_LUMINANCES, FEATURE_SET_PRIMARIES,
|
||||
FEATURE_WINDOWS_SCRGB, PRIMARIES_ADOBE_RGB, PRIMARIES_BT2020,
|
||||
PRIMARIES_CIE1931_XYZ, PRIMARIES_DCI_P3, PRIMARIES_DISPLAY_P3,
|
||||
PRIMARIES_GENERIC_FILM, PRIMARIES_NTSC, PRIMARIES_PAL, PRIMARIES_PAL_M,
|
||||
PRIMARIES_SRGB, RENDER_INTENT_PERCEPTUAL, TRANSFER_FUNCTION_BT1886,
|
||||
TRANSFER_FUNCTION_EXT_LINEAR, TRANSFER_FUNCTION_EXT_SRGB,
|
||||
TRANSFER_FUNCTION_GAMMA22, TRANSFER_FUNCTION_GAMMA28,
|
||||
TRANSFER_FUNCTION_LOG_100, TRANSFER_FUNCTION_LOG_316, TRANSFER_FUNCTION_SRGB,
|
||||
TRANSFER_FUNCTION_ST240, TRANSFER_FUNCTION_ST428, TRANSFER_FUNCTION_ST2084_PQ,
|
||||
},
|
||||
wp_color_management_output_v1::WpColorManagementOutputV1,
|
||||
wp_color_management_surface_feedback_v1::WpColorManagementSurfaceFeedbackV1,
|
||||
wp_image_description_creator_params_v1::WpImageDescriptionCreatorParamsV1,
|
||||
wp_image_description_v1::WpImageDescriptionV1,
|
||||
},
|
||||
wl_surface::wp_color_management_surface_v1::{
|
||||
WpColorManagementSurfaceV1, WpColorManagementSurfaceV1Error,
|
||||
},
|
||||
wp_color_management_output_v1::WpColorManagementOutputV1,
|
||||
wp_color_management_surface_feedback_v1::WpColorManagementSurfaceFeedbackV1,
|
||||
wp_color_management_surface_v1::WpColorManagementSurfaceV1,
|
||||
wp_image_description_creator_params_v1::WpImageDescriptionCreatorParamsV1,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
|
|
@ -63,8 +75,30 @@ impl WpColorManagerV1 {
|
|||
fn send_capabilities(&self) {
|
||||
self.send_supported_intent(RENDER_INTENT_PERCEPTUAL);
|
||||
self.send_supported_feature(FEATURE_PARAMETRIC);
|
||||
self.send_supported_feature(FEATURE_SET_PRIMARIES);
|
||||
self.send_supported_feature(FEATURE_SET_LUMINANCES);
|
||||
self.send_supported_feature(FEATURE_WINDOWS_SCRGB);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_BT1886);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_GAMMA22);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_GAMMA28);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_ST240);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_EXT_LINEAR);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_LOG_100);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_LOG_316);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_SRGB);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_EXT_SRGB);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_ST2084_PQ);
|
||||
self.send_supported_tf_named(TRANSFER_FUNCTION_ST428);
|
||||
self.send_supported_primaries_named(PRIMARIES_SRGB);
|
||||
self.send_supported_primaries_named(PRIMARIES_PAL_M);
|
||||
self.send_supported_primaries_named(PRIMARIES_PAL);
|
||||
self.send_supported_primaries_named(PRIMARIES_NTSC);
|
||||
self.send_supported_primaries_named(PRIMARIES_GENERIC_FILM);
|
||||
self.send_supported_primaries_named(PRIMARIES_BT2020);
|
||||
self.send_supported_primaries_named(PRIMARIES_CIE1931_XYZ);
|
||||
self.send_supported_primaries_named(PRIMARIES_DCI_P3);
|
||||
self.send_supported_primaries_named(PRIMARIES_DISPLAY_P3);
|
||||
self.send_supported_primaries_named(PRIMARIES_ADOBE_RGB);
|
||||
self.send_done();
|
||||
}
|
||||
|
||||
|
|
@ -123,15 +157,17 @@ impl WpColorManagerV1RequestHandler for WpColorManagerV1 {
|
|||
}
|
||||
|
||||
fn get_surface(&self, req: GetSurface, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let _ = self.client.lookup(req.surface)?;
|
||||
let surface = self.client.lookup(req.surface)?;
|
||||
let obj = Rc::new(WpColorManagementSurfaceV1 {
|
||||
id: req.id,
|
||||
client: self.client.clone(),
|
||||
version: self.version,
|
||||
tracker: Default::default(),
|
||||
surface: surface.clone(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
obj.install()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -170,6 +206,9 @@ impl WpColorManagerV1RequestHandler for WpColorManagerV1 {
|
|||
client: self.client.clone(),
|
||||
version: self.version,
|
||||
tracker: Default::default(),
|
||||
tf: Default::default(),
|
||||
primaries: Default::default(),
|
||||
luminance: Default::default(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
|
|
@ -178,10 +217,20 @@ impl WpColorManagerV1RequestHandler for WpColorManagerV1 {
|
|||
|
||||
fn create_windows_scrgb(
|
||||
&self,
|
||||
_req: CreateWindowsScrgb,
|
||||
req: CreateWindowsScrgb,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
Err(WpColorManagerV1Error::CreateWindowsScrgbNotSupported)
|
||||
let obj = Rc::new(WpImageDescriptionV1 {
|
||||
id: req.image_description,
|
||||
client: self.client.clone(),
|
||||
version: self.version,
|
||||
tracker: Default::default(),
|
||||
description: self.client.state.color_manager.windows_scrgb().clone(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
obj.send_ready();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +271,7 @@ pub enum WpColorManagerV1Error {
|
|||
ClientError(Box<ClientError>),
|
||||
#[error("create_icc_creator is not supported")]
|
||||
CreateIccCreatorNotSupported,
|
||||
#[error("create_windows_scrgb is not supported")]
|
||||
CreateWindowsScrgbNotSupported,
|
||||
#[error(transparent)]
|
||||
Surface(#[from] WpColorManagementSurfaceV1Error),
|
||||
}
|
||||
efrom!(WpColorManagerV1Error, ClientError);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,27 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
cmm::{
|
||||
cmm_luminance::Luminance,
|
||||
cmm_primaries::{NamedPrimaries, Primaries},
|
||||
cmm_transfer_function::TransferFunction,
|
||||
},
|
||||
ifs::color_management::{
|
||||
consts::{PRIMARIES_SRGB, TRANSFER_FUNCTION_SRGB},
|
||||
MIN_LUM_MUL_INV, PRIMARIES_MUL_INV,
|
||||
consts::{
|
||||
PRIMARIES_ADOBE_RGB, PRIMARIES_BT2020, PRIMARIES_CIE1931_XYZ, PRIMARIES_DCI_P3,
|
||||
PRIMARIES_DISPLAY_P3, PRIMARIES_GENERIC_FILM, PRIMARIES_NTSC, PRIMARIES_PAL,
|
||||
PRIMARIES_PAL_M, PRIMARIES_SRGB, TRANSFER_FUNCTION_BT1886,
|
||||
TRANSFER_FUNCTION_EXT_LINEAR, TRANSFER_FUNCTION_EXT_SRGB,
|
||||
TRANSFER_FUNCTION_GAMMA22, TRANSFER_FUNCTION_GAMMA28, TRANSFER_FUNCTION_LOG_100,
|
||||
TRANSFER_FUNCTION_LOG_316, TRANSFER_FUNCTION_SRGB, TRANSFER_FUNCTION_ST240,
|
||||
TRANSFER_FUNCTION_ST428, TRANSFER_FUNCTION_ST2084_PQ,
|
||||
},
|
||||
wp_image_description_v1::WpImageDescriptionV1,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
utils::ordered_float::F64,
|
||||
wire::{
|
||||
WpImageDescriptionCreatorParamsV1Id,
|
||||
wp_image_description_creator_params_v1::{
|
||||
|
|
@ -16,7 +31,7 @@ use {
|
|||
},
|
||||
},
|
||||
},
|
||||
std::rc::Rc,
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
|
|
@ -25,30 +40,74 @@ pub struct WpImageDescriptionCreatorParamsV1 {
|
|||
pub client: Rc<Client>,
|
||||
pub version: Version,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub tf: Cell<Option<TransferFunction>>,
|
||||
pub primaries: Cell<Option<(Option<NamedPrimaries>, Primaries)>>,
|
||||
pub luminance: Cell<Option<Luminance>>,
|
||||
}
|
||||
|
||||
impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreatorParamsV1 {
|
||||
type Error = WpImageDescriptionCreatorParamsV1Error;
|
||||
|
||||
fn create(&self, req: Create, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let Some(transfer_function) = self.tf.get() else {
|
||||
return Err(WpImageDescriptionCreatorParamsV1Error::TfNotSet);
|
||||
};
|
||||
let Some((named_primaries, primaries)) = self.primaries.get() else {
|
||||
return Err(WpImageDescriptionCreatorParamsV1Error::PrimariesNotSet);
|
||||
};
|
||||
let default_luminance = match transfer_function {
|
||||
TransferFunction::Bt1886 => Luminance::BT1886,
|
||||
TransferFunction::St2084Pq => Luminance::ST2084_PQ,
|
||||
_ => Luminance::SRGB,
|
||||
};
|
||||
let mut luminance = self.luminance.get().unwrap_or(default_luminance);
|
||||
if transfer_function == TransferFunction::St2084Pq {
|
||||
luminance.max.0 = luminance.min.0 + 10_000.0;
|
||||
}
|
||||
if luminance.max.0 <= luminance.min.0 || luminance.white.0 <= luminance.min.0 {
|
||||
return Err(WpImageDescriptionCreatorParamsV1Error::MinLuminanceTooLow);
|
||||
}
|
||||
let description = self.client.state.color_manager.get_description(
|
||||
named_primaries,
|
||||
primaries,
|
||||
luminance,
|
||||
transfer_function,
|
||||
);
|
||||
let obj = Rc::new(WpImageDescriptionV1 {
|
||||
id: req.image_description,
|
||||
client: self.client.clone(),
|
||||
version: self.version,
|
||||
tracker: Default::default(),
|
||||
description,
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
obj.send_ready(0);
|
||||
obj.send_ready();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_tf_named(&self, req: SetTfNamed, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
if req.tf != TRANSFER_FUNCTION_SRGB {
|
||||
return Err(WpImageDescriptionCreatorParamsV1Error::UnsupportedTf(
|
||||
req.tf,
|
||||
));
|
||||
let tf = match req.tf {
|
||||
TRANSFER_FUNCTION_BT1886 => TransferFunction::Bt1886,
|
||||
TRANSFER_FUNCTION_GAMMA22 => TransferFunction::Gamma22,
|
||||
TRANSFER_FUNCTION_GAMMA28 => TransferFunction::Gamma28,
|
||||
TRANSFER_FUNCTION_ST240 => TransferFunction::St240,
|
||||
TRANSFER_FUNCTION_EXT_LINEAR => TransferFunction::Linear,
|
||||
TRANSFER_FUNCTION_LOG_100 => TransferFunction::Log100,
|
||||
TRANSFER_FUNCTION_LOG_316 => TransferFunction::Log316,
|
||||
TRANSFER_FUNCTION_SRGB => TransferFunction::Srgb,
|
||||
TRANSFER_FUNCTION_EXT_SRGB => TransferFunction::ExtSrgb,
|
||||
TRANSFER_FUNCTION_ST2084_PQ => TransferFunction::St2084Pq,
|
||||
TRANSFER_FUNCTION_ST428 => TransferFunction::St428,
|
||||
_ => {
|
||||
return Err(WpImageDescriptionCreatorParamsV1Error::UnsupportedTf(
|
||||
req.tf,
|
||||
));
|
||||
}
|
||||
};
|
||||
if self.tf.replace(Some(tf)).is_some() {
|
||||
return Err(WpImageDescriptionCreatorParamsV1Error::TfAlreadySet);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -62,20 +121,55 @@ impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreat
|
|||
req: SetPrimariesNamed,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
if req.primaries != PRIMARIES_SRGB {
|
||||
return Err(
|
||||
WpImageDescriptionCreatorParamsV1Error::UnsupportedPrimaries(req.primaries),
|
||||
);
|
||||
let primaries = match req.primaries {
|
||||
PRIMARIES_SRGB => NamedPrimaries::Srgb,
|
||||
PRIMARIES_PAL_M => NamedPrimaries::PalM,
|
||||
PRIMARIES_PAL => NamedPrimaries::Pal,
|
||||
PRIMARIES_NTSC => NamedPrimaries::Ntsc,
|
||||
PRIMARIES_GENERIC_FILM => NamedPrimaries::GenericFilm,
|
||||
PRIMARIES_BT2020 => NamedPrimaries::Bt2020,
|
||||
PRIMARIES_CIE1931_XYZ => NamedPrimaries::Cie1931Xyz,
|
||||
PRIMARIES_DCI_P3 => NamedPrimaries::DciP3,
|
||||
PRIMARIES_DISPLAY_P3 => NamedPrimaries::DisplayP3,
|
||||
PRIMARIES_ADOBE_RGB => NamedPrimaries::AdobeRgb,
|
||||
_ => {
|
||||
return Err(
|
||||
WpImageDescriptionCreatorParamsV1Error::UnsupportedPrimaries(req.primaries),
|
||||
);
|
||||
}
|
||||
};
|
||||
let primaries = (Some(primaries), primaries.primaries());
|
||||
if self.primaries.replace(Some(primaries)).is_some() {
|
||||
return Err(WpImageDescriptionCreatorParamsV1Error::PrimariesAlreadySet);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_primaries(&self, _req: SetPrimaries, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
Err(WpImageDescriptionCreatorParamsV1Error::SetPrimariesNotSupported)
|
||||
fn set_primaries(&self, req: SetPrimaries, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let map = |n: i32| F64(n as f64 * PRIMARIES_MUL_INV);
|
||||
let primaries = Primaries {
|
||||
r: (map(req.r_x), map(req.r_y)),
|
||||
g: (map(req.g_x), map(req.g_y)),
|
||||
b: (map(req.b_x), map(req.b_y)),
|
||||
wp: (map(req.w_x), map(req.w_y)),
|
||||
};
|
||||
let primaries = (None, primaries);
|
||||
if self.primaries.replace(Some(primaries)).is_some() {
|
||||
return Err(WpImageDescriptionCreatorParamsV1Error::PrimariesAlreadySet);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_luminances(&self, _req: SetLuminances, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
Err(WpImageDescriptionCreatorParamsV1Error::SetLuminancesNotSupported)
|
||||
fn set_luminances(&self, req: SetLuminances, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let luminance = Luminance {
|
||||
min: F64(req.min_lum as f64 * MIN_LUM_MUL_INV),
|
||||
max: F64(req.max_lum as f64),
|
||||
white: F64(req.reference_lum as f64),
|
||||
};
|
||||
if self.luminance.replace(Some(luminance)).is_some() {
|
||||
return Err(WpImageDescriptionCreatorParamsV1Error::LuminancesAlreadySet);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_mastering_display_primaries(
|
||||
|
|
@ -120,15 +214,23 @@ pub enum WpImageDescriptionCreatorParamsV1Error {
|
|||
SetMasteringLuminanceNotSupported,
|
||||
#[error("set_mastering_display_primaries is not supported")]
|
||||
SetMasteringDisplayPrimariesNotSupported,
|
||||
#[error("set_luminances is not supported")]
|
||||
SetLuminancesNotSupported,
|
||||
#[error("set_primaries is not supported")]
|
||||
SetPrimariesNotSupported,
|
||||
#[error("{} is not a supported named primary", .0)]
|
||||
UnsupportedPrimaries(u32),
|
||||
#[error("set_tf_power is not supported")]
|
||||
SetTfPowerNotSupported,
|
||||
#[error("{} is not a supported named transfer function", .0)]
|
||||
UnsupportedTf(u32),
|
||||
#[error("The transfer function has already been set")]
|
||||
TfAlreadySet,
|
||||
#[error("The primaries have already been set")]
|
||||
PrimariesAlreadySet,
|
||||
#[error("The luminances have already been set")]
|
||||
LuminancesAlreadySet,
|
||||
#[error("The minimum luminance is too low")]
|
||||
MinLuminanceTooLow,
|
||||
#[error("The transfer function was not set")]
|
||||
TfNotSet,
|
||||
#[error("The primaries were not set")]
|
||||
PrimariesNotSet,
|
||||
}
|
||||
efrom!(WpImageDescriptionCreatorParamsV1Error, ClientError);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
cmm::cmm_description::ColorDescription,
|
||||
ifs::color_management::wp_image_description_info_v1::WpImageDescriptionInfoV1,
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
|
|
@ -15,6 +16,7 @@ pub struct WpImageDescriptionV1 {
|
|||
pub client: Rc<Client>,
|
||||
pub version: Version,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub description: Rc<ColorDescription>,
|
||||
}
|
||||
|
||||
impl WpImageDescriptionV1 {
|
||||
|
|
@ -27,10 +29,10 @@ impl WpImageDescriptionV1 {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn send_ready(&self, identity: u32) {
|
||||
pub fn send_ready(&self) {
|
||||
self.client.event(Ready {
|
||||
self_id: self.id,
|
||||
identity,
|
||||
identity: self.description.id.into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
cmm::cmm_description::ColorDescription,
|
||||
gfx_api::{
|
||||
AcquireSync, AsyncShmGfxTextureCallback, BufferResv, GfxError, GfxFramebuffer,
|
||||
GfxTexture, ReleaseSync, STAGING_DOWNLOAD, SyncFile,
|
||||
|
|
@ -199,6 +200,7 @@ impl ExtImageCopyCaptureFrameV1 {
|
|||
self: &Rc<Self>,
|
||||
on: &OutputNode,
|
||||
texture: &Rc<dyn GfxTexture>,
|
||||
cd: &Rc<ColorDescription>,
|
||||
resv: Option<&Rc<dyn BufferResv>>,
|
||||
acquire_sync: &AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
|
|
@ -215,10 +217,12 @@ impl ExtImageCopyCaptureFrameV1 {
|
|||
resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
&fb,
|
||||
aq,
|
||||
re,
|
||||
jay_config::video::Transform::None,
|
||||
self.client.state.color_manager.srgb_srgb(),
|
||||
on.global.pos.get(),
|
||||
render_hardware_cursors,
|
||||
x_off,
|
||||
|
|
@ -236,6 +240,7 @@ impl ExtImageCopyCaptureFrameV1 {
|
|||
fb.render_node(
|
||||
aq,
|
||||
re,
|
||||
self.client.state.color_manager.srgb_srgb(),
|
||||
node,
|
||||
&self.client.state,
|
||||
Some(node.node_absolute_position()),
|
||||
|
|
@ -246,6 +251,7 @@ impl ExtImageCopyCaptureFrameV1 {
|
|||
false,
|
||||
jay_config::video::Transform::None,
|
||||
None,
|
||||
self.client.state.color_manager.srgb_linear(),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
cmm::cmm_description::ColorDescription,
|
||||
format::{FORMATS, Format},
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture,
|
||||
|
|
@ -213,6 +214,7 @@ impl ExtImageCopyCaptureSessionV1 {
|
|||
self: &Rc<Self>,
|
||||
on: &OutputNode,
|
||||
texture: &Rc<dyn GfxTexture>,
|
||||
cd: &Rc<ColorDescription>,
|
||||
resv: Option<&Rc<dyn BufferResv>>,
|
||||
acquire_sync: &AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
|
|
@ -226,6 +228,7 @@ impl ExtImageCopyCaptureSessionV1 {
|
|||
frame.copy_texture(
|
||||
on,
|
||||
texture,
|
||||
cd,
|
||||
resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError},
|
||||
cmm::cmm_transfer_function::TransferFunction,
|
||||
globals::{Global, GlobalName},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
theme::{Color, TransferFunction},
|
||||
theme::Color,
|
||||
wire::{
|
||||
JayCompositorId,
|
||||
jay_damage_tracking::{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use {
|
|||
crate::{
|
||||
allocator::{AllocatorError, BO_USE_LINEAR, BO_USE_RENDERING, BufferObject},
|
||||
client::{Client, ClientError},
|
||||
cmm::cmm_description::ColorDescription,
|
||||
format::XRGB8888,
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync,
|
||||
|
|
@ -193,6 +194,7 @@ impl JayScreencast {
|
|||
let res = buffer.fb.render_node(
|
||||
AcquireSync::Implicit,
|
||||
ReleaseSync::Implicit,
|
||||
self.client.state.color_manager.srgb_srgb(),
|
||||
tl.tl_as_node(),
|
||||
&self.client.state,
|
||||
Some(tl.node_absolute_position()),
|
||||
|
|
@ -203,6 +205,7 @@ impl JayScreencast {
|
|||
false,
|
||||
Transform::None,
|
||||
None,
|
||||
self.client.state.color_manager.srgb_linear(),
|
||||
);
|
||||
match res {
|
||||
Ok(_) => {
|
||||
|
|
@ -304,6 +307,7 @@ impl JayScreencast {
|
|||
&self,
|
||||
on: &OutputNode,
|
||||
texture: &Rc<dyn GfxTexture>,
|
||||
cd: &Rc<ColorDescription>,
|
||||
resv: Option<&Rc<dyn BufferResv>>,
|
||||
acquire_sync: &AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
|
|
@ -332,10 +336,12 @@ impl JayScreencast {
|
|||
resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
&buffer.fb,
|
||||
AcquireSync::Implicit,
|
||||
ReleaseSync::Implicit,
|
||||
Transform::None,
|
||||
self.client.state.color_manager.srgb_srgb(),
|
||||
on.global.pos.get(),
|
||||
render_hardware_cursors,
|
||||
x_off,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ pub mod ext_session_lock_surface_v1;
|
|||
pub mod tray;
|
||||
pub mod wl_subsurface;
|
||||
pub mod wp_alpha_modifier_surface_v1;
|
||||
pub mod wp_color_management_surface_v1;
|
||||
pub mod wp_commit_timer_v1;
|
||||
pub mod wp_fifo_v1;
|
||||
pub mod wp_fractional_scale_v1;
|
||||
|
|
@ -22,6 +23,7 @@ use {
|
|||
crate::{
|
||||
backend::KeyState,
|
||||
client::{Client, ClientError},
|
||||
cmm::cmm_description::ColorDescription,
|
||||
cursor_user::{CursorUser, CursorUserId},
|
||||
damage::DamageMatrix,
|
||||
drm_feedback::DrmFeedback,
|
||||
|
|
@ -104,6 +106,7 @@ use {
|
|||
rc::{Rc, Weak},
|
||||
},
|
||||
thiserror::Error,
|
||||
wp_color_management_surface_v1::WpColorManagementSurfaceV1,
|
||||
zwp_idle_inhibitor_v1::ZwpIdleInhibitorV1,
|
||||
};
|
||||
|
||||
|
|
@ -332,6 +335,8 @@ pub struct WlSurface {
|
|||
commit_timer: CloneCell<Option<Rc<WpCommitTimerV1>>>,
|
||||
before_latch_listener: EventListener<dyn BeforeLatchListener>,
|
||||
is_opaque: Cell<bool>,
|
||||
color_management_surface: CloneCell<Option<Rc<WpColorManagementSurfaceV1>>>,
|
||||
color_description: CloneCell<Option<Rc<ColorDescription>>>,
|
||||
}
|
||||
|
||||
impl Debug for WlSurface {
|
||||
|
|
@ -461,6 +466,7 @@ struct PendingState {
|
|||
fifo_barrier_wait: bool,
|
||||
commit_time: Option<u64>,
|
||||
tray_item_ack_serial: Option<u32>,
|
||||
color_description: Option<Option<Rc<ColorDescription>>>,
|
||||
}
|
||||
|
||||
struct AttachedSubsurfaceState {
|
||||
|
|
@ -513,6 +519,7 @@ impl PendingState {
|
|||
opt!(alpha_multiplier);
|
||||
opt!(commit_time);
|
||||
opt!(tray_item_ack_serial);
|
||||
opt!(color_description);
|
||||
{
|
||||
let (dx1, dy1) = self.offset;
|
||||
let (dx2, dy2) = mem::take(&mut next.offset);
|
||||
|
|
@ -670,6 +677,8 @@ impl WlSurface {
|
|||
commit_timer: Default::default(),
|
||||
before_latch_listener: EventListener::new(slf.clone()),
|
||||
is_opaque: Cell::new(false),
|
||||
color_management_surface: Default::default(),
|
||||
color_description: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1137,6 +1146,11 @@ impl WlSurface {
|
|||
}
|
||||
}
|
||||
}
|
||||
let mut color_description_changed = false;
|
||||
if let Some(desc) = pending.color_description.take() {
|
||||
color_description_changed = true;
|
||||
self.color_description.set(desc);
|
||||
}
|
||||
let mut alpha_changed = false;
|
||||
if let Some(alpha) = pending.alpha_multiplier.take() {
|
||||
alpha_changed = true;
|
||||
|
|
@ -1144,8 +1158,11 @@ impl WlSurface {
|
|||
}
|
||||
let buffer_abs_pos = self.buffer_abs_pos.get();
|
||||
let mut max_surface_size = buffer_abs_pos.size();
|
||||
let mut damage_full =
|
||||
scale_changed || buffer_transform_changed || viewport_changed || alpha_changed;
|
||||
let mut damage_full = scale_changed
|
||||
|| buffer_transform_changed
|
||||
|| viewport_changed
|
||||
|| alpha_changed
|
||||
|| color_description_changed;
|
||||
let mut buffer_changed = false;
|
||||
let mut old_raw_size = None;
|
||||
let (mut dx, mut dy) = mem::take(&mut pending.offset);
|
||||
|
|
@ -1658,6 +1675,13 @@ impl WlSurface {
|
|||
pub fn opaque_region(&self) -> Option<Rc<Region>> {
|
||||
self.opaque_region.get()
|
||||
}
|
||||
|
||||
pub fn color_description(&self) -> Rc<ColorDescription> {
|
||||
match self.color_description.get() {
|
||||
Some(cd) => cd,
|
||||
None => self.client.state.color_manager.srgb_srgb().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
@ -1689,6 +1713,7 @@ impl Object for WlSurface {
|
|||
self.text_input_connections.clear();
|
||||
self.fifo.take();
|
||||
self.commit_timer.take();
|
||||
self.color_management_surface.take();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::color_management::consts::RENDER_INTENT_PERCEPTUAL,
|
||||
ifs::{color_management, wl_surface::WlSurface},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{
|
||||
|
|
@ -21,12 +21,26 @@ pub struct WpColorManagementSurfaceV1 {
|
|||
pub client: Rc<Client>,
|
||||
pub version: Version,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub surface: Rc<WlSurface>,
|
||||
}
|
||||
|
||||
impl WpColorManagementSurfaceV1 {
|
||||
pub fn install(self: &Rc<Self>) -> Result<(), WpColorManagementSurfaceV1Error> {
|
||||
if self.surface.color_management_surface.is_some() {
|
||||
return Err(WpColorManagementSurfaceV1Error::HasSurface);
|
||||
}
|
||||
self.surface
|
||||
.color_management_surface
|
||||
.set(Some(self.clone()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 {
|
||||
type Error = WpColorManagementSurfaceV1Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.surface.color_management_surface.take();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -36,12 +50,13 @@ impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 {
|
|||
req: SetImageDescription,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
let _ = self.client.lookup(req.image_description)?;
|
||||
if req.render_intent != RENDER_INTENT_PERCEPTUAL {
|
||||
if req.render_intent != color_management::RENDER_INTENT_PERCEPTUAL {
|
||||
return Err(WpColorManagementSurfaceV1Error::UnsupportedRenderIntent(
|
||||
req.render_intent,
|
||||
));
|
||||
}
|
||||
let desc = self.client.lookup(req.image_description)?;
|
||||
self.surface.pending.borrow_mut().color_description = Some(Some(desc.description.clone()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -50,6 +65,7 @@ impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 {
|
|||
_req: UnsetImageDescription,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.surface.pending.borrow_mut().color_description = Some(None);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -69,5 +85,7 @@ pub enum WpColorManagementSurfaceV1Error {
|
|||
ClientError(Box<ClientError>),
|
||||
#[error("{} is not a supported render intent", .0)]
|
||||
UnsupportedRenderIntent(u32),
|
||||
#[error("wl_surface already has a color-management extension")]
|
||||
HasSurface,
|
||||
}
|
||||
efrom!(WpColorManagementSurfaceV1Error, ClientError);
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage},
|
||||
cmm::cmm_description::{ColorDescription, LinearColorDescription},
|
||||
cpu_worker::CpuWorker,
|
||||
format::{ARGB8888, Format, XRGB8888},
|
||||
gfx_api::{
|
||||
|
|
@ -391,10 +392,13 @@ impl GfxFramebuffer for TestGfxFb {
|
|||
self: Rc<Self>,
|
||||
_acquire_sync: AcquireSync,
|
||||
_release_sync: ReleaseSync,
|
||||
_cd: &Rc<ColorDescription>,
|
||||
ops: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
_clear_cd: &Rc<LinearColorDescription>,
|
||||
_region: &Region,
|
||||
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
_blend_cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
|
||||
let points = rect.to_points();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::cmm_transfer_function::TransferFunction,
|
||||
it::{
|
||||
test_error::TestResult, test_ifs::test_buffer::TestBuffer, test_object::TestObject,
|
||||
test_transport::TestTransport,
|
||||
},
|
||||
theme::{Color, TransferFunction},
|
||||
theme::Color,
|
||||
wire::{WpSinglePixelBufferManagerV1Id, wp_single_pixel_buffer_manager_v1::*},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
|
|
|
|||
|
|
@ -185,10 +185,10 @@ macro_rules! shared_ids {
|
|||
}
|
||||
|
||||
macro_rules! linear_ids {
|
||||
($ids:ident, $id:ident) => {
|
||||
($ids:ident, $id:ident $(,)?) => {
|
||||
linear_ids!($ids, $id, u32);
|
||||
};
|
||||
($ids:ident, $id:ident, $ty:ty) => {
|
||||
($ids:ident, $id:ident, $ty:ty $(,)?) => {
|
||||
pub struct $ids {
|
||||
next: crate::utils::numcell::NumCell<$ty>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@
|
|||
clippy::manual_is_ascii_check,
|
||||
clippy::needless_borrow,
|
||||
clippy::unnecessary_cast,
|
||||
clippy::manual_flatten
|
||||
clippy::manual_flatten,
|
||||
clippy::manual_bits
|
||||
)]
|
||||
#![warn(clippy::allow_attributes, unsafe_op_in_unsafe_fn)]
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ mod bugs;
|
|||
mod cli;
|
||||
mod client;
|
||||
mod clientmem;
|
||||
mod cmm;
|
||||
mod compositor;
|
||||
mod config;
|
||||
mod cpu_worker;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use {
|
|||
crate::{
|
||||
async_engine::AsyncEngine,
|
||||
cli::GlobalArgs,
|
||||
cmm::cmm_manager::ColorManager,
|
||||
dbus::{
|
||||
BUS_DEST, BUS_PATH, DBUS_NAME_FLAG_DO_NOT_QUEUE, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER,
|
||||
Dbus, DbusSocket,
|
||||
|
|
@ -207,6 +208,7 @@ async fn run_async(
|
|||
render_ctxs: Default::default(),
|
||||
dma_buf_ids: Default::default(),
|
||||
pw_con: pw_con.as_ref().map(|c| c.con.clone()),
|
||||
color_manager: ColorManager::new(),
|
||||
});
|
||||
if let Some(pw_con) = &pw_con {
|
||||
pw_con.con.owner.set(Some(state.clone()));
|
||||
|
|
@ -302,6 +304,7 @@ struct PortalState {
|
|||
render_ctxs: CopyHashMap<c::dev_t, Weak<PortalRenderCtx>>,
|
||||
dma_buf_ids: Rc<DmaBufIds>,
|
||||
pw_con: Option<Rc<PwCon>>,
|
||||
color_manager: Rc<ColorManager>,
|
||||
}
|
||||
|
||||
impl PortalState {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::cmm_transfer_function::TransferFunction,
|
||||
format::ARGB8888,
|
||||
gfx_api::{GfxContext, GfxTexture},
|
||||
pango::{
|
||||
|
|
@ -7,7 +8,7 @@ use {
|
|||
consts::{CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE},
|
||||
},
|
||||
rect::Rect,
|
||||
theme::{Color, TransferFunction},
|
||||
theme::Color,
|
||||
},
|
||||
std::{ops::Neg, rc::Rc, sync::Arc},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use {
|
|||
crate::{
|
||||
allocator::{BO_USE_RENDERING, BufferObject, BufferUsage},
|
||||
async_engine::{Phase, SpawnedFuture},
|
||||
cmm::cmm_manager::ColorManager,
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
format::ARGB8888,
|
||||
|
|
@ -59,7 +60,7 @@ pub trait GuiElement {
|
|||
max_width: f32,
|
||||
max_height: f32,
|
||||
) -> (f32, f32);
|
||||
fn render_at(&self, r: &mut RendererBase, x: f32, y: f32);
|
||||
fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x: f32, y: f32);
|
||||
fn child_at(&self, x: f32, y: f32) -> Option<Rc<dyn GuiElement>>;
|
||||
|
||||
fn hover_cursor(&self) -> KnownCursor {
|
||||
|
|
@ -190,7 +191,9 @@ impl GuiElement for Button {
|
|||
(extents.width, extents.height)
|
||||
}
|
||||
|
||||
fn render_at(&self, r: &mut RendererBase, x1: f32, y1: f32) {
|
||||
fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x1: f32, y1: f32) {
|
||||
let srgb_srgb = color_manager.srgb_srgb();
|
||||
let srgb = &srgb_srgb.linear;
|
||||
let x2 = x1 + self.data.width.get();
|
||||
let y2 = y1 + self.data.height.get();
|
||||
let border = self.border.get();
|
||||
|
|
@ -201,7 +204,7 @@ impl GuiElement for Button {
|
|||
(x1, y1 + border, x1 + border, y2 - border),
|
||||
(x2 - border, y1 + border, x2, y2 - border),
|
||||
];
|
||||
r.fill_boxes_f(&rects, &self.border_color.get());
|
||||
r.fill_boxes_f(&rects, &self.border_color.get(), srgb);
|
||||
}
|
||||
{
|
||||
let rects = [(x1 + border, y1 + border, x2 - border, y2 - border)];
|
||||
|
|
@ -209,7 +212,7 @@ impl GuiElement for Button {
|
|||
true => self.bg_color.get(),
|
||||
false => self.bg_hover_color.get(),
|
||||
};
|
||||
r.fill_boxes_f(&rects, &color);
|
||||
r.fill_boxes_f(&rects, &color, srgb);
|
||||
}
|
||||
if let Some(tex) = self.tex.get() {
|
||||
let (tx, ty) = r.scale_point_f(x1 + self.tex_off_x.get(), y1 + self.tex_off_y.get());
|
||||
|
|
@ -226,6 +229,7 @@ impl GuiElement for Button {
|
|||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
srgb_srgb,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -311,7 +315,7 @@ impl GuiElement for Label {
|
|||
(width as f32 / scale, height as f32 / scale)
|
||||
}
|
||||
|
||||
fn render_at(&self, r: &mut RendererBase, x: f32, y: f32) {
|
||||
fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x: f32, y: f32) {
|
||||
if let Some(tex) = self.tex.get() {
|
||||
let (tx, ty) = r.scale_point_f(x, y);
|
||||
r.render_texture(
|
||||
|
|
@ -327,6 +331,7 @@ impl GuiElement for Label {
|
|||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
color_manager.srgb_srgb(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -439,9 +444,14 @@ impl GuiElement for Flow {
|
|||
(w.min(max_width), h.min(max_height))
|
||||
}
|
||||
|
||||
fn render_at(&self, r: &mut RendererBase, x: f32, y: f32) {
|
||||
fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x: f32, y: f32) {
|
||||
for element in self.elements.borrow_mut().deref() {
|
||||
element.render_at(r, x + element.data().x.get(), y + element.data().y.get());
|
||||
element.render_at(
|
||||
color_manager,
|
||||
r,
|
||||
x + element.data().x.get(),
|
||||
y + element.data().y.get(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -634,12 +644,15 @@ impl WindowData {
|
|||
let res = buf.fb.render_custom(
|
||||
AcquireSync::Implicit,
|
||||
ReleaseSync::Implicit,
|
||||
self.dpy.state.color_manager.srgb_srgb(),
|
||||
self.scale.get(),
|
||||
Some(&Color::from_gray_srgb(0)),
|
||||
&self.dpy.state.color_manager.srgb_srgb().linear,
|
||||
None,
|
||||
self.dpy.state.color_manager.srgb_linear(),
|
||||
&mut |r| {
|
||||
if let Some(content) = self.content.get() {
|
||||
content.render_at(r, 0.0, 0.0)
|
||||
content.render_at(&self.dpy.state.color_manager, r, 0.0, 0.0)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use {
|
|||
renderer::renderer_base::RendererBase,
|
||||
scale::Scale,
|
||||
state::State,
|
||||
theme::{Color, TransferFunction},
|
||||
theme::Color,
|
||||
tree::{
|
||||
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
|
||||
ToplevelNodeBase, WorkspaceNode,
|
||||
|
|
@ -77,6 +77,8 @@ impl Renderer<'_> {
|
|||
}
|
||||
let theme = &self.state.theme;
|
||||
let th = theme.sizes.title_height.get();
|
||||
let srgb_srgb = self.state.color_manager.srgb_srgb();
|
||||
let srgb = &srgb_srgb.linear;
|
||||
if let Some(fs) = fullscreen {
|
||||
fs.tl_as_node().node_render(self, x, y, None);
|
||||
} else {
|
||||
|
|
@ -89,26 +91,28 @@ impl Renderer<'_> {
|
|||
let bar_bg = self.base.scale_rect(bar_bg);
|
||||
let c = theme.colors.bar_background.get();
|
||||
self.base
|
||||
.fill_boxes3(slice::from_ref(&bar_bg), &c, None, x, y, true);
|
||||
.fill_boxes3(slice::from_ref(&bar_bg), &c, None, srgb, x, y, true);
|
||||
let rd = output.render_data.borrow_mut();
|
||||
if let Some(aw) = &rd.active_workspace {
|
||||
let c = match aw.captured {
|
||||
true => theme.colors.captured_focused_title_background.get(),
|
||||
false => theme.colors.focused_title_background.get(),
|
||||
};
|
||||
self.base.fill_boxes2(slice::from_ref(&aw.rect), &c, x, y);
|
||||
self.base
|
||||
.fill_boxes2(slice::from_ref(&aw.rect), &c, srgb, x, y);
|
||||
}
|
||||
let c = theme.colors.separator.get();
|
||||
self.base
|
||||
.fill_boxes2(slice::from_ref(&rd.underline), &c, x, y);
|
||||
.fill_boxes2(slice::from_ref(&rd.underline), &c, srgb, x, y);
|
||||
let c = theme.colors.unfocused_title_background.get();
|
||||
self.base.fill_boxes2(&rd.inactive_workspaces, &c, x, y);
|
||||
self.base
|
||||
.fill_boxes2(&rd.inactive_workspaces, &c, srgb, x, y);
|
||||
let c = theme.colors.captured_unfocused_title_background.get();
|
||||
self.base
|
||||
.fill_boxes2(&rd.captured_inactive_workspaces, &c, x, y);
|
||||
.fill_boxes2(&rd.captured_inactive_workspaces, &c, srgb, x, y);
|
||||
let c = theme.colors.attention_requested_background.get();
|
||||
self.base
|
||||
.fill_boxes2(&rd.attention_requested_workspaces, &c, x, y);
|
||||
.fill_boxes2(&rd.attention_requested_workspaces, &c, srgb, x, y);
|
||||
let scale = output.global.persistent.scale.get();
|
||||
for title in &rd.titles {
|
||||
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
|
||||
|
|
@ -125,6 +129,7 @@ impl Renderer<'_> {
|
|||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
self.state.color_manager.srgb_srgb(),
|
||||
);
|
||||
}
|
||||
if let Some(status) = &rd.status {
|
||||
|
|
@ -143,6 +148,7 @@ impl Renderer<'_> {
|
|||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
srgb_srgb,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -181,7 +187,7 @@ impl Renderer<'_> {
|
|||
if ws.render_highlight.get() > 0 {
|
||||
let color = self.state.theme.colors.highlight.get();
|
||||
let bounds = ws.position.get().at_point(x, y + th + 1);
|
||||
self.base.fill_boxes(&[bounds], &color);
|
||||
self.base.fill_boxes(&[bounds], &color, srgb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,6 +209,7 @@ impl Renderer<'_> {
|
|||
self.base.fill_boxes(
|
||||
std::slice::from_ref(&pos.at_point(x, y)),
|
||||
&Color::from_srgba_straight(20, 20, 20, 255),
|
||||
&self.state.color_manager.srgb_srgb().linear,
|
||||
);
|
||||
if let Some(tex) = placeholder.textures.borrow().get(&self.base.scale) {
|
||||
if let Some(texture) = tex.texture() {
|
||||
|
|
@ -222,6 +229,7 @@ impl Renderer<'_> {
|
|||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
self.state.color_manager.srgb_srgb(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -230,17 +238,21 @@ impl Renderer<'_> {
|
|||
|
||||
pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
||||
{
|
||||
let srgb_srgb = self.state.color_manager.srgb_srgb();
|
||||
let srgb = &srgb_srgb.linear;
|
||||
let rd = container.render_data.borrow_mut();
|
||||
let c = self.state.theme.colors.unfocused_title_background.get();
|
||||
self.base.fill_boxes2(&rd.title_rects, &c, x, y);
|
||||
self.base.fill_boxes2(&rd.title_rects, &c, srgb, x, y);
|
||||
let c = self.state.theme.colors.focused_title_background.get();
|
||||
self.base.fill_boxes2(&rd.active_title_rects, &c, x, y);
|
||||
self.base
|
||||
.fill_boxes2(&rd.active_title_rects, &c, srgb, x, y);
|
||||
let c = self.state.theme.colors.attention_requested_background.get();
|
||||
self.base.fill_boxes2(&rd.attention_title_rects, &c, x, y);
|
||||
self.base
|
||||
.fill_boxes2(&rd.attention_title_rects, &c, srgb, x, y);
|
||||
let c = self.state.theme.colors.separator.get();
|
||||
self.base.fill_boxes2(&rd.underline_rects, &c, x, y);
|
||||
self.base.fill_boxes2(&rd.underline_rects, &c, srgb, x, y);
|
||||
let c = self.state.theme.colors.border.get();
|
||||
self.base.fill_boxes2(&rd.border_rects, &c, x, y);
|
||||
self.base.fill_boxes2(&rd.border_rects, &c, srgb, x, y);
|
||||
if let Some(lar) = &rd.last_active_rect {
|
||||
let c = self
|
||||
.state
|
||||
|
|
@ -248,7 +260,8 @@ impl Renderer<'_> {
|
|||
.colors
|
||||
.focused_inactive_title_background
|
||||
.get();
|
||||
self.base.fill_boxes2(std::slice::from_ref(lar), &c, x, y);
|
||||
self.base
|
||||
.fill_boxes2(std::slice::from_ref(lar), &c, srgb, x, y);
|
||||
}
|
||||
if let Some(titles) = rd.titles.get(&self.base.scale) {
|
||||
for title in titles {
|
||||
|
|
@ -268,6 +281,7 @@ impl Renderer<'_> {
|
|||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
srgb_srgb,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -340,14 +354,22 @@ impl Renderer<'_> {
|
|||
};
|
||||
let color = self.state.theme.colors.highlight.get();
|
||||
self.base.ops.push(GfxApiOpt::Sync);
|
||||
self.base
|
||||
.fill_scaled_boxes(slice::from_ref(bounds), &color, None);
|
||||
self.base.fill_scaled_boxes(
|
||||
slice::from_ref(bounds),
|
||||
&color,
|
||||
None,
|
||||
&self.state.color_manager.srgb_srgb().linear,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn render_highlight(&mut self, rect: &Rect) {
|
||||
let color = self.state.theme.colors.highlight.get();
|
||||
self.base.ops.push(GfxApiOpt::Sync);
|
||||
self.base.fill_boxes(slice::from_ref(rect), &color);
|
||||
self.base.fill_boxes(
|
||||
slice::from_ref(rect),
|
||||
&color,
|
||||
&self.state.color_manager.srgb_srgb().linear,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32, bounds: Option<&Rect>) {
|
||||
|
|
@ -422,6 +444,7 @@ impl Renderer<'_> {
|
|||
bounds: Option<&Rect>,
|
||||
) {
|
||||
let alpha = surface.alpha();
|
||||
let cd = surface.color_description();
|
||||
if let Some(tex) = buffer.buffer.get_texture(surface) {
|
||||
let mut opaque = surface.opaque();
|
||||
if !opaque && tex.format().has_alpha {
|
||||
|
|
@ -440,6 +463,7 @@ impl Renderer<'_> {
|
|||
AcquireSync::Unnecessary,
|
||||
buffer.release_sync,
|
||||
opaque,
|
||||
&cd,
|
||||
);
|
||||
} else if let Some(color) = &buffer.buffer.color {
|
||||
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
||||
|
|
@ -449,14 +473,15 @@ impl Renderer<'_> {
|
|||
};
|
||||
if !rect.is_empty() {
|
||||
let color = Color::from_u32_premultiplied(
|
||||
TransferFunction::Srgb,
|
||||
cd.transfer_function,
|
||||
color[0],
|
||||
color[1],
|
||||
color[2],
|
||||
color[3],
|
||||
);
|
||||
self.base.ops.push(GfxApiOpt::Sync);
|
||||
self.base.fill_scaled_boxes(&[rect], &color, alpha);
|
||||
self.base
|
||||
.fill_scaled_boxes(&[rect], &color, alpha, &cd.linear);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -488,12 +513,14 @@ impl Renderer<'_> {
|
|||
Rect::new_sized(x + pos.width() - bw, y + bw, bw, pos.height() - bw).unwrap(),
|
||||
Rect::new_sized(x + bw, y + pos.height() - bw, pos.width() - 2 * bw, bw).unwrap(),
|
||||
];
|
||||
self.base.fill_boxes(&borders, &bc);
|
||||
let srgb_srgb = self.state.color_manager.srgb_srgb();
|
||||
let srgb = &srgb_srgb.linear;
|
||||
self.base.fill_boxes(&borders, &bc, srgb);
|
||||
let title = [Rect::new_sized(x + bw, y + bw, pos.width() - 2 * bw, th).unwrap()];
|
||||
self.base.fill_boxes(&title, &tc);
|
||||
self.base.fill_boxes(&title, &tc, srgb);
|
||||
let title_underline =
|
||||
[Rect::new_sized(x + bw, y + bw + th, pos.width() - 2 * bw, 1).unwrap()];
|
||||
self.base.fill_boxes(&title_underline, &uc);
|
||||
self.base.fill_boxes(&title_underline, &uc, srgb);
|
||||
if let Some(title) = floating.title_textures.borrow().get(&self.base.scale) {
|
||||
if let Some(texture) = title.texture() {
|
||||
let rect = floating.title_rect.get().move_(x, y);
|
||||
|
|
@ -512,6 +539,7 @@ impl Renderer<'_> {
|
|||
AcquireSync::None,
|
||||
ReleaseSync::None,
|
||||
false,
|
||||
srgb_srgb,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::cmm_description::{ColorDescription, LinearColorDescription},
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture,
|
||||
ReleaseSync, SampleRect,
|
||||
|
|
@ -64,16 +65,29 @@ impl RendererBase<'_> {
|
|||
rect
|
||||
}
|
||||
|
||||
pub fn fill_scaled_boxes(&mut self, boxes: &[Rect], color: &Color, alpha: Option<f32>) {
|
||||
self.fill_boxes3(boxes, color, alpha, 0, 0, true);
|
||||
pub fn fill_scaled_boxes(
|
||||
&mut self,
|
||||
boxes: &[Rect],
|
||||
color: &Color,
|
||||
alpha: Option<f32>,
|
||||
cd: &Rc<LinearColorDescription>,
|
||||
) {
|
||||
self.fill_boxes3(boxes, color, alpha, cd, 0, 0, true);
|
||||
}
|
||||
|
||||
pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color) {
|
||||
self.fill_boxes3(boxes, color, None, 0, 0, false);
|
||||
pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color, cd: &Rc<LinearColorDescription>) {
|
||||
self.fill_boxes3(boxes, color, None, cd, 0, 0, false);
|
||||
}
|
||||
|
||||
pub fn fill_boxes2(&mut self, boxes: &[Rect], color: &Color, dx: i32, dy: i32) {
|
||||
self.fill_boxes3(boxes, color, None, dx, dy, false);
|
||||
pub fn fill_boxes2(
|
||||
&mut self,
|
||||
boxes: &[Rect],
|
||||
color: &Color,
|
||||
cd: &Rc<LinearColorDescription>,
|
||||
dx: i32,
|
||||
dy: i32,
|
||||
) {
|
||||
self.fill_boxes3(boxes, color, None, cd, dx, dy, false);
|
||||
}
|
||||
|
||||
pub fn fill_boxes3(
|
||||
|
|
@ -81,6 +95,7 @@ impl RendererBase<'_> {
|
|||
boxes: &[Rect],
|
||||
color: &Color,
|
||||
alpha: Option<f32>,
|
||||
cd: &Rc<LinearColorDescription>,
|
||||
dx: i32,
|
||||
dy: i32,
|
||||
scaled: bool,
|
||||
|
|
@ -106,18 +121,25 @@ impl RendererBase<'_> {
|
|||
),
|
||||
color: *color,
|
||||
alpha,
|
||||
cd: cd.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill_boxes_f(&mut self, boxes: &[(f32, f32, f32, f32)], color: &Color) {
|
||||
self.fill_boxes2_f(boxes, color, 0.0, 0.0);
|
||||
pub fn fill_boxes_f(
|
||||
&mut self,
|
||||
boxes: &[(f32, f32, f32, f32)],
|
||||
color: &Color,
|
||||
cd: &Rc<LinearColorDescription>,
|
||||
) {
|
||||
self.fill_boxes2_f(boxes, color, cd, 0.0, 0.0);
|
||||
}
|
||||
|
||||
pub fn fill_boxes2_f(
|
||||
&mut self,
|
||||
boxes: &[(f32, f32, f32, f32)],
|
||||
color: &Color,
|
||||
cd: &Rc<LinearColorDescription>,
|
||||
dx: f32,
|
||||
dy: f32,
|
||||
) {
|
||||
|
|
@ -139,6 +161,7 @@ impl RendererBase<'_> {
|
|||
),
|
||||
color: *color,
|
||||
alpha: None,
|
||||
cd: cd.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -157,6 +180,7 @@ impl RendererBase<'_> {
|
|||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
opaque: bool,
|
||||
cd: &Rc<ColorDescription>,
|
||||
) {
|
||||
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
||||
|
||||
|
|
@ -200,6 +224,7 @@ impl RendererBase<'_> {
|
|||
acquire_sync,
|
||||
release_sync,
|
||||
opaque,
|
||||
cd: cd.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ pub fn take_screenshot(
|
|||
fb.render_node(
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::Implicit,
|
||||
state.color_manager.srgb_srgb(),
|
||||
state.root.deref(),
|
||||
state,
|
||||
Some(state.root.extents.get()),
|
||||
|
|
@ -89,6 +90,7 @@ pub fn take_screenshot(
|
|||
false,
|
||||
Transform::None,
|
||||
None,
|
||||
state.color_manager.srgb_linear(),
|
||||
)?;
|
||||
let drm = match allocator.drm() {
|
||||
Some(drm) => Some(drm.dup_render()?.fd().clone()),
|
||||
|
|
|
|||
16
src/state.rs
16
src/state.rs
|
|
@ -11,6 +11,7 @@ use {
|
|||
cli::RunArgs,
|
||||
client::{Client, ClientId, Clients, NUM_CACHED_SERIAL_RANGES, SerialRange},
|
||||
clientmem::ClientMemOffset,
|
||||
cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager},
|
||||
compositor::LIBEI_SOCKET,
|
||||
config::ConfigProxy,
|
||||
cpu_worker::CpuWorker,
|
||||
|
|
@ -234,6 +235,7 @@ pub struct State {
|
|||
pub data_control_device_ids: DataControlDeviceIds,
|
||||
pub workspace_managers: WorkspaceManagerState,
|
||||
pub color_management_enabled: Cell<bool>,
|
||||
pub color_manager: Rc<ColorManager>,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -976,15 +978,18 @@ impl State {
|
|||
&self,
|
||||
output: &OutputNode,
|
||||
fb: &Rc<dyn GfxFramebuffer>,
|
||||
cd: &Rc<ColorDescription>,
|
||||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
tex: &Rc<dyn GfxTexture>,
|
||||
render_hw_cursor: bool,
|
||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||
blend_cd: &Rc<ColorDescription>,
|
||||
) -> Result<Option<SyncFile>, GfxError> {
|
||||
let sync_file = fb.render_output(
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
output,
|
||||
self,
|
||||
Some(output.global.pos.get()),
|
||||
|
|
@ -992,10 +997,12 @@ impl State {
|
|||
render_hw_cursor,
|
||||
true,
|
||||
blend_buffer,
|
||||
blend_cd,
|
||||
)?;
|
||||
output.latched(false);
|
||||
output.perform_screencopies(
|
||||
tex,
|
||||
cd,
|
||||
None,
|
||||
&AcquireSync::Unnecessary,
|
||||
ReleaseSync::None,
|
||||
|
|
@ -1013,10 +1020,12 @@ impl State {
|
|||
resv: Option<&Rc<dyn BufferResv>>,
|
||||
acquire_sync: &AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
src_cd: &Rc<ColorDescription>,
|
||||
target: &Rc<dyn GfxFramebuffer>,
|
||||
target_acquire_sync: AcquireSync,
|
||||
target_release_sync: ReleaseSync,
|
||||
target_transform: Transform,
|
||||
target_cd: &Rc<ColorDescription>,
|
||||
position: Rect,
|
||||
render_hardware_cursors: bool,
|
||||
x_off: i32,
|
||||
|
|
@ -1050,6 +1059,7 @@ impl State {
|
|||
acquire_sync.clone(),
|
||||
release_sync,
|
||||
false,
|
||||
src_cd,
|
||||
);
|
||||
if render_hardware_cursors {
|
||||
if let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get() {
|
||||
|
|
@ -1066,15 +1076,19 @@ impl State {
|
|||
target.render(
|
||||
target_acquire_sync,
|
||||
target_release_sync,
|
||||
target_cd,
|
||||
&ops,
|
||||
Some(&Color::SOLID_BLACK),
|
||||
&target_cd.linear,
|
||||
None,
|
||||
target_cd,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn perform_shm_screencopy(
|
||||
&self,
|
||||
src: &Rc<dyn GfxTexture>,
|
||||
src_cd: &Rc<ColorDescription>,
|
||||
acquire_sync: &AcquireSync,
|
||||
position: Rect,
|
||||
x_off: i32,
|
||||
|
|
@ -1105,10 +1119,12 @@ impl State {
|
|||
None,
|
||||
acquire_sync,
|
||||
ReleaseSync::None,
|
||||
src_cd,
|
||||
&fb.clone().into_fb(),
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::None,
|
||||
transform,
|
||||
self.color_manager.srgb_srgb(),
|
||||
position,
|
||||
true,
|
||||
x_off - capture.rect.x1(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
cmm::cmm_transfer_function::TransferFunction,
|
||||
cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker, PendingJob},
|
||||
format::ARGB8888,
|
||||
gfx_api::{
|
||||
|
|
@ -14,7 +15,7 @@ use {
|
|||
},
|
||||
},
|
||||
rect::{Rect, Region},
|
||||
theme::{Color, TransferFunction},
|
||||
theme::Color,
|
||||
utils::{
|
||||
clonecell::CloneCell, double_buffered::DoubleBuffered, on_drop_event::OnDropEvent,
|
||||
},
|
||||
|
|
|
|||
124
src/theme.rs
124
src/theme.rs
|
|
@ -1,14 +1,11 @@
|
|||
#![expect(clippy::excessive_precision)]
|
||||
|
||||
use {
|
||||
crate::utils::clonecell::CloneCell,
|
||||
crate::{cmm::cmm_transfer_function::TransferFunction, utils::clonecell::CloneCell},
|
||||
num_traits::Float,
|
||||
std::{cell::Cell, cmp::Ordering, ops::Mul, sync::Arc},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TransferFunction {
|
||||
Srgb,
|
||||
Linear,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Color {
|
||||
r: f32,
|
||||
|
|
@ -83,6 +80,51 @@ impl Color {
|
|||
fn linear(c: f32) -> f32 {
|
||||
c
|
||||
}
|
||||
fn st2084_pq(c: f32) -> f32 {
|
||||
let cp = c.powf(1.0 / 78.84375);
|
||||
let num = (cp - 0.8359375).max(0.0);
|
||||
let den = 18.8515625 - 18.6875 * cp;
|
||||
(num / den).powf(1.0 / 0.1593017578125)
|
||||
}
|
||||
fn ext_srgb(c: f32) -> f32 {
|
||||
let c = c.clamp(-0.6038, 7.5913);
|
||||
if c <= -0.0031308 {
|
||||
-1.055 * (-c).powf(1.0 / 2.4) + 0.055
|
||||
} else if c <= 0.0031308 {
|
||||
c * 12.92
|
||||
} else {
|
||||
1.055 * c.powf(1.0 / 2.4) - 0.055
|
||||
}
|
||||
}
|
||||
fn bt1886(c: f32) -> f32 {
|
||||
if c < 0.081 {
|
||||
c / 4.5
|
||||
} else {
|
||||
((c + 0.099) / 1.099).powf(1.0 / 0.45)
|
||||
}
|
||||
}
|
||||
fn st240(c: f32) -> f32 {
|
||||
if c < 0.0913 {
|
||||
c / 4.0
|
||||
} else {
|
||||
((c + 0.1115) / 1.1115).powf(1.0 / 0.45)
|
||||
}
|
||||
}
|
||||
fn log100(c: f32) -> f32 {
|
||||
10.0.powf(2.0 * (c - 1.0))
|
||||
}
|
||||
fn log316(c: f32) -> f32 {
|
||||
10.0.powf(2.5 * (c - 1.0))
|
||||
}
|
||||
fn st428(c: f32) -> f32 {
|
||||
c.powf(2.6) * 52.37 / 48.0
|
||||
}
|
||||
fn gamma22(c: f32) -> f32 {
|
||||
c.powf(2.2)
|
||||
}
|
||||
fn gamma28(c: f32) -> f32 {
|
||||
c.powf(2.8)
|
||||
}
|
||||
macro_rules! convert {
|
||||
($tf:ident) => {{
|
||||
r = $tf(r);
|
||||
|
|
@ -93,6 +135,15 @@ impl Color {
|
|||
match transfer_function {
|
||||
TransferFunction::Srgb => convert!(srgb),
|
||||
TransferFunction::Linear => convert!(linear),
|
||||
TransferFunction::St2084Pq => convert!(st2084_pq),
|
||||
TransferFunction::Bt1886 => convert!(bt1886),
|
||||
TransferFunction::Gamma22 => convert!(gamma22),
|
||||
TransferFunction::Gamma28 => convert!(gamma28),
|
||||
TransferFunction::St240 => convert!(st240),
|
||||
TransferFunction::ExtSrgb => convert!(ext_srgb),
|
||||
TransferFunction::Log100 => convert!(log100),
|
||||
TransferFunction::Log316 => convert!(log316),
|
||||
TransferFunction::St428 => convert!(st428),
|
||||
}
|
||||
Self { r, g, b, a: 1.0 }
|
||||
}
|
||||
|
|
@ -191,6 +242,56 @@ impl Color {
|
|||
fn linear(c: f32) -> f32 {
|
||||
c
|
||||
}
|
||||
fn st2084_pq(c: f32) -> f32 {
|
||||
let c = c.clamp(0.0, 1.0);
|
||||
let num = 0.8359375 + 18.8515625 * c.powf(0.1593017578125);
|
||||
let den = 1.0 + 18.6875 * c.powf(0.1593017578125);
|
||||
(num / den).powf(78.84375)
|
||||
}
|
||||
fn ext_srgb(c: f32) -> f32 {
|
||||
if c < -0.04045 {
|
||||
-((c - 0.055) / -1.055).powf(2.4)
|
||||
} else if c < 0.04045 {
|
||||
c / 12.92
|
||||
} else {
|
||||
((c + 0.055) / 1.055).powf(2.4)
|
||||
}
|
||||
}
|
||||
fn bt1886(c: f32) -> f32 {
|
||||
if c < 0.018 {
|
||||
4.5 * c
|
||||
} else {
|
||||
1.099 * c.powf(0.45) - 0.099
|
||||
}
|
||||
}
|
||||
fn st240(c: f32) -> f32 {
|
||||
if c < 0.0228 {
|
||||
4.0 * c
|
||||
} else {
|
||||
1.1115 * c.powf(0.45) - 0.1115
|
||||
}
|
||||
}
|
||||
fn log100(c: f32) -> f32 {
|
||||
let c = c.clamp(0.0, 1.0);
|
||||
if c < 0.01 { 0.0 } else { 1.0 + c.log10() / 2.0 }
|
||||
}
|
||||
fn log316(c: f32) -> f32 {
|
||||
let c = c.clamp(0.0, 1.0);
|
||||
if c < 10.0.sqrt() / 1000.0 {
|
||||
0.0
|
||||
} else {
|
||||
1.0 + c.log10() / 2.5
|
||||
}
|
||||
}
|
||||
fn st428(c: f32) -> f32 {
|
||||
(48.0 * c / 52.37).powf(1.0 / 2.6)
|
||||
}
|
||||
fn gamma22(c: f32) -> f32 {
|
||||
c.powf(1.0 / 2.2)
|
||||
}
|
||||
fn gamma28(c: f32) -> f32 {
|
||||
c.powf(1.0 / 2.8)
|
||||
}
|
||||
macro_rules! convert {
|
||||
($tf:ident) => {{
|
||||
for c in &mut res[..3] {
|
||||
|
|
@ -207,6 +308,15 @@ impl Color {
|
|||
match transfer_function {
|
||||
TransferFunction::Srgb => convert!(srgb),
|
||||
TransferFunction::Linear => convert!(linear),
|
||||
TransferFunction::St2084Pq => convert!(st2084_pq),
|
||||
TransferFunction::Bt1886 => convert!(bt1886),
|
||||
TransferFunction::Gamma22 => convert!(gamma22),
|
||||
TransferFunction::Gamma28 => convert!(gamma28),
|
||||
TransferFunction::St240 => convert!(st240),
|
||||
TransferFunction::ExtSrgb => convert!(ext_srgb),
|
||||
TransferFunction::Log100 => convert!(log100),
|
||||
TransferFunction::Log316 => convert!(log316),
|
||||
TransferFunction::St428 => convert!(st428),
|
||||
}
|
||||
if self.a < 1.0 {
|
||||
for c in &mut res[..3] {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use {
|
|||
crate::{
|
||||
backend::{HardwareCursor, KeyState, Mode},
|
||||
client::ClientId,
|
||||
cmm::cmm_description::ColorDescription,
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
|
||||
|
|
@ -252,6 +253,7 @@ impl OutputNode {
|
|||
pub fn perform_screencopies(
|
||||
&self,
|
||||
tex: &Rc<dyn GfxTexture>,
|
||||
cd: &Rc<ColorDescription>,
|
||||
resv: Option<&Rc<dyn BufferResv>>,
|
||||
acquire_sync: &AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
|
|
@ -267,6 +269,7 @@ impl OutputNode {
|
|||
}
|
||||
self.perform_wlr_screencopies(
|
||||
tex,
|
||||
cd,
|
||||
resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
|
|
@ -279,6 +282,7 @@ impl OutputNode {
|
|||
sc.copy_texture(
|
||||
self,
|
||||
tex,
|
||||
cd,
|
||||
resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
|
|
@ -292,6 +296,7 @@ impl OutputNode {
|
|||
sc.copy_texture(
|
||||
self,
|
||||
tex,
|
||||
cd,
|
||||
resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
|
|
@ -306,6 +311,7 @@ impl OutputNode {
|
|||
pub fn perform_wlr_screencopies(
|
||||
&self,
|
||||
tex: &Rc<dyn GfxTexture>,
|
||||
cd: &Rc<ColorDescription>,
|
||||
resv: Option<&Rc<dyn BufferResv>>,
|
||||
acquire_sync: &AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
|
|
@ -337,6 +343,7 @@ impl OutputNode {
|
|||
WlBufferStorage::Shm { mem, stride } => {
|
||||
let res = self.state.perform_shm_screencopy(
|
||||
tex,
|
||||
cd,
|
||||
acquire_sync,
|
||||
self.global.pos.get(),
|
||||
x_off,
|
||||
|
|
@ -375,10 +382,12 @@ impl OutputNode {
|
|||
resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
cd,
|
||||
&fb,
|
||||
AcquireSync::Implicit,
|
||||
ReleaseSync::Implicit,
|
||||
self.global.persistent.transform.get(),
|
||||
self.state.color_manager.srgb_srgb(),
|
||||
self.global.pos.get(),
|
||||
render_hardware_cursors,
|
||||
x_off - capture.rect.x1(),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ pub mod double_click_state;
|
|||
pub mod errorfmt;
|
||||
pub mod event_listener;
|
||||
pub mod fdcloser;
|
||||
pub mod free_list;
|
||||
pub mod geometric_decay;
|
||||
pub mod gfx_api_ext;
|
||||
pub mod hash_map_ext;
|
||||
|
|
@ -37,6 +38,7 @@ pub mod opaque;
|
|||
pub mod opaque_cell;
|
||||
pub mod opt;
|
||||
pub mod option_ext;
|
||||
pub mod ordered_float;
|
||||
pub mod oserror;
|
||||
pub mod page_size;
|
||||
pub mod pending_serial;
|
||||
|
|
|
|||
93
src/utils/free_list.rs
Normal file
93
src/utils/free_list.rs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use {
|
||||
crate::utils::ptr_ext::MutPtrExt,
|
||||
std::{
|
||||
array,
|
||||
cell::UnsafeCell,
|
||||
fmt::{Debug, Formatter},
|
||||
marker::PhantomData,
|
||||
},
|
||||
};
|
||||
|
||||
type Seg = usize;
|
||||
const SEG_SIZE: usize = size_of::<Seg>() * 8;
|
||||
|
||||
pub struct FreeList<T, const N: usize> {
|
||||
levels: UnsafeCell<[Vec<Seg>; N]>,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Default for FreeList<T, N> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
levels: UnsafeCell::new(array::from_fn(|_| Vec::new())),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Debug for FreeList<T, N> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("FreeList")
|
||||
.field("levels", self.get())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> FreeList<T, N> {
|
||||
fn get(&self) -> &mut [Vec<Seg>; N] {
|
||||
unsafe { self.levels.get().deref_mut() }
|
||||
}
|
||||
|
||||
pub fn release(&self, n: T)
|
||||
where
|
||||
T: Into<u32>,
|
||||
{
|
||||
let mut ext = n.into() as usize;
|
||||
let mut int;
|
||||
let levels = self.get();
|
||||
assert!(ext / SEG_SIZE < levels[0].len());
|
||||
for level in self.get() {
|
||||
int = ext % SEG_SIZE;
|
||||
ext /= SEG_SIZE;
|
||||
unsafe {
|
||||
*level.get_unchecked_mut(ext) |= 1 << int;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acquire(&self) -> T
|
||||
where
|
||||
u32: Into<T>,
|
||||
{
|
||||
let mut ext = 'last: {
|
||||
let level = &mut self.get()[N - 1];
|
||||
for (idx, &seg) in level.iter().enumerate() {
|
||||
if seg != 0 {
|
||||
break 'last idx;
|
||||
}
|
||||
}
|
||||
level.len()
|
||||
};
|
||||
for level in self.get().iter_mut().rev() {
|
||||
if ext == level.len() {
|
||||
level.push(!0);
|
||||
}
|
||||
let seg = unsafe { level.get_unchecked(ext) };
|
||||
ext = SEG_SIZE * ext + seg.trailing_zeros() as usize;
|
||||
}
|
||||
let id = ext as u32;
|
||||
for level in self.get().iter_mut() {
|
||||
let int = ext % SEG_SIZE;
|
||||
ext /= SEG_SIZE;
|
||||
let seg = unsafe { level.get_unchecked_mut(ext) };
|
||||
*seg &= !(1 << int);
|
||||
if *seg != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
id.into()
|
||||
}
|
||||
}
|
||||
40
src/utils/free_list/tests.rs
Normal file
40
src/utils/free_list/tests.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
use crate::utils::free_list::FreeList;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let list = FreeList::<u32, 3>::default();
|
||||
for i in 0..4097 {
|
||||
assert_eq!(list.acquire(), i);
|
||||
}
|
||||
list.release(100);
|
||||
assert_eq!(list.acquire(), 100);
|
||||
assert_eq!(list.acquire(), 4097);
|
||||
for i in 1..22 {
|
||||
list.release(i);
|
||||
}
|
||||
for i in 1..22 {
|
||||
assert_eq!(list.acquire(), i);
|
||||
}
|
||||
assert_eq!(list.acquire(), 4098);
|
||||
for i in 64..128 {
|
||||
list.release(i);
|
||||
}
|
||||
for i in 64..128 {
|
||||
assert_eq!(list.acquire(), i);
|
||||
}
|
||||
assert_eq!(list.acquire(), 4099);
|
||||
for i in 0..4100 {
|
||||
list.release(i);
|
||||
}
|
||||
for i in 0..4101 {
|
||||
assert_eq!(list.acquire(), i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn release_out_of_bounds() {
|
||||
let list = FreeList::<u32, 3>::default();
|
||||
list.acquire();
|
||||
list.release(500);
|
||||
}
|
||||
67
src/utils/ordered_float.rs
Normal file
67
src/utils/ordered_float.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use std::{
|
||||
fmt::{Debug, Display, Formatter},
|
||||
hash::{Hash, Hasher},
|
||||
ops::{Add, Div, Mul, Sub},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct F64(pub f64);
|
||||
|
||||
impl Eq for F64 {}
|
||||
|
||||
impl PartialEq for F64 {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.to_bits() == other.0.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for F64 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<F64> for F64 {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: F64) -> Self::Output {
|
||||
Self(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<F64> for F64 {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: F64) -> Self::Output {
|
||||
Self(self.0 - rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<F64> for F64 {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: F64) -> Self::Output {
|
||||
Self(self.0 * rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<F64> for F64 {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: F64) -> Self::Output {
|
||||
Self(self.0 / rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for F64 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for F64 {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue