offscreen: Take damage into account when rendering

Does not yet signal the damage outside, but does skip rerendering if there was
no damage.
This commit is contained in:
Ivan Molodetskikh
2025-03-01 09:45:57 +03:00
parent 74a30be10b
commit efd8372b20
+48 -33
View File
@@ -2,15 +2,16 @@ use std::cell::RefCell;
use anyhow::Context as _; use anyhow::Context as _;
use smithay::backend::allocator::Fourcc; use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::damage::OutputDamageTracker;
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement}; use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture}; use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
use smithay::backend::renderer::sync::SyncPoint; use smithay::backend::renderer::sync::SyncPoint;
use smithay::backend::renderer::{Bind as _, Offscreen as _, Texture as _}; use smithay::backend::renderer::{Bind as _, Color32F, Offscreen as _, Texture as _};
use smithay::utils::{Logical, Point, Scale, Transform}; use smithay::utils::{Logical, Point, Scale, Transform};
use super::encompassing_geo;
use super::texture::TextureBuffer; use super::texture::TextureBuffer;
use super::{encompassing_geo, render_elements};
/// Buffer for offscreen rendering. /// Buffer for offscreen rendering.
#[derive(Debug)] #[derive(Debug)]
@@ -18,7 +19,13 @@ pub struct OffscreenBuffer {
/// The cached texture buffer. /// The cached texture buffer.
/// ///
/// Lazily created when `render` is called. Recreated when necessary. /// Lazily created when `render` is called. Recreated when necessary.
buffer: RefCell<Option<TextureBuffer<GlesTexture>>>, inner: RefCell<Option<Inner>>,
}
#[derive(Debug)]
struct Inner {
buffer: TextureBuffer<GlesTexture>,
damage: OutputDamageTracker,
} }
impl OffscreenBuffer { impl OffscreenBuffer {
@@ -31,20 +38,20 @@ impl OffscreenBuffer {
let _span = tracy_client::span!("OffscreenBuffer::render"); let _span = tracy_client::span!("OffscreenBuffer::render");
let geo = encompassing_geo(scale, elements.iter()); let geo = encompassing_geo(scale, elements.iter());
let elements = elements.iter().rev().map(|ele| { let elements = Vec::from_iter(elements.iter().map(|ele| {
RelocateRenderElement::from_element(ele, geo.loc.upscale(-1), Relocate::Relative) RelocateRenderElement::from_element(ele, geo.loc.upscale(-1), Relocate::Relative)
}); }));
let buffer_size = geo.size.to_logical(1).to_buffer(1, Transform::Normal); let buffer_size = geo.size.to_logical(1).to_buffer(1, Transform::Normal);
let offset = geo.loc.to_f64().to_logical(scale); let offset = geo.loc.to_f64().to_logical(scale);
let mut buffer = self.buffer.borrow_mut(); let mut inner = self.inner.borrow_mut();
// Check if we need to create or recreate the texture. // Check if we need to create or recreate the texture.
let size_string; let size_string;
let mut reason = ""; let mut reason = "";
if let Some(buf) = buffer.as_mut() { if let Some(Inner { buffer, .. }) = inner.as_mut() {
let old_size = buf.texture().size(); let old_size = buffer.texture().size();
if old_size != buffer_size { if old_size != buffer_size {
size_string = format!( size_string = format!(
"size changed from {} × {} to {} × {}", "size changed from {} × {} to {} × {}",
@@ -52,18 +59,18 @@ impl OffscreenBuffer {
); );
reason = &size_string; reason = &size_string;
*buffer = None; *inner = None;
} else if !buf.is_texture_reference_unique() { } else if !buffer.is_texture_reference_unique() {
reason = "not unique"; reason = "not unique";
*buffer = None; *inner = None;
} }
} else { } else {
reason = "first render"; reason = "first render";
} }
let buffer = if let Some(buffer) = buffer.as_mut() { let inner = if let Some(inner) = inner.as_mut() {
buffer inner
} else { } else {
trace!("creating new texture: {reason}"); trace!("creating new texture: {reason}");
let span = tracy_client::span!("creating offscreen buffer"); let span = tracy_client::span!("creating offscreen buffer");
@@ -73,44 +80,52 @@ impl OffscreenBuffer {
.create_buffer(Fourcc::Abgr8888, buffer_size) .create_buffer(Fourcc::Abgr8888, buffer_size)
.context("error creating texture")?; .context("error creating texture")?;
buffer.insert(TextureBuffer::from_texture( let buffer = TextureBuffer::from_texture(
renderer, renderer,
texture, texture,
scale, scale,
Transform::Normal, Transform::Normal,
Vec::new(), Vec::new(),
)) );
let damage = OutputDamageTracker::new(geo.size, scale, Transform::Normal);
inner.insert(Inner { buffer, damage })
}; };
// Update the texture scale. // Recreate the damage tracker if the scale changes. We already recreate it for buffer size
buffer.set_texture_scale(scale); // changes, and transform is always Normal.
if inner.buffer.texture_scale() != scale {
inner.buffer.set_texture_scale(scale);
// Increment the commit counter since we're rendering new contents to the buffer. trace!("recreating damage tracker due to scale change");
buffer.increment_commit_counter(); inner.damage = OutputDamageTracker::new(geo.size, scale, Transform::Normal);
}
// Render to the buffer. let res = {
let mut texture = buffer.texture().clone(); let mut texture = inner.buffer.texture().clone();
let mut target = renderer let mut target = renderer.bind(&mut texture)?;
.bind(&mut texture) inner.damage.render_output(
.context("error binding texture")?;
let sync_point = render_elements(
renderer, renderer,
&mut target, &mut target,
geo.size, 1,
scale, &elements,
Transform::Normal, Color32F::TRANSPARENT,
elements, )?
)?; };
Ok((buffer.clone(), sync_point, offset)) if res.damage.is_some() {
// Increment the commit counter if some contents updated.
inner.buffer.increment_commit_counter();
}
Ok((inner.buffer.clone(), res.sync, offset))
} }
} }
impl Default for OffscreenBuffer { impl Default for OffscreenBuffer {
fn default() -> Self { fn default() -> Self {
OffscreenBuffer { OffscreenBuffer {
buffer: RefCell::new(None), inner: RefCell::new(None),
} }
} }
} }