diff --git a/src/backend/renderer/gles2/mod.rs b/src/backend/renderer/gles2/mod.rs index 762100b..00d2344 100644 --- a/src/backend/renderer/gles2/mod.rs +++ b/src/backend/renderer/gles2/mod.rs @@ -44,23 +44,27 @@ pub mod ffi { include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); } -// This static is used to assign every created Renderer a unique ID (until is overflows...). -// -// This id is used to differenciate between user_data of different renderers, because one -// cannot assume, that resources between two renderers are (and even can be) shared. -static RENDERER_COUNTER: AtomicUsize = AtomicUsize::new(0); - #[derive(Debug, Clone)] -struct Gles2Program { +struct Gles2TexProgram { program: ffi::types::GLuint, uniform_tex: ffi::types::GLint, uniform_matrix: ffi::types::GLint, uniform_invert_y: ffi::types::GLint, uniform_alpha: ffi::types::GLint, + attrib_vert: ffi::types::GLint, attrib_position: ffi::types::GLint, attrib_tex_coords: ffi::types::GLint, } +#[derive(Debug, Clone)] +struct Gles2SolidProgram { + program: ffi::types::GLuint, + uniform_matrix: ffi::types::GLint, + uniform_color: ffi::types::GLint, + attrib_vert: ffi::types::GLint, + attrib_position: ffi::types::GLint, +} + /// A handle to a GLES2 texture #[derive(Debug, Clone)] pub struct Gles2Texture(Rc); @@ -158,40 +162,20 @@ struct Gles2Buffer { _dmabuf: Dmabuf, } -#[cfg(feature = "wayland_frontend")] -struct BufferEntry { - id: u32, - buffer: wl_buffer::WlBuffer, -} - -#[cfg(feature = "wayland_frontend")] -impl std::hash::Hash for BufferEntry { - fn hash(&self, hasher: &mut H) { - self.id.hash(hasher); - } -} -#[cfg(feature = "wayland_frontend")] -impl PartialEq for BufferEntry { - fn eq(&self, other: &Self) -> bool { - self.buffer == other.buffer - } -} -#[cfg(feature = "wayland_frontend")] -impl Eq for BufferEntry {} - /// A renderer utilizing OpenGL ES 2 pub struct Gles2Renderer { - id: usize, buffers: Vec, target_buffer: Option, target_surface: Option>, extensions: Vec, - programs: [Gles2Program; shaders::FRAGMENT_COUNT], + tex_programs: [Gles2TexProgram; shaders::FRAGMENT_COUNT], + solid_program: Gles2SolidProgram, #[cfg(feature = "wayland_frontend")] dmabuf_cache: std::collections::HashMap, egl: EGLContext, #[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))] egl_reader: Option, + vbos: [ffi::types::GLuint; 2], gl: ffi::Gles2, destruction_callback: Receiver, // This field is only accessed if the image or wayland_frontend features are active @@ -206,14 +190,19 @@ pub struct Gles2Renderer { pub struct Gles2Frame { current_projection: Matrix3, gl: ffi::Gles2, - programs: [Gles2Program; shaders::FRAGMENT_COUNT], + tex_programs: [Gles2TexProgram; shaders::FRAGMENT_COUNT], + solid_program: Gles2SolidProgram, + vbos: [ffi::types::GLuint; 2], + size: Size, } impl fmt::Debug for Gles2Frame { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Gles2Frame") .field("current_projection", &self.current_projection) - .field("programs", &self.programs) + .field("tex_programs", &self.tex_programs) + .field("solid_program", &self.solid_program) + .field("size", &self.size) .finish_non_exhaustive() } } @@ -221,12 +210,12 @@ impl fmt::Debug for Gles2Frame { impl fmt::Debug for Gles2Renderer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Gles2Renderer") - .field("id", &self.id) .field("buffers", &self.buffers) .field("target_buffer", &self.target_buffer) .field("target_surface", &self.target_surface) .field("extensions", &self.extensions) - .field("programs", &self.programs) + .field("tex_programs", &self.tex_programs) + .field("solid_program", &self.solid_program) // ffi::Gles2 does not implement Debug .field("egl", &self.egl) .field("logger", &self.logger) @@ -382,9 +371,10 @@ unsafe fn link_program( Ok(program) } -unsafe fn texture_program(gl: &ffi::Gles2, frag: &'static str) -> Result { +unsafe fn texture_program(gl: &ffi::Gles2, frag: &'static str) -> Result { let program = link_program(gl, shaders::VERTEX_SHADER, frag)?; + let vert = CStr::from_bytes_with_nul(b"vert\0").expect("NULL terminated"); let position = CStr::from_bytes_with_nul(b"position\0").expect("NULL terminated"); let tex_coords = CStr::from_bytes_with_nul(b"tex_coords\0").expect("NULL terminated"); let tex = CStr::from_bytes_with_nul(b"tex\0").expect("NULL terminated"); @@ -392,17 +382,35 @@ unsafe fn texture_program(gl: &ffi::Gles2, frag: &'static str) -> Result Result { + let program = link_program(gl, shaders::VERTEX_SHADER_SOLID, shaders::FRAGMENT_SHADER_SOLID)?; + + let matrix = CStr::from_bytes_with_nul(b"matrix\0").expect("NULL terminated"); + let color = CStr::from_bytes_with_nul(b"color\0").expect("NULL terminated"); + let vert = CStr::from_bytes_with_nul(b"vert\0").expect("NULL terminated"); + let position = CStr::from_bytes_with_nul(b"position\0").expect("NULL terminated"); + + Ok(Gles2SolidProgram { + program, + uniform_matrix: gl.GetUniformLocation(program, matrix.as_ptr() as *const ffi::types::GLchar), + uniform_color: gl.GetUniformLocation(program, color.as_ptr() as *const ffi::types::GLchar), + attrib_vert: gl.GetAttribLocation(program, vert.as_ptr() as *const ffi::types::GLchar), + attrib_position: gl.GetAttribLocation(program, position.as_ptr() as *const ffi::types::GLchar), + }) +} + impl Gles2Renderer { /// Creates a new OpenGL ES 2 renderer from a given [`EGLContext`](crate::backend::egl::EGLBuffer). /// @@ -473,6 +481,16 @@ impl Gles2Renderer { if gl_version < version::GLES_3_0 && !exts.iter().any(|ext| ext == "GL_EXT_unpack_subimage") { return Err(Gles2Error::GLExtensionNotSupported(&["GL_EXT_unpack_subimage"])); } + // required for instanced damage rendering + if gl_version < version::GLES_3_0 + && !(exts.iter().any(|ext| ext == "GL_EXT_instanced_arrays") + && exts.iter().any(|ext| ext == "GL_EXT_draw_instanced")) + { + return Err(Gles2Error::GLExtensionNotSupported(&[ + "GL_EXT_instanced_arrays", + "GL_EXT_draw_instanced", + ])); + } let logger = if exts.iter().any(|ext| ext == "GL_KHR_debug") { let logger = Box::into_raw(Box::new(log.clone())); @@ -487,21 +505,33 @@ impl Gles2Renderer { (gl, exts, logger) }; - let programs = [ + let tex_programs = [ texture_program(&gl, shaders::FRAGMENT_SHADER_ABGR)?, texture_program(&gl, shaders::FRAGMENT_SHADER_XBGR)?, texture_program(&gl, shaders::FRAGMENT_SHADER_EXTERNAL)?, ]; + let solid_program = solid_program(&gl)?; + + let mut vbos = [0; 2]; + gl.GenBuffers(2, vbos.as_mut_ptr()); + gl.BindBuffer(ffi::ARRAY_BUFFER, vbos[0]); + gl.BufferData( + ffi::ARRAY_BUFFER, + (std::mem::size_of::() * VERTS.len()) as isize, + VERTS.as_ptr() as *const _, + ffi::STATIC_DRAW, + ); + gl.BindBuffer(ffi::ARRAY_BUFFER, 0); let (tx, rx) = channel(); let mut renderer = Gles2Renderer { - id: RENDERER_COUNTER.fetch_add(1, Ordering::SeqCst), gl, egl: context, #[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))] egl_reader: None, extensions: exts, - programs, + tex_programs, + solid_program, target_buffer: None, target_surface: None, buffers: Vec::new(), @@ -509,6 +539,7 @@ impl Gles2Renderer { dmabuf_cache: std::collections::HashMap::new(), destruction_callback: rx, destruction_callback_sender: tx, + vbos, logger_ptr, logger: log, _not_send: std::ptr::null_mut(), @@ -966,9 +997,11 @@ impl Drop for Gles2Renderer { unsafe { if self.egl.make_current().is_ok() { self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0); - for program in &self.programs { + for program in &self.tex_programs { self.gl.DeleteProgram(program.program); } + self.gl.DeleteProgram(self.solid_program.program); + self.gl.DeleteBuffers(2, self.vbos.as_ptr()); if self.extensions.iter().any(|ext| ext == "GL_KHR_debug") { self.gl.Disable(ffi::DEBUG_OUTPUT); @@ -1082,9 +1115,12 @@ impl Renderer for Gles2Renderer { let mut frame = Gles2Frame { gl: self.gl.clone(), - programs: self.programs.clone(), + tex_programs: self.tex_programs.clone(), + solid_program: self.solid_program.clone(), // output transformation passed in by the user current_projection: flip180 * transform.matrix() * renderer, + vbos: self.vbos, + size, }; let result = rendering(self, &mut frame); @@ -1124,17 +1160,85 @@ impl Frame for Gles2Frame { type Error = Gles2Error; type TextureId = Gles2Texture; - fn clear(&mut self, color: [f32; 4], at: Option>) -> Result<(), Self::Error> { + fn clear(&mut self, color: [f32; 4], at: &[Rectangle]) -> Result<(), Self::Error> { + if at.is_empty() { + return Ok(()); + } + + let mut mat = Matrix3::::identity(); + mat = mat * Matrix3::from_translation(Vector2::new(0.0, 0.0)); + mat = mat * Matrix3::from_nonuniform_scale(self.size.w as f32, self.size.h as f32); + mat = self.current_projection * mat; + + let damage = at + .iter() + .map(|rect| { + [ + rect.loc.x as f32 / self.size.w as f32, + rect.loc.y as f32 / self.size.h as f32, + rect.size.w as f32 / self.size.w as f32, + rect.size.h as f32 / self.size.h as f32, + ] + }) + .flatten() + .collect::>(); + unsafe { - if let Some(rect) = at { - self.gl.Enable(ffi::SCISSOR_TEST); - self.gl.Scissor(rect.loc.x, rect.loc.y, rect.size.w, rect.size.h); - } - self.gl.ClearColor(color[0], color[1], color[2], color[3]); - self.gl.Clear(ffi::COLOR_BUFFER_BIT); - if at.is_some() { - self.gl.Disable(ffi::SCISSOR_TEST); - } + self.gl.UseProgram(self.solid_program.program); + self.gl.Uniform4f( + self.solid_program.uniform_color, + color[0], + color[1], + color[2], + color[3], + ); + self.gl + .UniformMatrix3fv(self.solid_program.uniform_matrix, 1, ffi::FALSE, mat.as_ptr()); + + self.gl + .EnableVertexAttribArray(self.solid_program.attrib_vert as u32); + self.gl.BindBuffer(ffi::ARRAY_BUFFER, self.vbos[0]); + self.gl.VertexAttribPointer( + self.solid_program.attrib_vert as u32, + 2, + ffi::FLOAT, + ffi::FALSE, + 0, + std::ptr::null(), + ); + + self.gl + .EnableVertexAttribArray(self.solid_program.attrib_position as u32); + self.gl.BindBuffer(ffi::ARRAY_BUFFER, self.vbos[1]); + self.gl.BufferData( + ffi::ARRAY_BUFFER, + (std::mem::size_of::() * damage.len()) as isize, + damage.as_ptr() as *const _, + ffi::STREAM_DRAW, + ); + + self.gl.VertexAttribPointer( + self.solid_program.attrib_position as u32, + 4, + ffi::FLOAT, + ffi::FALSE, + 0, + std::ptr::null(), + ); + self.gl + .VertexAttribDivisor(self.solid_program.attrib_vert as u32, 0); + + self.gl + .VertexAttribDivisor(self.solid_program.attrib_position as u32, 1); + + self.gl + .DrawArraysInstanced(ffi::TRIANGLE_STRIP, 0, 4, at.len() as i32); + + self.gl.BindBuffer(ffi::ARRAY_BUFFER, 0); + self.gl + .DisableVertexAttribArray(self.solid_program.attrib_vert as u32); + self.gl + .DisableVertexAttribArray(self.solid_program.attrib_position as u32); } Ok(()) @@ -1145,6 +1249,7 @@ impl Frame for Gles2Frame { texture: &Self::TextureId, src: Rectangle, dest: Rectangle, + damage: &[Rectangle], transform: Transform, alpha: f32, ) -> Result<(), Self::Error> { @@ -1168,7 +1273,7 @@ impl Frame for Gles2Frame { let texture_mat = Matrix3::from_nonuniform_scale(tex_size.w as f32, tex_size.h as f32) .invert() .unwrap(); - let verts = [ + let tex_verts = [ (texture_mat * Vector3::new((src.loc.x + src.size.w) as f32, src.loc.y as f32, 0.0)).truncate(), // top-right (texture_mat * Vector3::new(src.loc.x as f32, src.loc.y as f32, 0.0)).truncate(), // top-left (texture_mat @@ -1180,7 +1285,21 @@ impl Frame for Gles2Frame { .truncate(), // bottom-right (texture_mat * Vector3::new(src.loc.x as f32, (src.loc.y + src.size.h) as f32, 0.0)).truncate(), // bottom-left ]; - self.render_texture(texture, mat, verts, alpha) + + let damage = damage + .iter() + .map(|rect| { + [ + rect.loc.x as f32 / dest.size.w as f32, + rect.loc.y as f32 / dest.size.h as f32, + rect.size.w as f32 / dest.size.w as f32, + rect.size.h as f32 / dest.size.h as f32, + ] + }) + .flatten() + .collect::>(); + + self.render_texture(texture, mat, Some(&damage), tex_verts, alpha) } } @@ -1191,6 +1310,7 @@ impl Gles2Frame { &mut self, tex: &Gles2Texture, mut matrix: Matrix3, + instances: Option<&[ffi::types::GLfloat]>, tex_coords: [Vector2; 4], alpha: f32, ) -> Result<(), Gles2Error> { @@ -1209,33 +1329,27 @@ impl Gles2Frame { self.gl.BindTexture(target, tex.0.texture); self.gl .TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32); - self.gl.UseProgram(self.programs[tex.0.texture_kind].program); + self.gl.UseProgram(self.tex_programs[tex.0.texture_kind].program); self.gl - .Uniform1i(self.programs[tex.0.texture_kind].uniform_tex, 0); + .Uniform1i(self.tex_programs[tex.0.texture_kind].uniform_tex, 0); self.gl.UniformMatrix3fv( - self.programs[tex.0.texture_kind].uniform_matrix, + self.tex_programs[tex.0.texture_kind].uniform_matrix, 1, ffi::FALSE, matrix.as_ptr(), ); self.gl.Uniform1i( - self.programs[tex.0.texture_kind].uniform_invert_y, + self.tex_programs[tex.0.texture_kind].uniform_invert_y, if tex.0.y_inverted { 1 } else { 0 }, ); self.gl - .Uniform1f(self.programs[tex.0.texture_kind].uniform_alpha, alpha); + .Uniform1f(self.tex_programs[tex.0.texture_kind].uniform_alpha, alpha); + self.gl + .EnableVertexAttribArray(self.tex_programs[tex.0.texture_kind].attrib_tex_coords as u32); self.gl.VertexAttribPointer( - self.programs[tex.0.texture_kind].attrib_position as u32, - 2, - ffi::FLOAT, - ffi::FALSE, - 0, - VERTS.as_ptr() as *const _, - ); - self.gl.VertexAttribPointer( - self.programs[tex.0.texture_kind].attrib_tex_coords as u32, + self.tex_programs[tex.0.texture_kind].attrib_tex_coords as u32, 2, ffi::FLOAT, ffi::FALSE, @@ -1244,18 +1358,55 @@ impl Gles2Frame { ); self.gl - .EnableVertexAttribArray(self.programs[tex.0.texture_kind].attrib_position as u32); - self.gl - .EnableVertexAttribArray(self.programs[tex.0.texture_kind].attrib_tex_coords as u32); + .EnableVertexAttribArray(self.tex_programs[tex.0.texture_kind].attrib_vert as u32); + self.gl.BindBuffer(ffi::ARRAY_BUFFER, self.vbos[0]); + self.gl.VertexAttribPointer( + self.solid_program.attrib_vert as u32, + 2, + ffi::FLOAT, + ffi::FALSE, + 0, + std::ptr::null(), + ); - self.gl.DrawArrays(ffi::TRIANGLE_STRIP, 0, 4); + let damage = instances.unwrap_or(&[0.0, 0.0, 1.0, 1.0]); + self.gl + .EnableVertexAttribArray(self.tex_programs[tex.0.texture_kind].attrib_position as u32); + self.gl.BindBuffer(ffi::ARRAY_BUFFER, self.vbos[1]); + self.gl.BufferData( + ffi::ARRAY_BUFFER, + (std::mem::size_of::() * damage.len()) as isize, + damage.as_ptr() as *const _, + ffi::STREAM_DRAW, + ); + + let count = (damage.len() / 4) as i32; + self.gl.VertexAttribPointer( + self.tex_programs[tex.0.texture_kind].attrib_position as u32, + 4, + ffi::FLOAT, + ffi::FALSE, + 0, + std::ptr::null(), + ); self.gl - .DisableVertexAttribArray(self.programs[tex.0.texture_kind].attrib_position as u32); + .VertexAttribDivisor(self.tex_programs[tex.0.texture_kind].attrib_vert as u32, 0); self.gl - .DisableVertexAttribArray(self.programs[tex.0.texture_kind].attrib_tex_coords as u32); + .VertexAttribDivisor(self.tex_programs[tex.0.texture_kind].attrib_tex_coords as u32, 0); + self.gl + .VertexAttribDivisor(self.tex_programs[tex.0.texture_kind].attrib_position as u32, 1); + self.gl.DrawArraysInstanced(ffi::TRIANGLE_STRIP, 0, 4, count); + + self.gl.BindBuffer(ffi::ARRAY_BUFFER, 0); self.gl.BindTexture(target, 0); + self.gl + .DisableVertexAttribArray(self.tex_programs[tex.0.texture_kind].attrib_tex_coords as u32); + self.gl + .DisableVertexAttribArray(self.tex_programs[tex.0.texture_kind].attrib_vert as u32); + self.gl + .DisableVertexAttribArray(self.tex_programs[tex.0.texture_kind].attrib_position as u32); } Ok(()) diff --git a/src/backend/renderer/gles2/shaders.rs b/src/backend/renderer/gles2/shaders.rs index f17ef6c..229ec22 100644 --- a/src/backend/renderer/gles2/shaders.rs +++ b/src/backend/renderer/gles2/shaders.rs @@ -2,29 +2,48 @@ * OpenGL Shaders */ pub const VERTEX_SHADER: &str = r#" + #version 100 uniform mat3 matrix; uniform bool invert_y; -attribute vec2 position; + +attribute vec2 vert; attribute vec2 tex_coords; +attribute vec4 position; + varying vec2 v_tex_coords; + +mat2 scale(vec2 scale_vec){ + return mat2( + scale_vec.x, 0.0, + 0.0, scale_vec.y + ); +} + void main() { - gl_Position = vec4(matrix * vec3(position, 1.0), 1.0); if (invert_y) { v_tex_coords = vec2(tex_coords.x, 1.0 - tex_coords.y); } else { v_tex_coords = tex_coords; } -}"#; + + vec2 transform_translation = position.xy; + vec2 transform_scale = position.zw; + v_tex_coords = (vec3((tex_coords * scale(transform_scale)) + transform_translation, 1.0)).xy; + gl_Position = vec4(matrix * vec3((vert * scale(transform_scale)) + transform_translation, 1.0), 1.0); +} +"#; pub const FRAGMENT_COUNT: usize = 3; pub const FRAGMENT_SHADER_ABGR: &str = r#" #version 100 + precision mediump float; uniform sampler2D tex; uniform float alpha; varying vec2 v_tex_coords; + void main() { gl_FragColor = texture2D(tex, v_tex_coords) * alpha; } @@ -32,10 +51,12 @@ void main() { pub const FRAGMENT_SHADER_XBGR: &str = r#" #version 100 + precision mediump float; uniform sampler2D tex; uniform float alpha; varying vec2 v_tex_coords; + void main() { gl_FragColor = vec4(texture2D(tex, v_tex_coords).rgb, 1.0) * alpha; } @@ -44,11 +65,45 @@ void main() { pub const FRAGMENT_SHADER_EXTERNAL: &str = r#" #version 100 #extension GL_OES_EGL_image_external : require + precision mediump float; uniform samplerExternalOES tex; uniform float alpha; varying vec2 v_tex_coords; + void main() { gl_FragColor = texture2D(tex, v_tex_coords) * alpha; } "#; + +pub const VERTEX_SHADER_SOLID: &str = r#" +#version 100 + +uniform mat3 matrix; +attribute vec2 vert; +attribute vec4 position; + +mat2 scale(vec2 scale_vec){ + return mat2( + scale_vec.x, 0.0, + 0.0, scale_vec.y + ); +} + +void main() { + vec2 transform_translation = position.xy; + vec2 transform_scale = position.zw; + gl_Position = vec4(matrix * vec3((vert * scale(transform_scale)) + transform_translation, 1.0), 1.0); +} +"#; + +pub const FRAGMENT_SHADER_SOLID: &str = r#" +#version 100 + +precision mediump float; +uniform vec4 color; + +void main() { + gl_FragColor = color; +} +"#; diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs index c71d770..e02de36 100644 --- a/src/backend/renderer/mod.rs +++ b/src/backend/renderer/mod.rs @@ -173,7 +173,7 @@ pub trait Frame { /// /// This operation is only valid in between a `begin` and `finish`-call. /// If called outside this operation may error-out, do nothing or modify future rendering results in any way. - fn clear(&mut self, color: [f32; 4], at: Option>) -> Result<(), Self::Error>; + fn clear(&mut self, color: [f32; 4], at: &[Rectangle]) -> Result<(), Self::Error>; /// Render a texture to the current target as a flat 2d-plane at a given /// position and applying the given transformation with the given alpha value. @@ -185,6 +185,7 @@ pub trait Frame { texture_scale: i32, output_scale: f64, src_transform: Transform, + damage: &[Rectangle], alpha: f32, ) -> Result<(), Self::Error> { self.render_texture_from_to( @@ -198,6 +199,7 @@ pub trait Frame { .to_f64() .to_physical(output_scale), ), + damage, src_transform, alpha, ) @@ -211,6 +213,7 @@ pub trait Frame { texture: &Self::TextureId, src: Rectangle, dst: Rectangle, + damage: &[Rectangle], src_transform: Transform, alpha: f32, ) -> Result<(), Self::Error>; diff --git a/src/backend/renderer/utils/mod.rs b/src/backend/renderer/utils/mod.rs index e9d4723..6949e88 100644 --- a/src/backend/renderer/utils/mod.rs +++ b/src/backend/renderer/utils/mod.rs @@ -1,6 +1,6 @@ use crate::{ backend::renderer::{buffer_dimensions, Frame, ImportAll, Renderer, Texture}, - utils::{Logical, Physical, Point, Size}, + utils::{Buffer, Logical, Physical, Point, Rectangle, Size}, wayland::compositor::{ is_sync_subsurface, with_surface_tree_upward, BufferAssignment, Damage, SubsurfaceCachedState, SurfaceAttributes, TraversalAction, @@ -66,12 +66,14 @@ pub fn on_commit_buffer_handler(surface: &WlSurface) { } } +/// TODO pub fn draw_surface_tree( renderer: &mut R, frame: &mut F, surface: &WlSurface, scale: f64, location: Point, + damage: &[Rectangle], log: &slog::Logger, ) -> Result<(), R::Error> where @@ -81,6 +83,10 @@ where T: Texture + 'static, { let mut result = Ok(()); + let damage = damage + .iter() + .map(|geo| geo.to_f64().to_physical(scale).to_i32_round()) + .collect::>(); with_surface_tree_upward( surface, location, @@ -137,15 +143,33 @@ where if let Some(data) = states.data_map.get::>() { let mut data = data.borrow_mut(); let buffer_scale = data.buffer_scale; + let buffer_dimensions = data.buffer_dimensions; let attributes = states.cached_state.current::(); if let Some(texture) = data.texture.as_mut().and_then(|x| x.downcast_mut::()) { // we need to re-extract the subsurface offset, as the previous closure // only passes it to our children + let mut surface_offset = (0, 0).into(); if states.role == Some("subsurface") { let current = states.cached_state.current::(); + surface_offset = current.location; location += current.location; } + let rect = Rectangle::::from_loc_and_size( + surface_offset.to_f64().to_physical(scale).to_i32_round(), + buffer_dimensions.unwrap(), + ); + let new_damage = damage + .iter() + .cloned() + .filter(|geo| geo.overlaps(rect)) + .map(|geo| geo.intersection(rect)) + .map(|mut geo| { + geo.loc -= rect.loc; + geo + }) + .collect::>(); + // TODO: Take wp_viewporter into account if let Err(err) = frame.render_texture_at( texture, @@ -153,6 +177,7 @@ where buffer_scale, scale, attributes.buffer_transform.into(), + &new_damage, 1.0, ) { result = Err(err); diff --git a/src/desktop/space.rs b/src/desktop/space.rs index 82dfe7d..aa2253c 100644 --- a/src/desktop/space.rs +++ b/src/desktop/space.rs @@ -450,6 +450,7 @@ impl Space { } // Optimize the damage for rendering + damage.dedup(); damage.retain(|rect| rect.overlaps(output_geo)); damage.retain(|rect| rect.size.h > 0 && rect.size.w > 0); for rect in damage.clone().iter() { @@ -460,6 +461,18 @@ impl Space { damage.retain(|other| !rect.contains_rect(*other)); } } + damage = damage.into_iter().fold(Vec::new(), |mut new_damage, rect| { + if let Some(existing) = new_damage.iter_mut().find(|other| rect.overlaps(**other)) { + *existing = existing.merge(rect); + } else { + new_damage.push(rect); + } + new_damage + }); + + if damage.is_empty() { + return Ok(false); + } let output_transform: Transform = output.current_transform().into(); if let Err(err) = renderer.render( @@ -471,13 +484,14 @@ impl Space { output_transform, |renderer, frame| { // First clear all damaged regions - for geo in &damage { - slog::trace!(self.logger, "Clearing at {:?}", geo); - frame.clear( - clear_color, - Some(geo.to_f64().to_physical(state.render_scale).to_i32_ceil()), - )?; - } + slog::trace!(self.logger, "Clearing at {:#?}", damage); + frame.clear( + clear_color, + &damage + .iter() + .map(|geo| geo.to_f64().to_physical(state.render_scale).to_i32_round()) + .collect::>(), + )?; // Then re-draw all window overlapping with a damage rect. for window in self.windows.iter() { @@ -485,8 +499,27 @@ impl Space { let mut loc = window_loc(window, &self.id); if damage.iter().any(|geo| wgeo.overlaps(*geo)) { loc -= output_geo.loc; - slog::trace!(self.logger, "Rendering window at {:?}", wgeo); - draw_window(renderer, frame, window, state.render_scale, loc, &self.logger)?; + let win_damage = damage + .iter() + .filter(|geo| geo.overlaps(wgeo)) + .map(|geo| geo.intersection(wgeo)) + .map(|geo| Rectangle::from_loc_and_size(geo.loc - wgeo.loc, geo.size)) + .collect::>(); + slog::trace!( + self.logger, + "Rendering window at {:?} with damage {:#?}", + wgeo, + damage + ); + draw_window( + renderer, + frame, + window, + state.render_scale, + loc, + &win_damage, + &self.logger, + )?; window_state(self.id, window).drawn = true; } } @@ -512,8 +545,7 @@ impl Space { .collect(); state.old_damage.push_front(new_damage); - // Return if we actually rendered something - Ok(!damage.is_empty()) + Ok(true) } pub fn send_frames(&self, all: bool, time: u32) { diff --git a/src/desktop/window.rs b/src/desktop/window.rs index 3da2cb4..6e8a5bc 100644 --- a/src/desktop/window.rs +++ b/src/desktop/window.rs @@ -471,6 +471,7 @@ pub fn draw_window( window: &Window, scale: f64, location: Point, + damage: &[Rectangle], log: &slog::Logger, ) -> Result<(), R::Error> where @@ -480,14 +481,30 @@ where T: Texture + 'static, { if let Some(surface) = window.toplevel().get_surface() { - draw_surface_tree(renderer, frame, surface, scale, location, log)?; + draw_surface_tree(renderer, frame, surface, scale, location, damage, log)?; for (popup, p_location) in PopupManager::popups_for_surface(surface) .ok() .into_iter() .flatten() { if let Some(surface) = popup.get_surface() { - draw_surface_tree(renderer, frame, surface, scale, location + p_location, log)?; + let damage = damage + .iter() + .cloned() + .map(|mut geo| { + geo.loc -= p_location; + geo + }) + .collect::>(); + draw_surface_tree( + renderer, + frame, + surface, + scale, + location + p_location, + &damage, + log, + )?; } } }