diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2025-02-27 08:09:44 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2025-03-10 07:59:14 +0300 |
| commit | 74a30be10b347d298189a071b8ff812aedc8b08a (patch) | |
| tree | 24192b6ec6f9b54f5067ab5a4b63066fbd282fbd /src/render_helpers | |
| parent | 1c521e48313b42d2e314d139c3cf4ac5dc44e7a7 (diff) | |
| download | niri-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.rs | 305 | ||||
| -rw-r--r-- | src/render_helpers/texture.rs | 11 |
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>, |
