mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
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:
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user