1
0
Fork 0
forked from wry/wry

gfx: attach color descriptions

This commit is contained in:
Julian Orth 2025-03-01 14:06:42 +01:00
parent 82085a3858
commit a174881138
24 changed files with 291 additions and 62 deletions

View file

@ -451,7 +451,9 @@ impl MetalConnector {
};
self.state.present_hardware_cursor(node, &mut c);
if c.cursor_swap_buffer {
c.sync_file = c.cursor_buffer.copy_to_dev(c.sync_file)?;
c.sync_file = c
.cursor_buffer
.copy_to_dev(&self.state.color_manager, c.sync_file)?;
}
self.cursor_swap_buffer.set(c.cursor_swap_buffer);
if c.sync_file.is_some() {
@ -747,12 +749,14 @@ impl MetalConnector {
.perform_render_pass(
AcquireSync::Unnecessary,
ReleaseSync::Explicit,
self.state.color_manager.srgb_srgb(),
&latched.pass,
&latched.damage,
buffer.blend_buffer.as_ref(),
self.state.color_manager.srgb_linear(),
)
.map_err(MetalError::RenderFrame)?;
sync_file = buffer.copy_to_dev(sf)?;
sync_file = buffer.copy_to_dev(&self.state.color_manager, sf)?;
fb = buffer.drm.clone();
tex = buffer.render_tex.clone();
}
@ -792,6 +796,7 @@ impl MetalConnector {
None => {
output.perform_screencopies(
&fb.tex,
self.state.color_manager.srgb_srgb(),
None,
&AcquireSync::Unnecessary,
ReleaseSync::None,
@ -804,6 +809,7 @@ impl MetalConnector {
Some(dsd) => {
output.perform_screencopies(
&dsd.tex,
self.state.color_manager.srgb_srgb(),
dsd.resv.as_ref(),
&dsd.acquire_sync,
dsd.release_sync,

View file

@ -14,6 +14,7 @@ use {
POST_COMMIT_MARGIN_DELTA, PresentFb,
},
},
cmm::cmm_manager::ColorManager,
drm_feedback::DrmFeedback,
edid::{CtaDataBlock, Descriptor, EdidExtension},
format::{ARGB8888, Format, XRGB8888},
@ -2704,7 +2705,11 @@ impl MetalBackend {
Err(e) => return Err(MetalError::ImportFb(e)),
};
dev_fb
.clear(AcquireSync::Unnecessary, ReleaseSync::None)
.clear(
AcquireSync::Unnecessary,
ReleaseSync::None,
self.state.color_manager.srgb_srgb(),
)
.map_err(MetalError::Clear)?;
let (dev_tex, render_tex, render_fb, render_bo) = if dev.id == render_ctx.dev_id {
let render_tex = match dev_img.to_texture() {
@ -2758,7 +2763,11 @@ impl MetalBackend {
Err(e) => return Err(MetalError::ImportFb(e)),
};
render_fb
.clear(AcquireSync::Unnecessary, ReleaseSync::None)
.clear(
AcquireSync::Unnecessary,
ReleaseSync::None,
self.state.color_manager.srgb_srgb(),
)
.map_err(MetalError::Clear)?;
let render_tex = match render_img.to_texture() {
Ok(fb) => fb,
@ -3030,7 +3039,11 @@ impl RenderBuffer {
.unwrap_or_else(|| self.dev_fb.clone())
}
pub fn copy_to_dev(&self, sync_file: Option<SyncFile>) -> Result<Option<SyncFile>, MetalError> {
pub fn copy_to_dev(
&self,
cm: &ColorManager,
sync_file: Option<SyncFile>,
) -> Result<Option<SyncFile>, MetalError> {
let Some(tex) = &self.dev_tex else {
return Ok(sync_file);
};
@ -3038,7 +3051,9 @@ impl RenderBuffer {
.copy_texture(
AcquireSync::Unnecessary,
ReleaseSync::Explicit,
cm.srgb_srgb(),
tex,
cm.srgb_srgb(),
None,
AcquireSync::from_sync_file(sync_file),
ReleaseSync::None,

View file

@ -751,11 +751,13 @@ impl XBackend {
let res = self.state.present_output(
&node,
&image.fb.get(),
self.state.color_manager.srgb_srgb(),
AcquireSync::Implicit,
ReleaseSync::Implicit,
&image.tex.get(),
true,
None,
self.state.color_manager.srgb_linear(),
);
if let Err(e) = res {
log::error!("Could not render screen: {}", ErrorFmt(e));

View file

@ -44,11 +44,9 @@ pub struct LinearColorDescription {
#[derive(Debug)]
pub struct ColorDescription {
pub id: ColorDescriptionId,
#[expect(dead_code)]
pub linear: Rc<LinearColorDescription>,
#[expect(dead_code)]
pub named_primaries: Option<NamedPrimaries>,
#[expect(dead_code)]
pub transfer_function: TransferFunction,
pub(super) shared: Rc<Shared>,
}

View file

@ -92,12 +92,10 @@ impl ColorManager {
})
}
#[expect(dead_code)]
pub fn srgb_srgb(&self) -> &Rc<ColorDescription> {
&self.srgb_srgb
}
#[expect(dead_code)]
pub fn srgb_linear(&self) -> &Rc<ColorDescription> {
&self.srgb_linear
}

View file

@ -153,6 +153,7 @@ fn start_compositor2(
let scales = RefCounted::default();
scales.add(Scale::from_int(1));
let cpu_worker = Rc::new(CpuWorker::new(&ring, &engine)?);
let color_manager = ColorManager::new();
let state = Rc::new(State {
kb_ctx,
backend: CloneCell::new(Rc::new(DummyBackend)),
@ -267,7 +268,7 @@ fn start_compositor2(
tablet_ids: Default::default(),
tablet_tool_ids: Default::default(),
tablet_pad_ids: Default::default(),
damage_visualizer: DamageVisualizer::new(&engine),
damage_visualizer: DamageVisualizer::new(&engine, &color_manager),
default_vrr_mode: Cell::new(VrrMode::NEVER),
default_vrr_cursor_hz: Cell::new(None),
default_tearing_mode: Cell::new(TearingMode::VARIANT_3),
@ -285,7 +286,7 @@ fn start_compositor2(
data_control_device_ids: Default::default(),
workspace_managers: Default::default(),
color_management_enabled: Cell::new(false),
color_manager: ColorManager::new(),
color_manager,
});
state.tracker.register(ClientId::from_raw(0));
create_dummy_output(&state);

View file

@ -398,6 +398,7 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed
AcquireSync::None,
ReleaseSync::None,
false,
renderer.state.color_manager.srgb_srgb(),
);
}
}
@ -422,6 +423,7 @@ impl Cursor for StaticCursor {
AcquireSync::None,
ReleaseSync::None,
false,
renderer.state.color_manager.srgb_srgb(),
);
}
}
@ -464,6 +466,7 @@ impl Cursor for AnimatedCursor {
AcquireSync::None,
ReleaseSync::None,
false,
renderer.state.color_manager.srgb_srgb(),
);
}
}

View file

@ -507,6 +507,7 @@ impl CursorUser {
&self.group.state,
scale,
transform,
self.group.state.color_manager.srgb_srgb(),
);
match res {
Ok(sync_file) => {

View file

@ -1,6 +1,7 @@
use {
crate::{
async_engine::AsyncEngine,
cmm::cmm_manager::ColorManager,
fixed::Fixed,
gfx_api::GfxApiOpt,
ifs::wl_output::WlOutputGlobal,
@ -77,6 +78,7 @@ pub struct DamageVisualizer {
enabled: Cell<bool>,
decay: Cell<Duration>,
color: Cell<Color>,
color_manager: Rc<ColorManager>,
}
const MAX_RECTS: usize = 100_000;
@ -87,7 +89,7 @@ struct Damage {
}
impl DamageVisualizer {
pub fn new(eng: &Rc<AsyncEngine>) -> Self {
pub fn new(eng: &Rc<AsyncEngine>, color_manager: &Rc<ColorManager>) -> Self {
Self {
eng: eng.clone(),
entries: Default::default(),
@ -95,6 +97,7 @@ impl DamageVisualizer {
enabled: Default::default(),
decay: Cell::new(Duration::from_secs(2)),
color: Cell::new(Color::from_srgba_straight(255, 0, 0, 128)),
color_manager: color_manager.clone(),
}
}
@ -162,13 +165,14 @@ impl DamageVisualizer {
let dy = -cursor_rect.y1();
let decay_millis = decay.as_millis() as u64 as f32;
renderer.ops.push(GfxApiOpt::Sync);
let srgb = &self.color_manager.srgb_srgb().linear;
for entry in entries.iter().rev() {
let region = Region::new(entry.rect);
let region = region.subtract(&used);
if region.is_not_empty() {
let age = (now - entry.time).as_millis() as u64 as f32 / decay_millis;
let color = base_color * (1.0 - age);
renderer.fill_boxes2(region.rects(), &color, dx, dy);
renderer.fill_boxes2(region.rects(), &color, srgb, dx, dy);
used = used.union(&region);
}
}

View file

@ -1,6 +1,7 @@
use {
crate::{
allocator::Allocator,
cmm::cmm_description::{ColorDescription, LinearColorDescription},
cpu_worker::CpuWorker,
cursor::Cursor,
damage::DamageVisualizer,
@ -41,6 +42,7 @@ pub enum GfxApiOpt {
pub struct GfxRenderPass {
pub ops: Vec<GfxApiOpt>,
pub clear: Option<Color>,
pub clear_cd: Rc<LinearColorDescription>,
}
#[derive(Default, Debug, Copy, Clone, PartialEq)]
@ -194,6 +196,8 @@ pub struct FillRect {
pub rect: FramebufferRect,
pub color: Color,
pub alpha: Option<f32>,
#[expect(dead_code)]
pub cd: Rc<LinearColorDescription>,
}
impl FillRect {
@ -215,6 +219,8 @@ pub struct CopyTexture {
pub release_sync: ReleaseSync,
pub alpha: Option<f32>,
pub opaque: bool,
#[expect(dead_code)]
pub cd: Rc<ColorDescription>,
}
#[derive(Clone, Debug)]
@ -299,10 +305,13 @@ pub trait GfxFramebuffer: Debug {
self: Rc<Self>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
cd: &Rc<ColorDescription>,
ops: &[GfxApiOpt],
clear: Option<&Color>,
clear_cd: &Rc<LinearColorDescription>,
region: &Region,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError>;
fn format(&self) -> &'static Format;
@ -333,17 +342,23 @@ impl dyn GfxFramebuffer {
self: &Rc<Self>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
cd: &Rc<ColorDescription>,
ops: &[GfxApiOpt],
clear: Option<&Color>,
clear_cd: &Rc<LinearColorDescription>,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
self.clone().render_with_region(
acquire_sync,
release_sync,
cd,
ops,
clear,
clear_cd,
&self.full_region(),
blend_buffer,
blend_cd,
)
}
@ -351,17 +366,35 @@ impl dyn GfxFramebuffer {
self: &Rc<Self>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
self.clear_with(acquire_sync, release_sync, &Color::TRANSPARENT)
self.clear_with(
acquire_sync,
release_sync,
cd,
&Color::TRANSPARENT,
&cd.linear,
)
}
pub fn clear_with(
self: &Rc<Self>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
cd: &Rc<ColorDescription>,
color: &Color,
color_cd: &Rc<LinearColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
self.render(acquire_sync, release_sync, &[], Some(color), None)
self.render(
acquire_sync,
release_sync,
cd,
&[],
Some(color),
color_cd,
None,
cd,
)
}
pub fn logical_size(&self, transform: Transform) -> (i32, i32) {
@ -381,7 +414,9 @@ impl dyn GfxFramebuffer {
self: &Rc<Self>,
fb_acquire_sync: AcquireSync,
fb_release_sync: ReleaseSync,
fb_cd: &Rc<ColorDescription>,
texture: &Rc<dyn GfxTexture>,
texture_cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
@ -404,24 +439,46 @@ impl dyn GfxFramebuffer {
acquire_sync,
release_sync,
false,
texture_cd,
);
let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT);
self.render(fb_acquire_sync, fb_release_sync, &ops, clear, None)
self.render(
fb_acquire_sync,
fb_release_sync,
fb_cd,
&ops,
clear,
&fb_cd.linear,
None,
fb_cd,
)
}
pub fn render_custom(
self: &Rc<Self>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
cd: &Rc<ColorDescription>,
scale: Scale,
clear: Option<&Color>,
clear_cd: &Rc<LinearColorDescription>,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>,
f: &mut dyn FnMut(&mut RendererBase),
) -> Result<Option<SyncFile>, GfxError> {
let mut ops = vec![];
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
f(&mut renderer);
self.render(acquire_sync, release_sync, &ops, clear, blend_buffer)
self.render(
acquire_sync,
release_sync,
cd,
&ops,
clear,
clear_cd,
blend_buffer,
blend_cd,
)
}
pub fn create_render_pass(
@ -456,17 +513,22 @@ impl dyn GfxFramebuffer {
self: &Rc<Self>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
cd: &Rc<ColorDescription>,
pass: &GfxRenderPass,
region: &Region,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
self.clone().render_with_region(
acquire_sync,
release_sync,
cd,
&pass.ops,
pass.clear.as_ref(),
&pass.clear_cd,
region,
blend_buffer,
blend_cd,
)
}
@ -474,6 +536,7 @@ impl dyn GfxFramebuffer {
self: &Rc<Self>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
cd: &Rc<ColorDescription>,
node: &OutputNode,
state: &State,
cursor_rect: Option<Rect>,
@ -481,10 +544,12 @@ impl dyn GfxFramebuffer {
render_hardware_cursor: bool,
fill_black_in_grace_period: bool,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
self.render_node(
acquire_sync,
release_sync,
cd,
node,
state,
cursor_rect,
@ -495,6 +560,7 @@ impl dyn GfxFramebuffer {
fill_black_in_grace_period,
node.global.persistent.transform.get(),
blend_buffer,
blend_cd,
)
}
@ -502,6 +568,7 @@ impl dyn GfxFramebuffer {
self: &Rc<Self>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
cd: &Rc<ColorDescription>,
node: &dyn Node,
state: &State,
cursor_rect: Option<Rect>,
@ -512,6 +579,7 @@ impl dyn GfxFramebuffer {
fill_black_in_grace_period: bool,
transform: Transform,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
let pass = self.create_render_pass(
node,
@ -528,9 +596,11 @@ impl dyn GfxFramebuffer {
self.perform_render_pass(
acquire_sync,
release_sync,
cd,
&pass,
&self.full_region(),
blend_buffer,
blend_cd,
)
}
@ -542,6 +612,7 @@ impl dyn GfxFramebuffer {
state: &State,
scale: Scale,
transform: Transform,
cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
let mut ops = vec![];
let mut renderer = Renderer {
@ -557,9 +628,12 @@ impl dyn GfxFramebuffer {
self.render(
acquire_sync,
release_sync,
cd,
&ops,
Some(&Color::TRANSPARENT),
&cd.linear,
None,
cd,
)
}
}
@ -832,6 +906,7 @@ pub fn create_render_pass(
return GfxRenderPass {
ops: vec![],
clear: Some(Color::SOLID_BLACK),
clear_cd: state.color_manager.srgb_srgb().linear.clone(),
};
}
let mut ops = vec![];
@ -898,6 +973,7 @@ pub fn create_render_pass(
GfxRenderPass {
ops,
clear: Some(c),
clear_cd: state.color_manager.srgb_srgb().linear.clone(),
}
}

View file

@ -69,7 +69,7 @@ use {
crate::{
cmm::cmm_transfer_function::TransferFunction,
gfx_api::{
AcquireSync, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
AcquireSync, CopyTexture, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
ReleaseSync, SyncFile,
},
gfx_apis::gl::{
@ -205,10 +205,15 @@ enum RenderError {
#[derive(Default)]
struct GfxGlState {
triangles: RefCell<Vec<[f32; 2]>>,
fill_rect: VecStorage<FillRect>,
fill_rect: VecStorage<GlFillRect>,
copy_tex: VecStorage<&'static CopyTexture>,
}
struct GlFillRect {
pub rect: FramebufferRect,
pub color: Color,
}
fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
let mut state = fb.ctx.gl_state.borrow_mut();
let state = &mut *state;
@ -236,10 +241,9 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
}
}
GfxApiOpt::FillRect(f) => {
fill_rect.push(FillRect {
fill_rect.push(GlFillRect {
rect: f.rect,
color: f.effective_color(),
alpha: None,
});
i += 1;
}

View file

@ -1,6 +1,9 @@
use {
crate::{
cmm::cmm_transfer_function::TransferFunction,
cmm::{
cmm_description::{ColorDescription, LinearColorDescription},
cmm_transfer_function::TransferFunction,
},
format::Format,
gfx_api::{
AcquireSync, AsyncShmGfxTextureCallback, GfxApiOpt, GfxBlendBuffer, GfxError,
@ -105,10 +108,13 @@ impl GfxFramebuffer for Framebuffer {
self: Rc<Self>,
acquire_sync: AcquireSync,
_release_sync: ReleaseSync,
_cd: &Rc<ColorDescription>,
ops: &[GfxApiOpt],
clear: Option<&Color>,
_clear_cd: &Rc<LinearColorDescription>,
_region: &Region,
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
_blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
(*self)
.render(acquire_sync, ops, clear)

View file

@ -1,5 +1,6 @@
use {
crate::{
cmm::cmm_description::{ColorDescription, LinearColorDescription},
format::Format,
gfx_api::{
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
@ -552,10 +553,13 @@ impl GfxFramebuffer for VulkanImage {
self: Rc<Self>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
_cd: &Rc<ColorDescription>,
ops: &[GfxApiOpt],
clear: Option<&Color>,
_clear_cd: &Rc<LinearColorDescription>,
region: &Region,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
_blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
let mut blend_buffer =
blend_buffer.map(|b| b.clone().into_vk(&self.renderer.device.device));

View file

@ -1,6 +1,7 @@
use {
crate::{
client::{Client, ClientError},
cmm::cmm_description::ColorDescription,
gfx_api::{
AcquireSync, AsyncShmGfxTextureCallback, BufferResv, GfxError, GfxFramebuffer,
GfxTexture, ReleaseSync, STAGING_DOWNLOAD, SyncFile,
@ -199,6 +200,7 @@ impl ExtImageCopyCaptureFrameV1 {
self: &Rc<Self>,
on: &OutputNode,
texture: &Rc<dyn GfxTexture>,
cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
@ -215,10 +217,12 @@ impl ExtImageCopyCaptureFrameV1 {
resv,
acquire_sync,
release_sync,
cd,
&fb,
aq,
re,
jay_config::video::Transform::None,
self.client.state.color_manager.srgb_srgb(),
on.global.pos.get(),
render_hardware_cursors,
x_off,
@ -236,6 +240,7 @@ impl ExtImageCopyCaptureFrameV1 {
fb.render_node(
aq,
re,
self.client.state.color_manager.srgb_srgb(),
node,
&self.client.state,
Some(node.node_absolute_position()),
@ -246,6 +251,7 @@ impl ExtImageCopyCaptureFrameV1 {
false,
jay_config::video::Transform::None,
None,
self.client.state.color_manager.srgb_linear(),
)
});
}

View file

@ -1,6 +1,7 @@
use {
crate::{
client::{Client, ClientError},
cmm::cmm_description::ColorDescription,
format::{FORMATS, Format},
gfx_api::{
AcquireSync, BufferResv, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture,
@ -213,6 +214,7 @@ impl ExtImageCopyCaptureSessionV1 {
self: &Rc<Self>,
on: &OutputNode,
texture: &Rc<dyn GfxTexture>,
cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
@ -226,6 +228,7 @@ impl ExtImageCopyCaptureSessionV1 {
frame.copy_texture(
on,
texture,
cd,
resv,
acquire_sync,
release_sync,

View file

@ -2,6 +2,7 @@ use {
crate::{
allocator::{AllocatorError, BO_USE_LINEAR, BO_USE_RENDERING, BufferObject},
client::{Client, ClientError},
cmm::cmm_description::ColorDescription,
format::XRGB8888,
gfx_api::{
AcquireSync, BufferResv, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync,
@ -193,6 +194,7 @@ impl JayScreencast {
let res = buffer.fb.render_node(
AcquireSync::Implicit,
ReleaseSync::Implicit,
self.client.state.color_manager.srgb_srgb(),
tl.tl_as_node(),
&self.client.state,
Some(tl.node_absolute_position()),
@ -203,6 +205,7 @@ impl JayScreencast {
false,
Transform::None,
None,
self.client.state.color_manager.srgb_linear(),
);
match res {
Ok(_) => {
@ -304,6 +307,7 @@ impl JayScreencast {
&self,
on: &OutputNode,
texture: &Rc<dyn GfxTexture>,
cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
@ -332,10 +336,12 @@ impl JayScreencast {
resv,
acquire_sync,
release_sync,
cd,
&buffer.fb,
AcquireSync::Implicit,
ReleaseSync::Implicit,
Transform::None,
self.client.state.color_manager.srgb_srgb(),
on.global.pos.get(),
render_hardware_cursors,
x_off,

View file

@ -1,6 +1,7 @@
use {
crate::{
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage},
cmm::cmm_description::{ColorDescription, LinearColorDescription},
cpu_worker::CpuWorker,
format::{ARGB8888, Format, XRGB8888},
gfx_api::{
@ -391,10 +392,13 @@ impl GfxFramebuffer for TestGfxFb {
self: Rc<Self>,
_acquire_sync: AcquireSync,
_release_sync: ReleaseSync,
_cd: &Rc<ColorDescription>,
ops: &[GfxApiOpt],
clear: Option<&Color>,
_clear_cd: &Rc<LinearColorDescription>,
_region: &Region,
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
_blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
let points = rect.to_points();

View file

@ -10,6 +10,7 @@ use {
crate::{
async_engine::AsyncEngine,
cli::GlobalArgs,
cmm::cmm_manager::ColorManager,
dbus::{
BUS_DEST, BUS_PATH, DBUS_NAME_FLAG_DO_NOT_QUEUE, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER,
Dbus, DbusSocket,
@ -207,6 +208,7 @@ async fn run_async(
render_ctxs: Default::default(),
dma_buf_ids: Default::default(),
pw_con: pw_con.as_ref().map(|c| c.con.clone()),
color_manager: ColorManager::new(),
});
if let Some(pw_con) = &pw_con {
pw_con.con.owner.set(Some(state.clone()));
@ -302,6 +304,7 @@ struct PortalState {
render_ctxs: CopyHashMap<c::dev_t, Weak<PortalRenderCtx>>,
dma_buf_ids: Rc<DmaBufIds>,
pw_con: Option<Rc<PwCon>>,
color_manager: Rc<ColorManager>,
}
impl PortalState {

View file

@ -2,6 +2,7 @@ use {
crate::{
allocator::{BO_USE_RENDERING, BufferObject, BufferUsage},
async_engine::{Phase, SpawnedFuture},
cmm::cmm_manager::ColorManager,
cursor::KnownCursor,
fixed::Fixed,
format::ARGB8888,
@ -59,7 +60,7 @@ pub trait GuiElement {
max_width: f32,
max_height: f32,
) -> (f32, f32);
fn render_at(&self, r: &mut RendererBase, x: f32, y: f32);
fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x: f32, y: f32);
fn child_at(&self, x: f32, y: f32) -> Option<Rc<dyn GuiElement>>;
fn hover_cursor(&self) -> KnownCursor {
@ -190,7 +191,9 @@ impl GuiElement for Button {
(extents.width, extents.height)
}
fn render_at(&self, r: &mut RendererBase, x1: f32, y1: f32) {
fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x1: f32, y1: f32) {
let srgb_srgb = color_manager.srgb_srgb();
let srgb = &srgb_srgb.linear;
let x2 = x1 + self.data.width.get();
let y2 = y1 + self.data.height.get();
let border = self.border.get();
@ -201,7 +204,7 @@ impl GuiElement for Button {
(x1, y1 + border, x1 + border, y2 - border),
(x2 - border, y1 + border, x2, y2 - border),
];
r.fill_boxes_f(&rects, &self.border_color.get());
r.fill_boxes_f(&rects, &self.border_color.get(), srgb);
}
{
let rects = [(x1 + border, y1 + border, x2 - border, y2 - border)];
@ -209,7 +212,7 @@ impl GuiElement for Button {
true => self.bg_color.get(),
false => self.bg_hover_color.get(),
};
r.fill_boxes_f(&rects, &color);
r.fill_boxes_f(&rects, &color, srgb);
}
if let Some(tex) = self.tex.get() {
let (tx, ty) = r.scale_point_f(x1 + self.tex_off_x.get(), y1 + self.tex_off_y.get());
@ -226,6 +229,7 @@ impl GuiElement for Button {
AcquireSync::None,
ReleaseSync::None,
false,
srgb_srgb,
);
}
}
@ -311,7 +315,7 @@ impl GuiElement for Label {
(width as f32 / scale, height as f32 / scale)
}
fn render_at(&self, r: &mut RendererBase, x: f32, y: f32) {
fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x: f32, y: f32) {
if let Some(tex) = self.tex.get() {
let (tx, ty) = r.scale_point_f(x, y);
r.render_texture(
@ -327,6 +331,7 @@ impl GuiElement for Label {
AcquireSync::None,
ReleaseSync::None,
false,
color_manager.srgb_srgb(),
);
}
}
@ -439,9 +444,14 @@ impl GuiElement for Flow {
(w.min(max_width), h.min(max_height))
}
fn render_at(&self, r: &mut RendererBase, x: f32, y: f32) {
fn render_at(&self, color_manager: &ColorManager, r: &mut RendererBase, x: f32, y: f32) {
for element in self.elements.borrow_mut().deref() {
element.render_at(r, x + element.data().x.get(), y + element.data().y.get());
element.render_at(
color_manager,
r,
x + element.data().x.get(),
y + element.data().y.get(),
);
}
}
@ -634,12 +644,15 @@ impl WindowData {
let res = buf.fb.render_custom(
AcquireSync::Implicit,
ReleaseSync::Implicit,
self.dpy.state.color_manager.srgb_srgb(),
self.scale.get(),
Some(&Color::from_gray_srgb(0)),
&self.dpy.state.color_manager.srgb_srgb().linear,
None,
self.dpy.state.color_manager.srgb_linear(),
&mut |r| {
if let Some(content) = self.content.get() {
content.render_at(r, 0.0, 0.0)
content.render_at(&self.dpy.state.color_manager, r, 0.0, 0.0)
}
},
);

View file

@ -1,6 +1,5 @@
use {
crate::{
cmm::cmm_transfer_function::TransferFunction,
gfx_api::{AcquireSync, GfxApiOpt, ReleaseSync, SampleRect},
ifs::wl_surface::{
SurfaceBuffer, WlSurface,
@ -78,6 +77,8 @@ impl Renderer<'_> {
}
let theme = &self.state.theme;
let th = theme.sizes.title_height.get();
let srgb_srgb = self.state.color_manager.srgb_srgb();
let srgb = &srgb_srgb.linear;
if let Some(fs) = fullscreen {
fs.tl_as_node().node_render(self, x, y, None);
} else {
@ -90,26 +91,28 @@ impl Renderer<'_> {
let bar_bg = self.base.scale_rect(bar_bg);
let c = theme.colors.bar_background.get();
self.base
.fill_boxes3(slice::from_ref(&bar_bg), &c, None, x, y, true);
.fill_boxes3(slice::from_ref(&bar_bg), &c, None, srgb, x, y, true);
let rd = output.render_data.borrow_mut();
if let Some(aw) = &rd.active_workspace {
let c = match aw.captured {
true => theme.colors.captured_focused_title_background.get(),
false => theme.colors.focused_title_background.get(),
};
self.base.fill_boxes2(slice::from_ref(&aw.rect), &c, x, y);
self.base
.fill_boxes2(slice::from_ref(&aw.rect), &c, srgb, x, y);
}
let c = theme.colors.separator.get();
self.base
.fill_boxes2(slice::from_ref(&rd.underline), &c, x, y);
.fill_boxes2(slice::from_ref(&rd.underline), &c, srgb, x, y);
let c = theme.colors.unfocused_title_background.get();
self.base.fill_boxes2(&rd.inactive_workspaces, &c, x, y);
self.base
.fill_boxes2(&rd.inactive_workspaces, &c, srgb, x, y);
let c = theme.colors.captured_unfocused_title_background.get();
self.base
.fill_boxes2(&rd.captured_inactive_workspaces, &c, x, y);
.fill_boxes2(&rd.captured_inactive_workspaces, &c, srgb, x, y);
let c = theme.colors.attention_requested_background.get();
self.base
.fill_boxes2(&rd.attention_requested_workspaces, &c, x, y);
.fill_boxes2(&rd.attention_requested_workspaces, &c, srgb, x, y);
let scale = output.global.persistent.scale.get();
for title in &rd.titles {
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
@ -126,6 +129,7 @@ impl Renderer<'_> {
AcquireSync::None,
ReleaseSync::None,
false,
self.state.color_manager.srgb_srgb(),
);
}
if let Some(status) = &rd.status {
@ -144,6 +148,7 @@ impl Renderer<'_> {
AcquireSync::None,
ReleaseSync::None,
false,
srgb_srgb,
);
}
}
@ -182,7 +187,7 @@ impl Renderer<'_> {
if ws.render_highlight.get() > 0 {
let color = self.state.theme.colors.highlight.get();
let bounds = ws.position.get().at_point(x, y + th + 1);
self.base.fill_boxes(&[bounds], &color);
self.base.fill_boxes(&[bounds], &color, srgb);
}
}
}
@ -204,6 +209,7 @@ impl Renderer<'_> {
self.base.fill_boxes(
std::slice::from_ref(&pos.at_point(x, y)),
&Color::from_srgba_straight(20, 20, 20, 255),
&self.state.color_manager.srgb_srgb().linear,
);
if let Some(tex) = placeholder.textures.borrow().get(&self.base.scale) {
if let Some(texture) = tex.texture() {
@ -223,6 +229,7 @@ impl Renderer<'_> {
AcquireSync::None,
ReleaseSync::None,
false,
self.state.color_manager.srgb_srgb(),
);
}
}
@ -231,17 +238,21 @@ impl Renderer<'_> {
pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
{
let srgb_srgb = self.state.color_manager.srgb_srgb();
let srgb = &srgb_srgb.linear;
let rd = container.render_data.borrow_mut();
let c = self.state.theme.colors.unfocused_title_background.get();
self.base.fill_boxes2(&rd.title_rects, &c, x, y);
self.base.fill_boxes2(&rd.title_rects, &c, srgb, x, y);
let c = self.state.theme.colors.focused_title_background.get();
self.base.fill_boxes2(&rd.active_title_rects, &c, x, y);
self.base
.fill_boxes2(&rd.active_title_rects, &c, srgb, x, y);
let c = self.state.theme.colors.attention_requested_background.get();
self.base.fill_boxes2(&rd.attention_title_rects, &c, x, y);
self.base
.fill_boxes2(&rd.attention_title_rects, &c, srgb, x, y);
let c = self.state.theme.colors.separator.get();
self.base.fill_boxes2(&rd.underline_rects, &c, x, y);
self.base.fill_boxes2(&rd.underline_rects, &c, srgb, x, y);
let c = self.state.theme.colors.border.get();
self.base.fill_boxes2(&rd.border_rects, &c, x, y);
self.base.fill_boxes2(&rd.border_rects, &c, srgb, x, y);
if let Some(lar) = &rd.last_active_rect {
let c = self
.state
@ -249,7 +260,8 @@ impl Renderer<'_> {
.colors
.focused_inactive_title_background
.get();
self.base.fill_boxes2(std::slice::from_ref(lar), &c, x, y);
self.base
.fill_boxes2(std::slice::from_ref(lar), &c, srgb, x, y);
}
if let Some(titles) = rd.titles.get(&self.base.scale) {
for title in titles {
@ -269,6 +281,7 @@ impl Renderer<'_> {
AcquireSync::None,
ReleaseSync::None,
false,
srgb_srgb,
);
}
}
@ -341,14 +354,22 @@ impl Renderer<'_> {
};
let color = self.state.theme.colors.highlight.get();
self.base.ops.push(GfxApiOpt::Sync);
self.base
.fill_scaled_boxes(slice::from_ref(bounds), &color, None);
self.base.fill_scaled_boxes(
slice::from_ref(bounds),
&color,
None,
&self.state.color_manager.srgb_srgb().linear,
);
}
pub fn render_highlight(&mut self, rect: &Rect) {
let color = self.state.theme.colors.highlight.get();
self.base.ops.push(GfxApiOpt::Sync);
self.base.fill_boxes(slice::from_ref(rect), &color);
self.base.fill_boxes(
slice::from_ref(rect),
&color,
&self.state.color_manager.srgb_srgb().linear,
);
}
pub fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32, bounds: Option<&Rect>) {
@ -423,6 +444,7 @@ impl Renderer<'_> {
bounds: Option<&Rect>,
) {
let alpha = surface.alpha();
let cd = self.state.color_manager.srgb_srgb();
if let Some(tex) = buffer.buffer.get_texture(surface) {
let mut opaque = surface.opaque();
if !opaque && tex.format().has_alpha {
@ -441,6 +463,7 @@ impl Renderer<'_> {
AcquireSync::Unnecessary,
buffer.release_sync,
opaque,
cd,
);
} else if let Some(color) = &buffer.buffer.color {
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
@ -450,14 +473,15 @@ impl Renderer<'_> {
};
if !rect.is_empty() {
let color = Color::from_u32_premultiplied(
TransferFunction::Srgb,
cd.transfer_function,
color[0],
color[1],
color[2],
color[3],
);
self.base.ops.push(GfxApiOpt::Sync);
self.base.fill_scaled_boxes(&[rect], &color, alpha);
self.base
.fill_scaled_boxes(&[rect], &color, alpha, &cd.linear);
}
}
} else {
@ -489,12 +513,14 @@ impl Renderer<'_> {
Rect::new_sized(x + pos.width() - bw, y + bw, bw, pos.height() - bw).unwrap(),
Rect::new_sized(x + bw, y + pos.height() - bw, pos.width() - 2 * bw, bw).unwrap(),
];
self.base.fill_boxes(&borders, &bc);
let srgb_srgb = self.state.color_manager.srgb_srgb();
let srgb = &srgb_srgb.linear;
self.base.fill_boxes(&borders, &bc, srgb);
let title = [Rect::new_sized(x + bw, y + bw, pos.width() - 2 * bw, th).unwrap()];
self.base.fill_boxes(&title, &tc);
self.base.fill_boxes(&title, &tc, srgb);
let title_underline =
[Rect::new_sized(x + bw, y + bw + th, pos.width() - 2 * bw, 1).unwrap()];
self.base.fill_boxes(&title_underline, &uc);
self.base.fill_boxes(&title_underline, &uc, srgb);
if let Some(title) = floating.title_textures.borrow().get(&self.base.scale) {
if let Some(texture) = title.texture() {
let rect = floating.title_rect.get().move_(x, y);
@ -513,6 +539,7 @@ impl Renderer<'_> {
AcquireSync::None,
ReleaseSync::None,
false,
srgb_srgb,
);
}
}

View file

@ -1,5 +1,6 @@
use {
crate::{
cmm::cmm_description::{ColorDescription, LinearColorDescription},
gfx_api::{
AcquireSync, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture,
ReleaseSync, SampleRect,
@ -64,16 +65,29 @@ impl RendererBase<'_> {
rect
}
pub fn fill_scaled_boxes(&mut self, boxes: &[Rect], color: &Color, alpha: Option<f32>) {
self.fill_boxes3(boxes, color, alpha, 0, 0, true);
pub fn fill_scaled_boxes(
&mut self,
boxes: &[Rect],
color: &Color,
alpha: Option<f32>,
cd: &Rc<LinearColorDescription>,
) {
self.fill_boxes3(boxes, color, alpha, cd, 0, 0, true);
}
pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color) {
self.fill_boxes3(boxes, color, None, 0, 0, false);
pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color, cd: &Rc<LinearColorDescription>) {
self.fill_boxes3(boxes, color, None, cd, 0, 0, false);
}
pub fn fill_boxes2(&mut self, boxes: &[Rect], color: &Color, dx: i32, dy: i32) {
self.fill_boxes3(boxes, color, None, dx, dy, false);
pub fn fill_boxes2(
&mut self,
boxes: &[Rect],
color: &Color,
cd: &Rc<LinearColorDescription>,
dx: i32,
dy: i32,
) {
self.fill_boxes3(boxes, color, None, cd, dx, dy, false);
}
pub fn fill_boxes3(
@ -81,6 +95,7 @@ impl RendererBase<'_> {
boxes: &[Rect],
color: &Color,
alpha: Option<f32>,
cd: &Rc<LinearColorDescription>,
dx: i32,
dy: i32,
scaled: bool,
@ -106,18 +121,25 @@ impl RendererBase<'_> {
),
color: *color,
alpha,
cd: cd.clone(),
}));
}
}
pub fn fill_boxes_f(&mut self, boxes: &[(f32, f32, f32, f32)], color: &Color) {
self.fill_boxes2_f(boxes, color, 0.0, 0.0);
pub fn fill_boxes_f(
&mut self,
boxes: &[(f32, f32, f32, f32)],
color: &Color,
cd: &Rc<LinearColorDescription>,
) {
self.fill_boxes2_f(boxes, color, cd, 0.0, 0.0);
}
pub fn fill_boxes2_f(
&mut self,
boxes: &[(f32, f32, f32, f32)],
color: &Color,
cd: &Rc<LinearColorDescription>,
dx: f32,
dy: f32,
) {
@ -139,6 +161,7 @@ impl RendererBase<'_> {
),
color: *color,
alpha: None,
cd: cd.clone(),
}));
}
}
@ -157,6 +180,7 @@ impl RendererBase<'_> {
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
opaque: bool,
cd: &Rc<ColorDescription>,
) {
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
@ -200,6 +224,7 @@ impl RendererBase<'_> {
acquire_sync,
release_sync,
opaque,
cd: cd.clone(),
}));
}
}

View file

@ -79,6 +79,7 @@ pub fn take_screenshot(
fb.render_node(
AcquireSync::Unnecessary,
ReleaseSync::Implicit,
state.color_manager.srgb_srgb(),
state.root.deref(),
state,
Some(state.root.extents.get()),
@ -89,6 +90,7 @@ pub fn take_screenshot(
false,
Transform::None,
None,
state.color_manager.srgb_linear(),
)?;
let drm = match allocator.drm() {
Some(drm) => Some(drm.dup_render()?.fd().clone()),

View file

@ -11,7 +11,7 @@ use {
cli::RunArgs,
client::{Client, ClientId, Clients, NUM_CACHED_SERIAL_RANGES, SerialRange},
clientmem::ClientMemOffset,
cmm::cmm_manager::ColorManager,
cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager},
compositor::LIBEI_SOCKET,
config::ConfigProxy,
cpu_worker::CpuWorker,
@ -235,7 +235,6 @@ pub struct State {
pub data_control_device_ids: DataControlDeviceIds,
pub workspace_managers: WorkspaceManagerState,
pub color_management_enabled: Cell<bool>,
#[expect(dead_code)]
pub color_manager: Rc<ColorManager>,
}
@ -979,15 +978,18 @@ impl State {
&self,
output: &OutputNode,
fb: &Rc<dyn GfxFramebuffer>,
cd: &Rc<ColorDescription>,
acquire_sync: AcquireSync,
release_sync: ReleaseSync,
tex: &Rc<dyn GfxTexture>,
render_hw_cursor: bool,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> {
let sync_file = fb.render_output(
acquire_sync,
release_sync,
cd,
output,
self,
Some(output.global.pos.get()),
@ -995,10 +997,12 @@ impl State {
render_hw_cursor,
true,
blend_buffer,
blend_cd,
)?;
output.latched(false);
output.perform_screencopies(
tex,
cd,
None,
&AcquireSync::Unnecessary,
ReleaseSync::None,
@ -1016,10 +1020,12 @@ impl State {
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
src_cd: &Rc<ColorDescription>,
target: &Rc<dyn GfxFramebuffer>,
target_acquire_sync: AcquireSync,
target_release_sync: ReleaseSync,
target_transform: Transform,
target_cd: &Rc<ColorDescription>,
position: Rect,
render_hardware_cursors: bool,
x_off: i32,
@ -1053,6 +1059,7 @@ impl State {
acquire_sync.clone(),
release_sync,
false,
src_cd,
);
if render_hardware_cursors {
if let Some(cursor_user_group) = self.cursor_user_group_hardware_cursor.get() {
@ -1069,15 +1076,19 @@ impl State {
target.render(
target_acquire_sync,
target_release_sync,
target_cd,
&ops,
Some(&Color::SOLID_BLACK),
&target_cd.linear,
None,
target_cd,
)
}
pub fn perform_shm_screencopy(
&self,
src: &Rc<dyn GfxTexture>,
src_cd: &Rc<ColorDescription>,
acquire_sync: &AcquireSync,
position: Rect,
x_off: i32,
@ -1108,10 +1119,12 @@ impl State {
None,
acquire_sync,
ReleaseSync::None,
src_cd,
&fb.clone().into_fb(),
AcquireSync::Unnecessary,
ReleaseSync::None,
transform,
self.color_manager.srgb_srgb(),
position,
true,
x_off - capture.rect.x1(),

View file

@ -2,6 +2,7 @@ use {
crate::{
backend::{HardwareCursor, KeyState, Mode},
client::ClientId,
cmm::cmm_description::ColorDescription,
cursor::KnownCursor,
fixed::Fixed,
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
@ -252,6 +253,7 @@ impl OutputNode {
pub fn perform_screencopies(
&self,
tex: &Rc<dyn GfxTexture>,
cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
@ -267,6 +269,7 @@ impl OutputNode {
}
self.perform_wlr_screencopies(
tex,
cd,
resv,
acquire_sync,
release_sync,
@ -279,6 +282,7 @@ impl OutputNode {
sc.copy_texture(
self,
tex,
cd,
resv,
acquire_sync,
release_sync,
@ -292,6 +296,7 @@ impl OutputNode {
sc.copy_texture(
self,
tex,
cd,
resv,
acquire_sync,
release_sync,
@ -306,6 +311,7 @@ impl OutputNode {
pub fn perform_wlr_screencopies(
&self,
tex: &Rc<dyn GfxTexture>,
cd: &Rc<ColorDescription>,
resv: Option<&Rc<dyn BufferResv>>,
acquire_sync: &AcquireSync,
release_sync: ReleaseSync,
@ -337,6 +343,7 @@ impl OutputNode {
WlBufferStorage::Shm { mem, stride } => {
let res = self.state.perform_shm_screencopy(
tex,
cd,
acquire_sync,
self.global.pos.get(),
x_off,
@ -375,10 +382,12 @@ impl OutputNode {
resv,
acquire_sync,
release_sync,
cd,
&fb,
AcquireSync::Implicit,
ReleaseSync::Implicit,
self.global.persistent.transform.get(),
self.state.color_manager.srgb_srgb(),
self.global.pos.get(),
render_hardware_cursors,
x_off - capture.rect.x1(),