metal: track per-framebuffer damage
This commit is contained in:
parent
f80ac20220
commit
07fb198eb4
17 changed files with 334 additions and 151 deletions
|
|
@ -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<OutputNode>) -> Option<Latched> {
|
||||
let damage = self.has_damage.get();
|
||||
if damage == 0 {
|
||||
fn latch(&self, node: &Rc<OutputNode>, buffer: &RenderBuffer) -> Option<Latched> {
|
||||
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<MetalPlane>,
|
||||
pass: &GfxRenderPass,
|
||||
latched: &Latched,
|
||||
try_direct_scanout: bool,
|
||||
) -> Result<PresentFb, MetalError> {
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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::<N>());
|
||||
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<RenderBuffer, MetalError> {
|
||||
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<DrmFramebuffer>,
|
||||
pub _dev_bo: GbmBo,
|
||||
pub damage_queue: DamageQueue,
|
||||
pub dev_bo: GbmBo,
|
||||
pub _render_bo: Option<GbmBo>,
|
||||
// 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 {
|
||||
|
|
|
|||
|
|
@ -544,7 +544,9 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
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,
|
||||
|
|
|
|||
150
src/damage.rs
150
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<State>) {
|
|||
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 _,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Option<SyncFile>, 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<Option<SyncFile>, GfxError>;
|
||||
|
||||
fn format(&self) -> &'static Format;
|
||||
|
|
@ -395,8 +411,15 @@ impl dyn GfxFramebuffer {
|
|||
acquire_sync: AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
pass: &GfxRenderPass,
|
||||
region: &Region,
|
||||
) -> Result<Option<SyncFile>, 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(
|
||||
|
|
|
|||
|
|
@ -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<Option<SyncFile>, GfxError> {
|
||||
self.render(acquire_sync, ops, clear).map_err(|e| e.into())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Option<SyncFile>, 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())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Option<SyncFile>, VulkanError> {
|
||||
zone!("execute");
|
||||
let res = self.try_execute(fb, fb_acquire_sync, fb_release_sync, opts, clear);
|
||||
|
|
|
|||
|
|
@ -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<State>,
|
||||
pub state: Rc<State>,
|
||||
pub connector: Rc<ConnectorData>,
|
||||
pub pos: Cell<Rect>,
|
||||
pub output_id: Rc<OutputId>,
|
||||
|
|
@ -71,6 +72,7 @@ pub struct WlOutputGlobal {
|
|||
pub legacy_scale: Cell<u32>,
|
||||
pub persistent: Rc<PersistentOutputState>,
|
||||
pub opt: Rc<OutputGlobalOpt>,
|
||||
pub damage_matrix: Cell<DamageMatrix>,
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
|
|
|||
|
|
@ -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<Self>) {
|
||||
if self.visible.get() {
|
||||
|
|
|
|||
|
|
@ -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<Option<SyncFile>, GfxError> {
|
||||
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
|
||||
let points = rect.to_points();
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<UnsafeCell<Vec<Vec<Rect>>>>,
|
||||
}
|
||||
|
||||
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<const N: usize>() -> [DamageQueue; N] {
|
||||
let datas = Rc::new(UnsafeCell::new(vec![vec!(); N]));
|
||||
|
|
|
|||
|
|
@ -349,7 +349,9 @@ pub struct ConnectorData {
|
|||
pub drm_dev: Option<Rc<DrmDevData>>,
|
||||
pub async_event: Rc<AsyncEvent>,
|
||||
pub damaged: Cell<bool>,
|
||||
pub damage: RefCell<Vec<Rect>>,
|
||||
pub needs_vblank_emulation: Cell<bool>,
|
||||
pub damage_intersect: Cell<Rect>,
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
|||
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());
|
||||
|
|
|
|||
|
|
@ -794,13 +794,17 @@ impl OutputNode {
|
|||
}
|
||||
|
||||
fn change_extents_(self: &Rc<Self>, 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() {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ pub trait TransformExt: Sized {
|
|||
fn from_wl(wl: i32) -> Option<Self>;
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue