From 07fb198eb4d65162cae6e70e4b2cff0ab8d6b760 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 18 Feb 2025 16:43:30 +0100 Subject: [PATCH] metal: track per-framebuffer damage --- src/backends/metal/present.rs | 60 +++++++--- src/backends/metal/video.rs | 32 ++++- src/compositor.rs | 2 + src/damage.rs | 150 +++++++++++++++++++++++- src/gfx_api.rs | 27 ++++- src/gfx_apis/gl/renderer/framebuffer.rs | 3 +- src/gfx_apis/vulkan/image.rs | 5 +- src/gfx_apis/vulkan/renderer.rs | 2 + src/ifs/wl_output.rs | 47 +++++++- src/ifs/wl_surface.rs | 112 +----------------- src/it/test_gfx_api.rs | 3 +- src/rect.rs | 5 + src/rect/region.rs | 14 ++- src/state.rs | 3 + src/tasks/connector.rs | 2 + src/tree/output.rs | 8 +- src/utils/transform_ext.rs | 10 ++ 17 files changed, 334 insertions(+), 151 deletions(-) diff --git a/src/backends/metal/present.rs b/src/backends/metal/present.rs index 279f44f0..25d9f3dc 100644 --- a/src/backends/metal/present.rs +++ b/src/backends/metal/present.rs @@ -11,6 +11,7 @@ use { create_render_pass, AcquireSync, BufferResv, GfxApiOpt, GfxRenderPass, GfxTexture, ReleaseSync, SyncFile, }, + rect::Region, theme::Color, time::Time, tracy::FrameName, @@ -30,7 +31,8 @@ use { struct Latched { pass: GfxRenderPass, - damage: u64, + damage_count: u64, + damage: Region, } #[derive(Debug)] @@ -172,24 +174,24 @@ impl MetalConnector { Some(b) => b, _ => return Ok(()), }; + let buffer = &buffers[self.next_buffer.get() % buffers.len()]; if self.has_damage.get() > 0 || self.cursor_damage.get() { node.schedule.commit_cursor(); } self.latch_cursor(&node)?; let cursor_programming = self.compute_cursor_programming(); - let latched = self.latch(&node); + let latched = self.latch(&node, buffer); node.latched(self.try_async_flip()); if cursor_programming.is_none() && latched.is_none() { return Ok(()); } - let buffer = &buffers[self.next_buffer.get() % buffers.len()]; let mut present_fb = None; let mut direct_scanout_id = None; if let Some(latched) = &latched { - let fb = self.prepare_present_fb(buffer, &plane, &latched.pass, true)?; + let fb = self.prepare_present_fb(buffer, &plane, latched, true)?; direct_scanout_id = fb.direct_scanout_data.as_ref().map(|d| d.dma_buf_id); present_fb = Some(fb); } @@ -212,12 +214,8 @@ impl MetalConnector { ); if res.is_err() { if let Some(dsd_id) = direct_scanout_id { - let fb = self.prepare_present_fb( - buffer, - &plane, - &latched.as_ref().unwrap().pass, - false, - )?; + let fb = + self.prepare_present_fb(buffer, &plane, latched.as_ref().unwrap(), false)?; present_fb = Some(fb); self.await_present_fb(present_fb.as_mut()).await; res = self.program_connector( @@ -241,7 +239,14 @@ impl MetalConnector { } } } + let reset_damage = || { + for buffer in &*buffers { + buffer.damage_queue.clear(); + } + buffers[0].damage_full(); + }; if let Err(e) = res { + reset_damage(); 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(()); @@ -265,7 +270,10 @@ impl MetalConnector { self.presentation_is_zero_copy .set(fb.direct_scanout_data.is_some()); if fb.direct_scanout_data.is_none() { + buffer.damage_queue.clear(); self.next_buffer.fetch_add(1); + } else { + reset_damage(); } self.next_framebuffer.set(Some(fb)); } @@ -275,7 +283,7 @@ impl MetalConnector { } self.can_present.set(false); if let Some(latched) = latched { - self.has_damage.fetch_sub(latched.damage); + self.has_damage.fetch_sub(latched.damage_count); } self.cursor_changed.set(false); Ok(()) @@ -487,12 +495,19 @@ impl MetalConnector { Some(programming) } - fn latch(&self, node: &Rc) -> Option { - let damage = self.has_damage.get(); - if damage == 0 { + fn latch(&self, node: &Rc, buffer: &RenderBuffer) -> Option { + let damage_count = self.has_damage.get(); + if damage_count == 0 { return None; } node.global.connector.damaged.set(false); + let damage = { + node.global.add_visualizer_damage(); + let damage = &mut *node.global.connector.damage.borrow_mut(); + buffer.damage_queue.damage(damage); + damage.clear(); + buffer.damage_queue.get() + }; let render_hw_cursor = !self.cursor_enabled.get(); let mode = node.global.mode.get(); let pass = create_render_pass( @@ -508,7 +523,11 @@ impl MetalConnector { node.global.persistent.transform.get(), Some(&self.state.damage_visualizer), ); - Some(Latched { pass, damage }) + Some(Latched { + pass, + damage_count, + damage, + }) } fn trim_scanout_cache(&self) { @@ -692,7 +711,7 @@ impl MetalConnector { &self, buffer: &RenderBuffer, plane: &Rc, - pass: &GfxRenderPass, + latched: &Latched, try_direct_scanout: bool, ) -> Result { self.trim_scanout_cache(); @@ -706,7 +725,7 @@ impl MetalConnector { && self.dev.is_render_device(); let mut direct_scanout_data = None; if try_direct_scanout { - direct_scanout_data = self.prepare_direct_scanout(&pass, plane); + direct_scanout_data = self.prepare_direct_scanout(&latched.pass, plane); } let direct_scanout_active = direct_scanout_data.is_some(); if self.direct_scanout_active.replace(direct_scanout_active) != direct_scanout_active { @@ -723,7 +742,12 @@ impl MetalConnector { None => { let sf = buffer .render_fb() - .perform_render_pass(AcquireSync::Unnecessary, ReleaseSync::Explicit, pass) + .perform_render_pass( + AcquireSync::Unnecessary, + ReleaseSync::Explicit, + &latched.pass, + &latched.damage, + ) .map_err(MetalError::RenderFrame)?; sync_file = buffer.copy_to_dev(sf)?; fb = buffer.drm.clone(); diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 61657522..a2069903 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -25,6 +25,7 @@ use { wl_output::OutputId, wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY}, }, + rect::{DamageQueue, Rect}, state::State, tree::OutputNode, udev::UdevDevice, @@ -2600,12 +2601,26 @@ impl MetalBackend { ctx: &MetalRenderContext, cursor: bool, ) -> Result<[RenderBuffer; N], MetalError> { - let create = - || self.create_scanout_buffer(dev, format, plane_modifiers, width, height, ctx, cursor); + let mut damage_queue = ArrayVec::from(DamageQueue::new::()); + let mut create = || { + self.create_scanout_buffer( + dev, + format, + plane_modifiers, + width, + height, + ctx, + cursor, + damage_queue.pop().unwrap(), + ) + }; let mut array = ArrayVec::<_, N>::new(); for _ in 0..N { array.push(create()?); } + if let Some(buffer) = array.first() { + buffer.damage_full(); + } Ok(array.into_inner().unwrap()) } @@ -2618,6 +2633,7 @@ impl MetalBackend { height: i32, render_ctx: &MetalRenderContext, cursor: bool, + damage_queue: DamageQueue, ) -> Result { let ctx = dev.ctx.get(); let dev_gfx_formats = ctx.gfx.formats(); @@ -2746,7 +2762,8 @@ impl MetalBackend { }; Ok(RenderBuffer { drm: drm_fb, - _dev_bo: dev_bo, + damage_queue, + dev_bo, _render_bo: render_bo, dev_fb, dev_tex, @@ -2970,7 +2987,8 @@ impl MetalBackend { #[derive(Debug)] pub struct RenderBuffer { pub drm: Rc, - pub _dev_bo: GbmBo, + pub damage_queue: DamageQueue, + pub dev_bo: GbmBo, pub _render_bo: Option, // ctx = dev // buffer location = dev @@ -3010,6 +3028,12 @@ impl RenderBuffer { ) .map_err(MetalError::CopyToOutput) } + + pub fn damage_full(&self) { + let dmabuf = self.dev_bo.dmabuf(); + let rect = Rect::new_sized_unchecked(0, 0, dmabuf.width, dmabuf.height); + self.damage_queue.damage(&[rect]); + } } fn modes_equal(a: &DrmModeInfo, b: &DrmModeInfo) -> bool { diff --git a/src/compositor.rs b/src/compositor.rs index 28a9a905..1467b0c0 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -544,7 +544,9 @@ fn create_dummy_output(state: &Rc) { drm_dev: None, async_event: Default::default(), damaged: Cell::new(false), + damage: Default::default(), needs_vblank_emulation: Cell::new(false), + damage_intersect: Default::default(), }); let schedule = Rc::new(OutputSchedule::new( &state.ring, diff --git a/src/damage.rs b/src/damage.rs index ec54877f..24e4bef2 100644 --- a/src/damage.rs +++ b/src/damage.rs @@ -1,14 +1,19 @@ use { crate::{ async_engine::AsyncEngine, + fixed::Fixed, + ifs::wl_output::WlOutputGlobal, rect::{Rect, Region}, renderer::renderer_base::RendererBase, state::State, theme::Color, time::Time, - utils::{asyncevent::AsyncEvent, errorfmt::ErrorFmt, timer::TimerFd}, + utils::{ + asyncevent::AsyncEvent, errorfmt::ErrorFmt, timer::TimerFd, transform_ext::TransformExt, + }, }, isnt::std_1::primitive::IsntSliceExt, + jay_config::video::Transform, std::{ cell::{Cell, RefCell}, collections::VecDeque, @@ -56,6 +61,9 @@ pub async fn visualize_damage(state: Rc) { fn damage_all(state: &State) { for connector in state.connectors.lock().values() { if connector.connected.get() { + let damage = &mut *connector.damage.borrow_mut(); + damage.clear(); + damage.push(connector.damage_intersect.get()); connector.damage(); } } @@ -126,10 +134,7 @@ impl DamageVisualizer { self.color.set(color); } - pub fn render(&self, cursor_rect: &Rect, renderer: &mut RendererBase<'_>) { - if !self.enabled.get() { - return; - } + fn trim(&self) { let now = self.eng.now(); let entries = &mut *self.entries.borrow_mut(); let decay = self.decay.get(); @@ -140,6 +145,16 @@ impl DamageVisualizer { break; } } + } + + pub fn render(&self, cursor_rect: &Rect, renderer: &mut RendererBase<'_>) { + if !self.enabled.get() { + return; + } + self.trim(); + let now = self.eng.now(); + let entries = &*self.entries.borrow(); + let decay = self.decay.get(); let base_color = self.color.get(); let mut used = Region::empty(); let dx = -cursor_rect.x1(); @@ -156,4 +171,129 @@ impl DamageVisualizer { } } } + + pub fn copy_damage(&self, output: &WlOutputGlobal) { + if !self.enabled.get() { + return; + } + self.trim(); + let entries = &*self.entries.borrow(); + let pos = output.pos.get(); + for entry in entries { + if entry.rect.intersects(&pos) { + output.add_damage_area(&entry.rect); + } + } + } +} + +#[derive(Copy, Clone, Debug)] +pub struct DamageMatrix { + transform: Transform, + mx: f64, + my: f64, + dx: f64, + dy: f64, + smear: i32, +} + +impl Default for DamageMatrix { + fn default() -> Self { + Self { + transform: Default::default(), + mx: 1.0, + my: 1.0, + dx: 0.0, + dy: 0.0, + smear: 0, + } + } +} + +impl DamageMatrix { + pub fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect { + let x1 = rect.x1() - self.smear; + let x2 = rect.x2() + self.smear; + let y1 = rect.y1() - self.smear; + let y2 = rect.y2() + self.smear; + let [x1, y1, x2, y2] = match self.transform { + Transform::None => [x1, y1, x2, y2], + Transform::Rotate90 => [-y2, x1, -y1, x2], + Transform::Rotate180 => [-x2, -y2, -x1, -y1], + Transform::Rotate270 => [y1, -x2, y2, -x1], + Transform::Flip => [-x2, y1, -x1, y2], + Transform::FlipRotate90 => [y1, x1, y2, x2], + Transform::FlipRotate180 => [x1, -y2, x2, -y1], + Transform::FlipRotate270 => [-y2, -x2, -y1, -x1], + }; + let x1 = (x1 as f64 * self.mx + self.dx).floor() as i32 + dx; + let y1 = (y1 as f64 * self.my + self.dy).floor() as i32 + dy; + let x2 = (x2 as f64 * self.mx + self.dx).ceil() as i32 + dx; + let y2 = (y2 as f64 * self.my + self.dy).ceil() as i32 + dy; + Rect::new(x1, y1, x2, y2).unwrap() + } + + pub fn new( + transform: Transform, + legacy_scale: i32, + buffer_width: i32, + buffer_height: i32, + viewport: Option<[Fixed; 4]>, + dst_width: i32, + dst_height: i32, + ) -> DamageMatrix { + let mut buffer_width = buffer_width as f64; + let mut buffer_height = buffer_height as f64; + let dst_width = dst_width as f64; + let dst_height = dst_height as f64; + + let mut mx = 1.0; + let mut my = 1.0; + if legacy_scale != 1 { + let scale_inv = 1.0 / (legacy_scale as f64); + mx = scale_inv; + my = scale_inv; + buffer_width *= scale_inv; + buffer_height *= scale_inv; + } + let (mut buffer_width, mut buffer_height) = + transform.maybe_swap((buffer_width, buffer_height)); + let (mut dx, mut dy) = match transform { + Transform::None => (0.0, 0.0), + Transform::Rotate90 => (buffer_width, 0.0), + Transform::Rotate180 => (buffer_width, buffer_height), + Transform::Rotate270 => (0.0, buffer_height), + Transform::Flip => (buffer_width, 0.0), + Transform::FlipRotate90 => (0.0, 0.0), + Transform::FlipRotate180 => (0.0, buffer_height), + Transform::FlipRotate270 => (buffer_width, buffer_height), + }; + if let Some([x, y, w, h]) = viewport { + dx -= x.to_f64(); + dy -= y.to_f64(); + buffer_width = w.to_f64(); + buffer_height = h.to_f64(); + } + let mut smear = false; + if dst_width != buffer_width { + let scale = dst_width / buffer_width; + mx *= scale; + dx *= scale; + smear |= dst_width > buffer_width; + } + if dst_height != buffer_height { + let scale = dst_height / buffer_height; + my *= scale; + dy *= scale; + smear |= dst_height > buffer_height; + } + DamageMatrix { + transform, + mx, + my, + dx, + dy, + smear: smear as _, + } + } } diff --git a/src/gfx_api.rs b/src/gfx_api.rs index ded86452..3176a0c5 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -257,12 +257,28 @@ pub enum ResetStatus { pub trait GfxFramebuffer: Debug { fn physical_size(&self) -> (i32, i32); + fn full_region(&self) -> Region { + let (width, height) = self.physical_size(); + Region::new2(Rect::new_sized_unchecked(0, 0, width, height)) + } + fn render( &self, acquire_sync: AcquireSync, release_sync: ReleaseSync, ops: &[GfxApiOpt], clear: Option<&Color>, + ) -> Result, GfxError> { + self.render_with_region(acquire_sync, release_sync, ops, clear, &self.full_region()) + } + + fn render_with_region( + &self, + acquire_sync: AcquireSync, + release_sync: ReleaseSync, + ops: &[GfxApiOpt], + clear: Option<&Color>, + region: &Region, ) -> Result, GfxError>; fn format(&self) -> &'static Format; @@ -395,8 +411,15 @@ impl dyn GfxFramebuffer { acquire_sync: AcquireSync, release_sync: ReleaseSync, pass: &GfxRenderPass, + region: &Region, ) -> Result, GfxError> { - self.render(acquire_sync, release_sync, &pass.ops, pass.clear.as_ref()) + self.render_with_region( + acquire_sync, + release_sync, + &pass.ops, + pass.clear.as_ref(), + region, + ) } pub fn render_output( @@ -451,7 +474,7 @@ impl dyn GfxFramebuffer { transform, None, ); - self.perform_render_pass(acquire_sync, release_sync, &pass) + self.perform_render_pass(acquire_sync, release_sync, &pass, &self.full_region()) } pub fn render_hardware_cursor( diff --git a/src/gfx_apis/gl/renderer/framebuffer.rs b/src/gfx_apis/gl/renderer/framebuffer.rs index 190e47e2..810c81b8 100644 --- a/src/gfx_apis/gl/renderer/framebuffer.rs +++ b/src/gfx_apis/gl/renderer/framebuffer.rs @@ -99,12 +99,13 @@ impl GfxFramebuffer for Framebuffer { (self.gl.width, self.gl.height) } - fn render( + fn render_with_region( &self, acquire_sync: AcquireSync, _release_sync: ReleaseSync, ops: &[GfxApiOpt], clear: Option<&Color>, + _region: &Region, ) -> Result, GfxError> { self.render(acquire_sync, ops, clear).map_err(|e| e.into()) } diff --git a/src/gfx_apis/vulkan/image.rs b/src/gfx_apis/vulkan/image.rs index 088be323..5fe04966 100644 --- a/src/gfx_apis/vulkan/image.rs +++ b/src/gfx_apis/vulkan/image.rs @@ -530,15 +530,16 @@ impl GfxFramebuffer for VulkanImage { (self.width as _, self.height as _) } - fn render( + fn render_with_region( &self, acquire_sync: AcquireSync, release_sync: ReleaseSync, ops: &[GfxApiOpt], clear: Option<&Color>, + region: &Region, ) -> Result, GfxError> { self.renderer - .execute(self, acquire_sync, release_sync, ops, clear) + .execute(self, acquire_sync, release_sync, ops, clear, region) .map_err(|e| e.into()) } diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 3b06b10d..d3715426 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -27,6 +27,7 @@ use { VulkanError, }, io_uring::IoUring, + rect::Region, theme::Color, utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack}, video::dmabuf::{dma_buf_export_sync_file, DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE}, @@ -965,6 +966,7 @@ impl VulkanRenderer { fb_release_sync: ReleaseSync, opts: &[GfxApiOpt], clear: Option<&Color>, + _region: &Region, ) -> Result, VulkanError> { zone!("execute"); let res = self.try_execute(fb, fb_acquire_sync, fb_release_sync, opts, clear); diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index 82c10f83..ec56fa92 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -4,6 +4,7 @@ use { crate::{ backend, client::{Client, ClientError, ClientId}, + damage::DamageMatrix, format::{Format, XRGB8888}, globals::{Global, GlobalName}, ifs::{wl_surface::WlSurface, zxdg_output_v1::ZxdgOutputV1}, @@ -55,7 +56,7 @@ const MODE_PREFERRED: u32 = 2; pub struct WlOutputGlobal { pub name: GlobalName, - pub _state: Rc, + pub state: Rc, pub connector: Rc, pub pos: Cell, pub output_id: Rc, @@ -71,6 +72,7 @@ pub struct WlOutputGlobal { pub legacy_scale: Cell, pub persistent: Rc, pub opt: Rc, + pub damage_matrix: Cell, } #[derive(Default)] @@ -151,9 +153,9 @@ impl WlOutputGlobal { persistent_state.transform.get(), scale, ); - Self { + let global = Self { name, - _state: state.clone(), + state: state.clone(), connector: connector.clone(), pos: Cell::new(Rect::new_sized(x, y, width, height).unwrap()), output_id: output_id.clone(), @@ -169,7 +171,10 @@ impl WlOutputGlobal { legacy_scale: Cell::new(scale.round_up()), persistent: persistent_state.clone(), opt: Default::default(), - } + damage_matrix: Default::default(), + }; + global.update_damage_matrix(); + global } pub fn position(&self) -> Rect { @@ -253,6 +258,40 @@ impl WlOutputGlobal { .get() .maybe_swap((mode.width, mode.height)) } + + pub fn update_damage_matrix(&self) { + let pos = self.pos.get(); + let mode = self.mode.get(); + let matrix = DamageMatrix::new( + self.persistent.transform.get().inverse(), + 1, + pos.width(), + pos.height(), + None, + mode.width, + mode.height, + ); + self.damage_matrix.set(matrix); + self.connector + .damage_intersect + .set(Rect::new_sized_unchecked(0, 0, mode.width, mode.height)); + } + + pub fn add_damage_area(&self, area: &Rect) { + let pos = self.pos.get(); + let rect = area.move_(-pos.x1(), -pos.y1()); + let mut rect = self.damage_matrix.get().apply(0, 0, rect); + let damage = &mut *self.connector.damage.borrow_mut(); + const MAX_CONNECTOR_DAMAGE: usize = 32; + if damage.len() >= MAX_CONNECTOR_DAMAGE { + rect = rect.union(damage.pop().unwrap()); + } + damage.push(rect.intersect(self.connector.damage_intersect.get())); + } + + pub fn add_visualizer_damage(&self) { + self.state.damage_visualizer.copy_damage(self); + } } global_base!(WlOutputGlobal, WlOutput, WlOutputError); diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 605a9fe7..27727807 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -23,6 +23,7 @@ use { backend::KeyState, client::{Client, ClientError}, cursor_user::{CursorUser, CursorUserId}, + damage::DamageMatrix, drm_feedback::DrmFeedback, fixed::Fixed, gfx_api::{ @@ -2027,117 +2028,6 @@ efrom!(WlSurfaceError, XdgSurfaceError); efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error); efrom!(WlSurfaceError, CommitTimelineError); -#[derive(Copy, Clone, Debug)] -struct DamageMatrix { - transform: Transform, - mx: f64, - my: f64, - dx: f64, - dy: f64, - smear: i32, -} - -impl Default for DamageMatrix { - fn default() -> Self { - Self { - transform: Default::default(), - mx: 1.0, - my: 1.0, - dx: 0.0, - dy: 0.0, - smear: 0, - } - } -} - -impl DamageMatrix { - fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect { - let x1 = rect.x1() - self.smear; - let x2 = rect.x2() + self.smear; - let y1 = rect.y1() - self.smear; - let y2 = rect.y2() + self.smear; - let [x1, y1, x2, y2] = match self.transform { - Transform::None => [x1, y1, x2, y2], - Transform::Rotate90 => [-y2, x1, -y1, x2], - Transform::Rotate180 => [-x2, -y2, -x1, -y1], - Transform::Rotate270 => [y1, -x2, y2, -x1], - Transform::Flip => [-x2, y1, -x1, y2], - Transform::FlipRotate90 => [y1, x1, y2, x2], - Transform::FlipRotate180 => [x1, -y2, x2, -y1], - Transform::FlipRotate270 => [-y2, -x2, -y1, -x1], - }; - let x1 = (x1 as f64 * self.mx + self.dx).floor() as i32 + dx; - let y1 = (y1 as f64 * self.my + self.dy).floor() as i32 + dy; - let x2 = (x2 as f64 * self.mx + self.dx).ceil() as i32 + dx; - let y2 = (y2 as f64 * self.my + self.dy).ceil() as i32 + dy; - Rect::new(x1, y1, x2, y2).unwrap() - } - - fn new( - transform: Transform, - legacy_scale: i32, - buffer_width: i32, - buffer_height: i32, - viewport: Option<[Fixed; 4]>, - dst_width: i32, - dst_height: i32, - ) -> DamageMatrix { - let mut buffer_width = buffer_width as f64; - let mut buffer_height = buffer_height as f64; - let dst_width = dst_width as f64; - let dst_height = dst_height as f64; - - let mut mx = 1.0; - let mut my = 1.0; - if legacy_scale != 1 { - let scale_inv = 1.0 / (legacy_scale as f64); - mx = scale_inv; - my = scale_inv; - buffer_width *= scale_inv; - buffer_height *= scale_inv; - } - let (mut buffer_width, mut buffer_height) = - transform.maybe_swap((buffer_width, buffer_height)); - let (mut dx, mut dy) = match transform { - Transform::None => (0.0, 0.0), - Transform::Rotate90 => (buffer_width, 0.0), - Transform::Rotate180 => (buffer_width, buffer_height), - Transform::Rotate270 => (0.0, buffer_height), - Transform::Flip => (buffer_width, 0.0), - Transform::FlipRotate90 => (0.0, 0.0), - Transform::FlipRotate180 => (0.0, buffer_height), - Transform::FlipRotate270 => (buffer_width, buffer_height), - }; - if let Some([x, y, w, h]) = viewport { - dx -= x.to_f64(); - dy -= y.to_f64(); - buffer_width = w.to_f64(); - buffer_height = h.to_f64(); - } - let mut smear = false; - if dst_width != buffer_width { - let scale = dst_width / buffer_width; - mx *= scale; - dx *= scale; - smear |= dst_width > buffer_width; - } - if dst_height != buffer_height { - let scale = dst_height / buffer_height; - my *= scale; - dy *= scale; - smear |= dst_height > buffer_height; - } - DamageMatrix { - transform, - mx, - my, - dx, - dy, - smear: smear as _, - } - } -} - impl VblankListener for WlSurface { fn after_vblank(self: Rc) { if self.visible.get() { diff --git a/src/it/test_gfx_api.rs b/src/it/test_gfx_api.rs index 36a03040..ce81814b 100644 --- a/src/it/test_gfx_api.rs +++ b/src/it/test_gfx_api.rs @@ -376,12 +376,13 @@ impl GfxFramebuffer for TestGfxFb { } } - fn render( + fn render_with_region( &self, _acquire_sync: AcquireSync, _release_sync: ReleaseSync, ops: &[GfxApiOpt], clear: Option<&Color>, + _region: &Region, ) -> Result, GfxError> { let fb_points = |width: i32, height: i32, rect: &FramebufferRect| { let points = rect.to_points(); diff --git a/src/rect.rs b/src/rect.rs index 03640a01..d9a22cb5 100644 --- a/src/rect.rs +++ b/src/rect.rs @@ -164,6 +164,11 @@ impl Rect { self.raw.x1 == self.raw.x2 || self.raw.y1 == self.raw.y2 } + #[expect(dead_code)] + pub fn is_not_empty(&self) -> bool { + !self.is_empty() + } + #[expect(dead_code)] pub fn to_origin(&self) -> Self { Self { diff --git a/src/rect/region.rs b/src/rect/region.rs index 6ea178de..d385c7ad 100644 --- a/src/rect/region.rs +++ b/src/rect/region.rs @@ -11,7 +11,13 @@ use { RectRaw, }, smallvec::SmallVec, - std::{cell::UnsafeCell, mem, ops::Deref, rc::Rc}, + std::{ + cell::UnsafeCell, + fmt::{Debug, Formatter}, + mem, + ops::Deref, + rc::Rc, + }, }; thread_local! { @@ -196,6 +202,12 @@ pub struct DamageQueue { datas: Rc>>>, } +impl Debug for DamageQueue { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DamageQueue").finish_non_exhaustive() + } +} + impl DamageQueue { pub fn new() -> [DamageQueue; N] { let datas = Rc::new(UnsafeCell::new(vec![vec!(); N])); diff --git a/src/state.rs b/src/state.rs index ac79f8ab..a2354d86 100644 --- a/src/state.rs +++ b/src/state.rs @@ -349,7 +349,9 @@ pub struct ConnectorData { pub drm_dev: Option>, pub async_event: Rc, pub damaged: Cell, + pub damage: RefCell>, pub needs_vblank_emulation: Cell, + pub damage_intersect: Cell, } pub struct OutputData { @@ -831,6 +833,7 @@ impl State { self.damage_visualizer.add(rect); for output in self.root.outputs.lock().values() { if output.global.pos.get().intersects(&rect) { + output.global.add_damage_area(&rect); if cursor && output.schedule.defer_cursor_updates() { output.schedule.software_cursor_changed(); } else { diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 747bbcbd..7c84c0bc 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -35,7 +35,9 @@ pub fn handle(state: &Rc, connector: &Rc) { drm_dev: drm_dev.clone(), async_event: Rc::new(AsyncEvent::default()), damaged: Cell::new(false), + damage: Default::default(), needs_vblank_emulation: Cell::new(false), + damage_intersect: Default::default(), }); if let Some(dev) = drm_dev { dev.connectors.set(id, data.clone()); diff --git a/src/tree/output.rs b/src/tree/output.rs index 5c4630e7..5c16ff7a 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -794,13 +794,17 @@ impl OutputNode { } fn change_extents_(self: &Rc, rect: &Rect) { - if self.node_visible() { + let visible = self.node_visible(); + if visible { let old_pos = self.global.pos.get(); self.state.damage(old_pos); - self.state.damage(*rect); } self.global.persistent.pos.set((rect.x1(), rect.y1())); self.global.pos.set(*rect); + self.global.update_damage_matrix(); + if visible { + self.state.damage(*rect); + } self.state.output_extents_changed(); self.update_rects(); if let Some(ls) = self.lock_surface.get() { diff --git a/src/utils/transform_ext.rs b/src/utils/transform_ext.rs index 63f5a975..f7b965d0 100644 --- a/src/utils/transform_ext.rs +++ b/src/utils/transform_ext.rs @@ -18,6 +18,8 @@ pub trait TransformExt: Sized { fn from_wl(wl: i32) -> Option; fn apply_point(self, width: i32, height: i32, point: (i32, i32)) -> (i32, i32); + + fn inverse(self) -> Self; } impl TransformExt for Transform { @@ -68,4 +70,12 @@ impl TransformExt for Transform { FlipRotate270 => (width - y, height - x), } } + + fn inverse(self) -> Self { + match self { + Rotate90 => Rotate270, + Rotate270 => Rotate90, + _ => self, + } + } }