diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 759e60f5..7843b5fe 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -221,7 +221,6 @@ impl Backend for MetalBackend { connector.crtc.take(); connector.on_change.clear(); connector.present_trigger.clear(); - connector.render_result.take(); connector.active_framebuffer.take(); connector.next_framebuffer.take(); } diff --git a/src/backends/metal/present.rs b/src/backends/metal/present.rs index be9f266d..789a4a3f 100644 --- a/src/backends/metal/present.rs +++ b/src/backends/metal/present.rs @@ -174,7 +174,7 @@ impl MetalConnector { self.latch_cursor(&node)?; let cursor_programming = self.compute_cursor_programming(); let latched = self.latch(&node); - node.schedule.latched(); + node.latched(); if cursor_programming.is_none() && latched.is_none() { return Ok(()); @@ -237,9 +237,6 @@ impl MetalConnector { } } if let Err(e) = res { - self.render_result - .borrow_mut() - .discard_presentation_feedback(); if let MetalError::Commit(DrmError::Atomic(OsError(c::EACCES))) = e { log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master"); return Ok(()); @@ -272,6 +269,10 @@ impl MetalConnector { self.can_present.set(false); if let Some(latched) = latched { self.has_damage.fetch_sub(latched.damage); + node.global + .connector + .damaged + .set(self.has_damage.is_not_zero()); } self.cursor_changed.set(false); Ok(()) @@ -485,8 +486,6 @@ impl MetalConnector { if damage == 0 { return None; } - let mut rr = self.render_result.borrow_mut(); - rr.output_id = node.id; let render_hw_cursor = !self.cursor_enabled.get(); let mode = node.global.mode.get(); let pass = create_render_pass( @@ -494,7 +493,6 @@ impl MetalConnector { &**node, &self.state, Some(node.global.pos.get()), - Some(&mut rr), node.global.persistent.scale.get(), true, render_hw_cursor, @@ -502,7 +500,6 @@ impl MetalConnector { node.global.persistent.transform.get(), Some(&self.state.damage_visualizer), ); - rr.dispatch_frame_requests(self.state.now_msec()); Some(Latched { pass, damage }) } diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 3c80bf87..dc84e43f 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -26,7 +26,6 @@ use { wl_output::OutputId, wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC}, }, - renderer::RenderResult, state::State, udev::UdevDevice, utils::{ @@ -447,8 +446,6 @@ pub struct MetalConnector { pub present_trigger: AsyncEvent, - pub render_result: RefCell, - pub cursor_x: Cell, pub cursor_y: Cell, pub cursor_enabled: Cell, @@ -1044,7 +1041,6 @@ fn create_connector( crtc: Default::default(), on_change: Default::default(), present_trigger: Default::default(), - render_result: RefCell::new(Default::default()), cursor_x: Cell::new(0), cursor_y: Cell::new(0), cursor_enabled: Cell::new(false), @@ -1912,6 +1908,7 @@ impl MetalBackend { }; self.update_sequence(&connector, sequence); connector.queue_sequence(); + self.state.vblank(connector.connector_id); let dd = connector.display.borrow(); connector .next_flip_nsec @@ -1978,27 +1975,14 @@ impl MetalBackend { .set(tv_sec as u64 * 1_000_000_000 + tv_usec as u64 * 1000 + dd.refresh as u64); { let global = self.state.root.outputs.get(&connector.connector_id); - let mut rr = connector.render_result.borrow_mut(); if let Some(g) = &global { - let refresh = dd.refresh; - let bindings = g.global.bindings.borrow_mut(); - for fb in rr.presentation_feedbacks.drain(..) { - if let Some(bindings) = bindings.get(&fb.client.id) { - for binding in bindings.values() { - fb.send_sync_output(binding); - } - } - fb.send_presented( - tv_sec as _, - tv_usec * 1000, - refresh, - connector.sequence.get(), - KIND_VSYNC | KIND_HW_COMPLETION, - ); - let _ = fb.client.remove_obj(&*fb); - } - } else { - rr.discard_presentation_feedback(); + g.presented( + tv_sec as _, + tv_usec * 1000, + dd.refresh, + connector.sequence.get(), + KIND_VSYNC | KIND_HW_COMPLETION, + ); } } } diff --git a/src/backends/x.rs b/src/backends/x.rs index c66a46eb..2235a565 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -12,7 +12,6 @@ use { format::XRGB8888, gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture}, ifs::wl_output::OutputId, - renderer::RenderResult, state::State, utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, @@ -239,7 +238,6 @@ pub async fn create(state: &Rc) -> Result, XBackendError> { root, scheduled_present: Default::default(), grab_requests: Default::default(), - render_result: Default::default(), drm_device_id: state.drm_dev_ids.next(), drm_dev, }); @@ -274,7 +272,6 @@ pub struct XBackend { root: u32, scheduled_present: AsyncQueue>, grab_requests: AsyncQueue<(Rc, bool)>, - render_result: RefCell, drm_device_id: DrmDeviceId, drm_dev: dev_t, } @@ -702,6 +699,7 @@ impl XBackend { } else { image.render_on_idle.set(true); } + self.state.vblank(output.id); Ok(()) } @@ -745,13 +743,9 @@ impl XBackend { image.last_serial.set(serial); if let Some(node) = self.state.root.outputs.get(&output.id) { - let res = self.state.present_output( - &node, - &image.fb.get(), - &image.tex.get(), - &mut self.render_result.borrow_mut(), - true, - ); + let res = self + .state + .present_output(&node, &image.fb.get(), &image.tex.get(), true); if let Err(e) = res { log::error!("Could not render screen: {}", ErrorFmt(e)); return; diff --git a/src/compositor.rs b/src/compositor.rs index 1a93f071..3389115d 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -449,10 +449,19 @@ fn create_dummy_output(state: &Rc) { let connector = Rc::new(DummyOutput { id: state.connector_ids.next(), }) as Rc; + let connector_data = Rc::new(ConnectorData { + connector, + handler: Cell::new(None), + connected: Cell::new(true), + name: "Dummy".to_string(), + drm_dev: None, + async_event: Default::default(), + damaged: Cell::new(false), + }); let schedule = Rc::new(OutputSchedule::new( &state.ring, &state.eng, - &connector, + &connector_data, &persistent_state, )); let dummy_output = Rc::new(OutputNode { @@ -460,14 +469,7 @@ fn create_dummy_output(state: &Rc) { global: Rc::new(WlOutputGlobal::new( state.globals.name(), state, - &Rc::new(ConnectorData { - connector, - handler: Cell::new(None), - connected: Cell::new(true), - name: "Dummy".to_string(), - drm_dev: None, - async_event: Default::default(), - }), + &connector_data, Vec::new(), &backend::Mode { width: 0, @@ -502,7 +504,9 @@ fn create_dummy_output(state: &Rc) { screencopies: Default::default(), title_visible: Cell::new(false), schedule, + vblank_event: Default::default(), latch_event: Default::default(), + presentation_event: Default::default(), }); let dummy_workspace = Rc::new(WorkspaceNode { id: state.node_ids.next(), diff --git a/src/damage.rs b/src/damage.rs index 9523415a..ec54877f 100644 --- a/src/damage.rs +++ b/src/damage.rs @@ -56,7 +56,7 @@ pub async fn visualize_damage(state: Rc) { fn damage_all(state: &State) { for connector in state.connectors.lock().values() { if connector.connected.get() { - connector.connector.damage(); + connector.damage(); } } } diff --git a/src/gfx_api.rs b/src/gfx_api.rs index d1a74828..612930a1 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -8,7 +8,7 @@ use { fixed::Fixed, format::Format, rect::{Rect, Region}, - renderer::{renderer_base::RendererBase, RenderResult, Renderer}, + renderer::{renderer_base::RendererBase, Renderer}, scale::Scale, state::State, theme::Color, @@ -345,7 +345,6 @@ impl dyn GfxFramebuffer { node: &dyn Node, state: &State, cursor_rect: Option, - result: Option<&mut RenderResult>, scale: Scale, render_cursor: bool, render_hardware_cursor: bool, @@ -358,7 +357,6 @@ impl dyn GfxFramebuffer { node, state, cursor_rect, - result, scale, render_cursor, render_hardware_cursor, @@ -377,7 +375,6 @@ impl dyn GfxFramebuffer { node: &OutputNode, state: &State, cursor_rect: Option, - result: Option<&mut RenderResult>, scale: Scale, render_hardware_cursor: bool, ) -> Result, GfxError> { @@ -385,7 +382,6 @@ impl dyn GfxFramebuffer { node, state, cursor_rect, - result, scale, true, render_hardware_cursor, @@ -399,7 +395,6 @@ impl dyn GfxFramebuffer { node: &dyn Node, state: &State, cursor_rect: Option, - result: Option<&mut RenderResult>, scale: Scale, render_cursor: bool, render_hardware_cursor: bool, @@ -410,7 +405,6 @@ impl dyn GfxFramebuffer { node, state, cursor_rect, - result, scale, render_cursor, render_hardware_cursor, @@ -432,7 +426,6 @@ impl dyn GfxFramebuffer { let mut renderer = Renderer { base: self.renderer_base(&mut ops, scale, transform), state, - result: None, logical_extents: Rect::new_empty(0, 0), pixel_extents: { let (width, height) = self.logical_size(transform); @@ -638,7 +631,6 @@ pub fn create_render_pass( node: &dyn Node, state: &State, cursor_rect: Option, - result: Option<&mut RenderResult>, scale: Scale, render_cursor: bool, render_hardware_cursor: bool, @@ -650,7 +642,6 @@ pub fn create_render_pass( let mut renderer = Renderer { base: renderer_base(physical_size, &mut ops, scale, transform), state, - result, logical_extents: node.node_absolute_position().at_point(0, 0), pixel_extents: { let (width, height) = logical_size(physical_size, transform); diff --git a/src/ifs/jay_screencast.rs b/src/ifs/jay_screencast.rs index e559876f..8e90ce53 100644 --- a/src/ifs/jay_screencast.rs +++ b/src/ifs/jay_screencast.rs @@ -192,7 +192,6 @@ impl JayScreencast { tl.tl_as_node(), &self.client.state, Some(tl.node_absolute_position()), - None, scale, true, true, diff --git a/src/ifs/wl_compositor.rs b/src/ifs/wl_compositor.rs index c7575663..4d0a1ebc 100644 --- a/src/ifs/wl_compositor.rs +++ b/src/ifs/wl_compositor.rs @@ -50,7 +50,7 @@ impl WlCompositorRequestHandler for WlCompositor { type Error = WlCompositorError; fn create_surface(&self, req: CreateSurface, _slf: &Rc) -> Result<(), Self::Error> { - let surface = Rc::new(WlSurface::new(req.id, &self.client, self.version)); + let surface = Rc::new_cyclic(|slf| WlSurface::new(req.id, &self.client, self.version, slf)); track!(self.client, surface); self.client.add_client_obj(&surface)?; if self.client.is_xwayland { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 80c396d4..478ba507 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -63,13 +63,15 @@ use { rect::{DamageQueue, Rect, Region}, renderer::Renderer, tree::{ - ContainerNode, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, NodeVisitorBase, - OutputNode, OutputNodeId, PlaceholderNode, ToplevelNode, + ContainerNode, FindTreeResult, FoundNode, LatchListener, Node, NodeId, NodeVisitor, + NodeVisitorBase, OutputNode, PlaceholderNode, PresentationListener, ToplevelNode, + VblankListener, }, utils::{ cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap, - double_buffered::DoubleBuffered, errorfmt::ErrorFmt, linkedlist::LinkedList, - numcell::NumCell, smallmap::SmallMap, transform_ext::TransformExt, + double_buffered::DoubleBuffered, errorfmt::ErrorFmt, event_listener::EventListener, + linkedlist::LinkedList, numcell::NumCell, smallmap::SmallMap, + transform_ext::TransformExt, }, video::{ dmabuf::DMA_BUF_SYNC_READ, @@ -91,7 +93,7 @@ use { fmt::{Debug, Formatter}, mem, ops::{Deref, DerefMut}, - rc::Rc, + rc::{Rc, Weak}, }, thiserror::Error, zwp_idle_inhibitor_v1::ZwpIdleInhibitorV1, @@ -274,15 +276,14 @@ pub struct WlSurface { pub buffer_abs_pos: Cell, pub need_extents_update: Cell, pub buffer: CloneCell>>, - buffer_presented: Cell, - buffer_had_frame_request: Cell, pub shm_textures: DoubleBuffered, pub buf_x: NumCell, pub buf_y: NumCell, pub children: RefCell>>, ext: CloneCell>, - pub frame_requests: RefCell>>, - pub presentation_feedback: RefCell>>, + frame_requests: RefCell>>, + presentation_feedback: RefCell>>, + latched_presentation_feedback: RefCell>>, seat_state: NodeSeatState, toplevel: CloneCell>>, cursors: SmallMap, 1>, @@ -306,6 +307,11 @@ pub struct WlSurface { alpha_modifier: CloneCell>>, alpha: Cell>, pub text_input_connections: SmallMap, 1>, + vblank_listener: EventListener, + latch_listener: EventListener, + presentation_listener: EventListener, + commit_version: NumCell, + latched_commit_version: Cell, } impl Debug for WlSurface { @@ -566,7 +572,7 @@ pub struct StackElement { } impl WlSurface { - pub fn new(id: WlSurfaceId, client: &Rc, version: Version) -> Self { + pub fn new(id: WlSurfaceId, client: &Rc, version: Version, slf: &Weak) -> Self { Self { id, node_id: client.state.node_ids.next(), @@ -587,8 +593,6 @@ impl WlSurface { buffer_abs_pos: Cell::new(Default::default()), need_extents_update: Default::default(), buffer: Default::default(), - buffer_presented: Default::default(), - buffer_had_frame_request: Default::default(), shm_textures: DoubleBuffered::new(DamageQueue::new().map(|damage| SurfaceShmTexture { tex: Default::default(), damage, @@ -599,6 +603,7 @@ impl WlSurface { ext: CloneCell::new(client.state.none_surface_ext.clone()), frame_requests: Default::default(), presentation_feedback: Default::default(), + latched_presentation_feedback: Default::default(), seat_state: Default::default(), toplevel: Default::default(), cursors: Default::default(), @@ -622,6 +627,11 @@ impl WlSurface { alpha_modifier: Default::default(), alpha: Default::default(), text_input_connections: Default::default(), + vblank_listener: EventListener::new(slf.clone()), + latch_listener: EventListener::new(slf.clone()), + presentation_listener: EventListener::new(slf.clone()), + commit_version: Default::default(), + latched_commit_version: Default::default(), } } @@ -652,6 +662,9 @@ impl WlSurface { if old.id == output.id { return; } + if self.visible.get() { + self.attach_events_to_output(output); + } output.global.send_enter(self); old.global.send_leave(self); if old.global.persistent.scale.get() != output.global.persistent.scale.get() { @@ -1107,10 +1120,6 @@ impl WlSurface { release, }; self.buffer.set(Some(Rc::new(surface_buffer))); - if pending.has_damage() { - self.buffer_presented.set(false); - self.buffer_had_frame_request.set(false); - } } else { self.reset_shm_textures(); self.buf_x.set(0); @@ -1222,22 +1231,20 @@ impl WlSurface { damage_full = true; } } - let had_frame_requests = self.buffer_had_frame_request.get(); let has_frame_requests = { let frs = &mut *self.frame_requests.borrow_mut(); frs.append(&mut pending.frame_request); frs.is_not_empty() }; - self.buffer_had_frame_request - .set(had_frame_requests || has_frame_requests); - { + let has_presentation_feedback = { let mut fbs = self.presentation_feedback.borrow_mut(); for fb in fbs.drain(..) { fb.send_discarded(); let _ = self.client.remove_obj(&*fb); } mem::swap(fbs.deref_mut(), &mut pending.presentation_feedback); - } + fbs.is_not_empty() + }; { if let Some(region) = pending.input_region.take() { self.input_region.set(region); @@ -1278,6 +1285,13 @@ impl WlSurface { } self.ext.get().after_apply_commit(); if self.visible.get() { + let output = self.output.get(); + if has_frame_requests { + self.vblank_listener.attach(&output.vblank_event); + } + if has_presentation_feedback { + self.latch_listener.attach(&output.latch_event); + } if damage_full { let mut damage = buffer_abs_pos .with_size(max_surface_size.0, max_surface_size.1) @@ -1286,35 +1300,16 @@ impl WlSurface { damage = damage.intersect(tl.node_absolute_position()); } self.client.state.damage(damage); - } else if self.buffer_presented.get() { - // If the currently attached buffer has already been fully presented ... - if has_frame_requests { - // ... and there are new frame requests ... - if had_frame_requests { - // ... and we've already dispatched frame requests for that buffer, - // then schedule new presentation of the primary output and - // unset the buffer_presented flag. This is for clients that - // send frame requests at an uncapped rate and expect the - // compositor to dispatch frame requests at the monitor - // refresh rate (e.g. firefox). This is very inefficient and - // disables VRR from working correctly. But since only firefox - // is affected, I'm ok with this. - let rect = self.output.get().global.pos.get(); - self.client.state.damage(rect); - self.buffer_presented.set(false); - } else { - // ... and this is the first commit that attaches a frame request - // for that buffer, then dispatch the frame requests - // immediately. - let now = self.client.state.now_msec() as _; - for fr in self.frame_requests.borrow_mut().drain(..) { - fr.send_done(now); - let _ = fr.client.remove_obj(&*fr); - } - } - } - } else { + } else if pending.has_damage() { self.apply_damage(pending); + if has_frame_requests { + output.global.connector.damage(); + } + } else if has_frame_requests && output.schedule.vrr_enabled() { + // Frame requests must be dispatched at the highest possible frame rate. + // Therefore we must trigger a vsync of the output as soon as possible. + let rect = output.global.pos.get(); + self.client.state.damage(rect); } } pending.buffer_damage.clear(); @@ -1327,6 +1322,7 @@ impl WlSurface { } } } + self.commit_version.fetch_add(1); Ok(()) } @@ -1469,10 +1465,18 @@ impl WlSurface { } } + fn attach_events_to_output(&self, output: &OutputNode) { + self.vblank_listener.attach(&output.vblank_event); + self.latch_listener.attach(&output.latch_event); + } + pub fn set_visible(&self, visible: bool) { if self.visible.replace(visible) == visible { return; } + if visible { + self.attach_events_to_output(&self.output.get()); + } for (_, inhibitor) in &self.idle_inhibitors { if visible { inhibitor.activate(); @@ -1491,12 +1495,6 @@ impl WlSurface { self.seat_state.set_visible(self, visible); } - pub fn presented(&self, on: OutputNodeId) { - if on == self.output.get().id { - self.buffer_presented.set(true); - } - } - pub fn detach_node(&self, set_invisible: bool) { for (_, constraint) in &self.constraints { constraint.deactivate(); @@ -1586,6 +1584,7 @@ impl Object for WlSurface { self.idle_inhibitors.clear(); mem::take(self.pending.borrow_mut().deref_mut()); self.presentation_feedback.borrow_mut().clear(); + self.latched_presentation_feedback.borrow_mut().clear(); self.viewporter.take(); self.fractional_scale.take(); self.tearing_control.take(); @@ -2053,3 +2052,62 @@ impl DamageMatrix { } } } + +impl VblankListener for WlSurface { + fn after_vblank(self: Rc) { + if self.visible.get() { + let now = self.client.state.now_usec(); + for fr in self.frame_requests.borrow_mut().drain(..) { + fr.send_done(now as _); + let _ = fr.client.remove_obj(&*fr); + } + } + self.vblank_listener.detach(); + } +} + +impl LatchListener for WlSurface { + fn after_latch(self: Rc) { + if self.visible.get() { + if self.latched_commit_version.get() < self.commit_version.get() { + let latched = &mut *self.latched_presentation_feedback.borrow_mut(); + for pf in latched.drain(..) { + pf.send_discarded(); + let _ = pf.client.remove_obj(&*pf); + } + latched.append(&mut self.presentation_feedback.borrow_mut()); + if latched.is_not_empty() { + self.presentation_listener + .attach(&self.output.get().presentation_event); + } + self.latched_commit_version.set(self.commit_version.get()); + } + } + self.latch_listener.detach(); + } +} + +impl PresentationListener for WlSurface { + fn presented( + self: Rc, + output: &OutputNode, + tv_sec: u64, + tv_nsec: u32, + refresh: u32, + seq: u64, + flags: u32, + ) { + let bindings = output.global.bindings.borrow(); + let bindings = bindings.get(&self.client.id); + for pf in self.latched_presentation_feedback.borrow_mut().drain(..) { + if let Some(bindings) = bindings { + for binding in bindings.values() { + pf.send_sync_output(binding); + } + } + pf.send_presented(tv_sec, tv_nsec, refresh, seq, flags); + let _ = pf.client.remove_obj(&*pf); + } + self.presentation_listener.detach(); + } +} diff --git a/src/ifs/wl_surface/cursor.rs b/src/ifs/wl_surface/cursor.rs index 00163a0b..d9007dd1 100644 --- a/src/ifs/wl_surface/cursor.rs +++ b/src/ifs/wl_surface/cursor.rs @@ -107,6 +107,10 @@ impl Cursor for CursorSurface { fr.send_discarded(); let _ = fr.client.remove_obj(fr.deref()); } + for fr in node.latched_presentation_feedback.borrow_mut().drain(..) { + fr.send_discarded(); + let _ = fr.client.remove_obj(fr.deref()); + } node.node_visit_children(self); } } diff --git a/src/ifs/wl_surface/x_surface/xwindow.rs b/src/ifs/wl_surface/x_surface/xwindow.rs index 43755334..c5968d1b 100644 --- a/src/ifs/wl_surface/x_surface/xwindow.rs +++ b/src/ifs/wl_surface/x_surface/xwindow.rs @@ -423,6 +423,9 @@ impl ToplevelNodeBase for Xwindow { if self.data.info.override_redirect.get() { self.data.state.damage(old); self.data.state.damage(*rect); + let (x, y) = rect.center(); + let output = self.data.state.find_closest_output(x, y).0; + self.x.surface.set_output(&output); } else { self.data .state diff --git a/src/ifs/zwlr_screencopy_frame_v1.rs b/src/ifs/zwlr_screencopy_frame_v1.rs index a304c830..44afe8c5 100644 --- a/src/ifs/zwlr_screencopy_frame_v1.rs +++ b/src/ifs/zwlr_screencopy_frame_v1.rs @@ -117,7 +117,7 @@ impl ZwlrScreencopyFrameV1 { self.buffer.set(Some(buffer)); if !with_damage { if let Some(global) = self.output.get() { - global.connector.connector.damage(); + global.connector.damage(); } } self.with_damage.set(with_damage); diff --git a/src/it/test_utils.rs b/src/it/test_utils.rs index cb047de7..5b2820aa 100644 --- a/src/it/test_utils.rs +++ b/src/it/test_utils.rs @@ -2,7 +2,6 @@ pub mod test_container_node_ext; pub mod test_expected_event; pub mod test_object_ext; pub mod test_ouput_node_ext; -pub mod test_rect_ext; pub mod test_surface_ext; pub mod test_toplevel_node_ext; pub mod test_window; diff --git a/src/it/test_utils/test_rect_ext.rs b/src/it/test_utils/test_rect_ext.rs deleted file mode 100644 index dac41c91..00000000 --- a/src/it/test_utils/test_rect_ext.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::rect::Rect; - -pub trait TestRectExt { - fn center(&self) -> (i32, i32); -} - -impl TestRectExt for Rect { - fn center(&self) -> (i32, i32) { - ((self.x1() + self.x2()) / 2, (self.y1() + self.y2()) / 2) - } -} diff --git a/src/it/test_utils/test_toplevel_node_ext.rs b/src/it/test_utils/test_toplevel_node_ext.rs index 80f679a1..208fd456 100644 --- a/src/it/test_utils/test_toplevel_node_ext.rs +++ b/src/it/test_utils/test_toplevel_node_ext.rs @@ -1,4 +1,4 @@ -use crate::{it::test_utils::test_rect_ext::TestRectExt, tree::ToplevelNode}; +use crate::tree::ToplevelNode; pub trait TestToplevelNodeExt { fn center(&self) -> (i32, i32); diff --git a/src/it/tests/t0025_dnd_focus_change.rs b/src/it/tests/t0025_dnd_focus_change.rs index dbc87a69..14315027 100644 --- a/src/it/tests/t0025_dnd_focus_change.rs +++ b/src/it/tests/t0025_dnd_focus_change.rs @@ -1,7 +1,7 @@ use { crate::{ ifs::wl_seat::BTN_LEFT, - it::{test_error::TestResult, test_utils::test_rect_ext::TestRectExt, testrun::TestRun}, + it::{test_error::TestResult, testrun::TestRun}, tree::Node, }, std::rc::Rc, diff --git a/src/it/tests/t0027_input_region.rs b/src/it/tests/t0027_input_region.rs index 2411a40c..311dee12 100644 --- a/src/it/tests/t0027_input_region.rs +++ b/src/it/tests/t0027_input_region.rs @@ -1,6 +1,6 @@ use { crate::{ - it::{test_error::TestResult, test_utils::test_rect_ext::TestRectExt, testrun::TestRun}, + it::{test_error::TestResult, testrun::TestRun}, tree::Node, }, std::rc::Rc, diff --git a/src/output_schedule.rs b/src/output_schedule.rs index bb6e9da4..df83d6af 100644 --- a/src/output_schedule.rs +++ b/src/output_schedule.rs @@ -1,9 +1,10 @@ use { crate::{ async_engine::AsyncEngine, - backend::{Connector, HardwareCursor}, + backend::HardwareCursor, ifs::wl_output::PersistentOutputState, io_uring::{IoUring, IoUringError}, + state::ConnectorData, utils::{ asyncevent::AsyncEvent, cell_ext::CellExt, clonecell::CloneCell, errorfmt::ErrorFmt, numcell::NumCell, @@ -18,7 +19,7 @@ pub struct OutputSchedule { changed: AsyncEvent, run: Cell, - connector: Rc, + connector: Rc, hardware_cursor: CloneCell>>, persistent: Rc, @@ -42,7 +43,7 @@ impl OutputSchedule { pub fn new( ring: &Rc, eng: &Rc, - connector: &Rc, + connector: &Rc, persistent: &Rc, ) -> Self { let slf = Self { diff --git a/src/rect.rs b/src/rect.rs index a8bd1230..14991e37 100644 --- a/src/rect.rs +++ b/src/rect.rs @@ -236,4 +236,11 @@ impl Rect { pub fn size(&self) -> (i32, i32) { (self.width(), self.height()) } + + pub fn center(&self) -> (i32, i32) { + ( + self.raw.x1 + self.width() / 2, + self.raw.y1 + self.height() / 2, + ) + } } diff --git a/src/renderer.rs b/src/renderer.rs index 03c2b87b..b5fb7e16 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,15 +1,11 @@ use { crate::{ gfx_api::{AcquireSync, GfxApiOpt, ReleaseSync, SampleRect}, - ifs::{ - wl_callback::WlCallback, - wl_surface::{ - x_surface::xwindow::Xwindow, - xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface}, - zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, - SurfaceBuffer, WlSurface, - }, - wp_presentation_feedback::WpPresentationFeedback, + ifs::wl_surface::{ + x_surface::xwindow::Xwindow, + xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface}, + zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, + SurfaceBuffer, WlSurface, }, rect::Rect, renderer::renderer_base::RendererBase, @@ -17,62 +13,18 @@ use { state::State, theme::Color, tree::{ - ContainerNode, DisplayNode, FloatNode, OutputNode, OutputNodeId, PlaceholderNode, - ToplevelData, ToplevelNodeBase, WorkspaceNode, + ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData, + ToplevelNodeBase, WorkspaceNode, }, }, - std::{ - fmt::{Debug, Formatter}, - ops::Deref, - rc::Rc, - slice, - }, + std::{ops::Deref, rc::Rc, slice}, }; pub mod renderer_base; -pub struct RenderResult { - pub frame_requests: Vec>, - pub presentation_feedbacks: Vec>, - pub output_id: OutputNodeId, -} - -impl Default for RenderResult { - fn default() -> Self { - Self { - frame_requests: Default::default(), - presentation_feedbacks: Default::default(), - output_id: OutputNodeId::none(), - } - } -} - -impl RenderResult { - pub fn dispatch_frame_requests(&mut self, now: u64) { - for fr in self.frame_requests.drain(..) { - fr.send_done(now as _); - let _ = fr.client.remove_obj(&*fr); - } - } - - pub fn discard_presentation_feedback(&mut self) { - for fb in self.presentation_feedbacks.drain(..) { - fb.send_discarded(); - let _ = fb.client.remove_obj(&*fb); - } - } -} - -impl Debug for RenderResult { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("RenderResult").finish_non_exhaustive() - } -} - pub struct Renderer<'a> { pub base: RendererBase<'a>, pub state: &'a State, - pub result: Option<&'a mut RenderResult>, pub logical_extents: Rect, pub pixel_extents: Rect, } @@ -437,17 +389,6 @@ impl Renderer<'_> { } else { self.render_buffer(surface, &buffer, alpha, x, y, *tpoints, size, bounds); } - if let Some(result) = self.result.as_deref_mut() { - { - let mut fr = surface.frame_requests.borrow_mut(); - result.frame_requests.extend(fr.drain(..)); - } - { - let mut fbs = surface.presentation_feedback.borrow_mut(); - result.presentation_feedbacks.extend(fbs.drain(..)); - } - surface.presented(result.output_id); - } } pub fn render_buffer( diff --git a/src/screenshoter.rs b/src/screenshoter.rs index 4fb7221e..eebf9da9 100644 --- a/src/screenshoter.rs +++ b/src/screenshoter.rs @@ -80,7 +80,6 @@ pub fn take_screenshot( state.root.deref(), state, Some(state.root.extents.get()), - None, Scale::from_int(1), include_cursor, true, diff --git a/src/state.rs b/src/state.rs index e5669e04..be74949a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -63,7 +63,7 @@ use { leaks::Tracker, logger::Logger, rect::Rect, - renderer::{RenderResult, Renderer}, + renderer::Renderer, scale::Scale, security_context_acceptor::SecurityContextAcceptors, theme::{Color, Theme}, @@ -300,6 +300,7 @@ pub struct ConnectorData { pub name: String, pub drm_dev: Option>, pub async_event: Rc, + pub damaged: Cell, } pub struct OutputData { @@ -321,6 +322,14 @@ pub struct DrmDevData { pub lease_global: Rc, } +impl ConnectorData { + pub fn damage(&self) { + if !self.damaged.replace(true) { + self.connector.damage(); + } + } +} + impl DrmDevData { pub fn make_render_device(&self) { log::info!( @@ -761,7 +770,7 @@ impl State { if cursor && output.schedule.defer_cursor_updates() { output.schedule.software_cursor_changed(); } else { - output.global.connector.connector.damage(); + output.global.connector.damage(); } } } @@ -892,19 +901,17 @@ impl State { output: &OutputNode, fb: &Rc, tex: &Rc, - rr: &mut RenderResult, render_hw_cursor: bool, ) -> Result, GfxError> { let sync_file = fb.render_output( output, self, Some(output.global.pos.get()), - Some(rr), output.global.persistent.scale.get(), render_hw_cursor, )?; + output.latched(); output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None); - rr.dispatch_frame_requests(self.now_msec()); Ok(sync_file) } @@ -923,7 +930,6 @@ impl State { let mut renderer = Renderer { base: target.renderer_base(&mut ops, Scale::from_int(1), Transform::None), state: self, - result: None, logical_extents: position.at_point(0, 0), pixel_extents: { let (width, height) = target.logical_size(Transform::None); @@ -1173,6 +1179,12 @@ impl State { self.ei_acceptor_future.take(); } } + + pub fn vblank(&self, connector: ConnectorId) { + if let Some(output) = self.root.outputs.get(&connector) { + output.vblank(); + } + } } #[derive(Debug, Error)] diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 5dbf2290..52d47a14 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -31,6 +31,7 @@ pub fn handle(state: &Rc, connector: &Rc) { name: connector.kernel_id().to_string(), drm_dev: drm_dev.clone(), async_event: Rc::new(AsyncEvent::default()), + damaged: Cell::new(false), }); if let Some(dev) = drm_dev { dev.connectors.set(id, data.clone()); @@ -137,7 +138,7 @@ impl ConnectorHandler { let schedule = Rc::new(OutputSchedule::new( &self.state.ring, &self.state.eng, - &self.data.connector, + &self.data, &desired_state, )); let _schedule = self.state.eng.spawn(schedule.clone().drive()); @@ -176,6 +177,8 @@ impl ConnectorHandler { title_visible: Default::default(), schedule, latch_event: Default::default(), + vblank_event: Default::default(), + presentation_event: Default::default(), }); on.update_visible(); on.update_rects(); diff --git a/src/tree/output.rs b/src/tree/output.rs index 98bc6d6f..f43e961c 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -81,12 +81,30 @@ pub struct OutputNode { pub title_visible: Cell, pub schedule: Rc, pub latch_event: EventSource, + pub vblank_event: EventSource, + pub presentation_event: EventSource, } pub trait LatchListener { fn after_latch(self: Rc); } +pub trait VblankListener { + fn after_vblank(self: Rc); +} + +pub trait PresentationListener { + fn presented( + self: Rc, + output: &OutputNode, + tv_sec: u64, + tv_nsec: u32, + refresh: u32, + seq: u64, + flags: u32, + ); +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub enum PointerType { Seat(SeatId), @@ -106,6 +124,25 @@ pub async fn output_render_data(state: Rc) { } impl OutputNode { + pub fn latched(&self) { + self.schedule.latched(); + for listener in self.latch_event.iter() { + listener.after_latch(); + } + } + + pub fn vblank(&self) { + for listener in self.vblank_event.iter() { + listener.after_vblank(); + } + } + + pub fn presented(&self, tv_sec: u64, tv_nsec: u32, refresh: u32, seq: u64, flags: u32) { + for listener in self.presentation_event.iter() { + listener.presented(self, tv_sec, tv_nsec, refresh, seq, flags); + } + } + pub fn update_exclusive_zones(self: &Rc) { let mut exclusive = ExclusiveSize::default(); for layer in &self.layers { @@ -153,9 +190,6 @@ impl OutputNode { y_off: i32, size: Option<(i32, i32)>, ) { - for listener in self.latch_event.iter() { - listener.after_latch(); - } if let Some(workspace) = self.workspace.get() { if !workspace.may_capture.get() { return;