From da84e9ec27e327ad06bd7db6af42494ab31e7182 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 18 Feb 2024 20:29:23 +0100 Subject: [PATCH] metal: implement direct scanout --- src/backend.rs | 4 + src/backends/metal.rs | 3 + src/backends/metal/video.rs | 333 +++++++++++++++--- src/compositor.rs | 1 + src/drm_feedback.rs | 91 ++++- src/format.rs | 64 ++-- src/gfx_api.rs | 101 +++++- src/gfx_apis/gl/renderer/context.rs | 1 + src/gfx_apis/gl/renderer/image.rs | 1 + src/gfx_apis/gl/renderer/texture.rs | 7 +- src/gfx_apis/vulkan/image.rs | 9 +- src/ifs/wl_output.rs | 56 +-- src/ifs/wl_surface.rs | 34 +- src/ifs/wl_surface/x_surface/xwindow.rs | 4 + .../wl_surface/xdg_surface/xdg_toplevel.rs | 9 +- src/ifs/zwlr_screencopy_frame_v1.rs | 9 + src/ifs/zwlr_screencopy_manager_v1.rs | 1 + src/ifs/zwp_linux_dmabuf_feedback_v1.rs | 35 +- src/ifs/zwp_linux_dmabuf_v1.rs | 18 +- src/renderer.rs | 9 + src/state.rs | 12 +- src/tree/output.rs | 2 +- src/tree/toplevel.rs | 7 +- src/tree/workspace.rs | 10 + src/video/drm.rs | 9 +- 25 files changed, 682 insertions(+), 148 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index 75410dd9..53cf4b0e 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,6 +1,7 @@ use { crate::{ async_engine::SpawnedFuture, + drm_feedback::DrmFeedback, fixed::Fixed, gfx_api::GfxFramebuffer, ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL}, @@ -79,6 +80,9 @@ pub trait Connector { fn damage(&self); fn drm_dev(&self) -> Option; fn set_enabled(&self, enabled: bool); + fn drm_feedback(&self) -> Option> { + None + } } #[derive(Debug)] diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 0d323120..6f18a7d7 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -11,6 +11,7 @@ use { }, backends::metal::video::{MetalDrmDeviceData, MetalRenderContext, PendingDrmDevice}, dbus::{DbusError, SignalHandler}, + drm_feedback::DrmFeedback, gfx_api::GfxError, libinput::{ consts::{ @@ -130,6 +131,7 @@ pub struct MetalBackend { pause_handler: Cell>, resume_handler: Cell>, ctx: CloneCell>>, + default_feedback: CloneCell>>, } impl Debug for MetalBackend { @@ -253,6 +255,7 @@ pub async fn create(state: &Rc) -> Result, MetalError> { pause_handler: Default::default(), resume_handler: Default::default(), ctx: Default::default(), + default_feedback: Default::default(), }); metal.pause_handler.set(Some({ let mtl = metal.clone(); diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 5873eb7a..b0ff64be 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -6,12 +6,14 @@ use { ConnectorKernelId, DrmDeviceId, HardwareCursor, MonitorInfo, }, backends::metal::{MetalBackend, MetalError}, + drm_feedback::DrmFeedback, edid::Descriptor, format::{Format, ARGB8888, XRGB8888}, - gfx_api::{GfxContext, GfxFramebuffer, GfxTexture}, + gfx_api::{BufferPoints, GfxApiOpt, GfxContext, GfxFramebuffer, GfxRenderPass, GfxTexture}, ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC}, renderer::RenderResult, state::State, + tree::OutputNode, udev::UdevDevice, utils::{ asyncevent::AsyncEvent, bitflags::BitflagsExt, clonecell::CloneCell, @@ -19,6 +21,7 @@ use { oserror::OsError, syncqueue::SyncQueue, }, video::{ + dmabuf::DmaBufId, drm::{ drm_mode_modeinfo, Change, ConnectorStatus, ConnectorType, DrmBlob, DrmConnector, DrmCrtc, DrmEncoder, DrmError, DrmEvent, DrmFramebuffer, DrmMaster, DrmModeInfo, @@ -36,13 +39,14 @@ use { jay_config::video::GfxApi, std::{ cell::{Cell, RefCell}, + collections::VecDeque, ffi::CString, fmt::{Debug, Formatter}, mem, ops::DerefMut, - rc::Rc, + rc::{Rc, Weak}, }, - uapi::{c, c::dev_t}, + uapi::c::{self, dev_t}, }; pub struct PendingDrmDevice { @@ -202,6 +206,11 @@ pub struct MetalConnector { pub cursor_buffers: CloneCell>>, pub cursor_front_buffer: NumCell, pub cursor_swap_buffer: Cell, + + pub drm_feedback: CloneCell>>, + pub scanout_buffers: RefCell>, + pub active_framebuffers: RefCell>, + pub direct_scanout_active: Cell, } #[derive(Debug)] @@ -310,11 +319,39 @@ impl Debug for OnChange { } } +#[derive(Debug)] +pub struct DirectScanoutCache { + tex: Weak, + fb: Option>, +} + +#[derive(Debug)] +pub struct DirectScanoutData { + tex: Rc, + fb: Rc, + dma_buf_id: DmaBufId, + acquired: Cell, +} + +impl Drop for DirectScanoutData { + fn drop(&mut self) { + if self.acquired.replace(false) { + self.tex.reservations().release(); + } + } +} + +#[derive(Debug)] +pub struct PresentFb { + fb: Rc, + direct_scanout_data: Option, +} + impl MetalConnector { async fn present_loop(self: Rc) { loop { self.present_trigger.triggered().await; - self.present(); + let _ = self.present(true); } } @@ -351,49 +388,178 @@ impl MetalConnector { self.present_trigger.trigger(); } - pub fn present(&self) { - let crtc = match self.crtc.get() { - Some(crtc) => crtc, - _ => return, - }; - if (!self.has_damage.get() && !self.cursor_changed.get()) || !self.can_present.get() { - return; + fn trim_scanout_cache(&self) { + self.scanout_buffers + .borrow_mut() + .retain(|_, buffer| buffer.tex.strong_count() > 0); + } + + fn prepare_direct_scanout( + &self, + pass: &GfxRenderPass, + plane: &Rc, + ) -> Option { + if pass.ops.len() != 1 { + return None; } - if !crtc.active.value.get() { - return; + let GfxApiOpt::CopyTexture(ct) = &pass.ops[0] else { + return None; + }; + if ct.source != BufferPoints::identity() { + return None; } - let plane = match self.primary_plane.get() { - Some(p) => p, - _ => return, + if ct.target.x1 != 0.0 + || ct.target.y1 != 0.0 + || ct.target.x2 != plane.mode_w.get() as f32 + || ct.target.y2 != plane.mode_h.get() as f32 + { + return None; + } + let Some(dmabuf) = ct.tex.dmabuf() else { + return None; }; - let buffers = match self.buffers.get() { - Some(b) => b, - _ => return, - }; - let cursor = self.cursor_plane.get(); - let mut changes = self.master.change(); - if self.has_damage.get() { - if !self.backend.check_render_context(&self.dev) { - return; + let mut cache = self.scanout_buffers.borrow_mut(); + if let Some(buffer) = cache.get(&dmabuf.id) { + return buffer.fb.as_ref().map(|fb| DirectScanoutData { + tex: buffer.tex.upgrade().unwrap(), + fb: fb.clone(), + dma_buf_id: dmabuf.id, + acquired: Default::default(), + }); + } + let format = 'format: { + if let Some(f) = plane.formats.get(&dmabuf.format.drm) { + break 'format f; } - let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()]; - if let Some(node) = self.state.root.outputs.get(&self.connector_id) { - let mut rr = self.render_result.borrow_mut(); - let render_fb = buffer.render_fb(); - self.state.present_output( - &node, - &render_fb, - &buffer.render_tex, - &mut rr, - !self.cursor_enabled.get(), + if let Some(opaque) = dmabuf.format.opaque { + if let Some(f) = plane.formats.get(&opaque.drm) { + break 'format f; + } + } + return None; + }; + if !format.modifiers.contains(&dmabuf.modifier) { + return None; + } + let data = match self.dev.master.add_fb(dmabuf, Some(format.format)) { + Ok(fb) => Some(DirectScanoutData { + tex: ct.tex.clone(), + fb: Rc::new(fb), + dma_buf_id: dmabuf.id, + acquired: Default::default(), + }), + Err(e) => { + log::debug!( + "Could not import dmabuf for direct scanout: {}", + ErrorFmt(e) ); + None + } + }; + cache.insert( + dmabuf.id, + DirectScanoutCache { + tex: Rc::downgrade(&ct.tex), + fb: data.as_ref().map(|dsd| dsd.fb.clone()), + }, + ); + data + } + + fn prepare_present_fb( + &self, + rr: &mut RenderResult, + buffer: &RenderBuffer, + plane: &Rc, + output: &OutputNode, + try_direct_scanout: bool, + ) -> PresentFb { + self.trim_scanout_cache(); + let buffer_fb = buffer.render_fb(); + let render_hw_cursor = !self.cursor_enabled.get(); + let pass = buffer_fb.create_render_pass( + output, + &self.state, + Some(output.global.pos.get()), + Some(rr), + output.global.preferred_scale.get(), + render_hw_cursor, + ); + let try_direct_scanout = try_direct_scanout && !output.global.have_shm_screencopies(); + let mut direct_scanout_data = None; + if try_direct_scanout { + if let Some(dsd) = self.prepare_direct_scanout(&pass, plane) { + output.perform_screencopies(None, &dsd.tex, !render_hw_cursor); + direct_scanout_data = Some(dsd); + } + } + let direct_scanout_active = direct_scanout_data.is_some(); + if self.direct_scanout_active.replace(direct_scanout_active) != direct_scanout_active { + let change = match direct_scanout_active { + true => "Enabling", + false => "Disabling", + }; + log::debug!("{} direct scanout on {}", change, self.kernel_id()); + } + let fb = match &direct_scanout_data { + None => { + self.next_buffer.fetch_add(1); + buffer_fb.perform_render_pass(pass); if let Some(tex) = &buffer.dev_tex { buffer.dev_fb.copy_texture(tex, 0, 0); } + output.perform_screencopies( + Some(&*buffer_fb), + &buffer.render_tex, + !render_hw_cursor, + ); + buffer.drm.clone() + } + Some(dsd) => dsd.fb.clone(), + }; + PresentFb { + fb, + direct_scanout_data, + } + } + + pub fn present(&self, try_direct_scanout: bool) -> Result<(), ()> { + let crtc = match self.crtc.get() { + Some(crtc) => crtc, + _ => return Ok(()), + }; + if (!self.has_damage.get() && !self.cursor_changed.get()) || !self.can_present.get() { + return Ok(()); + } + if !crtc.active.value.get() { + return Ok(()); + } + let plane = match self.primary_plane.get() { + Some(p) => p, + _ => return Ok(()), + }; + let buffers = match self.buffers.get() { + Some(b) => b, + _ => return Ok(()), + }; + let cursor = self.cursor_plane.get(); + let mut new_fb = None; + let mut changes = self.master.change(); + if self.has_damage.get() { + if !self.backend.check_render_context(&self.dev) { + return Ok(()); + } + if let Some(node) = self.state.root.outputs.get(&self.connector_id) { + let buffer = &buffers[self.next_buffer.get() % buffers.len()]; + let mut rr = self.render_result.borrow_mut(); + let fb = + self.prepare_present_fb(&mut rr, buffer, &plane, &node, try_direct_scanout); + rr.dispatch_frame_requests(); + changes.change_object(plane.id, |c| { + c.change(plane.fb_id, fb.fb.id().0 as _); + }); + new_fb = Some(fb); } - changes.change_object(plane.id, |c| { - c.change(plane.fb_id, buffer.drm.id().0 as _); - }); } if self.cursor_changed.get() && cursor.is_some() { let plane = cursor.unwrap(); @@ -434,12 +600,63 @@ impl MetalConnector { 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)), + _ => 'handle_failure: { + if let Some(fb) = &new_fb { + if let Some(dsd) = &fb.direct_scanout_data { + if self.present(false).is_ok() { + let mut cache = self.scanout_buffers.borrow_mut(); + if let Some(buffer) = cache.remove(&dsd.dma_buf_id) { + cache.insert( + dsd.dma_buf_id, + DirectScanoutCache { + tex: buffer.tex, + fb: None, + }, + ); + } + break 'handle_failure; + } + } + } + log::error!("Could not set plane framebuffer: {}", ErrorFmt(e)); + } } + Err(()) } else { + if let Some(fb) = new_fb { + if let Some(dsd) = &fb.direct_scanout_data { + dsd.tex.reservations().acquire(); + dsd.acquired.set(true); + } + self.active_framebuffers.borrow_mut().push_back(fb); + } self.can_present.set(false); self.has_damage.set(false); self.cursor_changed.set(false); + Ok(()) + } + } + + pub fn update_drm_feedback(&self) { + let fb = self.compute_drm_feedback(); + self.drm_feedback.set(fb); + } + + fn compute_drm_feedback(&self) -> Option> { + let default = self.backend.default_feedback.get()?; + let plane = self.primary_plane.get()?; + let mut formats = vec![]; + for (format, info) in &plane.formats { + for modifier in &info.modifiers { + formats.push((*format, *modifier)); + } + } + match default.for_scanout(&self.state.drm_feedback_ids, self.dev.devnum, &formats) { + Ok(fb) => fb.map(Rc::new), + Err(e) => { + log::error!("Could not compute connector feedback: {}", ErrorFmt(e)); + None + } } } } @@ -488,6 +705,10 @@ impl Connector for MetalConnector { } } } + + fn drm_feedback(&self) -> Option> { + self.drm_feedback.get() + } } #[derive(Debug)] @@ -522,7 +743,7 @@ pub enum PlaneType { #[derive(Debug)] pub struct PlaneFormat { - _format: &'static Format, + format: &'static Format, modifiers: IndexSet, } @@ -538,6 +759,9 @@ pub struct MetalPlane { pub assigned: Cell, + pub mode_w: Cell, + pub mode_h: Cell, + pub crtc_id: MutableProperty, pub crtc_x: MutableProperty, pub crtc_y: MutableProperty, @@ -611,6 +835,10 @@ fn create_connector( cursor_changed: Cell::new(false), cursor_front_buffer: Default::default(), cursor_swap_buffer: Cell::new(false), + drm_feedback: Default::default(), + scanout_buffers: Default::default(), + active_framebuffers: Default::default(), + direct_scanout_active: Cell::new(false), }); let futures = ConnectorFutures { present: backend @@ -786,7 +1014,7 @@ fn create_plane(plane: DrmPlane, master: &Rc) -> Result) -> Result) -> Result return, }; connector.can_present.set(true); + { + let mut scanout_buffers = connector.active_framebuffers.borrow_mut(); + while scanout_buffers.len() > 1 { + scanout_buffers.pop_front(); + } + } if connector.has_damage.get() || connector.cursor_changed.get() { connector.schedule_present(); } @@ -1465,6 +1701,14 @@ impl MetalBackend { } let ctx = dev.ctx.get(); self.state.set_render_ctx(Some(ctx.gfx.clone())); + let fb = match DrmFeedback::new(&self.state.drm_feedback_ids, &*ctx.gfx) { + Ok(fb) => Some(Rc::new(fb)), + Err(e) => { + log::error!("Could not create feedback for new context: {}", ErrorFmt(e)); + None + } + }; + self.default_feedback.set(fb); self.ctx.set(Some(ctx)); for dev in self.device_holder.drm_devices.lock().values() { self.re_init_drm_device(&dev); @@ -1562,6 +1806,7 @@ impl MetalBackend { continue; } connector.send_hardware_cursor(); + connector.update_drm_feedback(); } Ok(()) } @@ -1704,7 +1949,7 @@ impl MetalBackend { Ok(b) => b, Err(e) => return Err(MetalError::ScanoutBuffer(e)), }; - let drm_fb = match dev.master.add_fb(dev_bo.dmabuf()) { + let drm_fb = match dev.master.add_fb(dev_bo.dmabuf(), None) { Ok(fb) => Rc::new(fb), Err(e) => return Err(MetalError::Framebuffer(e)), }; @@ -1917,6 +2162,8 @@ impl MetalBackend { c.change(primary_plane.src_h.id, (mode.vdisplay as u64) << 16); }); primary_plane.assigned.set(true); + primary_plane.mode_w.set(mode.hdisplay as _); + primary_plane.mode_h.set(mode.vdisplay as _); primary_plane.crtc_id.value.set(crtc.id); primary_plane.crtc_x.value.set(0); primary_plane.crtc_y.value.set(0); diff --git a/src/compositor.rs b/src/compositor.rs index 37bacd32..291b64d8 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -202,6 +202,7 @@ fn start_compositor2( activation_tokens: Default::default(), toplevel_lists: Default::default(), dma_buf_ids: Default::default(), + drm_feedback_ids: Default::default(), }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); diff --git a/src/drm_feedback.rs b/src/drm_feedback.rs index 381ab2c0..6a1251b2 100644 --- a/src/drm_feedback.rs +++ b/src/drm_feedback.rs @@ -1,24 +1,45 @@ use { - crate::{gfx_api::GfxContext, utils::oserror::OsError}, + crate::{gfx_api::GfxContext, utils::oserror::OsError, video::Modifier}, + ahash::AHashMap, byteorder::{NativeEndian, WriteBytesExt}, std::{io::Write, rc::Rc}, thiserror::Error, uapi::{c, OwnedFd}, }; -pub struct DrmFeedback { +linear_ids!(DrmFeedbackIds, DrmFeedbackId); + +#[derive(Debug)] +pub struct DrmFeedbackShared { pub fd: Rc, pub size: usize, - pub indices: Vec, pub main_device: c::dev_t, + pub indices: AHashMap<(u32, Modifier), u16>, +} + +#[derive(Debug)] +pub struct DrmFeedback { + pub id: DrmFeedbackId, + pub shared: Rc, + pub tranches: Vec, +} + +#[derive(Clone, Debug)] +pub struct DrmFeedbackTranche { + pub device: c::dev_t, + pub indices: Vec, + pub scanout: bool, } impl DrmFeedback { - pub fn new(ctx: &dyn GfxContext) -> Result { - let dev_t = uapi::fstat(ctx.gbm().drm.raw()) + pub fn new( + ids: &DrmFeedbackIds, + render_ctx: &dyn GfxContext, + ) -> Result { + let main_device = uapi::fstat(render_ctx.gbm().drm.raw()) .map_err(OsError::from)? .st_rdev; - let data = create_fd_data(ctx); + let (data, index_map) = create_fd_data(render_ctx); let mut memfd = uapi::memfd_create("drm_feedback", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap(); memfd.write_all(&data).unwrap(); @@ -28,27 +49,69 @@ impl DrmFeedback { c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE, ) .unwrap(); - let num_indices = data.len() / 16; - let indices = (0..num_indices).map(|v| v as u16).collect(); Ok(Self { - fd: Rc::new(memfd), - size: data.len(), - indices, - main_device: dev_t, + id: ids.next(), + tranches: vec![DrmFeedbackTranche { + device: main_device, + indices: (0..index_map.len()).map(|v| v as u16).collect(), + scanout: false, + }], + shared: Rc::new(DrmFeedbackShared { + fd: Rc::new(memfd), + size: data.len(), + main_device, + indices: index_map, + }), }) } + + pub fn for_scanout( + &self, + ids: &DrmFeedbackIds, + devnum: c::dev_t, + formats: &[(u32, Modifier)], + ) -> Result, DrmFeedbackError> { + let mut tranches = vec![]; + { + let mut indices = vec![]; + for (format, modifier) in formats { + if let Some(idx) = self.shared.indices.get(&(*format, *modifier)) { + indices.push(*idx); + } + } + if indices.len() > 0 { + tranches.push(DrmFeedbackTranche { + device: devnum, + indices, + scanout: true, + }); + } else { + return Ok(None); + } + } + tranches.extend(self.tranches.iter().cloned()); + Ok(Some(Self { + id: ids.next(), + shared: self.shared.clone(), + tranches, + })) + } } -fn create_fd_data(ctx: &dyn GfxContext) -> Vec { +fn create_fd_data(ctx: &dyn GfxContext) -> (Vec, AHashMap<(u32, Modifier), u16>) { let mut vec = vec![]; + let mut map = AHashMap::new(); + let mut pos = 0; for (format, info) in &*ctx.formats() { for modifier in &info.read_modifiers { vec.write_u32::(*format).unwrap(); vec.write_u32::(0).unwrap(); vec.write_u64::(*modifier).unwrap(); + map.insert((*format, *modifier), pos); + pos += 1; } } - vec + (vec, map) } #[derive(Debug, Error)] diff --git a/src/format.rs b/src/format.rs index f59978fc..6cec1995 100644 --- a/src/format.rs +++ b/src/format.rs @@ -26,6 +26,7 @@ pub struct Format { pub has_alpha: bool, pub shm_supported: bool, pub pipewire: SpaVideoFormat, + pub opaque: Option<&'static Format>, } impl PartialEq for Format { @@ -87,7 +88,6 @@ pub fn map_wayland_format_id(id: u32) -> u32 { } } -#[allow(dead_code)] pub static ARGB8888: &Format = &Format { name: "argb8888", bpp: 4, @@ -100,6 +100,7 @@ pub static ARGB8888: &Format = &Format { has_alpha: true, shm_supported: true, pipewire: SPA_VIDEO_FORMAT_BGRA, + opaque: Some(XRGB8888), }; pub static XRGB8888: &Format = &Format { @@ -114,38 +115,43 @@ pub static XRGB8888: &Format = &Format { has_alpha: false, shm_supported: true, pipewire: SPA_VIDEO_FORMAT_BGRx, + opaque: None, +}; + +static ABGR8888: &Format = &Format { + name: "abgr8888", + bpp: 4, + gl_format: GL_RGBA, + gl_type: GL_UNSIGNED_BYTE, + vk_format: vk::Format::R8G8B8A8_UNORM, + drm: fourcc_code('A', 'B', '2', '4'), + wl_id: None, + external_only_guess: false, + has_alpha: true, + shm_supported: true, + pipewire: SPA_VIDEO_FORMAT_RGBA, + opaque: Some(XBGR8888), +}; + +static XBGR8888: &Format = &Format { + name: "xbgr8888", + bpp: 4, + gl_format: GL_RGBA, + gl_type: GL_UNSIGNED_BYTE, + vk_format: vk::Format::R8G8B8A8_UNORM, + drm: fourcc_code('X', 'B', '2', '4'), + wl_id: None, + external_only_guess: false, + has_alpha: false, + shm_supported: true, + pipewire: SPA_VIDEO_FORMAT_RGBx, + opaque: None, }; pub static FORMATS: &[Format] = &[ - *ARGB8888, - *XRGB8888, + *ARGB8888, *XRGB8888, *ABGR8888, + *XBGR8888, // *NV12, - Format { - name: "abgr8888", - bpp: 4, - gl_format: GL_RGBA, - gl_type: GL_UNSIGNED_BYTE, - vk_format: vk::Format::R8G8B8A8_UNORM, - drm: fourcc_code('A', 'B', '2', '4'), - wl_id: None, - external_only_guess: false, - has_alpha: true, - shm_supported: true, - pipewire: SPA_VIDEO_FORMAT_RGBA, - }, - Format { - name: "xbgr8888", - bpp: 4, - gl_format: GL_RGBA, - gl_type: GL_UNSIGNED_BYTE, - vk_format: vk::Format::R8G8B8A8_UNORM, - drm: fourcc_code('X', 'B', '2', '4'), - wl_id: None, - external_only_guess: false, - has_alpha: false, - shm_supported: true, - pipewire: SPA_VIDEO_FORMAT_RGBx, - }, // Format { // name: "nv12", // bpp: 1, // wrong but only used for shm diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 1df27473..1d67dd9b 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -9,6 +9,7 @@ use { state::State, theme::Color, tree::Node, + utils::numcell::NumCell, video::{dmabuf::DmaBuf, gbm::GbmDevice, Modifier}, }, ahash::AHashMap, @@ -31,7 +32,12 @@ pub enum GfxApiOpt { CopyTexture(CopyTexture), } -#[derive(Default, Debug, Copy, Clone)] +pub struct GfxRenderPass { + pub ops: Vec, + pub clear: Option, +} + +#[derive(Default, Debug, Copy, Clone, PartialEq)] pub struct BufferPoint { pub x: f32, pub y: f32, @@ -41,9 +47,25 @@ impl BufferPoint { pub fn is_leq_1(&self) -> bool { self.x <= 1.0 && self.y <= 1.0 } + + pub fn top_left() -> Self { + Self { x: 0.0, y: 0.0 } + } + + pub fn top_right() -> Self { + Self { x: 1.0, y: 0.0 } + } + + pub fn bottom_left() -> Self { + Self { x: 0.0, y: 1.0 } + } + + pub fn bottom_right() -> Self { + Self { x: 1.0, y: 1.0 } + } } -#[derive(Default, Debug, Copy, Clone)] +#[derive(Default, Debug, Copy, Clone, PartialEq)] pub struct BufferPoints { pub top_left: BufferPoint, pub top_right: BufferPoint, @@ -79,6 +101,15 @@ impl BufferPoints { && self.bottom_left.is_leq_1() && self.bottom_right.is_leq_1() } + + pub fn identity() -> Self { + Self { + top_left: BufferPoint::top_left(), + top_right: BufferPoint::top_right(), + bottom_left: BufferPoint::bottom_left(), + bottom_right: BufferPoint::bottom_right(), + } + } } #[derive(Debug)] @@ -172,7 +203,7 @@ impl dyn GfxFramebuffer { self.render(ops, clear); } - pub fn render_node( + pub fn create_render_pass( &self, node: &dyn Node, state: &State, @@ -180,7 +211,7 @@ impl dyn GfxFramebuffer { result: Option<&mut RenderResult>, scale: Scale, render_hardware_cursor: bool, - ) { + ) -> GfxRenderPass { let mut ops = self.take_render_ops(); let (width, height) = self.size(); let mut renderer = Renderer { @@ -221,7 +252,34 @@ impl dyn GfxFramebuffer { } } let c = state.theme.colors.background.get(); - self.render(ops, Some(&c)); + GfxRenderPass { + ops, + clear: Some(c), + } + } + + pub fn perform_render_pass(&self, pass: GfxRenderPass) { + self.render(pass.ops, pass.clear.as_ref()) + } + + pub fn render_node( + &self, + node: &dyn Node, + state: &State, + cursor_rect: Option, + result: Option<&mut RenderResult>, + scale: Scale, + render_hardware_cursor: bool, + ) { + let pass = self.create_render_pass( + node, + state, + cursor_rect, + result, + scale, + render_hardware_cursor, + ); + self.perform_render_pass(pass); } pub fn render_hardware_cursor(&self, cursor: &dyn Cursor, state: &State, scale: Scale) { @@ -253,6 +311,38 @@ pub trait GfxImage { fn height(&self) -> i32; } +#[derive(Default)] +pub struct TextureReservations { + reservations: NumCell, + on_release: Cell>>, +} + +impl TextureReservations { + pub fn has_reservation(&self) -> bool { + self.reservations.get() != 0 + } + + pub fn acquire(&self) { + self.reservations.fetch_add(1); + } + + pub fn release(&self) { + if self.reservations.fetch_sub(1) == 1 { + if let Some(cb) = self.on_release.take() { + cb(); + } + } + } + + pub fn on_released(&self, cb: C) { + if self.has_reservation() { + self.on_release.set(Some(Box::new(cb))); + } else { + cb(); + } + } +} + pub trait GfxTexture: Debug { fn size(&self) -> (i32, i32); fn as_any(&self) -> &dyn Any; @@ -268,6 +358,7 @@ pub trait GfxTexture: Debug { shm: &[Cell], ) -> Result<(), GfxError>; fn dmabuf(&self) -> Option<&DmaBuf>; + fn reservations(&self) -> &TextureReservations; } pub trait GfxContext: Debug { diff --git a/src/gfx_apis/gl/renderer/context.rs b/src/gfx_apis/gl/renderer/context.rs index fc6adad6..7e633d7e 100644 --- a/src/gfx_apis/gl/renderer/context.rs +++ b/src/gfx_apis/gl/renderer/context.rs @@ -186,6 +186,7 @@ impl GlRenderContext { Ok(Rc::new(Texture { ctx: self.clone(), gl, + resv: Default::default(), })) } } diff --git a/src/gfx_apis/gl/renderer/image.rs b/src/gfx_apis/gl/renderer/image.rs index 23f3d848..c4a10b6b 100644 --- a/src/gfx_apis/gl/renderer/image.rs +++ b/src/gfx_apis/gl/renderer/image.rs @@ -28,6 +28,7 @@ impl Image { Ok(Rc::new(Texture { ctx: self.ctx.clone(), gl: GlTexture::import_img(&self.ctx.ctx, &self.gl)?, + resv: Default::default(), })) } diff --git a/src/gfx_apis/gl/renderer/texture.rs b/src/gfx_apis/gl/renderer/texture.rs index 8e1130f4..d2f54f05 100644 --- a/src/gfx_apis/gl/renderer/texture.rs +++ b/src/gfx_apis/gl/renderer/texture.rs @@ -1,7 +1,7 @@ use { crate::{ format::Format, - gfx_api::{GfxError, GfxTexture}, + gfx_api::{GfxError, GfxTexture, TextureReservations}, gfx_apis::gl::{gl::texture::GlTexture, renderer::context::GlRenderContext, RenderError}, video::dmabuf::DmaBuf, }, @@ -16,6 +16,7 @@ use { pub struct Texture { pub(in crate::gfx_apis::gl) ctx: Rc, pub(in crate::gfx_apis::gl) gl: GlTexture, + pub(in crate::gfx_apis::gl) resv: TextureReservations, } impl Debug for Texture { @@ -63,4 +64,8 @@ impl GfxTexture for Texture { fn dmabuf(&self) -> Option<&DmaBuf> { self.gl.img.as_ref().map(|i| &i.dmabuf) } + + fn reservations(&self) -> &TextureReservations { + &self.resv + } } diff --git a/src/gfx_apis/vulkan/image.rs b/src/gfx_apis/vulkan/image.rs index 52e1a7d2..88b40025 100644 --- a/src/gfx_apis/vulkan/image.rs +++ b/src/gfx_apis/vulkan/image.rs @@ -1,7 +1,7 @@ use { crate::{ format::Format, - gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture}, + gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, TextureReservations}, gfx_apis::vulkan::{ allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents, renderer::VulkanRenderer, util::OnDrop, VulkanError, @@ -53,6 +53,7 @@ pub struct VulkanImage { pub(super) is_undefined: Cell, pub(super) ty: VulkanImageMemory, pub(super) render_ops: CloneCell>, + pub(super) resv: TextureReservations, } pub enum VulkanImageMemory { @@ -211,6 +212,7 @@ impl VulkanRenderer { is_undefined: Cell::new(true), ty: VulkanImageMemory::Internal(shm), render_ops: Default::default(), + resv: Default::default(), })) } @@ -480,6 +482,7 @@ impl VulkanDmaBufImageTemplate { }), format: self.dmabuf.format, is_undefined: Cell::new(true), + resv: Default::default(), })) } } @@ -580,4 +583,8 @@ impl GfxTexture for VulkanImage { VulkanImageMemory::Internal(_) => None, } } + + fn reservations(&self) -> &TextureReservations { + &self.resv + } } diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index f1e3a70b..f02f6b66 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -202,9 +202,13 @@ impl WlOutputGlobal { Ok(()) } + pub fn have_shm_screencopies(&self) -> bool { + self.pending_captures.iter().any(|c| c.is_shm.get()) + } + pub fn perform_screencopies( &self, - fb: &dyn GfxFramebuffer, + fb: Option<&dyn GfxFramebuffer>, tex: &Rc, render_hardware_cursors: bool, ) { @@ -232,12 +236,13 @@ impl WlOutputGlobal { wl_buffer.storage.borrow_mut().deref() { let acc = mem.access(|mem| { - fb.copy_to_shm( - rect.x1(), - rect.y1(), - rect.width(), - rect.height(), - XRGB8888, + tex.clone().read_pixels( + capture.rect.x1(), + capture.rect.y1(), + capture.rect.width(), + capture.rect.height(), + *stride, + wl_buffer.format, mem, ) }); @@ -249,24 +254,25 @@ impl WlOutputGlobal { } }; if res.is_err() { - let acc = mem.access(|mem| { - tex.clone().read_pixels( - capture.rect.x1(), - capture.rect.y1(), - capture.rect.width(), - capture.rect.height(), - *stride, - wl_buffer.format, - mem, - ) - }); - res = match acc { - Ok(res) => res, - Err(e) => { - capture.client.error(e); - continue; - } - }; + if let Some(fb) = fb { + let acc = mem.access(|mem| { + fb.copy_to_shm( + rect.x1(), + rect.y1(), + rect.width(), + rect.height(), + XRGB8888, + mem, + ) + }); + res = match acc { + Ok(res) => res, + Err(e) => { + capture.client.error(e); + continue; + } + }; + } } if let Err(e) = res { log::warn!("Could not read texture to memory: {}", ErrorFmt(e)); diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 81f68c5d..710d2fa2 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -14,6 +14,7 @@ use { crate::{ backend::KeyState, client::{Client, ClientError, RequestParser}, + drm_feedback::DrmFeedback, fixed::Fixed, gfx_api::{BufferPoint, BufferPoints}, ifs::{ @@ -36,6 +37,7 @@ use { }, wp_content_type_v1::ContentType, wp_presentation_feedback::WpPresentationFeedback, + zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, }, leaks::Tracker, object::Object, @@ -53,7 +55,10 @@ use { numcell::NumCell, smallmap::SmallMap, }, - wire::{wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id}, + wire::{ + wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id, + ZwpLinuxDmabufFeedbackV1Id, + }, xkbcommon::ModifierState, xwayland::XWaylandEvent, }, @@ -259,6 +264,7 @@ pub struct WlSurface { version: u32, pub has_content_type_manager: Cell, content_type: Cell>, + pub drm_feedback: CopyHashMap>, } impl Debug for WlSurface { @@ -411,6 +417,7 @@ impl WlSurface { version, has_content_type_manager: Default::default(), content_type: Default::default(), + drm_feedback: Default::default(), } } @@ -762,7 +769,23 @@ impl WlSurface { if let Some(buffer) = self.buffer.take() { old_raw_size = Some(buffer.rect); if !buffer.destroyed() { - buffer.send_release(); + 'handle_release: { + if let Some(tex) = buffer.texture.get() { + let resv = tex.reservations(); + if resv.has_reservation() { + let buffer = Rc::downgrade(&buffer); + resv.on_released(move || { + if let Some(buffer) = buffer.upgrade() { + if !buffer.destroyed() { + buffer.send_release(); + } + } + }); + break 'handle_release; + } + } + buffer.send_release(); + } } } if let Some(buffer) = buffer_change { @@ -1065,6 +1088,12 @@ impl WlSurface { tl.tl_data().request_attention(tl.tl_as_node()); } } + + pub fn send_feedback(&self, fb: &DrmFeedback) { + for consumer in self.drm_feedback.lock().values() { + consumer.send_feedback(fb); + } + } } object_base! { @@ -1100,6 +1129,7 @@ impl Object for WlSurface { self.fractional_scale.take(); self.tearing_control.take(); self.constraints.clear(); + self.drm_feedback.clear(); } } diff --git a/src/ifs/wl_surface/x_surface/xwindow.rs b/src/ifs/wl_surface/x_surface/xwindow.rs index 960e124c..daa452bc 100644 --- a/src/ifs/wl_surface/x_surface/xwindow.rs +++ b/src/ifs/wl_surface/x_surface/xwindow.rs @@ -424,6 +424,10 @@ impl ToplevelNode for Xwindow { self.display_link.borrow_mut().take(); self.x.surface.destroy_node(); } + + fn tl_scanout_surface(&self) -> Option> { + Some(self.x.surface.clone()) + } } impl StackedNode for Xwindow { diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index c49837cc..60a21d87 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -8,7 +8,10 @@ use { ifs::{ ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, wl_seat::{NodeSeatState, SeatId, WlSeatGlobal}, - wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt}, + wl_surface::{ + xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt}, + WlSurface, + }, }, leaks::Tracker, object::Object, @@ -544,6 +547,10 @@ impl ToplevelNode for XdgToplevel { // self.map_tiled() // } // } + + fn tl_scanout_surface(&self) -> Option> { + Some(self.xdg.surface.clone()) + } } impl XdgSurfaceExt for XdgToplevel { diff --git a/src/ifs/zwlr_screencopy_frame_v1.rs b/src/ifs/zwlr_screencopy_frame_v1.rs index 1b7c1af0..fbc35fe4 100644 --- a/src/ifs/zwlr_screencopy_frame_v1.rs +++ b/src/ifs/zwlr_screencopy_frame_v1.rs @@ -33,6 +33,7 @@ pub struct ZwlrScreencopyFrameV1 { pub with_damage: Cell, pub output_link: Cell>>>, pub buffer: Cell>>, + pub is_shm: Cell, pub version: u32, } @@ -119,6 +120,14 @@ impl ZwlrScreencopyFrameV1 { return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride); } } + let is_shm = match &*buffer.storage.borrow() { + None => false, + Some(s) => match s { + WlBufferStorage::Shm { .. } => true, + WlBufferStorage::Dmabuf(_) => false, + }, + }; + self.is_shm.set(is_shm); self.buffer.set(Some(buffer)); if !with_damage { self.output.connector.connector.damage(); diff --git a/src/ifs/zwlr_screencopy_manager_v1.rs b/src/ifs/zwlr_screencopy_manager_v1.rs index 581d528f..6f712f50 100644 --- a/src/ifs/zwlr_screencopy_manager_v1.rs +++ b/src/ifs/zwlr_screencopy_manager_v1.rs @@ -112,6 +112,7 @@ impl ZwlrScreencopyManagerV1 { with_damage: Cell::new(false), output_link: Cell::new(None), buffer: Cell::new(None), + is_shm: Cell::new(false), version: self.version, }); track!(self.client, frame); diff --git a/src/ifs/zwp_linux_dmabuf_feedback_v1.rs b/src/ifs/zwp_linux_dmabuf_feedback_v1.rs index 37e3ce61..f8828ce2 100644 --- a/src/ifs/zwp_linux_dmabuf_feedback_v1.rs +++ b/src/ifs/zwp_linux_dmabuf_feedback_v1.rs @@ -1,13 +1,14 @@ use { crate::{ client::{Client, ClientError}, - drm_feedback::DrmFeedback, + drm_feedback::{DrmFeedback, DrmFeedbackId}, + ifs::wl_surface::WlSurface, leaks::Tracker, object::Object, utils::buffd::{MsgParser, MsgParserError}, wire::{zwp_linux_dmabuf_feedback_v1::*, ZwpLinuxDmabufFeedbackV1Id}, }, - std::rc::Rc, + std::{cell::Cell, rc::Rc}, thiserror::Error, uapi::{c, OwnedFd}, }; @@ -19,24 +20,37 @@ pub struct ZwpLinuxDmabufFeedbackV1 { pub id: ZwpLinuxDmabufFeedbackV1Id, pub client: Rc, pub tracker: Tracker, + pub last_feedback: Cell>, + pub surface: Option>, } impl ZwpLinuxDmabufFeedbackV1 { - pub fn new(id: ZwpLinuxDmabufFeedbackV1Id, client: &Rc) -> Self { + pub fn new( + id: ZwpLinuxDmabufFeedbackV1Id, + client: &Rc, + surface: Option<&Rc>, + ) -> Self { Self { id, client: client.clone(), tracker: Default::default(), + last_feedback: Default::default(), + surface: surface.cloned(), } } pub fn send_feedback(&self, feedback: &DrmFeedback) { - self.send_format_table(&feedback.fd, feedback.size); - self.send_main_device(feedback.main_device); - self.send_tranche_target_device(feedback.main_device); - self.send_tranche_formats(&feedback.indices); - self.send_tranche_flags(0); - self.send_tranche_done(); + if self.last_feedback.replace(Some(feedback.id)) == Some(feedback.id) { + return; + } + self.send_format_table(&feedback.shared.fd, feedback.shared.size); + self.send_main_device(feedback.shared.main_device); + for tranch in &feedback.tranches { + self.send_tranche_target_device(tranch.device); + self.send_tranche_formats(&tranch.indices); + self.send_tranche_flags(if tranch.scanout { SCANOUT } else { 0 }); + self.send_tranche_done(); + } self.send_done(); } @@ -96,6 +110,9 @@ impl ZwpLinuxDmabufFeedbackV1 { .state .drm_feedback_consumers .remove(&(self.client.id, self.id)); + if let Some(surface) = &self.surface { + surface.drm_feedback.remove(&self.id); + } } } diff --git a/src/ifs/zwp_linux_dmabuf_v1.rs b/src/ifs/zwp_linux_dmabuf_v1.rs index 5c8370a4..bd53f35c 100644 --- a/src/ifs/zwp_linux_dmabuf_v1.rs +++ b/src/ifs/zwp_linux_dmabuf_v1.rs @@ -3,7 +3,7 @@ use { client::{Client, ClientError}, globals::{Global, GlobalName}, ifs::{ - zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, + wl_surface::WlSurface, zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, }, leaks::Tracker, @@ -120,8 +120,9 @@ impl ZwpLinuxDmabufV1 { fn get_feedback( self: &Rc, id: ZwpLinuxDmabufFeedbackV1Id, - ) -> Result<(), ZwpLinuxDmabufV1Error> { - let fb = Rc::new(ZwpLinuxDmabufFeedbackV1::new(id, &self.client)); + surface: Option<&Rc>, + ) -> Result, ZwpLinuxDmabufV1Error> { + let fb = Rc::new(ZwpLinuxDmabufFeedbackV1::new(id, &self.client, surface)); track!(self.client, fb); self.client.add_client_obj(&fb)?; self.client @@ -131,7 +132,7 @@ impl ZwpLinuxDmabufV1 { if let Some(feedback) = self.client.state.drm_feedback.get() { fb.send_feedback(&feedback); } - Ok(()) + Ok(fb) } fn get_default_feedback( @@ -139,7 +140,8 @@ impl ZwpLinuxDmabufV1 { parser: MsgParser<'_, '_>, ) -> Result<(), ZwpLinuxDmabufV1Error> { let req: GetDefaultFeedback = self.client.parse(&**self, parser)?; - self.get_feedback(req.id) + self.get_feedback(req.id, None)?; + Ok(()) } fn get_surface_feedback( @@ -147,8 +149,10 @@ impl ZwpLinuxDmabufV1 { parser: MsgParser<'_, '_>, ) -> Result<(), ZwpLinuxDmabufV1Error> { let req: GetSurfaceFeedback = self.client.parse(&**self, parser)?; - let _surface = self.client.lookup(req.surface)?; - self.get_feedback(req.id) + let surface = self.client.lookup(req.surface)?; + let fb = self.get_feedback(req.id, Some(&surface))?; + surface.drm_feedback.set(req.id, fb); + Ok(()) } } diff --git a/src/renderer.rs b/src/renderer.rs index b3972c01..251e9c90 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -35,6 +35,15 @@ pub struct RenderResult { pub presentation_feedbacks: Vec>, } +impl RenderResult { + pub fn dispatch_frame_requests(&mut self) { + for fr in self.frame_requests.drain(..) { + fr.send_done(); + let _ = fr.client.remove_obj(&*fr); + } + } +} + impl Debug for RenderResult { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("RenderResult").finish_non_exhaustive() diff --git a/src/state.rs b/src/state.rs index a4fa07c9..c3cca7cf 100644 --- a/src/state.rs +++ b/src/state.rs @@ -12,7 +12,7 @@ use { config::ConfigProxy, cursor::{Cursor, ServerCursors}, dbus::Dbus, - drm_feedback::DrmFeedback, + drm_feedback::{DrmFeedback, DrmFeedbackIds}, fixed::Fixed, forker::ForkerProxy, gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture}, @@ -143,6 +143,7 @@ pub struct State { pub toplevel_lists: CopyHashMap<(ClientId, ExtForeignToplevelListV1Id), Rc>, pub dma_buf_ids: DmaBufIds, + pub drm_feedback_ids: DrmFeedbackIds, } // impl Drop for State { @@ -348,7 +349,7 @@ impl State { 'handle_new_feedback: { if let Some(ctx) = &ctx { - let feedback = match DrmFeedback::new(&**ctx) { + let feedback = match DrmFeedback::new(&self.drm_feedback_ids, &**ctx) { Ok(fb) => fb, Err(e) => { log::error!("Could not create new DRM feedback: {}", ErrorFmt(e)); @@ -750,11 +751,8 @@ impl State { output.global.preferred_scale.get(), render_hw_cursor, ); - for fr in rr.frame_requests.drain(..) { - fr.send_done(); - let _ = fr.client.remove_obj(&*fr); - } - output.perform_screencopies(&**fb, tex, !render_hw_cursor); + output.perform_screencopies(Some(&**fb), tex, !render_hw_cursor); + rr.dispatch_frame_requests(); } pub fn perform_screencopy( diff --git a/src/tree/output.rs b/src/tree/output.rs index 98667201..c18c2f87 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -80,7 +80,7 @@ pub async fn output_render_data(state: Rc) { impl OutputNode { pub fn perform_screencopies( &self, - fb: &dyn GfxFramebuffer, + fb: Option<&dyn GfxFramebuffer>, tex: &Rc, render_hardware_cursor: bool, ) { diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index f0e36fb4..25d4d2f2 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -5,6 +5,7 @@ use { ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, wl_seat::{collect_kb_foci, collect_kb_foci2, NodeSeatState, SeatId}, + wl_surface::WlSurface, }, rect::Rect, state::State, @@ -161,6 +162,10 @@ pub trait ToplevelNode: Node { fn tl_last_active_child(self: Rc) -> Rc { self.tl_into_dyn() } + + fn tl_scanout_surface(&self) -> Option> { + None + } } pub struct FullscreenedData { @@ -356,8 +361,8 @@ impl ToplevelData { }); drop(data); self.is_fullscreen.set(true); - ws.set_fullscreen_node(&node); node.tl_set_parent(ws.clone()); + ws.set_fullscreen_node(&node); node.clone().tl_set_workspace(ws); node.clone() .tl_change_extents(&ws.output.get().global.pos.get()); diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index dfc16acb..17f9d29f 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -143,6 +143,11 @@ impl WorkspaceNode { if plane_was_visible { self.plane_set_visible(false); } + if let Some(surface) = node.tl_scanout_surface() { + if let Some(fb) = self.output.get().global.connector.connector.drm_feedback() { + surface.send_feedback(&fb); + } + } } pub fn remove_fullscreen_node(&self) { @@ -151,6 +156,11 @@ impl WorkspaceNode { if self.visible.get() { self.plane_set_visible(true); } + if let Some(surface) = node.tl_scanout_surface() { + if let Some(fb) = surface.client.state.drm_feedback.get() { + surface.send_feedback(&fb); + } + } } } diff --git a/src/video/drm.rs b/src/video/drm.rs index e1e09f68..89fa48c6 100644 --- a/src/video/drm.rs +++ b/src/video/drm.rs @@ -32,6 +32,7 @@ use { use crate::{ backend, + format::Format, io_uring::{IoUring, IoUringError}, utils::{buf::Buf, errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt}, video::{ @@ -306,7 +307,11 @@ impl DrmMaster { } } - pub fn add_fb(self: &Rc, dma: &DmaBuf) -> Result { + pub fn add_fb( + self: &Rc, + dma: &DmaBuf, + format: Option<&Format>, + ) -> Result { let mut modifier = 0; let mut flags = 0; if dma.modifier != INVALID_MODIFIER { @@ -330,7 +335,7 @@ impl DrmMaster { self.raw(), dma.width as _, dma.height as _, - dma.format.drm, + format.unwrap_or(dma.format).drm, flags, handles, strides,