Fix damage handling to support multiple rectangles

This commit is contained in:
Victor Berger 2021-05-24 19:33:03 +02:00 committed by Victor Brekenfeld
parent 978ef1b393
commit 6bfe6e1f25
7 changed files with 36 additions and 38 deletions

View File

@ -15,7 +15,7 @@ use smithay::{
}, },
utils::Rectangle, utils::Rectangle,
wayland::{ wayland::{
compositor::{roles::Role, SubsurfaceRole, TraversalAction}, compositor::{roles::Role, SubsurfaceRole, TraversalAction, Damage},
data_device::DnDIconRole, data_device::DnDIconRole,
seat::CursorImageRole, seat::CursorImageRole,
}, },
@ -90,7 +90,11 @@ where
let mut data = data.borrow_mut(); let mut data = data.borrow_mut();
if data.texture.is_none() { if data.texture.is_none() {
if let Some(buffer) = data.current_state.buffer.take() { if let Some(buffer) = data.current_state.buffer.take() {
match renderer.import_buffer(&buffer, Some(&attributes.damage), egl_buffer_reader) { let damage = attributes.damage.iter().map(|dmg| match dmg {
Damage::Buffer(rect) => *rect,
Damage::Surface(rect) => rect.scale(attributes.buffer_scale),
}).collect::<Vec<_>>();
match renderer.import_buffer(&buffer, &damage, egl_buffer_reader) {
Ok(m) => { Ok(m) => {
let buffer = if smithay::wayland::shm::with_buffer_contents(&buffer, |_,_| ()).is_ok() { let buffer = if smithay::wayland::shm::with_buffer_contents(&buffer, |_,_| ()).is_ok() {
buffer.release(); buffer.release();

View File

@ -28,10 +28,10 @@ use crate::backend::egl::{
ffi::egl::{self as ffi_egl, types::EGLImage}, ffi::egl::{self as ffi_egl, types::EGLImage},
EGLContext, EGLSurface, Format as EGLFormat, MakeCurrentError, EGLContext, EGLSurface, Format as EGLFormat, MakeCurrentError,
}; };
use crate::backend::SwapBuffersError; use crate::{backend::SwapBuffersError, utils::Rectangle};
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
use crate::{backend::egl::display::EGLBufferReader, wayland::compositor::Damage}; use crate::backend::egl::display::EGLBufferReader;
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
use wayland_commons::user_data::UserDataMap; use wayland_commons::user_data::UserDataMap;
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
@ -503,7 +503,7 @@ impl Gles2Renderer {
fn import_shm( fn import_shm(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
mut damage: Option<crate::utils::Rectangle>, mut damage: &[crate::utils::Rectangle],
) -> Result<Gles2Texture, Gles2Error> { ) -> Result<Gles2Texture, Gles2Error> {
use crate::wayland::shm::with_buffer_contents; use crate::wayland::shm::with_buffer_contents;
@ -534,7 +534,7 @@ impl Gles2Renderer {
let mut tex = 0; let mut tex = 0;
unsafe { self.gl.GenTextures(1, &mut tex) }; unsafe { self.gl.GenTextures(1, &mut tex) };
// different buffer, upload in full // different buffer, upload in full
damage = None; damage = &[];
let texture = Gles2Texture(Rc::new(Gles2TextureInternal { let texture = Gles2Texture(Rc::new(Gles2TextureInternal {
texture: tex, texture: tex,
texture_kind: shader_idx, texture_kind: shader_idx,
@ -564,7 +564,7 @@ impl Gles2Renderer {
self.gl self.gl
.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32); .TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32);
self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, stride / pixelsize); self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, stride / pixelsize);
if let Some(region) = damage { for region in damage {
trace!(self.logger, "Uploading partial shm texture for {:?}", buffer); trace!(self.logger, "Uploading partial shm texture for {:?}", buffer);
self.gl.PixelStorei(ffi::UNPACK_SKIP_PIXELS, region.x); self.gl.PixelStorei(ffi::UNPACK_SKIP_PIXELS, region.x);
self.gl.PixelStorei(ffi::UNPACK_SKIP_ROWS, region.y); self.gl.PixelStorei(ffi::UNPACK_SKIP_ROWS, region.y);
@ -581,19 +581,6 @@ impl Gles2Renderer {
); );
self.gl.PixelStorei(ffi::UNPACK_SKIP_PIXELS, 0); self.gl.PixelStorei(ffi::UNPACK_SKIP_PIXELS, 0);
self.gl.PixelStorei(ffi::UNPACK_SKIP_ROWS, 0); self.gl.PixelStorei(ffi::UNPACK_SKIP_ROWS, 0);
} else {
trace!(self.logger, "Uploading shm texture for {:?}", buffer);
self.gl.TexImage2D(
ffi::TEXTURE_2D,
0,
gl_format as i32,
width,
height,
0,
gl_format,
ffi::UNSIGNED_BYTE as u32,
slice.as_ptr().offset(offset as isize) as *const _,
);
} }
self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, 0); self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, 0);
@ -930,7 +917,7 @@ impl Renderer for Gles2Renderer {
fn import_buffer( fn import_buffer(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
damage: Option<&Damage>, damage: &[Rectangle],
egl: Option<&EGLBufferReader>, egl: Option<&EGLBufferReader>,
) -> Result<Self::TextureId, Self::Error> { ) -> Result<Self::TextureId, Self::Error> {
let texture = if egl.and_then(|egl| egl.egl_buffer_dimensions(&buffer)).is_some() { let texture = if egl.and_then(|egl| egl.egl_buffer_dimensions(&buffer)).is_some() {
@ -938,10 +925,7 @@ impl Renderer for Gles2Renderer {
} else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() { } else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() {
self.import_shm( self.import_shm(
&buffer, &buffer,
damage.and_then(|damage| match damage { damage,
Damage::Buffer(rect) => Some(*rect),
_ => None,
}),
) )
} else { } else {
Err(Gles2Error::UnknownBufferType) Err(Gles2Error::UnknownBufferType)

View File

@ -10,13 +10,11 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error; use std::error::Error;
#[cfg(feature = "wayland_frontend")]
use crate::wayland::compositor::Damage;
use cgmath::{prelude::*, Matrix3, Vector2}; use cgmath::{prelude::*, Matrix3, Vector2};
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm}; use wayland_server::protocol::{wl_buffer, wl_shm};
use crate::backend::SwapBuffersError; use crate::{backend::SwapBuffersError, utils::Rectangle};
#[cfg(feature = "renderer_gl")] #[cfg(feature = "renderer_gl")]
pub mod gles2; pub mod gles2;
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))] #[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
@ -235,11 +233,14 @@ pub trait Renderer {
/// ///
/// The implementation defines, if the id keeps being valid, if the buffer is released, /// The implementation defines, if the id keeps being valid, if the buffer is released,
/// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again. /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
///
/// The `damage` argument provides a list of rectangle locating parts of the buffer that need to be updated. When provided
/// with an empty list `&[]`, the renderer is allows to not update the texture at all.
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))] #[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
fn import_buffer( fn import_buffer(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
damage: Option<&Damage>, damage: &[Rectangle],
egl: Option<&EGLBufferReader>, egl: Option<&EGLBufferReader>,
) -> Result<Self::TextureId, Self::Error>; ) -> Result<Self::TextureId, Self::Error>;

View File

@ -28,6 +28,7 @@ use winit::{
platform::unix::WindowExtUnix, platform::unix::WindowExtUnix,
window::{Window as WinitWindow, WindowBuilder}, window::{Window as WinitWindow, WindowBuilder},
}; };
use crate::utils::Rectangle;
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
use crate::backend::egl::display::EGLBufferReader; use crate::backend::egl::display::EGLBufferReader;

View File

@ -13,13 +13,13 @@ pub struct Rectangle {
impl Rectangle { impl Rectangle {
/// Checks whether given point is inside a rectangle /// Checks whether given point is inside a rectangle
pub fn contains(&self, point: (i32, i32)) -> bool { pub fn contains(self, point: (i32, i32)) -> bool {
let (x, y) = point; let (x, y) = point;
(x >= self.x) && (x < self.x + self.width) && (y >= self.y) && (y < self.y + self.height) (x >= self.x) && (x < self.x + self.width) && (y >= self.y) && (y < self.y + self.height)
} }
/// Checks whether a given rectangle overlaps with this one /// Checks whether a given rectangle overlaps with this one
pub fn overlaps(&self, other: &Rectangle) -> bool { pub fn overlaps(self, other: &Rectangle) -> bool {
// if the rectangle is not outside of the other // if the rectangle is not outside of the other
// they must overlap // they must overlap
!( !(
@ -33,4 +33,14 @@ impl Rectangle {
|| self.y > other.y + other.height || self.y > other.y + other.height
) )
} }
/// Scales the dimensions of this rectangle by given factor
pub fn scale(self, factor: i32) -> Rectangle {
Rectangle {
x: self.x * factor,
y: self.y * factor,
width: self.width * factor,
height: self.height * factor,
}
}
} }

View File

@ -78,7 +78,7 @@ where
} }
wl_surface::Request::Damage { x, y, width, height } => { wl_surface::Request::Damage { x, y, width, height } => {
SurfaceData::<R>::with_data(&surface, |d| { SurfaceData::<R>::with_data(&surface, |d| {
d.damage = Damage::Surface(Rectangle { x, y, width, height }) d.damage.push(Damage::Surface(Rectangle { x, y, width, height }));
}); });
} }
wl_surface::Request::Frame { callback } => { wl_surface::Request::Frame { callback } => {
@ -113,7 +113,7 @@ where
} }
wl_surface::Request::DamageBuffer { x, y, width, height } => { wl_surface::Request::DamageBuffer { x, y, width, height } => {
SurfaceData::<R>::with_data(&surface, |d| { SurfaceData::<R>::with_data(&surface, |d| {
d.damage = Damage::Buffer(Rectangle { x, y, width, height }) d.damage.push(Damage::Buffer(Rectangle { x, y, width, height }))
}); });
} }
wl_surface::Request::Destroy => { wl_surface::Request::Destroy => {

View File

@ -87,12 +87,10 @@ use wayland_server::{
Display, Filter, Global, UserDataMap, Display, Filter, Global, UserDataMap,
}; };
/// Description of which part of a surface /// Description of a part of a surface that
/// should be considered damaged and needs to be redrawn /// should be considered damaged and needs to be redrawn
#[derive(Debug)] #[derive(Debug)]
pub enum Damage { pub enum Damage {
/// The whole surface must be considered damaged (this is the default)
Full,
/// A rectangle containing the damaged zone, in surface coordinates /// A rectangle containing the damaged zone, in surface coordinates
Surface(Rectangle), Surface(Rectangle),
/// A rectangle containing the damaged zone, in buffer coordinates /// A rectangle containing the damaged zone, in buffer coordinates
@ -157,7 +155,7 @@ pub struct SurfaceAttributes {
/// ///
/// Hint provided by the client to suggest that only this part /// Hint provided by the client to suggest that only this part
/// of the surface was changed and needs to be redrawn /// of the surface was changed and needs to be redrawn
pub damage: Damage, pub damage: Vec<Damage>,
/// The frame callback associated with this surface for the commit /// The frame callback associated with this surface for the commit
/// ///
/// The be triggered to notify the client about when it would be a /// The be triggered to notify the client about when it would be a
@ -196,7 +194,7 @@ impl Default for SurfaceAttributes {
buffer_transform: wl_output::Transform::Normal, buffer_transform: wl_output::Transform::Normal,
opaque_region: None, opaque_region: None,
input_region: None, input_region: None,
damage: Damage::Full, damage: Vec::new(),
frame_callback: None, frame_callback: None,
user_data: UserDataMap::new(), user_data: UserDataMap::new(),
} }