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);
|
self.state.present_hardware_cursor(node, &mut c);
|
||||||
if c.cursor_swap_buffer {
|
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);
|
self.cursor_swap_buffer.set(c.cursor_swap_buffer);
|
||||||
if c.sync_file.is_some() {
|
if c.sync_file.is_some() {
|
||||||
|
|
@ -558,6 +560,10 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
return None;
|
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() {
|
if ct.alpha.is_some() {
|
||||||
// Direct scanout with alpha factor is not supported.
|
// Direct scanout with alpha factor is not supported.
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -747,12 +753,14 @@ impl MetalConnector {
|
||||||
.perform_render_pass(
|
.perform_render_pass(
|
||||||
AcquireSync::Unnecessary,
|
AcquireSync::Unnecessary,
|
||||||
ReleaseSync::Explicit,
|
ReleaseSync::Explicit,
|
||||||
|
self.state.color_manager.srgb_srgb(),
|
||||||
&latched.pass,
|
&latched.pass,
|
||||||
&latched.damage,
|
&latched.damage,
|
||||||
buffer.blend_buffer.as_ref(),
|
buffer.blend_buffer.as_ref(),
|
||||||
|
self.state.color_manager.srgb_linear(),
|
||||||
)
|
)
|
||||||
.map_err(MetalError::RenderFrame)?;
|
.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();
|
fb = buffer.drm.clone();
|
||||||
tex = buffer.render_tex.clone();
|
tex = buffer.render_tex.clone();
|
||||||
}
|
}
|
||||||
|
|
@ -792,6 +800,7 @@ impl MetalConnector {
|
||||||
None => {
|
None => {
|
||||||
output.perform_screencopies(
|
output.perform_screencopies(
|
||||||
&fb.tex,
|
&fb.tex,
|
||||||
|
self.state.color_manager.srgb_srgb(),
|
||||||
None,
|
None,
|
||||||
&AcquireSync::Unnecessary,
|
&AcquireSync::Unnecessary,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
|
@ -804,6 +813,7 @@ impl MetalConnector {
|
||||||
Some(dsd) => {
|
Some(dsd) => {
|
||||||
output.perform_screencopies(
|
output.perform_screencopies(
|
||||||
&dsd.tex,
|
&dsd.tex,
|
||||||
|
self.state.color_manager.srgb_srgb(),
|
||||||
dsd.resv.as_ref(),
|
dsd.resv.as_ref(),
|
||||||
&dsd.acquire_sync,
|
&dsd.acquire_sync,
|
||||||
dsd.release_sync,
|
dsd.release_sync,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use {
|
||||||
POST_COMMIT_MARGIN_DELTA, PresentFb,
|
POST_COMMIT_MARGIN_DELTA, PresentFb,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
cmm::cmm_manager::ColorManager,
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
edid::{CtaDataBlock, Descriptor, EdidExtension},
|
edid::{CtaDataBlock, Descriptor, EdidExtension},
|
||||||
format::{ARGB8888, Format, XRGB8888},
|
format::{ARGB8888, Format, XRGB8888},
|
||||||
|
|
@ -2704,7 +2705,11 @@ impl MetalBackend {
|
||||||
Err(e) => return Err(MetalError::ImportFb(e)),
|
Err(e) => return Err(MetalError::ImportFb(e)),
|
||||||
};
|
};
|
||||||
dev_fb
|
dev_fb
|
||||||
.clear(AcquireSync::Unnecessary, ReleaseSync::None)
|
.clear(
|
||||||
|
AcquireSync::Unnecessary,
|
||||||
|
ReleaseSync::None,
|
||||||
|
self.state.color_manager.srgb_srgb(),
|
||||||
|
)
|
||||||
.map_err(MetalError::Clear)?;
|
.map_err(MetalError::Clear)?;
|
||||||
let (dev_tex, render_tex, render_fb, render_bo) = if dev.id == render_ctx.dev_id {
|
let (dev_tex, render_tex, render_fb, render_bo) = if dev.id == render_ctx.dev_id {
|
||||||
let render_tex = match dev_img.to_texture() {
|
let render_tex = match dev_img.to_texture() {
|
||||||
|
|
@ -2758,7 +2763,11 @@ impl MetalBackend {
|
||||||
Err(e) => return Err(MetalError::ImportFb(e)),
|
Err(e) => return Err(MetalError::ImportFb(e)),
|
||||||
};
|
};
|
||||||
render_fb
|
render_fb
|
||||||
.clear(AcquireSync::Unnecessary, ReleaseSync::None)
|
.clear(
|
||||||
|
AcquireSync::Unnecessary,
|
||||||
|
ReleaseSync::None,
|
||||||
|
self.state.color_manager.srgb_srgb(),
|
||||||
|
)
|
||||||
.map_err(MetalError::Clear)?;
|
.map_err(MetalError::Clear)?;
|
||||||
let render_tex = match render_img.to_texture() {
|
let render_tex = match render_img.to_texture() {
|
||||||
Ok(fb) => fb,
|
Ok(fb) => fb,
|
||||||
|
|
@ -3030,7 +3039,11 @@ impl RenderBuffer {
|
||||||
.unwrap_or_else(|| self.dev_fb.clone())
|
.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 {
|
let Some(tex) = &self.dev_tex else {
|
||||||
return Ok(sync_file);
|
return Ok(sync_file);
|
||||||
};
|
};
|
||||||
|
|
@ -3038,7 +3051,9 @@ impl RenderBuffer {
|
||||||
.copy_texture(
|
.copy_texture(
|
||||||
AcquireSync::Unnecessary,
|
AcquireSync::Unnecessary,
|
||||||
ReleaseSync::Explicit,
|
ReleaseSync::Explicit,
|
||||||
|
cm.srgb_srgb(),
|
||||||
tex,
|
tex,
|
||||||
|
cm.srgb_srgb(),
|
||||||
None,
|
None,
|
||||||
AcquireSync::from_sync_file(sync_file),
|
AcquireSync::from_sync_file(sync_file),
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
|
|
||||||
|
|
@ -751,11 +751,13 @@ impl XBackend {
|
||||||
let res = self.state.present_output(
|
let res = self.state.present_output(
|
||||||
&node,
|
&node,
|
||||||
&image.fb.get(),
|
&image.fb.get(),
|
||||||
|
self.state.color_manager.srgb_srgb(),
|
||||||
AcquireSync::Implicit,
|
AcquireSync::Implicit,
|
||||||
ReleaseSync::Implicit,
|
ReleaseSync::Implicit,
|
||||||
&image.tex.get(),
|
&image.tex.get(),
|
||||||
true,
|
true,
|
||||||
None,
|
None,
|
||||||
|
self.state.color_manager.srgb_linear(),
|
||||||
);
|
);
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
log::error!("Could not render screen: {}", ErrorFmt(e));
|
log::error!("Could not render screen: {}", ErrorFmt(e));
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::{GlobalArgs, color::parse_color, duration::parse_duration},
|
cli::{GlobalArgs, color::parse_color, duration::parse_duration},
|
||||||
theme::TransferFunction,
|
cmm::cmm_transfer_function::TransferFunction,
|
||||||
tools::tool_client::{ToolClient, with_tool_client},
|
tools::tool_client::{ToolClient, with_tool_client},
|
||||||
wire::jay_damage_tracking::{SetVisualizerColor, SetVisualizerDecay, SetVisualizerEnabled},
|
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},
|
cli::{CliBackend, GlobalArgs, RunArgs},
|
||||||
client::{ClientId, Clients},
|
client::{ClientId, Clients},
|
||||||
clientmem::{self, ClientMemError},
|
clientmem::{self, ClientMemError},
|
||||||
|
cmm::cmm_manager::ColorManager,
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
cpu_worker::{CpuWorker, CpuWorkerError},
|
cpu_worker::{CpuWorker, CpuWorkerError},
|
||||||
damage::{DamageVisualizer, visualize_damage},
|
damage::{DamageVisualizer, visualize_damage},
|
||||||
|
|
@ -152,6 +153,7 @@ fn start_compositor2(
|
||||||
let scales = RefCounted::default();
|
let scales = RefCounted::default();
|
||||||
scales.add(Scale::from_int(1));
|
scales.add(Scale::from_int(1));
|
||||||
let cpu_worker = Rc::new(CpuWorker::new(&ring, &engine)?);
|
let cpu_worker = Rc::new(CpuWorker::new(&ring, &engine)?);
|
||||||
|
let color_manager = ColorManager::new();
|
||||||
let state = Rc::new(State {
|
let state = Rc::new(State {
|
||||||
kb_ctx,
|
kb_ctx,
|
||||||
backend: CloneCell::new(Rc::new(DummyBackend)),
|
backend: CloneCell::new(Rc::new(DummyBackend)),
|
||||||
|
|
@ -266,7 +268,7 @@ fn start_compositor2(
|
||||||
tablet_ids: Default::default(),
|
tablet_ids: Default::default(),
|
||||||
tablet_tool_ids: Default::default(),
|
tablet_tool_ids: Default::default(),
|
||||||
tablet_pad_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_mode: Cell::new(VrrMode::NEVER),
|
||||||
default_vrr_cursor_hz: Cell::new(None),
|
default_vrr_cursor_hz: Cell::new(None),
|
||||||
default_tearing_mode: Cell::new(TearingMode::VARIANT_3),
|
default_tearing_mode: Cell::new(TearingMode::VARIANT_3),
|
||||||
|
|
@ -284,6 +286,7 @@ fn start_compositor2(
|
||||||
data_control_device_ids: Default::default(),
|
data_control_device_ids: Default::default(),
|
||||||
workspace_managers: Default::default(),
|
workspace_managers: Default::default(),
|
||||||
color_management_enabled: Cell::new(false),
|
color_management_enabled: Cell::new(false),
|
||||||
|
color_manager,
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use {
|
||||||
self, ConnectorId, DrmDeviceId, InputDeviceAccelProfile, InputDeviceCapability,
|
self, ConnectorId, DrmDeviceId, InputDeviceAccelProfile, InputDeviceCapability,
|
||||||
InputDeviceId,
|
InputDeviceId,
|
||||||
},
|
},
|
||||||
|
cmm::cmm_transfer_function::TransferFunction,
|
||||||
compositor::MAX_EXTENTS,
|
compositor::MAX_EXTENTS,
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
format::config_formats,
|
format::config_formats,
|
||||||
|
|
@ -14,7 +15,7 @@ use {
|
||||||
output_schedule::map_cursor_hz,
|
output_schedule::map_cursor_hz,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State},
|
state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State},
|
||||||
theme::{Color, ThemeSized, TransferFunction},
|
theme::{Color, ThemeSized},
|
||||||
tree::{
|
tree::{
|
||||||
ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase, OutputNode,
|
ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase, OutputNode,
|
||||||
TearingMode, VrrMode, WsMoveConfig, move_ws_to_output,
|
TearingMode, VrrMode, WsMoveConfig, move_ws_to_output,
|
||||||
|
|
|
||||||
|
|
@ -398,6 +398,7 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
false,
|
||||||
|
renderer.state.color_manager.srgb_srgb(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -422,6 +423,7 @@ impl Cursor for StaticCursor {
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
false,
|
||||||
|
renderer.state.color_manager.srgb_srgb(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -464,6 +466,7 @@ impl Cursor for AnimatedCursor {
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
false,
|
||||||
|
renderer.state.color_manager.srgb_srgb(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -507,6 +507,7 @@ impl CursorUser {
|
||||||
&self.group.state,
|
&self.group.state,
|
||||||
scale,
|
scale,
|
||||||
transform,
|
transform,
|
||||||
|
self.group.state.color_manager.srgb_srgb(),
|
||||||
);
|
);
|
||||||
match res {
|
match res {
|
||||||
Ok(sync_file) => {
|
Ok(sync_file) => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::AsyncEngine,
|
async_engine::AsyncEngine,
|
||||||
|
cmm::cmm_manager::ColorManager,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::GfxApiOpt,
|
gfx_api::GfxApiOpt,
|
||||||
ifs::wl_output::WlOutputGlobal,
|
ifs::wl_output::WlOutputGlobal,
|
||||||
|
|
@ -77,6 +78,7 @@ pub struct DamageVisualizer {
|
||||||
enabled: Cell<bool>,
|
enabled: Cell<bool>,
|
||||||
decay: Cell<Duration>,
|
decay: Cell<Duration>,
|
||||||
color: Cell<Color>,
|
color: Cell<Color>,
|
||||||
|
color_manager: Rc<ColorManager>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_RECTS: usize = 100_000;
|
const MAX_RECTS: usize = 100_000;
|
||||||
|
|
@ -87,7 +89,7 @@ struct Damage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DamageVisualizer {
|
impl DamageVisualizer {
|
||||||
pub fn new(eng: &Rc<AsyncEngine>) -> Self {
|
pub fn new(eng: &Rc<AsyncEngine>, color_manager: &Rc<ColorManager>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
eng: eng.clone(),
|
eng: eng.clone(),
|
||||||
entries: Default::default(),
|
entries: Default::default(),
|
||||||
|
|
@ -95,6 +97,7 @@ impl DamageVisualizer {
|
||||||
enabled: Default::default(),
|
enabled: Default::default(),
|
||||||
decay: Cell::new(Duration::from_secs(2)),
|
decay: Cell::new(Duration::from_secs(2)),
|
||||||
color: Cell::new(Color::from_srgba_straight(255, 0, 0, 128)),
|
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 dy = -cursor_rect.y1();
|
||||||
let decay_millis = decay.as_millis() as u64 as f32;
|
let decay_millis = decay.as_millis() as u64 as f32;
|
||||||
renderer.ops.push(GfxApiOpt::Sync);
|
renderer.ops.push(GfxApiOpt::Sync);
|
||||||
|
let srgb = &self.color_manager.srgb_srgb().linear;
|
||||||
for entry in entries.iter().rev() {
|
for entry in entries.iter().rev() {
|
||||||
let region = Region::new(entry.rect);
|
let region = Region::new(entry.rect);
|
||||||
let region = region.subtract(&used);
|
let region = region.subtract(&used);
|
||||||
if region.is_not_empty() {
|
if region.is_not_empty() {
|
||||||
let age = (now - entry.time).as_millis() as u64 as f32 / decay_millis;
|
let age = (now - entry.time).as_millis() as u64 as f32 / decay_millis;
|
||||||
let color = base_color * (1.0 - age);
|
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);
|
used = used.union(®ion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
allocator::Allocator,
|
allocator::Allocator,
|
||||||
|
cmm::cmm_description::{ColorDescription, LinearColorDescription},
|
||||||
cpu_worker::CpuWorker,
|
cpu_worker::CpuWorker,
|
||||||
cursor::Cursor,
|
cursor::Cursor,
|
||||||
damage::DamageVisualizer,
|
damage::DamageVisualizer,
|
||||||
|
|
@ -41,6 +42,7 @@ pub enum GfxApiOpt {
|
||||||
pub struct GfxRenderPass {
|
pub struct GfxRenderPass {
|
||||||
pub ops: Vec<GfxApiOpt>,
|
pub ops: Vec<GfxApiOpt>,
|
||||||
pub clear: Option<Color>,
|
pub clear: Option<Color>,
|
||||||
|
pub clear_cd: Rc<LinearColorDescription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
||||||
|
|
@ -194,6 +196,7 @@ pub struct FillRect {
|
||||||
pub rect: FramebufferRect,
|
pub rect: FramebufferRect,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub alpha: Option<f32>,
|
pub alpha: Option<f32>,
|
||||||
|
pub cd: Rc<LinearColorDescription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FillRect {
|
impl FillRect {
|
||||||
|
|
@ -215,6 +218,7 @@ pub struct CopyTexture {
|
||||||
pub release_sync: ReleaseSync,
|
pub release_sync: ReleaseSync,
|
||||||
pub alpha: Option<f32>,
|
pub alpha: Option<f32>,
|
||||||
pub opaque: bool,
|
pub opaque: bool,
|
||||||
|
pub cd: Rc<ColorDescription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -299,10 +303,13 @@ pub trait GfxFramebuffer: Debug {
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
clear_cd: &Rc<LinearColorDescription>,
|
||||||
region: &Region,
|
region: &Region,
|
||||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
|
blend_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError>;
|
) -> Result<Option<SyncFile>, GfxError>;
|
||||||
|
|
||||||
fn format(&self) -> &'static Format;
|
fn format(&self) -> &'static Format;
|
||||||
|
|
@ -333,17 +340,23 @@ impl dyn GfxFramebuffer {
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
clear_cd: &Rc<LinearColorDescription>,
|
||||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
|
blend_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.clone().render_with_region(
|
self.clone().render_with_region(
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
cd,
|
||||||
ops,
|
ops,
|
||||||
clear,
|
clear,
|
||||||
|
clear_cd,
|
||||||
&self.full_region(),
|
&self.full_region(),
|
||||||
blend_buffer,
|
blend_buffer,
|
||||||
|
blend_cd,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -351,17 +364,35 @@ impl dyn GfxFramebuffer {
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> 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(
|
pub fn clear_with(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
color: &Color,
|
color: &Color,
|
||||||
|
color_cd: &Rc<LinearColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> 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) {
|
pub fn logical_size(&self, transform: Transform) -> (i32, i32) {
|
||||||
|
|
@ -381,7 +412,9 @@ impl dyn GfxFramebuffer {
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
fb_acquire_sync: AcquireSync,
|
fb_acquire_sync: AcquireSync,
|
||||||
fb_release_sync: ReleaseSync,
|
fb_release_sync: ReleaseSync,
|
||||||
|
fb_cd: &Rc<ColorDescription>,
|
||||||
texture: &Rc<dyn GfxTexture>,
|
texture: &Rc<dyn GfxTexture>,
|
||||||
|
texture_cd: &Rc<ColorDescription>,
|
||||||
resv: Option<&Rc<dyn BufferResv>>,
|
resv: Option<&Rc<dyn BufferResv>>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
|
@ -404,24 +437,46 @@ impl dyn GfxFramebuffer {
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
false,
|
false,
|
||||||
|
texture_cd,
|
||||||
);
|
);
|
||||||
let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT);
|
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(
|
pub fn render_custom(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
clear_cd: &Rc<LinearColorDescription>,
|
||||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
|
blend_cd: &Rc<ColorDescription>,
|
||||||
f: &mut dyn FnMut(&mut RendererBase),
|
f: &mut dyn FnMut(&mut RendererBase),
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let mut ops = vec![];
|
let mut ops = vec![];
|
||||||
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
|
||||||
f(&mut renderer);
|
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(
|
pub fn create_render_pass(
|
||||||
|
|
@ -456,17 +511,22 @@ impl dyn GfxFramebuffer {
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
pass: &GfxRenderPass,
|
pass: &GfxRenderPass,
|
||||||
region: &Region,
|
region: &Region,
|
||||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
|
blend_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.clone().render_with_region(
|
self.clone().render_with_region(
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
cd,
|
||||||
&pass.ops,
|
&pass.ops,
|
||||||
pass.clear.as_ref(),
|
pass.clear.as_ref(),
|
||||||
|
&pass.clear_cd,
|
||||||
region,
|
region,
|
||||||
blend_buffer,
|
blend_buffer,
|
||||||
|
blend_cd,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -474,6 +534,7 @@ impl dyn GfxFramebuffer {
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
node: &OutputNode,
|
node: &OutputNode,
|
||||||
state: &State,
|
state: &State,
|
||||||
cursor_rect: Option<Rect>,
|
cursor_rect: Option<Rect>,
|
||||||
|
|
@ -481,10 +542,12 @@ impl dyn GfxFramebuffer {
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
fill_black_in_grace_period: bool,
|
fill_black_in_grace_period: bool,
|
||||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
|
blend_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
self.render_node(
|
self.render_node(
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
cd,
|
||||||
node,
|
node,
|
||||||
state,
|
state,
|
||||||
cursor_rect,
|
cursor_rect,
|
||||||
|
|
@ -495,6 +558,7 @@ impl dyn GfxFramebuffer {
|
||||||
fill_black_in_grace_period,
|
fill_black_in_grace_period,
|
||||||
node.global.persistent.transform.get(),
|
node.global.persistent.transform.get(),
|
||||||
blend_buffer,
|
blend_buffer,
|
||||||
|
blend_cd,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -502,6 +566,7 @@ impl dyn GfxFramebuffer {
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
node: &dyn Node,
|
node: &dyn Node,
|
||||||
state: &State,
|
state: &State,
|
||||||
cursor_rect: Option<Rect>,
|
cursor_rect: Option<Rect>,
|
||||||
|
|
@ -512,6 +577,7 @@ impl dyn GfxFramebuffer {
|
||||||
fill_black_in_grace_period: bool,
|
fill_black_in_grace_period: bool,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
|
blend_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let pass = self.create_render_pass(
|
let pass = self.create_render_pass(
|
||||||
node,
|
node,
|
||||||
|
|
@ -528,9 +594,11 @@ impl dyn GfxFramebuffer {
|
||||||
self.perform_render_pass(
|
self.perform_render_pass(
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
cd,
|
||||||
&pass,
|
&pass,
|
||||||
&self.full_region(),
|
&self.full_region(),
|
||||||
blend_buffer,
|
blend_buffer,
|
||||||
|
blend_cd,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -542,6 +610,7 @@ impl dyn GfxFramebuffer {
|
||||||
state: &State,
|
state: &State,
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let mut ops = vec![];
|
let mut ops = vec![];
|
||||||
let mut renderer = Renderer {
|
let mut renderer = Renderer {
|
||||||
|
|
@ -557,9 +626,12 @@ impl dyn GfxFramebuffer {
|
||||||
self.render(
|
self.render(
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
cd,
|
||||||
&ops,
|
&ops,
|
||||||
Some(&Color::TRANSPARENT),
|
Some(&Color::TRANSPARENT),
|
||||||
|
&cd.linear,
|
||||||
None,
|
None,
|
||||||
|
cd,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -832,6 +904,7 @@ pub fn create_render_pass(
|
||||||
return GfxRenderPass {
|
return GfxRenderPass {
|
||||||
ops: vec![],
|
ops: vec![],
|
||||||
clear: Some(Color::SOLID_BLACK),
|
clear: Some(Color::SOLID_BLACK),
|
||||||
|
clear_cd: state.color_manager.srgb_srgb().linear.clone(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let mut ops = vec![];
|
let mut ops = vec![];
|
||||||
|
|
@ -898,6 +971,7 @@ pub fn create_render_pass(
|
||||||
GfxRenderPass {
|
GfxRenderPass {
|
||||||
ops,
|
ops,
|
||||||
clear: Some(c),
|
clear: Some(c),
|
||||||
|
clear_cd: state.color_manager.srgb_srgb().linear.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,9 @@ macro_rules! dynload {
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
cmm::cmm_transfer_function::TransferFunction,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
AcquireSync, CopyTexture, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
||||||
ReleaseSync, SyncFile,
|
ReleaseSync, SyncFile,
|
||||||
},
|
},
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
|
|
@ -84,7 +85,7 @@ use {
|
||||||
GL_TRIANGLE_STRIP, GL_TRIANGLES,
|
GL_TRIANGLE_STRIP, GL_TRIANGLES,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
theme::{Color, TransferFunction},
|
theme::Color,
|
||||||
utils::{errorfmt::ErrorFmt, rc_eq::rc_eq, vecstorage::VecStorage},
|
utils::{errorfmt::ErrorFmt, rc_eq::rc_eq, vecstorage::VecStorage},
|
||||||
video::{
|
video::{
|
||||||
dmabuf::DMA_BUF_SYNC_READ,
|
dmabuf::DMA_BUF_SYNC_READ,
|
||||||
|
|
@ -204,10 +205,15 @@ enum RenderError {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct GfxGlState {
|
struct GfxGlState {
|
||||||
triangles: RefCell<Vec<[f32; 2]>>,
|
triangles: RefCell<Vec<[f32; 2]>>,
|
||||||
fill_rect: VecStorage<FillRect>,
|
fill_rect: VecStorage<GlFillRect>,
|
||||||
copy_tex: VecStorage<&'static CopyTexture>,
|
copy_tex: VecStorage<&'static CopyTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GlFillRect {
|
||||||
|
pub rect: FramebufferRect,
|
||||||
|
pub color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
|
fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
|
||||||
let mut state = fb.ctx.gl_state.borrow_mut();
|
let mut state = fb.ctx.gl_state.borrow_mut();
|
||||||
let state = &mut *state;
|
let state = &mut *state;
|
||||||
|
|
@ -235,10 +241,9 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GfxApiOpt::FillRect(f) => {
|
GfxApiOpt::FillRect(f) => {
|
||||||
fill_rect.push(FillRect {
|
fill_rect.push(GlFillRect {
|
||||||
rect: f.rect,
|
rect: f.rect,
|
||||||
color: f.effective_color(),
|
color: f.effective_color(),
|
||||||
alpha: None,
|
|
||||||
});
|
});
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
cmm::{
|
||||||
|
cmm_description::{ColorDescription, LinearColorDescription},
|
||||||
|
cmm_transfer_function::TransferFunction,
|
||||||
|
},
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, AsyncShmGfxTextureCallback, GfxApiOpt, GfxBlendBuffer, GfxError,
|
AcquireSync, AsyncShmGfxTextureCallback, GfxApiOpt, GfxBlendBuffer, GfxError,
|
||||||
|
|
@ -18,7 +22,7 @@ use {
|
||||||
sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
|
sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
|
||||||
},
|
},
|
||||||
rect::Region,
|
rect::Region,
|
||||||
theme::{Color, TransferFunction},
|
theme::Color,
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
|
|
@ -104,10 +108,13 @@ impl GfxFramebuffer for Framebuffer {
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
_release_sync: ReleaseSync,
|
_release_sync: ReleaseSync,
|
||||||
|
_cd: &Rc<ColorDescription>,
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
_clear_cd: &Rc<LinearColorDescription>,
|
||||||
_region: &Region,
|
_region: &Region,
|
||||||
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
|
_blend_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
(*self)
|
(*self)
|
||||||
.render(acquire_sync, ops, clear)
|
.render(acquire_sync, ops, clear)
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,6 @@ impl VulkanRenderer {
|
||||||
ty: VulkanImageMemory::Blend(allocation),
|
ty: VulkanImageMemory::Blend(allocation),
|
||||||
bridge: None,
|
bridge: None,
|
||||||
sampled_image_descriptor: self.sampled_image_descriptor(view),
|
sampled_image_descriptor: self.sampled_image_descriptor(view),
|
||||||
descriptor_buffer_version: Default::default(),
|
|
||||||
descriptor_buffer_offset: Default::default(),
|
|
||||||
execution_version: Default::default(),
|
execution_version: Default::default(),
|
||||||
});
|
});
|
||||||
cached.insert_entry(Rc::downgrade(&img));
|
cached.insert_entry(Rc::downgrade(&img));
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ use {
|
||||||
DeviceSize,
|
DeviceSize,
|
||||||
},
|
},
|
||||||
gpu_alloc::UsageFlags,
|
gpu_alloc::UsageFlags,
|
||||||
std::{cell::RefCell, mem::ManuallyDrop, rc::Rc},
|
std::{cell::RefCell, mem::ManuallyDrop, ops::Deref, rc::Rc},
|
||||||
|
uapi::Packed,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct VulkanBufferCache {
|
pub struct VulkanBufferCache {
|
||||||
|
|
@ -20,6 +21,7 @@ pub struct VulkanBufferCache {
|
||||||
allocator: Rc<VulkanAllocator>,
|
allocator: Rc<VulkanAllocator>,
|
||||||
buffers: RefCell<Vec<VulkanBufferUnused>>,
|
buffers: RefCell<Vec<VulkanBufferUnused>>,
|
||||||
usage: BufferUsageFlags,
|
usage: BufferUsageFlags,
|
||||||
|
min_alignment: DeviceSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VulkanBuffer {
|
pub struct VulkanBuffer {
|
||||||
|
|
@ -40,12 +42,14 @@ impl VulkanBufferCache {
|
||||||
device: &Rc<VulkanDevice>,
|
device: &Rc<VulkanDevice>,
|
||||||
allocator: &Rc<VulkanAllocator>,
|
allocator: &Rc<VulkanAllocator>,
|
||||||
usage: BufferUsageFlags,
|
usage: BufferUsageFlags,
|
||||||
|
min_alignment: DeviceSize,
|
||||||
) -> Rc<Self> {
|
) -> Rc<Self> {
|
||||||
Rc::new(Self {
|
Rc::new(Self {
|
||||||
device: device.clone(),
|
device: device.clone(),
|
||||||
allocator: allocator.clone(),
|
allocator: allocator.clone(),
|
||||||
buffers: Default::default(),
|
buffers: Default::default(),
|
||||||
usage,
|
usage,
|
||||||
|
min_alignment,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,25 +59,27 @@ impl VulkanBufferCache {
|
||||||
for_sampler: bool,
|
for_sampler: bool,
|
||||||
) -> Rc<Self> {
|
) -> Rc<Self> {
|
||||||
let mut usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS;
|
let mut usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS;
|
||||||
|
let mut min_alignment = 1;
|
||||||
if for_sampler {
|
if for_sampler {
|
||||||
usage |= BufferUsageFlags::SAMPLER_DESCRIPTOR_BUFFER_EXT;
|
usage |= BufferUsageFlags::SAMPLER_DESCRIPTOR_BUFFER_EXT;
|
||||||
} else {
|
} else {
|
||||||
usage |= BufferUsageFlags::RESOURCE_DESCRIPTOR_BUFFER_EXT;
|
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 {
|
pub fn usage(&self) -> BufferUsageFlags {
|
||||||
self.usage
|
self.usage
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate(
|
pub fn allocate(self: &Rc<Self>, capacity: DeviceSize) -> Result<VulkanBuffer, VulkanError> {
|
||||||
self: &Rc<Self>,
|
|
||||||
capacity: DeviceSize,
|
|
||||||
align: DeviceSize,
|
|
||||||
) -> Result<VulkanBuffer, VulkanError> {
|
|
||||||
const MIN_ALLOCATION: DeviceSize = 1024;
|
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 = None;
|
||||||
let mut smallest_size = DeviceSize::MAX;
|
let mut smallest_size = DeviceSize::MAX;
|
||||||
let mut fitting = None;
|
let mut fitting = None;
|
||||||
|
|
@ -116,7 +122,7 @@ impl VulkanBufferCache {
|
||||||
let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) });
|
let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) });
|
||||||
let mut memory_requirements =
|
let mut memory_requirements =
|
||||||
unsafe { self.device.device.get_buffer_memory_requirements(buffer) };
|
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 allocation = {
|
||||||
let flags = UsageFlags::UPLOAD
|
let flags = UsageFlags::UPLOAD
|
||||||
| UsageFlags::FAST_DEVICE_ACCESS
|
| 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) device: Rc<VulkanDevice>,
|
||||||
pub(super) layout: DescriptorSetLayout,
|
pub(super) layout: DescriptorSetLayout,
|
||||||
pub(super) size: DeviceSize,
|
pub(super) size: DeviceSize,
|
||||||
pub(super) offsets: ArrayVec<DeviceSize, 1>,
|
pub(super) offsets: ArrayVec<DeviceSize, 2>,
|
||||||
pub(super) _sampler: Option<Rc<VulkanSampler>>,
|
pub(super) _sampler: Option<Rc<VulkanSampler>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,12 +87,20 @@ impl VulkanDevice {
|
||||||
pub(super) fn create_tex_resource_descriptor_set_layout(
|
pub(super) fn create_tex_resource_descriptor_set_layout(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> {
|
) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> {
|
||||||
let binding = DescriptorSetLayoutBinding::default()
|
let bindings = [
|
||||||
.stage_flags(ShaderStageFlags::FRAGMENT)
|
DescriptorSetLayoutBinding::default()
|
||||||
.descriptor_count(1)
|
.binding(0)
|
||||||
.descriptor_type(DescriptorType::SAMPLED_IMAGE);
|
.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()
|
let create_info = DescriptorSetLayoutCreateInfo::default()
|
||||||
.bindings(slice::from_ref(&binding))
|
.bindings(&bindings)
|
||||||
.flags(DescriptorSetLayoutCreateFlags::DESCRIPTOR_BUFFER_EXT);
|
.flags(DescriptorSetLayoutCreateFlags::DESCRIPTOR_BUFFER_EXT);
|
||||||
let layout = unsafe { self.device.create_descriptor_set_layout(&create_info, None) };
|
let layout = unsafe { self.device.create_descriptor_set_layout(&create_info, None) };
|
||||||
let layout = layout.map_err(VulkanError::CreateDescriptorSetLayout)?;
|
let layout = layout.map_err(VulkanError::CreateDescriptorSetLayout)?;
|
||||||
|
|
@ -101,6 +109,7 @@ impl VulkanDevice {
|
||||||
let mut offsets = ArrayVec::new();
|
let mut offsets = ArrayVec::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
offsets.push(db.get_descriptor_set_layout_binding_offset(layout, 0));
|
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 {
|
Ok(Rc::new(VulkanDescriptorSetLayout {
|
||||||
device: self.clone(),
|
device: self.clone(),
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,9 @@ use {
|
||||||
PhysicalDeviceDrmPropertiesEXT, PhysicalDeviceDynamicRenderingFeatures,
|
PhysicalDeviceDrmPropertiesEXT, PhysicalDeviceDynamicRenderingFeatures,
|
||||||
PhysicalDeviceExternalSemaphoreInfo, PhysicalDeviceProperties,
|
PhysicalDeviceExternalSemaphoreInfo, PhysicalDeviceProperties,
|
||||||
PhysicalDeviceProperties2, PhysicalDeviceSynchronization2Features,
|
PhysicalDeviceProperties2, PhysicalDeviceSynchronization2Features,
|
||||||
PhysicalDeviceTimelineSemaphoreFeatures, PhysicalDeviceVulkan12Properties, Queue,
|
PhysicalDeviceTimelineSemaphoreFeatures,
|
||||||
QueueFlags,
|
PhysicalDeviceUniformBufferStandardLayoutFeatures, PhysicalDeviceVulkan12Properties,
|
||||||
|
Queue, QueueFlags,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isnt::std_1::collections::IsntHashMap2Ext,
|
isnt::std_1::collections::IsntHashMap2Ext,
|
||||||
|
|
@ -75,6 +76,8 @@ pub struct VulkanDevice {
|
||||||
pub(super) sampler_descriptor_size: usize,
|
pub(super) sampler_descriptor_size: usize,
|
||||||
pub(super) sampled_image_descriptor_size: usize,
|
pub(super) sampled_image_descriptor_size: usize,
|
||||||
pub(super) is_anv: bool,
|
pub(super) is_anv: bool,
|
||||||
|
pub(super) uniform_buffer_offset_mask: DeviceSize,
|
||||||
|
pub(super) uniform_buffer_descriptor_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for VulkanDevice {
|
impl Drop for VulkanDevice {
|
||||||
|
|
@ -308,6 +311,9 @@ impl VulkanInstance {
|
||||||
PhysicalDeviceDescriptorBufferFeaturesEXT::default().descriptor_buffer(true);
|
PhysicalDeviceDescriptorBufferFeaturesEXT::default().descriptor_buffer(true);
|
||||||
let mut buffer_device_address_features =
|
let mut buffer_device_address_features =
|
||||||
PhysicalDeviceBufferDeviceAddressFeatures::default().buffer_device_address(true);
|
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();
|
let mut queue_create_infos = ArrayVec::<_, 2>::new();
|
||||||
queue_create_infos.push(
|
queue_create_infos.push(
|
||||||
DeviceQueueCreateInfo::default()
|
DeviceQueueCreateInfo::default()
|
||||||
|
|
@ -325,6 +331,7 @@ impl VulkanInstance {
|
||||||
.push_next(&mut semaphore_features)
|
.push_next(&mut semaphore_features)
|
||||||
.push_next(&mut synchronization2_features)
|
.push_next(&mut synchronization2_features)
|
||||||
.push_next(&mut dynamic_rendering_features)
|
.push_next(&mut dynamic_rendering_features)
|
||||||
|
.push_next(&mut uniform_buffer_standard_layout_features)
|
||||||
.queue_create_infos(&queue_create_infos)
|
.queue_create_infos(&queue_create_infos)
|
||||||
.enabled_extension_names(&enabled_extensions);
|
.enabled_extension_names(&enabled_extensions);
|
||||||
if supports_descriptor_buffer {
|
if supports_descriptor_buffer {
|
||||||
|
|
@ -382,6 +389,14 @@ impl VulkanInstance {
|
||||||
let mut descriptor_buffer_offset_mask = 0;
|
let mut descriptor_buffer_offset_mask = 0;
|
||||||
let mut sampler_descriptor_size = 0;
|
let mut sampler_descriptor_size = 0;
|
||||||
let mut sampled_image_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 {
|
if supports_descriptor_buffer {
|
||||||
descriptor_buffer_offset_mask = descriptor_buffer_props
|
descriptor_buffer_offset_mask = descriptor_buffer_props
|
||||||
.descriptor_buffer_offset_alignment
|
.descriptor_buffer_offset_alignment
|
||||||
|
|
@ -390,6 +405,7 @@ impl VulkanInstance {
|
||||||
- 1;
|
- 1;
|
||||||
sampler_descriptor_size = descriptor_buffer_props.sampler_descriptor_size;
|
sampler_descriptor_size = descriptor_buffer_props.sampler_descriptor_size;
|
||||||
sampled_image_descriptor_size = descriptor_buffer_props.sampled_image_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 =
|
let memory_properties =
|
||||||
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
|
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
|
||||||
|
|
@ -432,6 +448,8 @@ impl VulkanInstance {
|
||||||
blend_limits,
|
blend_limits,
|
||||||
is_anv: physical_device_vulkan12_properties.driver_id
|
is_anv: physical_device_vulkan12_properties.driver_id
|
||||||
== DriverId::INTEL_OPEN_SOURCE_MESA,
|
== DriverId::INTEL_OPEN_SOURCE_MESA,
|
||||||
|
uniform_buffer_offset_mask,
|
||||||
|
uniform_buffer_descriptor_size,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
cmm::cmm_description::{ColorDescription, LinearColorDescription},
|
||||||
format::Format,
|
format::Format,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
|
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
|
||||||
|
|
@ -20,8 +21,8 @@ use {
|
||||||
ash::vk::{
|
ash::vk::{
|
||||||
BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle,
|
BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle,
|
||||||
DescriptorDataEXT, DescriptorGetInfoEXT, DescriptorImageInfo, DescriptorType, DeviceMemory,
|
DescriptorDataEXT, DescriptorGetInfoEXT, DescriptorImageInfo, DescriptorType, DeviceMemory,
|
||||||
DeviceSize, Extent3D, ExternalMemoryHandleTypeFlags, ExternalMemoryImageCreateInfo,
|
Extent3D, ExternalMemoryHandleTypeFlags, ExternalMemoryImageCreateInfo, FormatFeatureFlags,
|
||||||
FormatFeatureFlags, Image, ImageAspectFlags, ImageCreateFlags, ImageCreateInfo,
|
Image, ImageAspectFlags, ImageCreateFlags, ImageCreateInfo,
|
||||||
ImageDrmFormatModifierExplicitCreateInfoEXT, ImageLayout, ImageMemoryRequirementsInfo2,
|
ImageDrmFormatModifierExplicitCreateInfoEXT, ImageLayout, ImageMemoryRequirementsInfo2,
|
||||||
ImagePlaneMemoryRequirementsInfo, ImageSubresourceRange, ImageTiling, ImageType,
|
ImagePlaneMemoryRequirementsInfo, ImageSubresourceRange, ImageTiling, ImageType,
|
||||||
ImageUsageFlags, ImageView, ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR,
|
ImageUsageFlags, ImageView, ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR,
|
||||||
|
|
@ -65,8 +66,6 @@ pub struct VulkanImage {
|
||||||
pub(super) ty: VulkanImageMemory,
|
pub(super) ty: VulkanImageMemory,
|
||||||
pub(super) bridge: Option<VulkanFramebufferBridge>,
|
pub(super) bridge: Option<VulkanFramebufferBridge>,
|
||||||
pub(super) sampled_image_descriptor: Box<[u8]>,
|
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>,
|
pub(super) execution_version: Cell<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -467,8 +466,6 @@ impl VulkanDmaBufImageTemplate {
|
||||||
}),
|
}),
|
||||||
bridge,
|
bridge,
|
||||||
sampled_image_descriptor: self.renderer.sampled_image_descriptor(texture_view),
|
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),
|
execution_version: Cell::new(0),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
@ -552,10 +549,13 @@ impl GfxFramebuffer for VulkanImage {
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
clear_cd: &Rc<LinearColorDescription>,
|
||||||
region: &Region,
|
region: &Region,
|
||||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
|
blend_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let mut blend_buffer =
|
let mut blend_buffer =
|
||||||
blend_buffer.map(|b| b.clone().into_vk(&self.renderer.device.device));
|
blend_buffer.map(|b| b.clone().into_vk(&self.renderer.device.device));
|
||||||
|
|
@ -574,10 +574,13 @@ impl GfxFramebuffer for VulkanImage {
|
||||||
&self,
|
&self,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
cd,
|
||||||
ops,
|
ops,
|
||||||
clear,
|
clear,
|
||||||
|
clear_cd,
|
||||||
region,
|
region,
|
||||||
blend_buffer,
|
blend_buffer,
|
||||||
|
blend_cd,
|
||||||
)
|
)
|
||||||
.map_err(|e| e.into())
|
.map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ pub(super) struct PipelineCreateInfo {
|
||||||
pub(super) eotf: u32,
|
pub(super) eotf: u32,
|
||||||
pub(super) oetf: u32,
|
pub(super) oetf: u32,
|
||||||
pub(super) descriptor_set_layouts: ArrayVec<Rc<VulkanDescriptorSetLayout>, 2>,
|
pub(super) descriptor_set_layouts: ArrayVec<Rc<VulkanDescriptorSetLayout>, 2>,
|
||||||
|
pub(super) has_color_management_data: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanDevice {
|
impl VulkanDevice {
|
||||||
|
|
@ -77,8 +78,8 @@ impl VulkanDevice {
|
||||||
};
|
};
|
||||||
let destroy_layout =
|
let destroy_layout =
|
||||||
OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) });
|
OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) });
|
||||||
let mut frag_spec_data = ArrayVec::<_, { 4 * 4 }>::new();
|
let mut frag_spec_data = ArrayVec::<_, { 5 * 4 }>::new();
|
||||||
let mut frag_spec_entries = ArrayVec::<_, 4>::new();
|
let mut frag_spec_entries = ArrayVec::<_, 5>::new();
|
||||||
let mut frag_spec_entry = |data: &[u8]| {
|
let mut frag_spec_entry = |data: &[u8]| {
|
||||||
let entry = SpecializationMapEntry::default()
|
let entry = SpecializationMapEntry::default()
|
||||||
.constant_id(frag_spec_entries.len() as _)
|
.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.has_alpha_mult as u32).to_ne_bytes());
|
||||||
frag_spec_entry(&info.eotf.to_ne_bytes());
|
frag_spec_entry(&info.eotf.to_ne_bytes());
|
||||||
frag_spec_entry(&info.oetf.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()
|
let frag_spec = SpecializationInfo::default()
|
||||||
.map_entries(&frag_spec_entries)
|
.map_entries(&frag_spec_entries)
|
||||||
.data(&frag_spec_data);
|
.data(&frag_spec_data);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::{AsyncEngine, SpawnedFuture},
|
async_engine::{AsyncEngine, SpawnedFuture},
|
||||||
|
cmm::{
|
||||||
|
cmm_description::{ColorDescription, LinearColorDescription, LinearColorDescriptionId},
|
||||||
|
cmm_transfer_function::TransferFunction,
|
||||||
|
cmm_transform::ColorMatrix,
|
||||||
|
},
|
||||||
cpu_worker::PendingJob,
|
cpu_worker::PendingJob,
|
||||||
format::XRGB8888,
|
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxBlendBuffer, GfxFormat,
|
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxBlendBuffer, GfxFormat,
|
||||||
GfxTexture, GfxWriteModifier, ReleaseSync, SyncFile,
|
GfxTexture, GfxWriteModifier, ReleaseSync, SyncFile,
|
||||||
|
|
@ -10,7 +14,7 @@ use {
|
||||||
gfx_apis::vulkan::{
|
gfx_apis::vulkan::{
|
||||||
VulkanError,
|
VulkanError,
|
||||||
allocator::{VulkanAllocator, VulkanThreadedAllocator},
|
allocator::{VulkanAllocator, VulkanThreadedAllocator},
|
||||||
buffer_cache::{VulkanBuffer, VulkanBufferCache},
|
buffer_cache::{GenericBufferWriter, VulkanBuffer, VulkanBufferCache},
|
||||||
command::{VulkanCommandBuffer, VulkanCommandPool},
|
command::{VulkanCommandBuffer, VulkanCommandPool},
|
||||||
descriptor::VulkanDescriptorSetLayout,
|
descriptor::VulkanDescriptorSetLayout,
|
||||||
descriptor_buffer::VulkanDescriptorBufferWriter,
|
descriptor_buffer::VulkanDescriptorBufferWriter,
|
||||||
|
|
@ -23,14 +27,14 @@ use {
|
||||||
shaders::{
|
shaders::{
|
||||||
FILL_FRAG, FILL_VERT, FillPushConstants, LEGACY_FILL_FRAG, LEGACY_FILL_VERT,
|
FILL_FRAG, FILL_VERT, FillPushConstants, LEGACY_FILL_FRAG, LEGACY_FILL_VERT,
|
||||||
LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants, LegacyTexPushConstants,
|
LEGACY_TEX_FRAG, LEGACY_TEX_VERT, LegacyFillPushConstants, LegacyTexPushConstants,
|
||||||
OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, TEX_VERT, TexPushConstants,
|
OUT_FRAG, OUT_VERT, OutPushConstants, TEX_FRAG, TEX_VERT, TexColorManagementData,
|
||||||
TexVertex, VulkanShader,
|
TexPushConstants, TexVertex, VulkanShader,
|
||||||
},
|
},
|
||||||
transfer_functions::{TF_LINEAR, TF_SRGB},
|
transfer_functions::{TF_LINEAR, TransferFunctionExt},
|
||||||
},
|
},
|
||||||
io_uring::IoUring,
|
io_uring::IoUring,
|
||||||
rect::{Rect, Region},
|
rect::{Rect, Region},
|
||||||
theme::{Color, TransferFunction},
|
theme::Color,
|
||||||
utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack},
|
utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack},
|
||||||
video::dmabuf::{DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, dma_buf_export_sync_file},
|
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,
|
self, AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferUsageFlags,
|
||||||
ClearAttachment, ClearColorValue, ClearRect, ClearValue, CommandBuffer,
|
ClearAttachment, ClearColorValue, ClearRect, ClearValue, CommandBuffer,
|
||||||
CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags,
|
CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags,
|
||||||
CopyImageInfo2, DependencyInfoKHR, DescriptorBufferBindingInfoEXT, DescriptorImageInfo,
|
CopyImageInfo2, DependencyInfoKHR, DescriptorAddressInfoEXT,
|
||||||
DescriptorType, DeviceAddress, DeviceSize, Extent2D, Extent3D, ImageAspectFlags,
|
DescriptorBufferBindingInfoEXT, DescriptorDataEXT, DescriptorGetInfoEXT,
|
||||||
ImageCopy2, ImageLayout, ImageMemoryBarrier2, ImageSubresourceLayers,
|
DescriptorImageInfo, DescriptorType, DeviceAddress, DeviceSize, Extent2D, Extent3D,
|
||||||
|
ImageAspectFlags, ImageCopy2, ImageLayout, ImageMemoryBarrier2, ImageSubresourceLayers,
|
||||||
ImageSubresourceRange, Offset2D, Offset3D, PipelineBindPoint, PipelineStageFlags2,
|
ImageSubresourceRange, Offset2D, Offset3D, PipelineBindPoint, PipelineStageFlags2,
|
||||||
QUEUE_FAMILY_FOREIGN_EXT, Rect2D, RenderingAttachmentInfo, RenderingInfo,
|
QUEUE_FAMILY_FOREIGN_EXT, Rect2D, RenderingAttachmentInfo, RenderingInfo,
|
||||||
SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport,
|
SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport,
|
||||||
|
|
@ -71,8 +76,11 @@ use {
|
||||||
pub struct VulkanRenderer {
|
pub struct VulkanRenderer {
|
||||||
pub(super) formats: Rc<AHashMap<u32, GfxFormat>>,
|
pub(super) formats: Rc<AHashMap<u32, GfxFormat>>,
|
||||||
pub(super) device: Rc<VulkanDevice>,
|
pub(super) device: Rc<VulkanDevice>,
|
||||||
pub(super) pipelines: StaticMap<RenderPass, CopyHashMap<vk::Format, Rc<VulkanFormatPipelines>>>,
|
pub(super) fill_pipelines: CopyHashMap<vk::Format, FillPipelines>,
|
||||||
pub(super) out_pipelines: RefCell<AHashMap<vk::Format, Rc<VulkanPipeline>>>,
|
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) gfx_command_buffers: CachedCommandBuffers,
|
||||||
pub(super) transfer_command_buffers: Option<CachedCommandBuffers>,
|
pub(super) transfer_command_buffers: Option<CachedCommandBuffers>,
|
||||||
pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>,
|
pub(super) wait_semaphores: Stack<Rc<VulkanSemaphore>>,
|
||||||
|
|
@ -101,6 +109,7 @@ pub struct VulkanRenderer {
|
||||||
pub(super) resource_descriptor_buffer_cache: Rc<VulkanBufferCache>,
|
pub(super) resource_descriptor_buffer_cache: Rc<VulkanBufferCache>,
|
||||||
pub(super) blend_buffers: RefCell<AHashMap<(u32, u32), Weak<VulkanImage>>>,
|
pub(super) blend_buffers: RefCell<AHashMap<(u32, u32), Weak<VulkanImage>>>,
|
||||||
pub(super) shader_buffer_cache: Rc<VulkanBufferCache>,
|
pub(super) shader_buffer_cache: Rc<VulkanBufferCache>,
|
||||||
|
pub(super) uniform_buffer_cache: Rc<VulkanBufferCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct CachedCommandBuffers {
|
pub(super) struct CachedCommandBuffers {
|
||||||
|
|
@ -130,13 +139,13 @@ pub(super) struct UsedTexture {
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Linearize)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Linearize)]
|
||||||
pub(super) enum TexCopyType {
|
pub(super) enum TexCopyType {
|
||||||
Identity,
|
Identity,
|
||||||
Multiply,
|
Multiply,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Linearize)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Linearize)]
|
||||||
pub(super) enum TexSourceType {
|
pub(super) enum TexSourceType {
|
||||||
Opaque,
|
Opaque,
|
||||||
HasAlpha,
|
HasAlpha,
|
||||||
|
|
@ -152,7 +161,7 @@ pub(super) struct Memory {
|
||||||
wait_semaphore_infos: Vec<SemaphoreSubmitInfo<'static>>,
|
wait_semaphore_infos: Vec<SemaphoreSubmitInfo<'static>>,
|
||||||
release_fence: Option<Rc<VulkanFence>>,
|
release_fence: Option<Rc<VulkanFence>>,
|
||||||
release_sync_file: Option<SyncFile>,
|
release_sync_file: Option<SyncFile>,
|
||||||
used_buffers: ArrayVec<VulkanBuffer, 3>,
|
used_buffers: ArrayVec<VulkanBuffer, 4>,
|
||||||
paint_bounds: StaticMap<RenderPass, Option<PaintRegion>>,
|
paint_bounds: StaticMap<RenderPass, Option<PaintRegion>>,
|
||||||
paint_regions: StaticMap<RenderPass, Vec<PaintRegion>>,
|
paint_regions: StaticMap<RenderPass, Vec<PaintRegion>>,
|
||||||
clear_rects: StaticMap<RenderPass, Vec<ClearRect>>,
|
clear_rects: StaticMap<RenderPass, Vec<ClearRect>>,
|
||||||
|
|
@ -167,6 +176,10 @@ pub(super) struct Memory {
|
||||||
tex_targets: Vec<[Point; 2]>,
|
tex_targets: Vec<[Point; 2]>,
|
||||||
data_buffer: Vec<u8>,
|
data_buffer: Vec<u8>,
|
||||||
out_address: DeviceAddress,
|
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];
|
type Point = [[f32; 2]; 4];
|
||||||
|
|
@ -187,6 +200,9 @@ struct VulkanTexOp {
|
||||||
copy_type: TexCopyType,
|
copy_type: TexCopyType,
|
||||||
range_address: DeviceAddress,
|
range_address: DeviceAddress,
|
||||||
instances: u32,
|
instances: u32,
|
||||||
|
tex_cd: Rc<ColorDescription>,
|
||||||
|
color_management_data_address: Option<DeviceAddress>,
|
||||||
|
resource_descriptor_buffer_offset: DeviceAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VulkanFillOp {
|
struct VulkanFillOp {
|
||||||
|
|
@ -221,12 +237,29 @@ pub(super) struct PendingFrame {
|
||||||
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
|
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
|
||||||
waiter: Cell<Option<SpawnedFuture<()>>>,
|
waiter: Cell<Option<SpawnedFuture<()>>>,
|
||||||
_release_fence: Option<Rc<VulkanFence>>,
|
_release_fence: Option<Rc<VulkanFence>>,
|
||||||
_used_buffers: ArrayVec<VulkanBuffer, 3>,
|
_used_buffers: ArrayVec<VulkanBuffer, 4>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct VulkanFormatPipelines {
|
type FillPipelines = Rc<StaticMap<TexSourceType, Rc<VulkanPipeline>>>;
|
||||||
pub(super) fill: StaticMap<TexSourceType, Rc<VulkanPipeline>>,
|
|
||||||
pub(super) tex: StaticMap<TexCopyType, 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 {
|
impl VulkanDevice {
|
||||||
|
|
@ -313,12 +346,19 @@ impl VulkanDevice {
|
||||||
let shader_buffer_cache = {
|
let shader_buffer_cache = {
|
||||||
// TODO: https://github.com/KhronosGroup/Vulkan-Samples/issues/1286
|
// TODO: https://github.com/KhronosGroup/Vulkan-Samples/issues/1286
|
||||||
let usage = BufferUsageFlags::SHADER_DEVICE_ADDRESS | BufferUsageFlags::STORAGE_BUFFER;
|
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 {
|
let render = Rc::new(VulkanRenderer {
|
||||||
formats: Rc::new(formats),
|
formats: Rc::new(formats),
|
||||||
device: self.clone(),
|
device: self.clone(),
|
||||||
pipelines: Default::default(),
|
fill_pipelines: Default::default(),
|
||||||
|
tex_pipelines: Default::default(),
|
||||||
out_pipelines: Default::default(),
|
out_pipelines: Default::default(),
|
||||||
gfx_command_buffers,
|
gfx_command_buffers,
|
||||||
transfer_command_buffers,
|
transfer_command_buffers,
|
||||||
|
|
@ -348,24 +388,18 @@ impl VulkanDevice {
|
||||||
resource_descriptor_buffer_cache,
|
resource_descriptor_buffer_cache,
|
||||||
blend_buffers: Default::default(),
|
blend_buffers: Default::default(),
|
||||||
shader_buffer_cache,
|
shader_buffer_cache,
|
||||||
|
uniform_buffer_cache,
|
||||||
});
|
});
|
||||||
render.get_or_create_pipelines(XRGB8888.vk_format, RenderPass::FrameBuffer)?;
|
|
||||||
Ok(render)
|
Ok(render)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanRenderer {
|
impl VulkanRenderer {
|
||||||
fn get_or_create_pipelines(
|
fn get_or_create_fill_pipelines(
|
||||||
&self,
|
&self,
|
||||||
format: vk::Format,
|
format: vk::Format,
|
||||||
pass: RenderPass,
|
) -> Result<FillPipelines, VulkanError> {
|
||||||
) -> Result<Rc<VulkanFormatPipelines>, VulkanError> {
|
if let Some(pl) = self.fill_pipelines.get(&format) {
|
||||||
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) {
|
|
||||||
return Ok(pl);
|
return Ok(pl);
|
||||||
}
|
}
|
||||||
let create_fill_pipeline = |src_has_alpha| {
|
let create_fill_pipeline = |src_has_alpha| {
|
||||||
|
|
@ -381,55 +415,121 @@ impl VulkanRenderer {
|
||||||
blend: src_has_alpha,
|
blend: src_has_alpha,
|
||||||
src_has_alpha,
|
src_has_alpha,
|
||||||
has_alpha_mult: false,
|
has_alpha_mult: false,
|
||||||
eotf,
|
// all transformations are applied in the compositor
|
||||||
oetf,
|
eotf: TF_LINEAR,
|
||||||
|
oetf: TF_LINEAR,
|
||||||
descriptor_set_layouts: Default::default(),
|
descriptor_set_layouts: Default::default(),
|
||||||
|
has_color_management_data: false,
|
||||||
};
|
};
|
||||||
self.device.create_pipeline2(info, push_size)
|
self.device.create_pipeline2(info, push_size)
|
||||||
};
|
};
|
||||||
let fill_opaque = create_fill_pipeline(false)?;
|
let fill_pipelines = Rc::new(static_map! {
|
||||||
let fill_alpha = create_fill_pipeline(true)?;
|
TexSourceType::HasAlpha => create_fill_pipeline(true)?,
|
||||||
let create_tex_pipeline = |src_has_alpha, has_alpha_mult| {
|
TexSourceType::Opaque => create_fill_pipeline(false)?,
|
||||||
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(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
pipelines.set(format, format_pipelines.clone());
|
self.fill_pipelines.set(format, fill_pipelines.clone());
|
||||||
Ok(format_pipelines)
|
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 {
|
pub(super) fn allocate_point(&self) -> u64 {
|
||||||
|
|
@ -445,7 +545,6 @@ impl VulkanRenderer {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
zone!("create_descriptor_buffers");
|
zone!("create_descriptor_buffers");
|
||||||
let version = self.allocate_point();
|
|
||||||
let memory = &mut *self.memory.borrow_mut();
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
let sampler_writer = &mut memory.sampler_descriptor_buffer_writer;
|
let sampler_writer = &mut memory.sampler_descriptor_buffer_writer;
|
||||||
sampler_writer.clear();
|
sampler_writer.clear();
|
||||||
|
|
@ -458,30 +557,47 @@ impl VulkanRenderer {
|
||||||
}
|
}
|
||||||
let resource_writer = &mut memory.resource_descriptor_buffer_writer;
|
let resource_writer = &mut memory.resource_descriptor_buffer_writer;
|
||||||
resource_writer.clear();
|
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 {
|
if let Some(bb) = bb {
|
||||||
let layout = self.out_descriptor_set_layout.as_ref().unwrap();
|
let layout = self.out_descriptor_set_layout.as_ref().unwrap();
|
||||||
let offset = resource_writer.next_offset();
|
memory.blend_buffer_descriptor_buffer_offset = resource_writer.next_offset();
|
||||||
bb.descriptor_buffer_offset.set(offset);
|
|
||||||
let mut writer = resource_writer.add_set(layout);
|
let mut writer = resource_writer.add_set(layout);
|
||||||
writer.write(layout.offsets[0], &bb.sampled_image_descriptor);
|
writer.write(layout.offsets[0], &bb.sampled_image_descriptor);
|
||||||
}
|
}
|
||||||
let tex_descriptor_set_layout = &self.tex_descriptor_set_layouts[1];
|
let tex_descriptor_set_layout = &self.tex_descriptor_set_layouts[1];
|
||||||
for pass in RenderPass::variants() {
|
for pass in RenderPass::variants() {
|
||||||
for cmd in &memory.ops[pass] {
|
for cmd in &mut memory.ops[pass] {
|
||||||
let VulkanOp::Tex(c) = cmd else {
|
let VulkanOp::Tex(c) = cmd else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let tex = &c.tex;
|
let tex = &c.tex;
|
||||||
if tex.descriptor_buffer_version.replace(version) == version {
|
c.resource_descriptor_buffer_offset = resource_writer.next_offset();
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let offset = resource_writer.next_offset();
|
|
||||||
tex.descriptor_buffer_offset.set(offset);
|
|
||||||
let mut writer = resource_writer.add_set(tex_descriptor_set_layout);
|
let mut writer = resource_writer.add_set(tex_descriptor_set_layout);
|
||||||
writer.write(
|
writer.write(
|
||||||
tex_descriptor_set_layout.offsets[0],
|
tex_descriptor_set_layout.offsets[0],
|
||||||
&tex.sampled_image_descriptor,
|
&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();
|
let mut infos = ArrayVec::<_, 2>::new();
|
||||||
|
|
@ -489,12 +605,7 @@ impl VulkanRenderer {
|
||||||
(&sampler_writer, &self.sampler_descriptor_buffer_cache),
|
(&sampler_writer, &self.sampler_descriptor_buffer_cache),
|
||||||
(&resource_writer, &self.resource_descriptor_buffer_cache),
|
(&resource_writer, &self.resource_descriptor_buffer_cache),
|
||||||
] {
|
] {
|
||||||
let mut min_alignment = 1;
|
let buffer = cache.allocate(writer.len() as DeviceSize)?;
|
||||||
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)?;
|
|
||||||
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
||||||
ptr::copy_nonoverlapping(writer.as_ptr(), ptr, writer.len())
|
ptr::copy_nonoverlapping(writer.as_ptr(), ptr, writer.len())
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -510,7 +621,12 @@ impl VulkanRenderer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_ops(&self, opts: &[GfxApiOpt]) {
|
fn convert_ops(
|
||||||
|
&self,
|
||||||
|
opts: &[GfxApiOpt],
|
||||||
|
blend_cd: &ColorDescription,
|
||||||
|
fb_cd: &ColorDescription,
|
||||||
|
) {
|
||||||
zone!("convert_ops");
|
zone!("convert_ops");
|
||||||
let memory = &mut *self.memory.borrow_mut();
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
for ops in memory.ops.values_mut() {
|
for ops in memory.ops.values_mut() {
|
||||||
|
|
@ -522,6 +638,8 @@ impl VulkanRenderer {
|
||||||
memory.tex_targets.clear();
|
memory.tex_targets.clear();
|
||||||
memory.fill_targets.clear();
|
memory.fill_targets.clear();
|
||||||
memory.data_buffer.clear();
|
memory.data_buffer.clear();
|
||||||
|
memory.uniform_buffer_writer.clear();
|
||||||
|
memory.color_transforms.map.clear();
|
||||||
let sync = |memory: &mut Memory| {
|
let sync = |memory: &mut Memory| {
|
||||||
for pass in RenderPass::variants() {
|
for pass in RenderPass::variants() {
|
||||||
let ops = &mut memory.ops_tmp[pass];
|
let ops = &mut memory.ops_tmp[pass];
|
||||||
|
|
@ -588,10 +706,6 @@ impl VulkanRenderer {
|
||||||
if !bounds.intersects(&target) {
|
if !bounds.intersects(&target) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let tf = match pass {
|
|
||||||
RenderPass::BlendBuffer => TransferFunction::Linear,
|
|
||||||
RenderPass::FrameBuffer => TransferFunction::Srgb,
|
|
||||||
};
|
|
||||||
let ops = &mut memory.ops_tmp[pass];
|
let ops = &mut memory.ops_tmp[pass];
|
||||||
let lo = memory.fill_targets.len();
|
let lo = memory.fill_targets.len();
|
||||||
for region in &memory.paint_regions[pass] {
|
for region in &memory.paint_regions[pass] {
|
||||||
|
|
@ -605,7 +719,15 @@ impl VulkanRenderer {
|
||||||
if lo == hi {
|
if lo == hi {
|
||||||
continue;
|
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 {
|
let source_type = match color[3] < 1.0 {
|
||||||
false => TexSourceType::Opaque,
|
false => TexSourceType::Opaque,
|
||||||
true => TexSourceType::HasAlpha,
|
true => TexSourceType::HasAlpha,
|
||||||
|
|
@ -661,6 +783,16 @@ impl VulkanRenderer {
|
||||||
true => TexSourceType::HasAlpha,
|
true => TexSourceType::HasAlpha,
|
||||||
false => TexSourceType::Opaque,
|
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 {
|
ops.push(VulkanOp::Tex(VulkanTexOp {
|
||||||
tex: tex.clone(),
|
tex: tex.clone(),
|
||||||
range: lo..hi,
|
range: lo..hi,
|
||||||
|
|
@ -672,6 +804,9 @@ impl VulkanRenderer {
|
||||||
copy_type,
|
copy_type,
|
||||||
range_address: 0,
|
range_address: 0,
|
||||||
instances: 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() {
|
if buf.is_empty() {
|
||||||
return Ok(());
|
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 {
|
buffer.buffer.allocation.upload(|ptr, _| unsafe {
|
||||||
ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len());
|
ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len());
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -722,6 +857,33 @@ impl VulkanRenderer {
|
||||||
Ok(())
|
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) {
|
fn collect_memory(&self) {
|
||||||
zone!("collect_memory");
|
zone!("collect_memory");
|
||||||
let memory = &mut *self.memory.borrow_mut();
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
|
|
@ -865,7 +1027,9 @@ impl VulkanRenderer {
|
||||||
buf: CommandBuffer,
|
buf: CommandBuffer,
|
||||||
target: &VulkanImage,
|
target: &VulkanImage,
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
clear_cd: &LinearColorDescription,
|
||||||
pass: RenderPass,
|
pass: RenderPass,
|
||||||
|
target_cd: &ColorDescription,
|
||||||
) {
|
) {
|
||||||
zone!("begin_rendering");
|
zone!("begin_rendering");
|
||||||
let memory = &mut *self.memory.borrow_mut();
|
let memory = &mut *self.memory.borrow_mut();
|
||||||
|
|
@ -874,12 +1038,12 @@ impl VulkanRenderer {
|
||||||
let clear_rects = &memory.clear_rects[pass];
|
let clear_rects = &memory.clear_rects[pass];
|
||||||
if let Some(clear) = clear {
|
if let Some(clear) = clear {
|
||||||
if clear_rects.is_not_empty() {
|
if clear_rects.is_not_empty() {
|
||||||
|
let color = memory
|
||||||
|
.color_transforms
|
||||||
|
.apply_to_color(clear_cd, target_cd, *clear);
|
||||||
let clear_value = ClearValue {
|
let clear_value = ClearValue {
|
||||||
color: ClearColorValue {
|
color: ClearColorValue {
|
||||||
float32: match pass {
|
float32: color.to_array(target_cd.transfer_function),
|
||||||
RenderPass::BlendBuffer => clear.to_array(TransferFunction::Linear),
|
|
||||||
RenderPass::FrameBuffer => clear.to_array(TransferFunction::Srgb),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let use_load_clear = clear_rects.len() == 1 && {
|
let use_load_clear = clear_rects.len() == 1 && {
|
||||||
|
|
@ -969,10 +1133,12 @@ impl VulkanRenderer {
|
||||||
buf: CommandBuffer,
|
buf: CommandBuffer,
|
||||||
target: &VulkanImage,
|
target: &VulkanImage,
|
||||||
pass: RenderPass,
|
pass: RenderPass,
|
||||||
|
target_cd: &ColorDescription,
|
||||||
) -> Result<(), VulkanError> {
|
) -> Result<(), VulkanError> {
|
||||||
zone!("record_draws");
|
zone!("record_draws");
|
||||||
let memory = &*self.memory.borrow();
|
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 dev = &self.device.device;
|
||||||
let mut current_pipeline = None;
|
let mut current_pipeline = None;
|
||||||
let mut bind = |pipeline: &VulkanPipeline| {
|
let mut bind = |pipeline: &VulkanPipeline| {
|
||||||
|
|
@ -986,7 +1152,7 @@ impl VulkanRenderer {
|
||||||
for opt in &memory.ops[pass] {
|
for opt in &memory.ops[pass] {
|
||||||
match opt {
|
match opt {
|
||||||
VulkanOp::Fill(r) => {
|
VulkanOp::Fill(r) => {
|
||||||
let pipeline = &pipelines.fill[r.source_type];
|
let pipeline = &fill_pl[r.source_type];
|
||||||
bind(pipeline);
|
bind(pipeline);
|
||||||
if self.device.descriptor_buffer.is_some() {
|
if self.device.descriptor_buffer.is_some() {
|
||||||
let push = FillPushConstants {
|
let push = FillPushConstants {
|
||||||
|
|
@ -1026,8 +1192,14 @@ impl VulkanRenderer {
|
||||||
}
|
}
|
||||||
VulkanOp::Tex(c) => {
|
VulkanOp::Tex(c) => {
|
||||||
let tex = &c.tex;
|
let tex = &c.tex;
|
||||||
let pipeline = &pipelines.tex[c.copy_type][c.source_type];
|
let pipeline = self.get_or_create_tex_pipeline(
|
||||||
bind(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()
|
let image_info = DescriptorImageInfo::default()
|
||||||
.image_view(tex.texture_view)
|
.image_view(tex.texture_view)
|
||||||
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
|
.image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
|
@ -1043,7 +1215,7 @@ impl VulkanRenderer {
|
||||||
pipeline.pipeline_layout,
|
pipeline.pipeline_layout,
|
||||||
0,
|
0,
|
||||||
&[0, 1],
|
&[0, 1],
|
||||||
&[0, tex.descriptor_buffer_offset.get()],
|
&[0, c.resource_descriptor_buffer_offset],
|
||||||
);
|
);
|
||||||
dev.cmd_push_constants(
|
dev.cmd_push_constants(
|
||||||
buf,
|
buf,
|
||||||
|
|
@ -1118,33 +1290,13 @@ impl VulkanRenderer {
|
||||||
&self,
|
&self,
|
||||||
buf: CommandBuffer,
|
buf: CommandBuffer,
|
||||||
fb: &VulkanImage,
|
fb: &VulkanImage,
|
||||||
bb: &VulkanImage,
|
fb_cd: &ColorDescription,
|
||||||
|
bb_cd: &ColorDescription,
|
||||||
) -> Result<(), VulkanError> {
|
) -> Result<(), VulkanError> {
|
||||||
zone!("blend_buffer_copy");
|
zone!("blend_buffer_copy");
|
||||||
let memory = &*self.memory.borrow();
|
let memory = &*self.memory.borrow();
|
||||||
let db = self.device.descriptor_buffer.as_ref().unwrap();
|
let db = self.device.descriptor_buffer.as_ref().unwrap();
|
||||||
let pipeline = match self.out_pipelines.borrow_mut().entry(fb.format.vk_format) {
|
let pipeline = self.get_or_create_out_pipeline(fb.format.vk_format, bb_cd, fb_cd)?;
|
||||||
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 push = OutPushConstants {
|
let push = OutPushConstants {
|
||||||
vertices: memory.out_address,
|
vertices: memory.out_address,
|
||||||
};
|
};
|
||||||
|
|
@ -1158,7 +1310,7 @@ impl VulkanRenderer {
|
||||||
pipeline.pipeline_layout,
|
pipeline.pipeline_layout,
|
||||||
0,
|
0,
|
||||||
&[1],
|
&[1],
|
||||||
&[bb.descriptor_buffer_offset.get()],
|
&[memory.blend_buffer_descriptor_buffer_offset],
|
||||||
);
|
);
|
||||||
dev.cmd_push_constants(
|
dev.cmd_push_constants(
|
||||||
buf,
|
buf,
|
||||||
|
|
@ -1530,20 +1682,26 @@ impl VulkanRenderer {
|
||||||
fb: &Rc<VulkanImage>,
|
fb: &Rc<VulkanImage>,
|
||||||
fb_acquire_sync: AcquireSync,
|
fb_acquire_sync: AcquireSync,
|
||||||
fb_release_sync: ReleaseSync,
|
fb_release_sync: ReleaseSync,
|
||||||
|
fb_cd: &Rc<ColorDescription>,
|
||||||
opts: &[GfxApiOpt],
|
opts: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
clear_cd: &Rc<LinearColorDescription>,
|
||||||
region: &Region,
|
region: &Region,
|
||||||
blend_buffer: Option<Rc<VulkanImage>>,
|
blend_buffer: Option<Rc<VulkanImage>>,
|
||||||
|
blend_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, VulkanError> {
|
) -> Result<Option<SyncFile>, VulkanError> {
|
||||||
zone!("execute");
|
zone!("execute");
|
||||||
let res = self.try_execute(
|
let res = self.try_execute(
|
||||||
fb,
|
fb,
|
||||||
fb_acquire_sync,
|
fb_acquire_sync,
|
||||||
fb_release_sync,
|
fb_release_sync,
|
||||||
|
fb_cd,
|
||||||
opts,
|
opts,
|
||||||
clear,
|
clear,
|
||||||
|
clear_cd,
|
||||||
region,
|
region,
|
||||||
blend_buffer,
|
blend_buffer,
|
||||||
|
blend_cd,
|
||||||
);
|
);
|
||||||
let sync_file = {
|
let sync_file = {
|
||||||
let mut memory = self.memory.borrow_mut();
|
let mut memory = self.memory.borrow_mut();
|
||||||
|
|
@ -1706,18 +1864,22 @@ impl VulkanRenderer {
|
||||||
fb: &Rc<VulkanImage>,
|
fb: &Rc<VulkanImage>,
|
||||||
fb_acquire_sync: AcquireSync,
|
fb_acquire_sync: AcquireSync,
|
||||||
fb_release_sync: ReleaseSync,
|
fb_release_sync: ReleaseSync,
|
||||||
|
fb_cd: &Rc<ColorDescription>,
|
||||||
opts: &[GfxApiOpt],
|
opts: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
clear_cd: &Rc<LinearColorDescription>,
|
||||||
region: &Region,
|
region: &Region,
|
||||||
mut blend_buffer: Option<Rc<VulkanImage>>,
|
mut blend_buffer: Option<Rc<VulkanImage>>,
|
||||||
|
bb_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<(), VulkanError> {
|
) -> Result<(), VulkanError> {
|
||||||
self.check_defunct()?;
|
self.check_defunct()?;
|
||||||
self.create_regions(fb, opts, clear, region, blend_buffer.as_deref());
|
self.create_regions(fb, opts, clear, region, blend_buffer.as_deref());
|
||||||
self.elide_blend_buffer(&mut blend_buffer);
|
self.elide_blend_buffer(&mut blend_buffer);
|
||||||
let bb = blend_buffer.as_deref();
|
let bb = blend_buffer.as_deref();
|
||||||
let buf = self.gfx_command_buffers.allocate()?;
|
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_data_buffer()?;
|
||||||
|
self.create_uniform_buffer()?;
|
||||||
self.collect_memory();
|
self.collect_memory();
|
||||||
self.begin_command_buffer(buf.buffer)?;
|
self.begin_command_buffer(buf.buffer)?;
|
||||||
self.create_descriptor_buffers(buf.buffer, bb)?;
|
self.create_descriptor_buffers(buf.buffer, bb)?;
|
||||||
|
|
@ -1725,18 +1887,20 @@ impl VulkanRenderer {
|
||||||
self.set_viewport(buf.buffer, fb);
|
self.set_viewport(buf.buffer, fb);
|
||||||
if let Some(bb) = bb {
|
if let Some(bb) = bb {
|
||||||
zone!("blend buffer pass");
|
zone!("blend buffer pass");
|
||||||
|
let rp = RenderPass::BlendBuffer;
|
||||||
self.blend_buffer_initial_barrier(buf.buffer, bb);
|
self.blend_buffer_initial_barrier(buf.buffer, bb);
|
||||||
self.begin_rendering(buf.buffer, bb, clear, RenderPass::BlendBuffer);
|
self.begin_rendering(buf.buffer, bb, clear, clear_cd, rp, bb_cd);
|
||||||
self.record_draws(buf.buffer, bb, RenderPass::BlendBuffer)?;
|
self.record_draws(buf.buffer, bb, rp, bb_cd)?;
|
||||||
self.end_rendering(buf.buffer);
|
self.end_rendering(buf.buffer);
|
||||||
self.blend_buffer_final_barrier(buf.buffer, bb);
|
self.blend_buffer_final_barrier(buf.buffer, bb);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
zone!("frame buffer pass");
|
zone!("frame buffer pass");
|
||||||
self.begin_rendering(buf.buffer, fb, clear, RenderPass::FrameBuffer);
|
let rp = RenderPass::FrameBuffer;
|
||||||
self.record_draws(buf.buffer, fb, RenderPass::FrameBuffer)?;
|
self.begin_rendering(buf.buffer, fb, clear, clear_cd, rp, fb_cd);
|
||||||
if let Some(bb) = bb {
|
self.record_draws(buf.buffer, fb, rp, fb_cd)?;
|
||||||
self.blend_buffer_copy(buf.buffer, fb, bb)?;
|
if bb.is_some() {
|
||||||
|
self.blend_buffer_copy(buf.buffer, fb, fb_cd, bb_cd)?;
|
||||||
}
|
}
|
||||||
self.end_rendering(buf.buffer);
|
self.end_rendering(buf.buffer);
|
||||||
}
|
}
|
||||||
|
|
@ -1955,3 +2119,67 @@ where
|
||||||
let y2 = y2.min(fb.height as i32);
|
let y2 = y2.min(fb.height as i32);
|
||||||
Some([x1, y1, x2, y2])
|
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 {}
|
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)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct LegacyTexPushConstants {
|
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 = 1) const bool has_alpha_multiplier = false;
|
||||||
layout(constant_id = 2) const uint eotf = 0;
|
layout(constant_id = 2) const uint eotf = 0;
|
||||||
layout(constant_id = 3) const uint oetf = 0;
|
layout(constant_id = 3) const uint oetf = 0;
|
||||||
|
layout(constant_id = 4) const bool has_matrix = false;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,35 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
|
#extension GL_EXT_scalar_block_layout : require
|
||||||
|
|
||||||
#include "frag_spec_const.glsl"
|
#include "frag_spec_const.glsl"
|
||||||
#include "transfer_functions.glsl"
|
#include "transfer_functions.glsl"
|
||||||
#include "tex.common.glsl"
|
#include "tex.common.glsl"
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform sampler sam;
|
layout(set = 0, binding = 0) uniform sampler sam;
|
||||||
layout(set = 1, binding = 0) uniform texture2D tex;
|
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) in vec2 tex_pos;
|
||||||
layout(location = 0) out vec4 out_color;
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 c = textureLod(sampler2D(tex, sam), tex_pos, 0);
|
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) {
|
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);
|
rgb = apply_eotf(rgb);
|
||||||
c.rgb = apply_oetf(c.rgb);
|
if (has_matrix) {
|
||||||
|
rgb = (cm_data.matrix * vec4(rgb, 1.0)).rgb;
|
||||||
|
}
|
||||||
|
rgb = apply_oetf(rgb);
|
||||||
if (src_has_alpha) {
|
if (src_has_alpha) {
|
||||||
c.rgb *= c.a;
|
rgb *= c.a;
|
||||||
}
|
}
|
||||||
|
c.rgb = rgb;
|
||||||
}
|
}
|
||||||
if (has_alpha_multiplier) {
|
if (has_alpha_multiplier) {
|
||||||
if (src_has_alpha) {
|
if (src_has_alpha) {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,17 @@
|
||||||
|
|
||||||
#include "frag_spec_const.glsl"
|
#include "frag_spec_const.glsl"
|
||||||
|
|
||||||
#define SRGB 0
|
#define TF_SRGB 0
|
||||||
#define LINEAR 1
|
#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) {
|
vec3 eotf_srgb(vec3 c) {
|
||||||
return mix(
|
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) {
|
vec3 apply_eotf(vec3 c) {
|
||||||
switch (eotf) {
|
switch (eotf) {
|
||||||
case SRGB: return eotf_srgb(c);
|
case TF_SRGB: return eotf_srgb(c);
|
||||||
case LINEAR: return 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;
|
default: return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 apply_oetf(vec3 c) {
|
vec3 apply_oetf(vec3 c) {
|
||||||
switch (oetf) {
|
switch (oetf) {
|
||||||
case SRGB: return oetf_srgb(c);
|
case TF_SRGB: return oetf_srgb(c);
|
||||||
case LINEAR: return 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;
|
default: return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -460,8 +460,6 @@ impl VulkanRenderer {
|
||||||
ty: VulkanImageMemory::Internal(shm),
|
ty: VulkanImageMemory::Internal(shm),
|
||||||
bridge: None,
|
bridge: None,
|
||||||
sampled_image_descriptor: self.sampled_image_descriptor(view),
|
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),
|
execution_version: Cell::new(0),
|
||||||
});
|
});
|
||||||
let shm = match &img.ty {
|
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_SRGB: u32 = 0;
|
||||||
pub const TF_LINEAR: u32 = 1;
|
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_output_v1;
|
||||||
pub mod wp_color_management_surface_feedback_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_color_manager_v1;
|
||||||
pub mod wp_image_description_creator_icc_v1;
|
pub mod wp_image_description_creator_icc_v1;
|
||||||
pub mod wp_image_description_creator_params_v1;
|
pub mod wp_image_description_creator_params_v1;
|
||||||
pub mod wp_image_description_info_v1;
|
pub mod wp_image_description_info_v1;
|
||||||
pub mod wp_image_description_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)]
|
#[expect(dead_code)]
|
||||||
mod consts {
|
mod consts {
|
||||||
pub(super) const RENDER_INTENT_PERCEPTUAL: u32 = 0;
|
pub const RENDER_INTENT_PERCEPTUAL: u32 = 0;
|
||||||
pub(super) const RENDER_INTENT_RELATIVE: u32 = 1;
|
pub const RENDER_INTENT_RELATIVE: u32 = 1;
|
||||||
pub(super) const RENDER_INTENT_SATURATION: u32 = 2;
|
pub const RENDER_INTENT_SATURATION: u32 = 2;
|
||||||
pub(super) const RENDER_INTENT_ABSOLUTE: u32 = 3;
|
pub const RENDER_INTENT_ABSOLUTE: u32 = 3;
|
||||||
pub(super) const RENDER_INTENT_RELATIVE_BPC: u32 = 4;
|
pub const RENDER_INTENT_RELATIVE_BPC: u32 = 4;
|
||||||
|
|
||||||
pub(super) const FEATURE_ICC_V2_V4: u32 = 0;
|
pub const FEATURE_ICC_V2_V4: u32 = 0;
|
||||||
pub(super) const FEATURE_PARAMETRIC: u32 = 1;
|
pub const FEATURE_PARAMETRIC: u32 = 1;
|
||||||
pub(super) const FEATURE_SET_PRIMARIES: u32 = 2;
|
pub const FEATURE_SET_PRIMARIES: u32 = 2;
|
||||||
pub(super) const FEATURE_SET_TF_POWER: u32 = 3;
|
pub const FEATURE_SET_TF_POWER: u32 = 3;
|
||||||
pub(super) const FEATURE_SET_LUMINANCES: u32 = 4;
|
pub const FEATURE_SET_LUMINANCES: u32 = 4;
|
||||||
pub(super) const FEATURE_SET_MASTERING_DISPLAY_PRIMARIES: u32 = 5;
|
pub const FEATURE_SET_MASTERING_DISPLAY_PRIMARIES: u32 = 5;
|
||||||
pub(super) const FEATURE_EXTENDED_TARGET_VOLUME: u32 = 6;
|
pub const FEATURE_EXTENDED_TARGET_VOLUME: u32 = 6;
|
||||||
pub(super) const FEATURE_WINDOWS_SCRGB: u32 = 7;
|
pub const FEATURE_WINDOWS_SCRGB: u32 = 7;
|
||||||
|
|
||||||
pub(super) const PRIMARIES_SRGB: u32 = 1;
|
pub const PRIMARIES_SRGB: u32 = 1;
|
||||||
pub(super) const PRIMARIES_PAL_M: u32 = 2;
|
pub const PRIMARIES_PAL_M: u32 = 2;
|
||||||
pub(super) const PRIMARIES_PAL: u32 = 3;
|
pub const PRIMARIES_PAL: u32 = 3;
|
||||||
pub(super) const PRIMARIES_NTSC: u32 = 4;
|
pub const PRIMARIES_NTSC: u32 = 4;
|
||||||
pub(super) const PRIMARIES_GENERIC_FILM: u32 = 5;
|
pub const PRIMARIES_GENERIC_FILM: u32 = 5;
|
||||||
pub(super) const PRIMARIES_BT2020: u32 = 6;
|
pub const PRIMARIES_BT2020: u32 = 6;
|
||||||
pub(super) const PRIMARIES_CIE1931_XYZ: u32 = 7;
|
pub const PRIMARIES_CIE1931_XYZ: u32 = 7;
|
||||||
pub(super) const PRIMARIES_DCI_P3: u32 = 8;
|
pub const PRIMARIES_DCI_P3: u32 = 8;
|
||||||
pub(super) const PRIMARIES_DISPLAY_P3: u32 = 9;
|
pub const PRIMARIES_DISPLAY_P3: u32 = 9;
|
||||||
pub(super) const PRIMARIES_ADOBE_RGB: u32 = 10;
|
pub const PRIMARIES_ADOBE_RGB: u32 = 10;
|
||||||
|
|
||||||
pub(super) const TRANSFER_FUNCTION_BT1886: u32 = 1;
|
pub const TRANSFER_FUNCTION_BT1886: u32 = 1;
|
||||||
pub(super) const TRANSFER_FUNCTION_GAMMA22: u32 = 2;
|
pub const TRANSFER_FUNCTION_GAMMA22: u32 = 2;
|
||||||
pub(super) const TRANSFER_FUNCTION_GAMMA28: u32 = 3;
|
pub const TRANSFER_FUNCTION_GAMMA28: u32 = 3;
|
||||||
pub(super) const TRANSFER_FUNCTION_ST240: u32 = 4;
|
pub const TRANSFER_FUNCTION_ST240: u32 = 4;
|
||||||
pub(super) const TRANSFER_FUNCTION_EXT_LINEAR: u32 = 5;
|
pub const TRANSFER_FUNCTION_EXT_LINEAR: u32 = 5;
|
||||||
pub(super) const TRANSFER_FUNCTION_LOG_100: u32 = 6;
|
pub const TRANSFER_FUNCTION_LOG_100: u32 = 6;
|
||||||
pub(super) const TRANSFER_FUNCTION_LOG_316: u32 = 7;
|
pub const TRANSFER_FUNCTION_LOG_316: u32 = 7;
|
||||||
pub(super) const TRANSFER_FUNCTION_XVYCC: u32 = 8;
|
pub const TRANSFER_FUNCTION_XVYCC: u32 = 8;
|
||||||
pub(super) const TRANSFER_FUNCTION_SRGB: u32 = 9;
|
pub const TRANSFER_FUNCTION_SRGB: u32 = 9;
|
||||||
pub(super) const TRANSFER_FUNCTION_EXT_SRGB: u32 = 10;
|
pub const TRANSFER_FUNCTION_EXT_SRGB: u32 = 10;
|
||||||
pub(super) const TRANSFER_FUNCTION_ST2084_PQ: u32 = 11;
|
pub const TRANSFER_FUNCTION_ST2084_PQ: u32 = 11;
|
||||||
pub(super) const TRANSFER_FUNCTION_ST428: u32 = 12;
|
pub const TRANSFER_FUNCTION_ST428: u32 = 12;
|
||||||
pub(super) const TRANSFER_FUNCTION_HLG: u32 = 13;
|
pub const TRANSFER_FUNCTION_HLG: u32 = 13;
|
||||||
|
|
||||||
pub(super) const CAUSE_LOW_VERSION: u32 = 0;
|
pub const CAUSE_LOW_VERSION: u32 = 0;
|
||||||
pub(super) const CAUSE_UNSUPPORTED: u32 = 1;
|
pub const CAUSE_UNSUPPORTED: u32 = 1;
|
||||||
pub(super) const CAUSE_OPERATING_SYSTEM: u32 = 2;
|
pub const CAUSE_OPERATING_SYSTEM: u32 = 2;
|
||||||
pub(super) const CAUSE_NO_OUTPUT: u32 = 3;
|
pub const CAUSE_NO_OUTPUT: u32 = 3;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,11 @@ impl WpColorManagementOutputV1RequestHandler for WpColorManagementOutputV1 {
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
version: self.version,
|
version: self.version,
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
|
description: self.client.state.color_manager.srgb_srgb().clone(),
|
||||||
});
|
});
|
||||||
track!(self.client, obj);
|
track!(self.client, obj);
|
||||||
self.client.add_client_obj(&obj)?;
|
self.client.add_client_obj(&obj)?;
|
||||||
obj.send_ready(0);
|
obj.send_ready();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,11 @@ impl WpColorManagementSurfaceFeedbackV1 {
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
version: self.version,
|
version: self.version,
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
|
description: self.client.state.color_manager.srgb_srgb().clone(),
|
||||||
});
|
});
|
||||||
track!(self.client, obj);
|
track!(self.client, obj);
|
||||||
self.client.add_client_obj(&obj)?;
|
self.client.add_client_obj(&obj)?;
|
||||||
obj.send_ready(0);
|
obj.send_ready();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,27 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
globals::{Global, GlobalName},
|
globals::{Global, GlobalName},
|
||||||
ifs::color_management::{
|
ifs::{
|
||||||
consts::{
|
color_management::{
|
||||||
FEATURE_PARAMETRIC, PRIMARIES_SRGB, RENDER_INTENT_PERCEPTUAL,
|
consts::{
|
||||||
TRANSFER_FUNCTION_SRGB,
|
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,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
|
|
@ -63,8 +75,30 @@ impl WpColorManagerV1 {
|
||||||
fn send_capabilities(&self) {
|
fn send_capabilities(&self) {
|
||||||
self.send_supported_intent(RENDER_INTENT_PERCEPTUAL);
|
self.send_supported_intent(RENDER_INTENT_PERCEPTUAL);
|
||||||
self.send_supported_feature(FEATURE_PARAMETRIC);
|
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_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_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();
|
self.send_done();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,15 +157,17 @@ impl WpColorManagerV1RequestHandler for WpColorManagerV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_surface(&self, req: GetSurface, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
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 {
|
let obj = Rc::new(WpColorManagementSurfaceV1 {
|
||||||
id: req.id,
|
id: req.id,
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
version: self.version,
|
version: self.version,
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
|
surface: surface.clone(),
|
||||||
});
|
});
|
||||||
track!(self.client, obj);
|
track!(self.client, obj);
|
||||||
self.client.add_client_obj(&obj)?;
|
self.client.add_client_obj(&obj)?;
|
||||||
|
obj.install()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,6 +206,9 @@ impl WpColorManagerV1RequestHandler for WpColorManagerV1 {
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
version: self.version,
|
version: self.version,
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
|
tf: Default::default(),
|
||||||
|
primaries: Default::default(),
|
||||||
|
luminance: Default::default(),
|
||||||
});
|
});
|
||||||
track!(self.client, obj);
|
track!(self.client, obj);
|
||||||
self.client.add_client_obj(&obj)?;
|
self.client.add_client_obj(&obj)?;
|
||||||
|
|
@ -178,10 +217,20 @@ impl WpColorManagerV1RequestHandler for WpColorManagerV1 {
|
||||||
|
|
||||||
fn create_windows_scrgb(
|
fn create_windows_scrgb(
|
||||||
&self,
|
&self,
|
||||||
_req: CreateWindowsScrgb,
|
req: CreateWindowsScrgb,
|
||||||
_slf: &Rc<Self>,
|
_slf: &Rc<Self>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> 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>),
|
ClientError(Box<ClientError>),
|
||||||
#[error("create_icc_creator is not supported")]
|
#[error("create_icc_creator is not supported")]
|
||||||
CreateIccCreatorNotSupported,
|
CreateIccCreatorNotSupported,
|
||||||
#[error("create_windows_scrgb is not supported")]
|
#[error(transparent)]
|
||||||
CreateWindowsScrgbNotSupported,
|
Surface(#[from] WpColorManagementSurfaceV1Error),
|
||||||
}
|
}
|
||||||
efrom!(WpColorManagerV1Error, ClientError);
|
efrom!(WpColorManagerV1Error, ClientError);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,27 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
|
cmm::{
|
||||||
|
cmm_luminance::Luminance,
|
||||||
|
cmm_primaries::{NamedPrimaries, Primaries},
|
||||||
|
cmm_transfer_function::TransferFunction,
|
||||||
|
},
|
||||||
ifs::color_management::{
|
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,
|
wp_image_description_v1::WpImageDescriptionV1,
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
|
utils::ordered_float::F64,
|
||||||
wire::{
|
wire::{
|
||||||
WpImageDescriptionCreatorParamsV1Id,
|
WpImageDescriptionCreatorParamsV1Id,
|
||||||
wp_image_description_creator_params_v1::{
|
wp_image_description_creator_params_v1::{
|
||||||
|
|
@ -16,7 +31,7 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::{cell::Cell, rc::Rc},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -25,30 +40,74 @@ pub struct WpImageDescriptionCreatorParamsV1 {
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub tracker: Tracker<Self>,
|
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 {
|
impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreatorParamsV1 {
|
||||||
type Error = WpImageDescriptionCreatorParamsV1Error;
|
type Error = WpImageDescriptionCreatorParamsV1Error;
|
||||||
|
|
||||||
fn create(&self, req: Create, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
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 {
|
let obj = Rc::new(WpImageDescriptionV1 {
|
||||||
id: req.image_description,
|
id: req.image_description,
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
version: self.version,
|
version: self.version,
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
|
description,
|
||||||
});
|
});
|
||||||
track!(self.client, obj);
|
track!(self.client, obj);
|
||||||
self.client.add_client_obj(&obj)?;
|
self.client.add_client_obj(&obj)?;
|
||||||
obj.send_ready(0);
|
obj.send_ready();
|
||||||
self.client.remove_obj(self)?;
|
self.client.remove_obj(self)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_tf_named(&self, req: SetTfNamed, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_tf_named(&self, req: SetTfNamed, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
if req.tf != TRANSFER_FUNCTION_SRGB {
|
let tf = match req.tf {
|
||||||
return Err(WpImageDescriptionCreatorParamsV1Error::UnsupportedTf(
|
TRANSFER_FUNCTION_BT1886 => TransferFunction::Bt1886,
|
||||||
req.tf,
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -62,20 +121,55 @@ impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreat
|
||||||
req: SetPrimariesNamed,
|
req: SetPrimariesNamed,
|
||||||
_slf: &Rc<Self>,
|
_slf: &Rc<Self>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
if req.primaries != PRIMARIES_SRGB {
|
let primaries = match req.primaries {
|
||||||
return Err(
|
PRIMARIES_SRGB => NamedPrimaries::Srgb,
|
||||||
WpImageDescriptionCreatorParamsV1Error::UnsupportedPrimaries(req.primaries),
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_primaries(&self, _req: SetPrimaries, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_primaries(&self, req: SetPrimaries, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
Err(WpImageDescriptionCreatorParamsV1Error::SetPrimariesNotSupported)
|
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> {
|
fn set_luminances(&self, req: SetLuminances, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
Err(WpImageDescriptionCreatorParamsV1Error::SetLuminancesNotSupported)
|
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(
|
fn set_mastering_display_primaries(
|
||||||
|
|
@ -120,15 +214,23 @@ pub enum WpImageDescriptionCreatorParamsV1Error {
|
||||||
SetMasteringLuminanceNotSupported,
|
SetMasteringLuminanceNotSupported,
|
||||||
#[error("set_mastering_display_primaries is not supported")]
|
#[error("set_mastering_display_primaries is not supported")]
|
||||||
SetMasteringDisplayPrimariesNotSupported,
|
SetMasteringDisplayPrimariesNotSupported,
|
||||||
#[error("set_luminances is not supported")]
|
|
||||||
SetLuminancesNotSupported,
|
|
||||||
#[error("set_primaries is not supported")]
|
|
||||||
SetPrimariesNotSupported,
|
|
||||||
#[error("{} is not a supported named primary", .0)]
|
#[error("{} is not a supported named primary", .0)]
|
||||||
UnsupportedPrimaries(u32),
|
UnsupportedPrimaries(u32),
|
||||||
#[error("set_tf_power is not supported")]
|
#[error("set_tf_power is not supported")]
|
||||||
SetTfPowerNotSupported,
|
SetTfPowerNotSupported,
|
||||||
#[error("{} is not a supported named transfer function", .0)]
|
#[error("{} is not a supported named transfer function", .0)]
|
||||||
UnsupportedTf(u32),
|
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);
|
efrom!(WpImageDescriptionCreatorParamsV1Error, ClientError);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
|
cmm::cmm_description::ColorDescription,
|
||||||
ifs::color_management::wp_image_description_info_v1::WpImageDescriptionInfoV1,
|
ifs::color_management::wp_image_description_info_v1::WpImageDescriptionInfoV1,
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
|
|
@ -15,6 +16,7 @@ pub struct WpImageDescriptionV1 {
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
|
pub description: Rc<ColorDescription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WpImageDescriptionV1 {
|
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.client.event(Ready {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
identity,
|
identity: self.description.id.into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
|
cmm::cmm_description::ColorDescription,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, AsyncShmGfxTextureCallback, BufferResv, GfxError, GfxFramebuffer,
|
AcquireSync, AsyncShmGfxTextureCallback, BufferResv, GfxError, GfxFramebuffer,
|
||||||
GfxTexture, ReleaseSync, STAGING_DOWNLOAD, SyncFile,
|
GfxTexture, ReleaseSync, STAGING_DOWNLOAD, SyncFile,
|
||||||
|
|
@ -199,6 +200,7 @@ impl ExtImageCopyCaptureFrameV1 {
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
on: &OutputNode,
|
on: &OutputNode,
|
||||||
texture: &Rc<dyn GfxTexture>,
|
texture: &Rc<dyn GfxTexture>,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
resv: Option<&Rc<dyn BufferResv>>,
|
resv: Option<&Rc<dyn BufferResv>>,
|
||||||
acquire_sync: &AcquireSync,
|
acquire_sync: &AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
|
@ -215,10 +217,12 @@ impl ExtImageCopyCaptureFrameV1 {
|
||||||
resv,
|
resv,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
cd,
|
||||||
&fb,
|
&fb,
|
||||||
aq,
|
aq,
|
||||||
re,
|
re,
|
||||||
jay_config::video::Transform::None,
|
jay_config::video::Transform::None,
|
||||||
|
self.client.state.color_manager.srgb_srgb(),
|
||||||
on.global.pos.get(),
|
on.global.pos.get(),
|
||||||
render_hardware_cursors,
|
render_hardware_cursors,
|
||||||
x_off,
|
x_off,
|
||||||
|
|
@ -236,6 +240,7 @@ impl ExtImageCopyCaptureFrameV1 {
|
||||||
fb.render_node(
|
fb.render_node(
|
||||||
aq,
|
aq,
|
||||||
re,
|
re,
|
||||||
|
self.client.state.color_manager.srgb_srgb(),
|
||||||
node,
|
node,
|
||||||
&self.client.state,
|
&self.client.state,
|
||||||
Some(node.node_absolute_position()),
|
Some(node.node_absolute_position()),
|
||||||
|
|
@ -246,6 +251,7 @@ impl ExtImageCopyCaptureFrameV1 {
|
||||||
false,
|
false,
|
||||||
jay_config::video::Transform::None,
|
jay_config::video::Transform::None,
|
||||||
None,
|
None,
|
||||||
|
self.client.state.color_manager.srgb_linear(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
|
cmm::cmm_description::ColorDescription,
|
||||||
format::{FORMATS, Format},
|
format::{FORMATS, Format},
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, BufferResv, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture,
|
AcquireSync, BufferResv, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture,
|
||||||
|
|
@ -213,6 +214,7 @@ impl ExtImageCopyCaptureSessionV1 {
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
on: &OutputNode,
|
on: &OutputNode,
|
||||||
texture: &Rc<dyn GfxTexture>,
|
texture: &Rc<dyn GfxTexture>,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
resv: Option<&Rc<dyn BufferResv>>,
|
resv: Option<&Rc<dyn BufferResv>>,
|
||||||
acquire_sync: &AcquireSync,
|
acquire_sync: &AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
|
@ -226,6 +228,7 @@ impl ExtImageCopyCaptureSessionV1 {
|
||||||
frame.copy_texture(
|
frame.copy_texture(
|
||||||
on,
|
on,
|
||||||
texture,
|
texture,
|
||||||
|
cd,
|
||||||
resv,
|
resv,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError},
|
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError},
|
||||||
|
cmm::cmm_transfer_function::TransferFunction,
|
||||||
globals::{Global, GlobalName},
|
globals::{Global, GlobalName},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
theme::{Color, TransferFunction},
|
theme::Color,
|
||||||
wire::{
|
wire::{
|
||||||
JayCompositorId,
|
JayCompositorId,
|
||||||
jay_damage_tracking::{
|
jay_damage_tracking::{
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
allocator::{AllocatorError, BO_USE_LINEAR, BO_USE_RENDERING, BufferObject},
|
allocator::{AllocatorError, BO_USE_LINEAR, BO_USE_RENDERING, BufferObject},
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
|
cmm::cmm_description::ColorDescription,
|
||||||
format::XRGB8888,
|
format::XRGB8888,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, BufferResv, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync,
|
AcquireSync, BufferResv, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync,
|
||||||
|
|
@ -193,6 +194,7 @@ impl JayScreencast {
|
||||||
let res = buffer.fb.render_node(
|
let res = buffer.fb.render_node(
|
||||||
AcquireSync::Implicit,
|
AcquireSync::Implicit,
|
||||||
ReleaseSync::Implicit,
|
ReleaseSync::Implicit,
|
||||||
|
self.client.state.color_manager.srgb_srgb(),
|
||||||
tl.tl_as_node(),
|
tl.tl_as_node(),
|
||||||
&self.client.state,
|
&self.client.state,
|
||||||
Some(tl.node_absolute_position()),
|
Some(tl.node_absolute_position()),
|
||||||
|
|
@ -203,6 +205,7 @@ impl JayScreencast {
|
||||||
false,
|
false,
|
||||||
Transform::None,
|
Transform::None,
|
||||||
None,
|
None,
|
||||||
|
self.client.state.color_manager.srgb_linear(),
|
||||||
);
|
);
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
|
@ -304,6 +307,7 @@ impl JayScreencast {
|
||||||
&self,
|
&self,
|
||||||
on: &OutputNode,
|
on: &OutputNode,
|
||||||
texture: &Rc<dyn GfxTexture>,
|
texture: &Rc<dyn GfxTexture>,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
resv: Option<&Rc<dyn BufferResv>>,
|
resv: Option<&Rc<dyn BufferResv>>,
|
||||||
acquire_sync: &AcquireSync,
|
acquire_sync: &AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
|
@ -332,10 +336,12 @@ impl JayScreencast {
|
||||||
resv,
|
resv,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
cd,
|
||||||
&buffer.fb,
|
&buffer.fb,
|
||||||
AcquireSync::Implicit,
|
AcquireSync::Implicit,
|
||||||
ReleaseSync::Implicit,
|
ReleaseSync::Implicit,
|
||||||
Transform::None,
|
Transform::None,
|
||||||
|
self.client.state.color_manager.srgb_srgb(),
|
||||||
on.global.pos.get(),
|
on.global.pos.get(),
|
||||||
render_hardware_cursors,
|
render_hardware_cursors,
|
||||||
x_off,
|
x_off,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ pub mod ext_session_lock_surface_v1;
|
||||||
pub mod tray;
|
pub mod tray;
|
||||||
pub mod wl_subsurface;
|
pub mod wl_subsurface;
|
||||||
pub mod wp_alpha_modifier_surface_v1;
|
pub mod wp_alpha_modifier_surface_v1;
|
||||||
|
pub mod wp_color_management_surface_v1;
|
||||||
pub mod wp_commit_timer_v1;
|
pub mod wp_commit_timer_v1;
|
||||||
pub mod wp_fifo_v1;
|
pub mod wp_fifo_v1;
|
||||||
pub mod wp_fractional_scale_v1;
|
pub mod wp_fractional_scale_v1;
|
||||||
|
|
@ -22,6 +23,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::KeyState,
|
backend::KeyState,
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
|
cmm::cmm_description::ColorDescription,
|
||||||
cursor_user::{CursorUser, CursorUserId},
|
cursor_user::{CursorUser, CursorUserId},
|
||||||
damage::DamageMatrix,
|
damage::DamageMatrix,
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
|
|
@ -104,6 +106,7 @@ use {
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
|
wp_color_management_surface_v1::WpColorManagementSurfaceV1,
|
||||||
zwp_idle_inhibitor_v1::ZwpIdleInhibitorV1,
|
zwp_idle_inhibitor_v1::ZwpIdleInhibitorV1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -332,6 +335,8 @@ pub struct WlSurface {
|
||||||
commit_timer: CloneCell<Option<Rc<WpCommitTimerV1>>>,
|
commit_timer: CloneCell<Option<Rc<WpCommitTimerV1>>>,
|
||||||
before_latch_listener: EventListener<dyn BeforeLatchListener>,
|
before_latch_listener: EventListener<dyn BeforeLatchListener>,
|
||||||
is_opaque: Cell<bool>,
|
is_opaque: Cell<bool>,
|
||||||
|
color_management_surface: CloneCell<Option<Rc<WpColorManagementSurfaceV1>>>,
|
||||||
|
color_description: CloneCell<Option<Rc<ColorDescription>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for WlSurface {
|
impl Debug for WlSurface {
|
||||||
|
|
@ -461,6 +466,7 @@ struct PendingState {
|
||||||
fifo_barrier_wait: bool,
|
fifo_barrier_wait: bool,
|
||||||
commit_time: Option<u64>,
|
commit_time: Option<u64>,
|
||||||
tray_item_ack_serial: Option<u32>,
|
tray_item_ack_serial: Option<u32>,
|
||||||
|
color_description: Option<Option<Rc<ColorDescription>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AttachedSubsurfaceState {
|
struct AttachedSubsurfaceState {
|
||||||
|
|
@ -513,6 +519,7 @@ impl PendingState {
|
||||||
opt!(alpha_multiplier);
|
opt!(alpha_multiplier);
|
||||||
opt!(commit_time);
|
opt!(commit_time);
|
||||||
opt!(tray_item_ack_serial);
|
opt!(tray_item_ack_serial);
|
||||||
|
opt!(color_description);
|
||||||
{
|
{
|
||||||
let (dx1, dy1) = self.offset;
|
let (dx1, dy1) = self.offset;
|
||||||
let (dx2, dy2) = mem::take(&mut next.offset);
|
let (dx2, dy2) = mem::take(&mut next.offset);
|
||||||
|
|
@ -670,6 +677,8 @@ impl WlSurface {
|
||||||
commit_timer: Default::default(),
|
commit_timer: Default::default(),
|
||||||
before_latch_listener: EventListener::new(slf.clone()),
|
before_latch_listener: EventListener::new(slf.clone()),
|
||||||
is_opaque: Cell::new(false),
|
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;
|
let mut alpha_changed = false;
|
||||||
if let Some(alpha) = pending.alpha_multiplier.take() {
|
if let Some(alpha) = pending.alpha_multiplier.take() {
|
||||||
alpha_changed = true;
|
alpha_changed = true;
|
||||||
|
|
@ -1144,8 +1158,11 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
let buffer_abs_pos = self.buffer_abs_pos.get();
|
let buffer_abs_pos = self.buffer_abs_pos.get();
|
||||||
let mut max_surface_size = buffer_abs_pos.size();
|
let mut max_surface_size = buffer_abs_pos.size();
|
||||||
let mut damage_full =
|
let mut damage_full = scale_changed
|
||||||
scale_changed || buffer_transform_changed || viewport_changed || alpha_changed;
|
|| buffer_transform_changed
|
||||||
|
|| viewport_changed
|
||||||
|
|| alpha_changed
|
||||||
|
|| color_description_changed;
|
||||||
let mut buffer_changed = false;
|
let mut buffer_changed = false;
|
||||||
let mut old_raw_size = None;
|
let mut old_raw_size = None;
|
||||||
let (mut dx, mut dy) = mem::take(&mut pending.offset);
|
let (mut dx, mut dy) = mem::take(&mut pending.offset);
|
||||||
|
|
@ -1658,6 +1675,13 @@ impl WlSurface {
|
||||||
pub fn opaque_region(&self) -> Option<Rc<Region>> {
|
pub fn opaque_region(&self) -> Option<Rc<Region>> {
|
||||||
self.opaque_region.get()
|
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! {
|
object_base! {
|
||||||
|
|
@ -1689,6 +1713,7 @@ impl Object for WlSurface {
|
||||||
self.text_input_connections.clear();
|
self.text_input_connections.clear();
|
||||||
self.fifo.take();
|
self.fifo.take();
|
||||||
self.commit_timer.take();
|
self.commit_timer.take();
|
||||||
|
self.color_management_surface.take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
ifs::color_management::consts::RENDER_INTENT_PERCEPTUAL,
|
ifs::{color_management, wl_surface::WlSurface},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
wire::{
|
wire::{
|
||||||
|
|
@ -21,12 +21,26 @@ pub struct WpColorManagementSurfaceV1 {
|
||||||
pub client: Rc<Client>,
|
pub client: Rc<Client>,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub tracker: Tracker<Self>,
|
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 {
|
impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 {
|
||||||
type Error = WpColorManagementSurfaceV1Error;
|
type Error = WpColorManagementSurfaceV1Error;
|
||||||
|
|
||||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.surface.color_management_surface.take();
|
||||||
self.client.remove_obj(self)?;
|
self.client.remove_obj(self)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -36,12 +50,13 @@ impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 {
|
||||||
req: SetImageDescription,
|
req: SetImageDescription,
|
||||||
_slf: &Rc<Self>,
|
_slf: &Rc<Self>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
let _ = self.client.lookup(req.image_description)?;
|
if req.render_intent != color_management::RENDER_INTENT_PERCEPTUAL {
|
||||||
if req.render_intent != RENDER_INTENT_PERCEPTUAL {
|
|
||||||
return Err(WpColorManagementSurfaceV1Error::UnsupportedRenderIntent(
|
return Err(WpColorManagementSurfaceV1Error::UnsupportedRenderIntent(
|
||||||
req.render_intent,
|
req.render_intent,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
let desc = self.client.lookup(req.image_description)?;
|
||||||
|
self.surface.pending.borrow_mut().color_description = Some(Some(desc.description.clone()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,6 +65,7 @@ impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 {
|
||||||
_req: UnsetImageDescription,
|
_req: UnsetImageDescription,
|
||||||
_slf: &Rc<Self>,
|
_slf: &Rc<Self>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
|
self.surface.pending.borrow_mut().color_description = Some(None);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -69,5 +85,7 @@ pub enum WpColorManagementSurfaceV1Error {
|
||||||
ClientError(Box<ClientError>),
|
ClientError(Box<ClientError>),
|
||||||
#[error("{} is not a supported render intent", .0)]
|
#[error("{} is not a supported render intent", .0)]
|
||||||
UnsupportedRenderIntent(u32),
|
UnsupportedRenderIntent(u32),
|
||||||
|
#[error("wl_surface already has a color-management extension")]
|
||||||
|
HasSurface,
|
||||||
}
|
}
|
||||||
efrom!(WpColorManagementSurfaceV1Error, ClientError);
|
efrom!(WpColorManagementSurfaceV1Error, ClientError);
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage},
|
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage},
|
||||||
|
cmm::cmm_description::{ColorDescription, LinearColorDescription},
|
||||||
cpu_worker::CpuWorker,
|
cpu_worker::CpuWorker,
|
||||||
format::{ARGB8888, Format, XRGB8888},
|
format::{ARGB8888, Format, XRGB8888},
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
|
|
@ -391,10 +392,13 @@ impl GfxFramebuffer for TestGfxFb {
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
_acquire_sync: AcquireSync,
|
_acquire_sync: AcquireSync,
|
||||||
_release_sync: ReleaseSync,
|
_release_sync: ReleaseSync,
|
||||||
|
_cd: &Rc<ColorDescription>,
|
||||||
ops: &[GfxApiOpt],
|
ops: &[GfxApiOpt],
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
|
_clear_cd: &Rc<LinearColorDescription>,
|
||||||
_region: &Region,
|
_region: &Region,
|
||||||
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
|
_blend_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
|
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
|
||||||
let points = rect.to_points();
|
let points = rect.to_points();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
cmm::cmm_transfer_function::TransferFunction,
|
||||||
it::{
|
it::{
|
||||||
test_error::TestResult, test_ifs::test_buffer::TestBuffer, test_object::TestObject,
|
test_error::TestResult, test_ifs::test_buffer::TestBuffer, test_object::TestObject,
|
||||||
test_transport::TestTransport,
|
test_transport::TestTransport,
|
||||||
},
|
},
|
||||||
theme::{Color, TransferFunction},
|
theme::Color,
|
||||||
wire::{WpSinglePixelBufferManagerV1Id, wp_single_pixel_buffer_manager_v1::*},
|
wire::{WpSinglePixelBufferManagerV1Id, wp_single_pixel_buffer_manager_v1::*},
|
||||||
},
|
},
|
||||||
std::{cell::Cell, rc::Rc},
|
std::{cell::Cell, rc::Rc},
|
||||||
|
|
|
||||||
|
|
@ -185,10 +185,10 @@ macro_rules! shared_ids {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! linear_ids {
|
macro_rules! linear_ids {
|
||||||
($ids:ident, $id:ident) => {
|
($ids:ident, $id:ident $(,)?) => {
|
||||||
linear_ids!($ids, $id, u32);
|
linear_ids!($ids, $id, u32);
|
||||||
};
|
};
|
||||||
($ids:ident, $id:ident, $ty:ty) => {
|
($ids:ident, $id:ident, $ty:ty $(,)?) => {
|
||||||
pub struct $ids {
|
pub struct $ids {
|
||||||
next: crate::utils::numcell::NumCell<$ty>,
|
next: crate::utils::numcell::NumCell<$ty>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,8 @@
|
||||||
clippy::manual_is_ascii_check,
|
clippy::manual_is_ascii_check,
|
||||||
clippy::needless_borrow,
|
clippy::needless_borrow,
|
||||||
clippy::unnecessary_cast,
|
clippy::unnecessary_cast,
|
||||||
clippy::manual_flatten
|
clippy::manual_flatten,
|
||||||
|
clippy::manual_bits
|
||||||
)]
|
)]
|
||||||
#![warn(clippy::allow_attributes, unsafe_op_in_unsafe_fn)]
|
#![warn(clippy::allow_attributes, unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
|
|
@ -54,6 +55,7 @@ mod bugs;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod client;
|
mod client;
|
||||||
mod clientmem;
|
mod clientmem;
|
||||||
|
mod cmm;
|
||||||
mod compositor;
|
mod compositor;
|
||||||
mod config;
|
mod config;
|
||||||
mod cpu_worker;
|
mod cpu_worker;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::AsyncEngine,
|
async_engine::AsyncEngine,
|
||||||
cli::GlobalArgs,
|
cli::GlobalArgs,
|
||||||
|
cmm::cmm_manager::ColorManager,
|
||||||
dbus::{
|
dbus::{
|
||||||
BUS_DEST, BUS_PATH, DBUS_NAME_FLAG_DO_NOT_QUEUE, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER,
|
BUS_DEST, BUS_PATH, DBUS_NAME_FLAG_DO_NOT_QUEUE, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER,
|
||||||
Dbus, DbusSocket,
|
Dbus, DbusSocket,
|
||||||
|
|
@ -207,6 +208,7 @@ async fn run_async(
|
||||||
render_ctxs: Default::default(),
|
render_ctxs: Default::default(),
|
||||||
dma_buf_ids: Default::default(),
|
dma_buf_ids: Default::default(),
|
||||||
pw_con: pw_con.as_ref().map(|c| c.con.clone()),
|
pw_con: pw_con.as_ref().map(|c| c.con.clone()),
|
||||||
|
color_manager: ColorManager::new(),
|
||||||
});
|
});
|
||||||
if let Some(pw_con) = &pw_con {
|
if let Some(pw_con) = &pw_con {
|
||||||
pw_con.con.owner.set(Some(state.clone()));
|
pw_con.con.owner.set(Some(state.clone()));
|
||||||
|
|
@ -302,6 +304,7 @@ struct PortalState {
|
||||||
render_ctxs: CopyHashMap<c::dev_t, Weak<PortalRenderCtx>>,
|
render_ctxs: CopyHashMap<c::dev_t, Weak<PortalRenderCtx>>,
|
||||||
dma_buf_ids: Rc<DmaBufIds>,
|
dma_buf_ids: Rc<DmaBufIds>,
|
||||||
pw_con: Option<Rc<PwCon>>,
|
pw_con: Option<Rc<PwCon>>,
|
||||||
|
color_manager: Rc<ColorManager>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PortalState {
|
impl PortalState {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
cmm::cmm_transfer_function::TransferFunction,
|
||||||
format::ARGB8888,
|
format::ARGB8888,
|
||||||
gfx_api::{GfxContext, GfxTexture},
|
gfx_api::{GfxContext, GfxTexture},
|
||||||
pango::{
|
pango::{
|
||||||
|
|
@ -7,7 +8,7 @@ use {
|
||||||
consts::{CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE},
|
consts::{CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE},
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
theme::{Color, TransferFunction},
|
theme::Color,
|
||||||
},
|
},
|
||||||
std::{ops::Neg, rc::Rc, sync::Arc},
|
std::{ops::Neg, rc::Rc, sync::Arc},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
allocator::{BO_USE_RENDERING, BufferObject, BufferUsage},
|
allocator::{BO_USE_RENDERING, BufferObject, BufferUsage},
|
||||||
async_engine::{Phase, SpawnedFuture},
|
async_engine::{Phase, SpawnedFuture},
|
||||||
|
cmm::cmm_manager::ColorManager,
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
format::ARGB8888,
|
format::ARGB8888,
|
||||||
|
|
@ -59,7 +60,7 @@ pub trait GuiElement {
|
||||||
max_width: f32,
|
max_width: f32,
|
||||||
max_height: f32,
|
max_height: f32,
|
||||||
) -> (f32, 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 child_at(&self, x: f32, y: f32) -> Option<Rc<dyn GuiElement>>;
|
||||||
|
|
||||||
fn hover_cursor(&self) -> KnownCursor {
|
fn hover_cursor(&self) -> KnownCursor {
|
||||||
|
|
@ -190,7 +191,9 @@ impl GuiElement for Button {
|
||||||
(extents.width, extents.height)
|
(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 x2 = x1 + self.data.width.get();
|
||||||
let y2 = y1 + self.data.height.get();
|
let y2 = y1 + self.data.height.get();
|
||||||
let border = self.border.get();
|
let border = self.border.get();
|
||||||
|
|
@ -201,7 +204,7 @@ impl GuiElement for Button {
|
||||||
(x1, y1 + border, x1 + border, y2 - border),
|
(x1, y1 + border, x1 + border, y2 - border),
|
||||||
(x2 - border, y1 + border, x2, 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)];
|
let rects = [(x1 + border, y1 + border, x2 - border, y2 - border)];
|
||||||
|
|
@ -209,7 +212,7 @@ impl GuiElement for Button {
|
||||||
true => self.bg_color.get(),
|
true => self.bg_color.get(),
|
||||||
false => self.bg_hover_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() {
|
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());
|
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,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
false,
|
||||||
|
srgb_srgb,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -311,7 +315,7 @@ impl GuiElement for Label {
|
||||||
(width as f32 / scale, height as f32 / scale)
|
(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() {
|
if let Some(tex) = self.tex.get() {
|
||||||
let (tx, ty) = r.scale_point_f(x, y);
|
let (tx, ty) = r.scale_point_f(x, y);
|
||||||
r.render_texture(
|
r.render_texture(
|
||||||
|
|
@ -327,6 +331,7 @@ impl GuiElement for Label {
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
false,
|
||||||
|
color_manager.srgb_srgb(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -439,9 +444,14 @@ impl GuiElement for Flow {
|
||||||
(w.min(max_width), h.min(max_height))
|
(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() {
|
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(
|
let res = buf.fb.render_custom(
|
||||||
AcquireSync::Implicit,
|
AcquireSync::Implicit,
|
||||||
ReleaseSync::Implicit,
|
ReleaseSync::Implicit,
|
||||||
|
self.dpy.state.color_manager.srgb_srgb(),
|
||||||
self.scale.get(),
|
self.scale.get(),
|
||||||
Some(&Color::from_gray_srgb(0)),
|
Some(&Color::from_gray_srgb(0)),
|
||||||
|
&self.dpy.state.color_manager.srgb_srgb().linear,
|
||||||
None,
|
None,
|
||||||
|
self.dpy.state.color_manager.srgb_linear(),
|
||||||
&mut |r| {
|
&mut |r| {
|
||||||
if let Some(content) = self.content.get() {
|
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,
|
renderer::renderer_base::RendererBase,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::State,
|
state::State,
|
||||||
theme::{Color, TransferFunction},
|
theme::Color,
|
||||||
tree::{
|
tree::{
|
||||||
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
|
ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
|
||||||
ToplevelNodeBase, WorkspaceNode,
|
ToplevelNodeBase, WorkspaceNode,
|
||||||
|
|
@ -77,6 +77,8 @@ impl Renderer<'_> {
|
||||||
}
|
}
|
||||||
let theme = &self.state.theme;
|
let theme = &self.state.theme;
|
||||||
let th = theme.sizes.title_height.get();
|
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 {
|
if let Some(fs) = fullscreen {
|
||||||
fs.tl_as_node().node_render(self, x, y, None);
|
fs.tl_as_node().node_render(self, x, y, None);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -89,26 +91,28 @@ impl Renderer<'_> {
|
||||||
let bar_bg = self.base.scale_rect(bar_bg);
|
let bar_bg = self.base.scale_rect(bar_bg);
|
||||||
let c = theme.colors.bar_background.get();
|
let c = theme.colors.bar_background.get();
|
||||||
self.base
|
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();
|
let rd = output.render_data.borrow_mut();
|
||||||
if let Some(aw) = &rd.active_workspace {
|
if let Some(aw) = &rd.active_workspace {
|
||||||
let c = match aw.captured {
|
let c = match aw.captured {
|
||||||
true => theme.colors.captured_focused_title_background.get(),
|
true => theme.colors.captured_focused_title_background.get(),
|
||||||
false => theme.colors.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();
|
let c = theme.colors.separator.get();
|
||||||
self.base
|
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();
|
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();
|
let c = theme.colors.captured_unfocused_title_background.get();
|
||||||
self.base
|
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();
|
let c = theme.colors.attention_requested_background.get();
|
||||||
self.base
|
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();
|
let scale = output.global.persistent.scale.get();
|
||||||
for title in &rd.titles {
|
for title in &rd.titles {
|
||||||
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
|
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
|
||||||
|
|
@ -125,6 +129,7 @@ impl Renderer<'_> {
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
false,
|
||||||
|
self.state.color_manager.srgb_srgb(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(status) = &rd.status {
|
if let Some(status) = &rd.status {
|
||||||
|
|
@ -143,6 +148,7 @@ impl Renderer<'_> {
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
false,
|
||||||
|
srgb_srgb,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +187,7 @@ impl Renderer<'_> {
|
||||||
if ws.render_highlight.get() > 0 {
|
if ws.render_highlight.get() > 0 {
|
||||||
let color = self.state.theme.colors.highlight.get();
|
let color = self.state.theme.colors.highlight.get();
|
||||||
let bounds = ws.position.get().at_point(x, y + th + 1);
|
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(
|
self.base.fill_boxes(
|
||||||
std::slice::from_ref(&pos.at_point(x, y)),
|
std::slice::from_ref(&pos.at_point(x, y)),
|
||||||
&Color::from_srgba_straight(20, 20, 20, 255),
|
&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(tex) = placeholder.textures.borrow().get(&self.base.scale) {
|
||||||
if let Some(texture) = tex.texture() {
|
if let Some(texture) = tex.texture() {
|
||||||
|
|
@ -222,6 +229,7 @@ impl Renderer<'_> {
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
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) {
|
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 rd = container.render_data.borrow_mut();
|
||||||
let c = self.state.theme.colors.unfocused_title_background.get();
|
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();
|
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();
|
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();
|
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();
|
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 {
|
if let Some(lar) = &rd.last_active_rect {
|
||||||
let c = self
|
let c = self
|
||||||
.state
|
.state
|
||||||
|
|
@ -248,7 +260,8 @@ impl Renderer<'_> {
|
||||||
.colors
|
.colors
|
||||||
.focused_inactive_title_background
|
.focused_inactive_title_background
|
||||||
.get();
|
.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) {
|
if let Some(titles) = rd.titles.get(&self.base.scale) {
|
||||||
for title in titles {
|
for title in titles {
|
||||||
|
|
@ -268,6 +281,7 @@ impl Renderer<'_> {
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
false,
|
||||||
|
srgb_srgb,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -340,14 +354,22 @@ impl Renderer<'_> {
|
||||||
};
|
};
|
||||||
let color = self.state.theme.colors.highlight.get();
|
let color = self.state.theme.colors.highlight.get();
|
||||||
self.base.ops.push(GfxApiOpt::Sync);
|
self.base.ops.push(GfxApiOpt::Sync);
|
||||||
self.base
|
self.base.fill_scaled_boxes(
|
||||||
.fill_scaled_boxes(slice::from_ref(bounds), &color, None);
|
slice::from_ref(bounds),
|
||||||
|
&color,
|
||||||
|
None,
|
||||||
|
&self.state.color_manager.srgb_srgb().linear,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_highlight(&mut self, rect: &Rect) {
|
pub fn render_highlight(&mut self, rect: &Rect) {
|
||||||
let color = self.state.theme.colors.highlight.get();
|
let color = self.state.theme.colors.highlight.get();
|
||||||
self.base.ops.push(GfxApiOpt::Sync);
|
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>) {
|
pub fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32, bounds: Option<&Rect>) {
|
||||||
|
|
@ -422,6 +444,7 @@ impl Renderer<'_> {
|
||||||
bounds: Option<&Rect>,
|
bounds: Option<&Rect>,
|
||||||
) {
|
) {
|
||||||
let alpha = surface.alpha();
|
let alpha = surface.alpha();
|
||||||
|
let cd = surface.color_description();
|
||||||
if let Some(tex) = buffer.buffer.get_texture(surface) {
|
if let Some(tex) = buffer.buffer.get_texture(surface) {
|
||||||
let mut opaque = surface.opaque();
|
let mut opaque = surface.opaque();
|
||||||
if !opaque && tex.format().has_alpha {
|
if !opaque && tex.format().has_alpha {
|
||||||
|
|
@ -440,6 +463,7 @@ impl Renderer<'_> {
|
||||||
AcquireSync::Unnecessary,
|
AcquireSync::Unnecessary,
|
||||||
buffer.release_sync,
|
buffer.release_sync,
|
||||||
opaque,
|
opaque,
|
||||||
|
&cd,
|
||||||
);
|
);
|
||||||
} else if let Some(color) = &buffer.buffer.color {
|
} else if let Some(color) = &buffer.buffer.color {
|
||||||
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
||||||
|
|
@ -449,14 +473,15 @@ impl Renderer<'_> {
|
||||||
};
|
};
|
||||||
if !rect.is_empty() {
|
if !rect.is_empty() {
|
||||||
let color = Color::from_u32_premultiplied(
|
let color = Color::from_u32_premultiplied(
|
||||||
TransferFunction::Srgb,
|
cd.transfer_function,
|
||||||
color[0],
|
color[0],
|
||||||
color[1],
|
color[1],
|
||||||
color[2],
|
color[2],
|
||||||
color[3],
|
color[3],
|
||||||
);
|
);
|
||||||
self.base.ops.push(GfxApiOpt::Sync);
|
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 {
|
} 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 + 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(),
|
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()];
|
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 =
|
let title_underline =
|
||||||
[Rect::new_sized(x + bw, y + bw + th, pos.width() - 2 * bw, 1).unwrap()];
|
[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(title) = floating.title_textures.borrow().get(&self.base.scale) {
|
||||||
if let Some(texture) = title.texture() {
|
if let Some(texture) = title.texture() {
|
||||||
let rect = floating.title_rect.get().move_(x, y);
|
let rect = floating.title_rect.get().move_(x, y);
|
||||||
|
|
@ -512,6 +539,7 @@ impl Renderer<'_> {
|
||||||
AcquireSync::None,
|
AcquireSync::None,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
false,
|
false,
|
||||||
|
srgb_srgb,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
cmm::cmm_description::{ColorDescription, LinearColorDescription},
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture,
|
AcquireSync, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture,
|
||||||
ReleaseSync, SampleRect,
|
ReleaseSync, SampleRect,
|
||||||
|
|
@ -64,16 +65,29 @@ impl RendererBase<'_> {
|
||||||
rect
|
rect
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_scaled_boxes(&mut self, boxes: &[Rect], color: &Color, alpha: Option<f32>) {
|
pub fn fill_scaled_boxes(
|
||||||
self.fill_boxes3(boxes, color, alpha, 0, 0, true);
|
&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) {
|
pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color, cd: &Rc<LinearColorDescription>) {
|
||||||
self.fill_boxes3(boxes, color, None, 0, 0, false);
|
self.fill_boxes3(boxes, color, None, cd, 0, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_boxes2(&mut self, boxes: &[Rect], color: &Color, dx: i32, dy: i32) {
|
pub fn fill_boxes2(
|
||||||
self.fill_boxes3(boxes, color, None, dx, dy, false);
|
&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(
|
pub fn fill_boxes3(
|
||||||
|
|
@ -81,6 +95,7 @@ impl RendererBase<'_> {
|
||||||
boxes: &[Rect],
|
boxes: &[Rect],
|
||||||
color: &Color,
|
color: &Color,
|
||||||
alpha: Option<f32>,
|
alpha: Option<f32>,
|
||||||
|
cd: &Rc<LinearColorDescription>,
|
||||||
dx: i32,
|
dx: i32,
|
||||||
dy: i32,
|
dy: i32,
|
||||||
scaled: bool,
|
scaled: bool,
|
||||||
|
|
@ -106,18 +121,25 @@ impl RendererBase<'_> {
|
||||||
),
|
),
|
||||||
color: *color,
|
color: *color,
|
||||||
alpha,
|
alpha,
|
||||||
|
cd: cd.clone(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_boxes_f(&mut self, boxes: &[(f32, f32, f32, f32)], color: &Color) {
|
pub fn fill_boxes_f(
|
||||||
self.fill_boxes2_f(boxes, color, 0.0, 0.0);
|
&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(
|
pub fn fill_boxes2_f(
|
||||||
&mut self,
|
&mut self,
|
||||||
boxes: &[(f32, f32, f32, f32)],
|
boxes: &[(f32, f32, f32, f32)],
|
||||||
color: &Color,
|
color: &Color,
|
||||||
|
cd: &Rc<LinearColorDescription>,
|
||||||
dx: f32,
|
dx: f32,
|
||||||
dy: f32,
|
dy: f32,
|
||||||
) {
|
) {
|
||||||
|
|
@ -139,6 +161,7 @@ impl RendererBase<'_> {
|
||||||
),
|
),
|
||||||
color: *color,
|
color: *color,
|
||||||
alpha: None,
|
alpha: None,
|
||||||
|
cd: cd.clone(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +180,7 @@ impl RendererBase<'_> {
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
opaque: bool,
|
opaque: bool,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
) {
|
) {
|
||||||
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
||||||
|
|
||||||
|
|
@ -200,6 +224,7 @@ impl RendererBase<'_> {
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
opaque,
|
opaque,
|
||||||
|
cd: cd.clone(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ pub fn take_screenshot(
|
||||||
fb.render_node(
|
fb.render_node(
|
||||||
AcquireSync::Unnecessary,
|
AcquireSync::Unnecessary,
|
||||||
ReleaseSync::Implicit,
|
ReleaseSync::Implicit,
|
||||||
|
state.color_manager.srgb_srgb(),
|
||||||
state.root.deref(),
|
state.root.deref(),
|
||||||
state,
|
state,
|
||||||
Some(state.root.extents.get()),
|
Some(state.root.extents.get()),
|
||||||
|
|
@ -89,6 +90,7 @@ pub fn take_screenshot(
|
||||||
false,
|
false,
|
||||||
Transform::None,
|
Transform::None,
|
||||||
None,
|
None,
|
||||||
|
state.color_manager.srgb_linear(),
|
||||||
)?;
|
)?;
|
||||||
let drm = match allocator.drm() {
|
let drm = match allocator.drm() {
|
||||||
Some(drm) => Some(drm.dup_render()?.fd().clone()),
|
Some(drm) => Some(drm.dup_render()?.fd().clone()),
|
||||||
|
|
|
||||||
16
src/state.rs
16
src/state.rs
|
|
@ -11,6 +11,7 @@ use {
|
||||||
cli::RunArgs,
|
cli::RunArgs,
|
||||||
client::{Client, ClientId, Clients, NUM_CACHED_SERIAL_RANGES, SerialRange},
|
client::{Client, ClientId, Clients, NUM_CACHED_SERIAL_RANGES, SerialRange},
|
||||||
clientmem::ClientMemOffset,
|
clientmem::ClientMemOffset,
|
||||||
|
cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager},
|
||||||
compositor::LIBEI_SOCKET,
|
compositor::LIBEI_SOCKET,
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
cpu_worker::CpuWorker,
|
cpu_worker::CpuWorker,
|
||||||
|
|
@ -234,6 +235,7 @@ pub struct State {
|
||||||
pub data_control_device_ids: DataControlDeviceIds,
|
pub data_control_device_ids: DataControlDeviceIds,
|
||||||
pub workspace_managers: WorkspaceManagerState,
|
pub workspace_managers: WorkspaceManagerState,
|
||||||
pub color_management_enabled: Cell<bool>,
|
pub color_management_enabled: Cell<bool>,
|
||||||
|
pub color_manager: Rc<ColorManager>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -976,15 +978,18 @@ impl State {
|
||||||
&self,
|
&self,
|
||||||
output: &OutputNode,
|
output: &OutputNode,
|
||||||
fb: &Rc<dyn GfxFramebuffer>,
|
fb: &Rc<dyn GfxFramebuffer>,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
acquire_sync: AcquireSync,
|
acquire_sync: AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
tex: &Rc<dyn GfxTexture>,
|
tex: &Rc<dyn GfxTexture>,
|
||||||
render_hw_cursor: bool,
|
render_hw_cursor: bool,
|
||||||
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
|
||||||
|
blend_cd: &Rc<ColorDescription>,
|
||||||
) -> Result<Option<SyncFile>, GfxError> {
|
) -> Result<Option<SyncFile>, GfxError> {
|
||||||
let sync_file = fb.render_output(
|
let sync_file = fb.render_output(
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
cd,
|
||||||
output,
|
output,
|
||||||
self,
|
self,
|
||||||
Some(output.global.pos.get()),
|
Some(output.global.pos.get()),
|
||||||
|
|
@ -992,10 +997,12 @@ impl State {
|
||||||
render_hw_cursor,
|
render_hw_cursor,
|
||||||
true,
|
true,
|
||||||
blend_buffer,
|
blend_buffer,
|
||||||
|
blend_cd,
|
||||||
)?;
|
)?;
|
||||||
output.latched(false);
|
output.latched(false);
|
||||||
output.perform_screencopies(
|
output.perform_screencopies(
|
||||||
tex,
|
tex,
|
||||||
|
cd,
|
||||||
None,
|
None,
|
||||||
&AcquireSync::Unnecessary,
|
&AcquireSync::Unnecessary,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
|
@ -1013,10 +1020,12 @@ impl State {
|
||||||
resv: Option<&Rc<dyn BufferResv>>,
|
resv: Option<&Rc<dyn BufferResv>>,
|
||||||
acquire_sync: &AcquireSync,
|
acquire_sync: &AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
src_cd: &Rc<ColorDescription>,
|
||||||
target: &Rc<dyn GfxFramebuffer>,
|
target: &Rc<dyn GfxFramebuffer>,
|
||||||
target_acquire_sync: AcquireSync,
|
target_acquire_sync: AcquireSync,
|
||||||
target_release_sync: ReleaseSync,
|
target_release_sync: ReleaseSync,
|
||||||
target_transform: Transform,
|
target_transform: Transform,
|
||||||
|
target_cd: &Rc<ColorDescription>,
|
||||||
position: Rect,
|
position: Rect,
|
||||||
render_hardware_cursors: bool,
|
render_hardware_cursors: bool,
|
||||||
x_off: i32,
|
x_off: i32,
|
||||||
|
|
@ -1050,6 +1059,7 @@ impl State {
|
||||||
acquire_sync.clone(),
|
acquire_sync.clone(),
|
||||||
release_sync,
|
release_sync,
|
||||||
false,
|
false,
|
||||||
|
src_cd,
|
||||||
);
|
);
|
||||||
if render_hardware_cursors {
|
if render_hardware_cursors {
|
||||||
if let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get() {
|
if let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get() {
|
||||||
|
|
@ -1066,15 +1076,19 @@ impl State {
|
||||||
target.render(
|
target.render(
|
||||||
target_acquire_sync,
|
target_acquire_sync,
|
||||||
target_release_sync,
|
target_release_sync,
|
||||||
|
target_cd,
|
||||||
&ops,
|
&ops,
|
||||||
Some(&Color::SOLID_BLACK),
|
Some(&Color::SOLID_BLACK),
|
||||||
|
&target_cd.linear,
|
||||||
None,
|
None,
|
||||||
|
target_cd,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perform_shm_screencopy(
|
pub fn perform_shm_screencopy(
|
||||||
&self,
|
&self,
|
||||||
src: &Rc<dyn GfxTexture>,
|
src: &Rc<dyn GfxTexture>,
|
||||||
|
src_cd: &Rc<ColorDescription>,
|
||||||
acquire_sync: &AcquireSync,
|
acquire_sync: &AcquireSync,
|
||||||
position: Rect,
|
position: Rect,
|
||||||
x_off: i32,
|
x_off: i32,
|
||||||
|
|
@ -1105,10 +1119,12 @@ impl State {
|
||||||
None,
|
None,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
|
src_cd,
|
||||||
&fb.clone().into_fb(),
|
&fb.clone().into_fb(),
|
||||||
AcquireSync::Unnecessary,
|
AcquireSync::Unnecessary,
|
||||||
ReleaseSync::None,
|
ReleaseSync::None,
|
||||||
transform,
|
transform,
|
||||||
|
self.color_manager.srgb_srgb(),
|
||||||
position,
|
position,
|
||||||
true,
|
true,
|
||||||
x_off - capture.rect.x1(),
|
x_off - capture.rect.x1(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
cmm::cmm_transfer_function::TransferFunction,
|
||||||
cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker, PendingJob},
|
cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker, PendingJob},
|
||||||
format::ARGB8888,
|
format::ARGB8888,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
|
|
@ -14,7 +15,7 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rect::{Rect, Region},
|
rect::{Rect, Region},
|
||||||
theme::{Color, TransferFunction},
|
theme::Color,
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, double_buffered::DoubleBuffered, on_drop_event::OnDropEvent,
|
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 {
|
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},
|
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)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
r: f32,
|
r: f32,
|
||||||
|
|
@ -83,6 +80,51 @@ impl Color {
|
||||||
fn linear(c: f32) -> f32 {
|
fn linear(c: f32) -> f32 {
|
||||||
c
|
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 {
|
macro_rules! convert {
|
||||||
($tf:ident) => {{
|
($tf:ident) => {{
|
||||||
r = $tf(r);
|
r = $tf(r);
|
||||||
|
|
@ -93,6 +135,15 @@ impl Color {
|
||||||
match transfer_function {
|
match transfer_function {
|
||||||
TransferFunction::Srgb => convert!(srgb),
|
TransferFunction::Srgb => convert!(srgb),
|
||||||
TransferFunction::Linear => convert!(linear),
|
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 }
|
Self { r, g, b, a: 1.0 }
|
||||||
}
|
}
|
||||||
|
|
@ -191,6 +242,56 @@ impl Color {
|
||||||
fn linear(c: f32) -> f32 {
|
fn linear(c: f32) -> f32 {
|
||||||
c
|
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 {
|
macro_rules! convert {
|
||||||
($tf:ident) => {{
|
($tf:ident) => {{
|
||||||
for c in &mut res[..3] {
|
for c in &mut res[..3] {
|
||||||
|
|
@ -207,6 +308,15 @@ impl Color {
|
||||||
match transfer_function {
|
match transfer_function {
|
||||||
TransferFunction::Srgb => convert!(srgb),
|
TransferFunction::Srgb => convert!(srgb),
|
||||||
TransferFunction::Linear => convert!(linear),
|
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 {
|
if self.a < 1.0 {
|
||||||
for c in &mut res[..3] {
|
for c in &mut res[..3] {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{HardwareCursor, KeyState, Mode},
|
backend::{HardwareCursor, KeyState, Mode},
|
||||||
client::ClientId,
|
client::ClientId,
|
||||||
|
cmm::cmm_description::ColorDescription,
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
|
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
|
||||||
|
|
@ -252,6 +253,7 @@ impl OutputNode {
|
||||||
pub fn perform_screencopies(
|
pub fn perform_screencopies(
|
||||||
&self,
|
&self,
|
||||||
tex: &Rc<dyn GfxTexture>,
|
tex: &Rc<dyn GfxTexture>,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
resv: Option<&Rc<dyn BufferResv>>,
|
resv: Option<&Rc<dyn BufferResv>>,
|
||||||
acquire_sync: &AcquireSync,
|
acquire_sync: &AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
|
@ -267,6 +269,7 @@ impl OutputNode {
|
||||||
}
|
}
|
||||||
self.perform_wlr_screencopies(
|
self.perform_wlr_screencopies(
|
||||||
tex,
|
tex,
|
||||||
|
cd,
|
||||||
resv,
|
resv,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
|
@ -279,6 +282,7 @@ impl OutputNode {
|
||||||
sc.copy_texture(
|
sc.copy_texture(
|
||||||
self,
|
self,
|
||||||
tex,
|
tex,
|
||||||
|
cd,
|
||||||
resv,
|
resv,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
|
@ -292,6 +296,7 @@ impl OutputNode {
|
||||||
sc.copy_texture(
|
sc.copy_texture(
|
||||||
self,
|
self,
|
||||||
tex,
|
tex,
|
||||||
|
cd,
|
||||||
resv,
|
resv,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
|
@ -306,6 +311,7 @@ impl OutputNode {
|
||||||
pub fn perform_wlr_screencopies(
|
pub fn perform_wlr_screencopies(
|
||||||
&self,
|
&self,
|
||||||
tex: &Rc<dyn GfxTexture>,
|
tex: &Rc<dyn GfxTexture>,
|
||||||
|
cd: &Rc<ColorDescription>,
|
||||||
resv: Option<&Rc<dyn BufferResv>>,
|
resv: Option<&Rc<dyn BufferResv>>,
|
||||||
acquire_sync: &AcquireSync,
|
acquire_sync: &AcquireSync,
|
||||||
release_sync: ReleaseSync,
|
release_sync: ReleaseSync,
|
||||||
|
|
@ -337,6 +343,7 @@ impl OutputNode {
|
||||||
WlBufferStorage::Shm { mem, stride } => {
|
WlBufferStorage::Shm { mem, stride } => {
|
||||||
let res = self.state.perform_shm_screencopy(
|
let res = self.state.perform_shm_screencopy(
|
||||||
tex,
|
tex,
|
||||||
|
cd,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
self.global.pos.get(),
|
self.global.pos.get(),
|
||||||
x_off,
|
x_off,
|
||||||
|
|
@ -375,10 +382,12 @@ impl OutputNode {
|
||||||
resv,
|
resv,
|
||||||
acquire_sync,
|
acquire_sync,
|
||||||
release_sync,
|
release_sync,
|
||||||
|
cd,
|
||||||
&fb,
|
&fb,
|
||||||
AcquireSync::Implicit,
|
AcquireSync::Implicit,
|
||||||
ReleaseSync::Implicit,
|
ReleaseSync::Implicit,
|
||||||
self.global.persistent.transform.get(),
|
self.global.persistent.transform.get(),
|
||||||
|
self.state.color_manager.srgb_srgb(),
|
||||||
self.global.pos.get(),
|
self.global.pos.get(),
|
||||||
render_hardware_cursors,
|
render_hardware_cursors,
|
||||||
x_off - capture.rect.x1(),
|
x_off - capture.rect.x1(),
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ pub mod double_click_state;
|
||||||
pub mod errorfmt;
|
pub mod errorfmt;
|
||||||
pub mod event_listener;
|
pub mod event_listener;
|
||||||
pub mod fdcloser;
|
pub mod fdcloser;
|
||||||
|
pub mod free_list;
|
||||||
pub mod geometric_decay;
|
pub mod geometric_decay;
|
||||||
pub mod gfx_api_ext;
|
pub mod gfx_api_ext;
|
||||||
pub mod hash_map_ext;
|
pub mod hash_map_ext;
|
||||||
|
|
@ -37,6 +38,7 @@ pub mod opaque;
|
||||||
pub mod opaque_cell;
|
pub mod opaque_cell;
|
||||||
pub mod opt;
|
pub mod opt;
|
||||||
pub mod option_ext;
|
pub mod option_ext;
|
||||||
|
pub mod ordered_float;
|
||||||
pub mod oserror;
|
pub mod oserror;
|
||||||
pub mod page_size;
|
pub mod page_size;
|
||||||
pub mod pending_serial;
|
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