aboutsummaryrefslogtreecommitdiff
path: root/src/render_helpers
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2025-02-27 08:09:44 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2025-03-10 07:59:14 +0300
commit74a30be10b347d298189a071b8ff812aedc8b08a (patch)
tree24192b6ec6f9b54f5067ab5a4b63066fbd282fbd /src/render_helpers
parent1c521e48313b42d2e314d139c3cf4ac5dc44e7a7 (diff)
downloadniri-74a30be10b347d298189a071b8ff812aedc8b08a.tar.gz
niri-74a30be10b347d298189a071b8ff812aedc8b08a.tar.bz2
niri-74a30be10b347d298189a071b8ff812aedc8b08a.zip
Cache texture in OpenAnimation
Don't recreate it unless the size changes. This lays the groundwork for also tracking damage in the future.
Diffstat (limited to 'src/render_helpers')
-rw-r--r--src/render_helpers/offscreen.rs305
-rw-r--r--src/render_helpers/texture.rs11
2 files changed, 96 insertions, 220 deletions
diff --git a/src/render_helpers/offscreen.rs b/src/render_helpers/offscreen.rs
index 9005e80c..9f4857af 100644
--- a/src/render_helpers/offscreen.rs
+++ b/src/render_helpers/offscreen.rs
@@ -1,251 +1,116 @@
+use std::cell::RefCell;
+
+use anyhow::Context as _;
use smithay::backend::allocator::Fourcc;
-use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
-use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
-use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer};
-use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};
-use smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform};
-
-use super::primary_gpu_texture::PrimaryGpuTextureRenderElement;
-use super::render_to_texture;
-use super::renderer::AsGlesFrame;
-use super::texture::{TextureBuffer, TextureRenderElement};
-use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};
-
-/// Renders elements into an off-screen buffer.
+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::utils::{Logical, Point, Scale, Transform};
+
+use super::texture::TextureBuffer;
+use super::{encompassing_geo, render_elements};
+
+/// Buffer for offscreen rendering.
#[derive(Debug)]
-pub struct OffscreenRenderElement {
- // The texture, if rendering succeeded.
- texture: Option<PrimaryGpuTextureRenderElement>,
- // The fallback buffer in case the rendering fails.
- fallback: SolidColorRenderElement,
+pub struct OffscreenBuffer {
+ /// The cached texture buffer.
+ ///
+ /// Lazily created when `render` is called. Recreated when necessary.
+ buffer: RefCell<Option<TextureBuffer<GlesTexture>>>,
}
-impl OffscreenRenderElement {
- pub fn new(
+impl OffscreenBuffer {
+ pub fn render(
+ &self,
renderer: &mut GlesRenderer,
- scale: i32,
+ scale: Scale<f64>,
elements: &[impl RenderElement<GlesRenderer>],
- result_alpha: f32,
- ) -> Self {
- let _span = tracy_client::span!("OffscreenRenderElement::new");
-
- let geo = elements
- .iter()
- .map(|ele| ele.geometry(Scale::from(f64::from(scale))))
- .reduce(|a, b| a.merge(b))
- .unwrap_or_default();
- let logical_size = geo.size.to_logical(scale);
-
- let fallback_buffer = SolidColorBuffer::new(logical_size, [1., 0., 0., 1.]);
- let fallback = SolidColorRenderElement::from_buffer(
- &fallback_buffer,
- geo.loc,
- Scale::from(scale as f64),
- result_alpha,
- Kind::Unspecified,
- );
+ ) -> anyhow::Result<(TextureBuffer<GlesTexture>, SyncPoint, Point<f64, Logical>)> {
+ let _span = tracy_client::span!("OffscreenBuffer::render");
+ let geo = encompassing_geo(scale, elements.iter());
let elements = elements.iter().rev().map(|ele| {
- RelocateRenderElement::from_element(ele, (-geo.loc.x, -geo.loc.y), Relocate::Relative)
+ RelocateRenderElement::from_element(ele, geo.loc.upscale(-1), Relocate::Relative)
});
- match render_to_texture(
- renderer,
- geo.size,
- Scale::from(scale as f64),
- Transform::Normal,
- Fourcc::Abgr8888,
- elements,
- ) {
- Ok((texture, _sync_point)) => {
- let buffer = TextureBuffer::from_texture(
- renderer,
- texture,
- scale as f64,
- Transform::Normal,
- Vec::new(),
- );
- let element = TextureRenderElement::from_texture_buffer(
- buffer,
- geo.loc.to_f64().to_logical(scale as f64),
- result_alpha,
- None,
- None,
- Kind::Unspecified,
- );
- Self {
- texture: Some(PrimaryGpuTextureRenderElement(element)),
- fallback,
- }
- }
- Err(err) => {
- warn!("error off-screening elements: {err:?}");
- Self {
- texture: None,
- fallback,
- }
- }
- }
- }
-}
+ let buffer_size = geo.size.to_logical(1).to_buffer(1, Transform::Normal);
+ let offset = geo.loc.to_f64().to_logical(scale);
-impl Element for OffscreenRenderElement {
- fn id(&self) -> &Id {
- if let Some(texture) = &self.texture {
- texture.id()
- } else {
- self.fallback.id()
- }
- }
+ let mut buffer = self.buffer.borrow_mut();
- fn current_commit(&self) -> CommitCounter {
- if let Some(texture) = &self.texture {
- texture.current_commit()
- } else {
- self.fallback.current_commit()
- }
- }
+ // 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 old_size != buffer_size {
+ size_string = format!(
+ "size changed from {} × {} to {} × {}",
+ old_size.w, old_size.h, buffer_size.w, buffer_size.h
+ );
+ reason = &size_string;
- fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
- if let Some(texture) = &self.texture {
- texture.geometry(scale)
- } else {
- self.fallback.geometry(scale)
- }
- }
+ *buffer = None;
+ } else if !buf.is_texture_reference_unique() {
+ reason = "not unique";
- fn transform(&self) -> Transform {
- if let Some(texture) = &self.texture {
- texture.transform()
+ *buffer = None;
+ }
} else {
- self.fallback.transform()
+ reason = "first render";
}
- }
- fn src(&self) -> Rectangle<f64, Buffer> {
- if let Some(texture) = &self.texture {
- texture.src()
+ let buffer = if let Some(buffer) = buffer.as_mut() {
+ buffer
} else {
- self.fallback.src()
- }
- }
+ trace!("creating new texture: {reason}");
+ let span = tracy_client::span!("creating offscreen buffer");
+ span.emit_text(reason);
- fn damage_since(
- &self,
- scale: Scale<f64>,
- commit: Option<CommitCounter>,
- ) -> DamageSet<i32, Physical> {
- if let Some(texture) = &self.texture {
- texture.damage_since(scale, commit)
- } else {
- self.fallback.damage_since(scale, commit)
- }
- }
+ let texture: GlesTexture = renderer
+ .create_buffer(Fourcc::Abgr8888, buffer_size)
+ .context("error creating texture")?;
- fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {
- if let Some(texture) = &self.texture {
- texture.opaque_regions(scale)
- } else {
- self.fallback.opaque_regions(scale)
- }
- }
+ buffer.insert(TextureBuffer::from_texture(
+ renderer,
+ texture,
+ scale,
+ Transform::Normal,
+ Vec::new(),
+ ))
+ };
- fn alpha(&self) -> f32 {
- if let Some(texture) = &self.texture {
- texture.alpha()
- } else {
- self.fallback.alpha()
- }
- }
+ // Update the texture scale.
+ buffer.set_texture_scale(scale);
- fn kind(&self) -> Kind {
- if let Some(texture) = &self.texture {
- texture.kind()
- } else {
- self.fallback.kind()
- }
- }
-}
+ // Increment the commit counter since we're rendering new contents to the buffer.
+ buffer.increment_commit_counter();
-impl RenderElement<GlesRenderer> for OffscreenRenderElement {
- fn draw(
- &self,
- frame: &mut GlesFrame<'_, '_>,
- src: Rectangle<f64, Buffer>,
- dst: Rectangle<i32, Physical>,
- damage: &[Rectangle<i32, Physical>],
- opaque_regions: &[Rectangle<i32, Physical>],
- ) -> Result<(), GlesError> {
- let gles_frame = frame.as_gles_frame();
- if let Some(texture) = &self.texture {
- RenderElement::<GlesRenderer>::draw(
- texture,
- gles_frame,
- src,
- dst,
- damage,
- opaque_regions,
- )?;
- } else {
- RenderElement::<GlesRenderer>::draw(
- &self.fallback,
- gles_frame,
- src,
- dst,
- damage,
- opaque_regions,
- )?;
- }
- Ok(())
- }
+ // Render to the buffer.
+ let mut texture = buffer.texture().clone();
+ let mut target = renderer
+ .bind(&mut texture)
+ .context("error binding texture")?;
- fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> {
- if let Some(texture) = &self.texture {
- texture.underlying_storage(renderer)
- } else {
- self.fallback.underlying_storage(renderer)
- }
- }
-}
+ let sync_point = render_elements(
+ renderer,
+ &mut target,
+ geo.size,
+ scale,
+ Transform::Normal,
+ elements,
+ )?;
-impl<'render> RenderElement<TtyRenderer<'render>> for OffscreenRenderElement {
- fn draw(
- &self,
- frame: &mut TtyFrame<'_, '_, '_>,
- src: Rectangle<f64, Buffer>,
- dst: Rectangle<i32, Physical>,
- damage: &[Rectangle<i32, Physical>],
- opaque_regions: &[Rectangle<i32, Physical>],
- ) -> Result<(), TtyRendererError<'render>> {
- let gles_frame = frame.as_gles_frame();
- if let Some(texture) = &self.texture {
- RenderElement::<GlesRenderer>::draw(
- texture,
- gles_frame,
- src,
- dst,
- damage,
- opaque_regions,
- )?;
- } else {
- RenderElement::<GlesRenderer>::draw(
- &self.fallback,
- gles_frame,
- src,
- dst,
- damage,
- opaque_regions,
- )?;
- }
- Ok(())
+ Ok((buffer.clone(), sync_point, offset))
}
+}
- fn underlying_storage(&self, renderer: &mut TtyRenderer<'render>) -> Option<UnderlyingStorage> {
- if let Some(texture) = &self.texture {
- texture.underlying_storage(renderer)
- } else {
- self.fallback.underlying_storage(renderer)
+impl Default for OffscreenBuffer {
+ fn default() -> Self {
+ OffscreenBuffer {
+ buffer: RefCell::new(None),
}
}
}
diff --git a/src/render_helpers/texture.rs b/src/render_helpers/texture.rs
index a5d38a6f..e88ac0e3 100644
--- a/src/render_helpers/texture.rs
+++ b/src/render_helpers/texture.rs
@@ -1,5 +1,6 @@
use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
+use smithay::backend::renderer::gles::GlesTexture;
use smithay::backend::renderer::utils::{CommitCounter, OpaqueRegions};
use smithay::backend::renderer::{Frame as _, ImportMem, Renderer, Texture};
use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};
@@ -104,6 +105,10 @@ impl<T> TextureBuffer<T> {
pub fn set_texture_transform(&mut self, transform: Transform) {
self.transform = transform;
}
+
+ pub fn increment_commit_counter(&mut self) {
+ self.commit_counter.increment();
+ }
}
impl<T: Texture> TextureBuffer<T> {
@@ -115,6 +120,12 @@ impl<T: Texture> TextureBuffer<T> {
}
}
+impl TextureBuffer<GlesTexture> {
+ pub fn is_texture_reference_unique(&mut self) -> bool {
+ self.texture.is_unique_reference()
+ }
+}
+
impl<T> TextureRenderElement<T> {
pub fn from_texture_buffer(
buffer: TextureBuffer<T>,