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
+49 -34
View File
@@ -2,15 +2,16 @@ use std::cell::RefCell;
use anyhow::Context as _;
use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::damage::OutputDamageTracker;
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
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 super::encompassing_geo;
use super::texture::TextureBuffer;
use super::{encompassing_geo, render_elements};
/// Buffer for offscreen rendering.
#[derive(Debug)]
@@ -18,7 +19,13 @@ pub struct OffscreenBuffer {
/// The cached texture buffer.
///
/// 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 {
@@ -31,20 +38,20 @@ impl OffscreenBuffer {
let _span = tracy_client::span!("OffscreenBuffer::render");
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)
});
}));
let buffer_size = geo.size.to_logical(1).to_buffer(1, Transform::Normal);
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.
let size_string;
let mut reason = "";
if let Some(buf) = buffer.as_mut() {
let old_size = buf.texture().size();
if let Some(Inner { buffer, .. }) = inner.as_mut() {
let old_size = buffer.texture().size();
if old_size != buffer_size {
size_string = format!(
"size changed from {} × {} to {} × {}",
@@ -52,18 +59,18 @@ impl OffscreenBuffer {
);
reason = &size_string;
*buffer = None;
} else if !buf.is_texture_reference_unique() {
*inner = None;
} else if !buffer.is_texture_reference_unique() {
reason = "not unique";
*buffer = None;
*inner = None;
}
} else {
reason = "first render";
}
let buffer = if let Some(buffer) = buffer.as_mut() {
buffer
let inner = if let Some(inner) = inner.as_mut() {
inner
} else {
trace!("creating new texture: {reason}");
let span = tracy_client::span!("creating offscreen buffer");
@@ -73,44 +80,52 @@ impl OffscreenBuffer {
.create_buffer(Fourcc::Abgr8888, buffer_size)
.context("error creating texture")?;
buffer.insert(TextureBuffer::from_texture(
let buffer = TextureBuffer::from_texture(
renderer,
texture,
scale,
Transform::Normal,
Vec::new(),
))
);
let damage = OutputDamageTracker::new(geo.size, scale, Transform::Normal);
inner.insert(Inner { buffer, damage })
};
// Update the texture scale.
buffer.set_texture_scale(scale);
// Recreate the damage tracker if the scale changes. We already recreate it for buffer size
// 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.
buffer.increment_commit_counter();
trace!("recreating damage tracker due to scale change");
inner.damage = OutputDamageTracker::new(geo.size, scale, Transform::Normal);
}
// Render to the buffer.
let mut texture = buffer.texture().clone();
let mut target = renderer
.bind(&mut texture)
.context("error binding texture")?;
let res = {
let mut texture = inner.buffer.texture().clone();
let mut target = renderer.bind(&mut texture)?;
inner.damage.render_output(
renderer,
&mut target,
1,
&elements,
Color32F::TRANSPARENT,
)?
};
let sync_point = render_elements(
renderer,
&mut target,
geo.size,
scale,
Transform::Normal,
elements,
)?;
if res.damage.is_some() {
// Increment the commit counter if some contents updated.
inner.buffer.increment_commit_counter();
}
Ok((buffer.clone(), sync_point, offset))
Ok((inner.buffer.clone(), res.sync, offset))
}
}
impl Default for OffscreenBuffer {
fn default() -> Self {
OffscreenBuffer {
buffer: RefCell::new(None),
inner: RefCell::new(None),
}
}
}