diff --git a/src/backend.rs b/src/backend.rs index 8007eb7b..130b17f0 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -69,6 +69,7 @@ pub trait Connector { fn kernel_id(&self) -> ConnectorKernelId; fn event(&self) -> Option; fn on_change(&self, cb: Rc); + fn damage(&self); } #[derive(Debug)] diff --git a/src/backends/dummy.rs b/src/backends/dummy.rs index 954df02f..c8b372c4 100644 --- a/src/backends/dummy.rs +++ b/src/backends/dummy.rs @@ -38,4 +38,8 @@ impl Connector for DummyOutput { fn on_change(&self, _cb: Rc) { // nothing } + + fn damage(&self) { + // nothing + } } diff --git a/src/backends/metal.rs b/src/backends/metal.rs index eb88cfab..7d3a5b5b 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -174,7 +174,7 @@ impl Backend for MetalBackend { if !idle { for device in devices.values() { for connector in device.connectors.values() { - self.present(connector); + connector.schedule_present(); } } } diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index ddedfd7f..5517d408 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -35,6 +35,8 @@ use { }, uapi::c, }; +use crate::async_engine::Phase; +use crate::utils::asyncevent::AsyncEvent; pub struct PendingDrmDevice { pub id: DrmId, @@ -75,12 +77,14 @@ impl Debug for HandleEvents { pub struct MetalDrmDevice { pub dev: Rc, pub connectors: AHashMap>, + pub futures: Vec, } #[derive(Debug)] pub struct MetalConnector { pub id: DrmConnector, pub master: Rc, + pub state: Rc, pub connector_id: ConnectorId, @@ -97,6 +101,9 @@ pub struct MetalConnector { pub buffers: CloneCell>>, pub next_buffer: NumCell, + pub can_present: Cell, + pub has_damage: Cell, + pub connector_type: ConnectorType, pub connector_type_id: u32, @@ -111,6 +118,18 @@ pub struct MetalConnector { pub crtc: CloneCell>>, pub on_change: OnChange, + + pub present_trigger: AsyncEvent, +} + +pub struct ConnectorFutures { + pub present: SpawnedFuture<()>, +} + +impl Debug for ConnectorFutures { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ConnectorFutures").finish_non_exhaustive() + } } #[derive(Default)] @@ -127,6 +146,60 @@ impl Debug for OnChange { } } +impl MetalConnector { + async fn present_loop(self: Rc) { + loop { + self.present_trigger.triggered().await; + self.present(); + } + } + + pub fn schedule_present(&self) { + self.present_trigger.trigger(); + } + + pub fn present(&self) { + let crtc = match self.crtc.get() { + Some(crtc) => crtc, + _ => return, + }; + if !self.has_damage.get() || !self.can_present.get() { + return; + } + if !crtc.active.value.get() { + return; + } + let buffers = match self.buffers.get() { + None => return, + Some(b) => b, + }; + let plane = match self.primary_plane.get() { + Some(p) => p, + _ => return, + }; + let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()]; + if let Some(node) = self.state.root.outputs.get(&self.connector_id) { + buffer + .egl + .render(&*node, &self.state, Some(node.global.pos.get())); + } + let mut changes = self.master.change(); + changes.change_object(plane.id, |c| { + c.change(plane.fb_id, buffer.drm.id().0 as _); + }); + if let Err(e) = changes.commit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, 0) { + match e { + DrmError::Atomic(OsError(c::EACCES)) => { + log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master"); + } + _ => log::error!("Could not set plane framebuffer: {}", ErrorFmt(e)), + } + } + self.can_present.set(false); + self.has_damage.set(false); + } +} + impl Connector for MetalConnector { fn id(&self) -> ConnectorId { self.connector_id @@ -146,6 +219,13 @@ impl Connector for MetalConnector { fn on_change(&self, cb: Rc) { self.on_change.on_change.set(Some(cb)); } + + fn damage(&self) { + self.has_damage.set(true); + if self.can_present.get() { + self.schedule_present(); + } + } } #[derive(Debug)] @@ -202,27 +282,29 @@ pub struct MetalPlane { } fn get_connectors( - state: &State, - dev: &MetalDrmDeviceStatic, + state: &Rc, + dev: &Rc, ids: &[DrmConnector], -) -> Result>, DrmError> { +) -> Result<(AHashMap>, Vec), DrmError> { let mut connectors = AHashMap::new(); + let mut futures = vec![]; for connector in ids { match create_connector(state, *connector, dev) { - Ok(e) => { - connectors.insert(e.id, Rc::new(e)); + Ok((con, fut)) => { + connectors.insert(con.id, con); + futures.push(fut); } Err(e) => return Err(DrmError::CreateConnector(Box::new(e))), } } - Ok(connectors) + Ok((connectors, futures)) } fn create_connector( - state: &State, + state: &Rc, connector: DrmConnector, - dev: &MetalDrmDeviceStatic, -) -> Result { + dev: &Rc, +) -> Result<(Rc, ConnectorFutures), DrmError> { let info = dev.master.get_connector_info(connector, true)?; let mut crtcs = AHashMap::new(); for encoder in info.encoders { @@ -303,9 +385,10 @@ fn create_connector( serial_number = edid.base_block.id_serial_number.to_string(); } } - Ok(MetalConnector { + let slf = Rc::new(MetalConnector { id: connector, master: dev.master.clone(), + state: state.clone(), connector_id: state.connector_ids.next(), crtcs, mode: CloneCell::new(info.modes.first().cloned().map(Rc::new)), @@ -316,6 +399,8 @@ fn create_connector( modes: info.modes, buffers: Default::default(), next_buffer: Default::default(), + can_present: Cell::new(true), + has_damage: Cell::new(true), connector_type, connector_type_id: info.connector_type_id, connection, @@ -326,7 +411,12 @@ fn create_connector( crtc_id: props.get("CRTC_ID")?.map(|v| DrmCrtc(v as _)), crtc: Default::default(), on_change: Default::default(), - }) + present_trigger: Default::default() + }); + let futures = ConnectorFutures { + present: state.eng.spawn2(Phase::Present, slf.clone().present_loop()), + }; + Ok((slf, futures)) } fn create_encoder( @@ -563,9 +653,9 @@ impl MetalBackend { }, }); - let connectors = get_connectors(&self.state, &dev, &resources.connectors)?; + let (connectors, futures) = get_connectors(&self.state, &dev, &resources.connectors)?; - let slf = Rc::new(MetalDrmDevice { dev, connectors }); + let slf = Rc::new(MetalDrmDevice { dev, connectors, futures }); self.init_drm_device(&slf)?; @@ -604,11 +694,11 @@ impl MetalBackend { } } - let handler = self + let drm_handler = self .state .eng .spawn(self.clone().handle_drm_events(slf.clone())); - slf.dev.handle_events.handle_events.set(Some(handler)); + slf.dev.handle_events.handle_events.set(Some(drm_handler)); self.state.set_render_ctx(&egl); @@ -650,7 +740,9 @@ impl MetalBackend { self.init_drm_device(dev)?; for connector in dev.connectors.values() { if connector.primary_plane.get().is_some() { - self.present(connector); + connector.can_present.set(true); + connector.has_damage.set(true); + connector.schedule_present(); } } Ok(()) @@ -702,7 +794,10 @@ impl MetalBackend { Some(c) => c, _ => return, }; - self.present(&connector); + connector.can_present.set(true); + if connector.has_damage.get() { + connector.schedule_present(); + } } fn reset_planes(&self, dev: &MetalDrmDevice, changes: &mut Change) { @@ -999,43 +1094,7 @@ impl MetalBackend { connector.connector_type_id, mode ); - self.present(connector); - } - - pub fn present(&self, connector: &Rc) { - let crtc = match connector.crtc.get() { - Some(crtc) => crtc, - _ => return, - }; - if !crtc.active.value.get() { - return; - } - let buffers = match connector.buffers.get() { - None => return, - Some(b) => b, - }; - let plane = match connector.primary_plane.get() { - Some(p) => p, - _ => return, - }; - let buffer = &buffers[connector.next_buffer.fetch_add(1) % buffers.len()]; - if let Some(node) = self.state.root.outputs.get(&connector.connector_id) { - buffer - .egl - .render(&*node, &self.state, Some(node.global.pos.get())); - } - let mut changes = connector.master.change(); - changes.change_object(plane.id, |c| { - c.change(plane.fb_id, buffer.drm.id().0 as _); - }); - if let Err(e) = changes.commit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, 0) { - match e { - DrmError::Atomic(OsError(c::EACCES)) => { - log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master"); - } - _ => log::error!("Could not set plane framebuffer: {}", ErrorFmt(e)), - } - } + connector.schedule_present(); } } diff --git a/src/backends/x.rs b/src/backends/x.rs index b5829d26..196caab6 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -910,6 +910,10 @@ impl Connector for XOutput { fn on_change(&self, cb: Rc) { self.cb.set(Some(cb)); } + + fn damage(&self) { + // nothing + } } struct XSeat { diff --git a/src/config/handler.rs b/src/config/handler.rs index 3e5cf969..de1c415b 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -9,8 +9,8 @@ use { state::{ConnectorData, DeviceHandlerData, OutputData, State}, tree::{ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase}, utils::{ - copyhashmap::CopyHashMap, debug_fn::debug_fn, errorfmt::ErrorFmt, - numcell::NumCell, stack::Stack, + copyhashmap::CopyHashMap, debug_fn::debug_fn, errorfmt::ErrorFmt, numcell::NumCell, + stack::Stack, }, xkbcommon::{XkbCommonError, XkbKeymap}, }, diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 8106f461..2f323100 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -214,7 +214,12 @@ impl WlSeatGlobal { if old_ws.id == ws.id { return; } - let cn = match tl.tl_data().parent.get().and_then(|p| p.node_into_containing_node()) { + let cn = match tl + .tl_data() + .parent + .get() + .and_then(|p| p.node_into_containing_node()) + { Some(cn) => cn, _ => return, }; @@ -226,7 +231,12 @@ impl WlSeatGlobal { } } if tl.tl_data().is_floating.get() { - self.state.map_floating(tl.clone(), tl.tl_data().float_width.get(), tl.tl_data().float_height.get(), ws); + self.state.map_floating( + tl.clone(), + tl.tl_data().float_width.get(), + tl.tl_data().float_height.get(), + ws, + ); } else { self.state.map_tiled_on(tl, ws); } diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index a7d3d778..0154dc0f 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -426,6 +426,7 @@ impl WlSeatGlobal { } pub(super) fn apply_changes(self: &Rc) { + self.state.damage(); self.pointer_owner.apply_changes(self); self.changes.set(0); } diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 16d63396..44bea82f 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -180,6 +180,7 @@ struct PendingState { opaque_region: Cell>>>, input_region: Cell>>>, frame_request: RefCell>>, + damage: Cell, } #[derive(Default)] @@ -403,6 +404,7 @@ impl WlSurface { fn damage(&self, parser: MsgParser<'_, '_>) -> Result<(), DamageError> { let _req: Damage = self.parse(parser)?; + self.pending.damage.set(true); Ok(()) } @@ -507,6 +509,7 @@ impl WlSurface { self.calculate_extents(); } ext.post_commit(); + self.client.state.damage(); Ok(()) } @@ -531,6 +534,7 @@ impl WlSurface { fn damage_buffer(&self, parser: MsgParser<'_, '_>) -> Result<(), DamageBufferError> { let _req: DamageBuffer = self.parse(parser)?; + self.pending.damage.set(true); Ok(()) } diff --git a/src/state.rs b/src/state.rs index b248a063..37459ea3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,3 +1,4 @@ +use std::fmt::{Debug, Formatter}; use { crate::{ async_engine::{AsyncEngine, SpawnedFuture}, @@ -94,6 +95,12 @@ pub struct State { pub run_toplevel: Rc, } +impl Debug for State { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("State").finish_non_exhaustive() + } +} + pub struct XWaylandState { pub enabled: Cell, pub handler: RefCell>>, @@ -379,4 +386,12 @@ impl State { } serial as _ } + + pub fn damage(&self) { + for connector in self.connectors.lock().values() { + if connector.connected.get() { + connector.connector.damage(); + } + } + } } diff --git a/src/tree/container.rs b/src/tree/container.rs index 34c03cbe..6e7a44d5 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -823,7 +823,9 @@ impl ContainerNode { // CASE 1: This is the only child of the container. Replace the container by the child. if self.num_children.get() == 1 { let parent = self.parent.get(); - if !self.toplevel_data.is_fullscreen.get() && parent.cnode_accepts_child(child.tl_as_node()) { + if !self.toplevel_data.is_fullscreen.get() + && parent.cnode_accepts_child(child.tl_as_node()) + { parent.cnode_replace_child(self.deref(), child.clone()); } return; diff --git a/src/tree/placeholder.rs b/src/tree/placeholder.rs index 85daf046..6d4fb4a2 100644 --- a/src/tree/placeholder.rs +++ b/src/tree/placeholder.rs @@ -29,7 +29,11 @@ impl PlaceholderNode { pub fn new_for(state: &Rc, node: Rc) -> Self { Self { id: state.node_ids.next(), - toplevel: ToplevelData::new(state, node.tl_data().title.borrow_mut().clone(), node.node_client()), + toplevel: ToplevelData::new( + state, + node.tl_data().title.borrow_mut().clone(), + node.node_client(), + ), destroyed: Default::default(), texture: Default::default(), } diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index 96fd6f28..e022af1d 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -60,11 +60,7 @@ pub trait ToplevelNode: Node { let data = self.tl_data(); if fullscreen { if let Some(ws) = data.workspace.get() { - data.set_fullscreen2( - &data.state, - self.clone().tl_into_dyn(), - &ws, - ); + data.set_fullscreen2(&data.state, self.clone().tl_into_dyn(), &ws); } } else { data.unset_fullscreen(&data.state, self.clone().tl_into_dyn()); diff --git a/src/utils/asyncevent.rs b/src/utils/asyncevent.rs index 44a139df..2917121e 100644 --- a/src/utils/asyncevent.rs +++ b/src/utils/asyncevent.rs @@ -1,3 +1,4 @@ +use std::fmt::{Debug, Formatter}; use { crate::utils::numcell::NumCell, std::{ @@ -14,6 +15,12 @@ pub struct AsyncEvent { waker: Cell>, } +impl Debug for AsyncEvent { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AsyncEvent").field("triggers", &self.triggers.get()).finish_non_exhaustive() + } +} + impl AsyncEvent { pub fn clear(&self) { self.waker.take();