renderer: Use fine grained damage for rendering

Up until now we always redrew the whole windows, which worked
fine - except for some transparency issues - but left performance
on the table.

So now we use instancing to render only the actually damaged regions
inside the window, which also fixes our few rendering issues.

We also use a shader now for clearing the screen to use instancing as
well and lower the amount of draw calls at the begin of a frame for
clearing parts of it.

And while we are add it we remove the last rendering artifacts
by optimizing the damage vector by deduplicating and merging rectangles.
This commit is contained in:
Victor Brekenfeld 2021-12-07 18:34:10 +01:00
parent f55f1bbbe0
commit 31b308836f
6 changed files with 374 additions and 91 deletions

View File

@ -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<Gles2TextureInternal>);
@ -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<H: std::hash::Hasher>(&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<WeakGles2Buffer>,
target_buffer: Option<Gles2Buffer>,
target_surface: Option<Rc<EGLSurface>>,
extensions: Vec<String>,
programs: [Gles2Program; shaders::FRAGMENT_COUNT],
tex_programs: [Gles2TexProgram; shaders::FRAGMENT_COUNT],
solid_program: Gles2SolidProgram,
#[cfg(feature = "wayland_frontend")]
dmabuf_cache: std::collections::HashMap<WeakDmabuf, Gles2Texture>,
egl: EGLContext,
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
egl_reader: Option<EGLBufferReader>,
vbos: [ffi::types::GLuint; 2],
gl: ffi::Gles2,
destruction_callback: Receiver<CleanupResource>,
// 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<f32>,
gl: ffi::Gles2,
programs: [Gles2Program; shaders::FRAGMENT_COUNT],
tex_programs: [Gles2TexProgram; shaders::FRAGMENT_COUNT],
solid_program: Gles2SolidProgram,
vbos: [ffi::types::GLuint; 2],
size: Size<i32, Physical>,
}
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<Gles2Program, Gles2Error> {
unsafe fn texture_program(gl: &ffi::Gles2, frag: &'static str) -> Result<Gles2TexProgram, Gles2Error> {
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<Gles2Pr
let invert_y = CStr::from_bytes_with_nul(b"invert_y\0").expect("NULL terminated");
let alpha = CStr::from_bytes_with_nul(b"alpha\0").expect("NULL terminated");
Ok(Gles2Program {
Ok(Gles2TexProgram {
program,
uniform_tex: gl.GetUniformLocation(program, tex.as_ptr() as *const ffi::types::GLchar),
uniform_matrix: gl.GetUniformLocation(program, matrix.as_ptr() as *const ffi::types::GLchar),
uniform_invert_y: gl.GetUniformLocation(program, invert_y.as_ptr() as *const ffi::types::GLchar),
uniform_alpha: gl.GetUniformLocation(program, alpha.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),
attrib_tex_coords: gl.GetAttribLocation(program, tex_coords.as_ptr() as *const ffi::types::GLchar),
})
}
unsafe fn solid_program(gl: &ffi::Gles2) -> Result<Gles2SolidProgram, Gles2Error> {
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::<ffi::types::GLfloat>() * 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<Rectangle<i32, Physical>>) -> Result<(), Self::Error> {
fn clear(&mut self, color: [f32; 4], at: &[Rectangle<i32, Physical>]) -> Result<(), Self::Error> {
if at.is_empty() {
return Ok(());
}
let mut mat = Matrix3::<f32>::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::<Vec<ffi::types::GLfloat>>();
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::<ffi::types::GLfloat>() * 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<i32, Buffer>,
dest: Rectangle<f64, Physical>,
damage: &[Rectangle<i32, Physical>],
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::<Vec<_>>();
self.render_texture(texture, mat, Some(&damage), tex_verts, alpha)
}
}
@ -1191,6 +1310,7 @@ impl Gles2Frame {
&mut self,
tex: &Gles2Texture,
mut matrix: Matrix3<f32>,
instances: Option<&[ffi::types::GLfloat]>,
tex_coords: [Vector2<f32>; 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::<ffi::types::GLfloat>() * 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(())

View File

@ -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;
}
"#;

View File

@ -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<Rectangle<i32, Physical>>) -> Result<(), Self::Error>;
fn clear(&mut self, color: [f32; 4], at: &[Rectangle<i32, Physical>]) -> 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<i32, Physical>],
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<i32, Buffer>,
dst: Rectangle<f64, Physical>,
damage: &[Rectangle<i32, Physical>],
src_transform: Transform,
alpha: f32,
) -> Result<(), Self::Error>;

View File

@ -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<R, E, F, T>(
renderer: &mut R,
frame: &mut F,
surface: &WlSurface,
scale: f64,
location: Point<i32, Logical>,
damage: &[Rectangle<i32, Logical>],
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::<Vec<_>>();
with_surface_tree_upward(
surface,
location,
@ -137,15 +143,33 @@ where
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
let mut data = data.borrow_mut();
let buffer_scale = data.buffer_scale;
let buffer_dimensions = data.buffer_dimensions;
let attributes = states.cached_state.current::<SurfaceAttributes>();
if let Some(texture) = data.texture.as_mut().and_then(|x| x.downcast_mut::<T>()) {
// 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::<SubsurfaceCachedState>();
surface_offset = current.location;
location += current.location;
}
let rect = Rectangle::<i32, Physical>::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::<Vec<_>>();
// 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);

View File

@ -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::<Vec<_>>(),
)?;
// 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::<Vec<_>>();
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) {

View File

@ -471,6 +471,7 @@ pub fn draw_window<R, E, F, T>(
window: &Window,
scale: f64,
location: Point<i32, Logical>,
damage: &[Rectangle<i32, Logical>],
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::<Vec<_>>();
draw_surface_tree(
renderer,
frame,
surface,
scale,
location + p_location,
&damage,
log,
)?;
}
}
}