diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2025-03-01 09:45:57 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2025-03-10 07:59:14 +0300 |
| commit | 7b033aa7c6ed4b634224b001b3514276b9c06c64 (patch) | |
| tree | cfbd975987b2f6c04ca8d7432891363f5dcfcfef /src | |
| parent | efd8372b202ec09fe8b07b38a592790cb88db67f (diff) | |
| download | niri-7b033aa7c6ed4b634224b001b3514276b9c06c64.tar.gz niri-7b033aa7c6ed4b634224b001b3514276b9c06c64.tar.bz2 niri-7b033aa7c6ed4b634224b001b3514276b9c06c64.zip | |
offscreen: Track and return damage
This is the second part of the damage equation: now the offscreen element
itself reports correct damage, so partial redraws to the texture don't cause
full redraws of the texture element itself.
Diffstat (limited to 'src')
| -rw-r--r-- | src/layout/opening_window.rs | 34 | ||||
| -rw-r--r-- | src/render_helpers/offscreen.rs | 244 | ||||
| -rw-r--r-- | src/render_helpers/texture.rs | 4 |
3 files changed, 230 insertions, 52 deletions
diff --git a/src/layout/opening_window.rs b/src/layout/opening_window.rs index 860c8101..7e24b4cb 100644 --- a/src/layout/opening_window.rs +++ b/src/layout/opening_window.rs @@ -12,11 +12,9 @@ use smithay::utils::{Logical, Point, Rectangle, Scale, Size}; use crate::animation::Animation; use crate::niri_render_elements; -use crate::render_helpers::offscreen::OffscreenBuffer; -use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement; +use crate::render_helpers::offscreen::{OffscreenBuffer, OffscreenRenderElement}; use crate::render_helpers::shader_element::ShaderRenderElement; use crate::render_helpers::shaders::{mat3_uniform, ProgramType, Shaders}; -use crate::render_helpers::texture::TextureRenderElement; #[derive(Debug)] pub struct OpenAnimation { @@ -27,7 +25,7 @@ pub struct OpenAnimation { niri_render_elements! { OpeningWindowRenderElement => { - Texture = RelocateRenderElement<RescaleRenderElement<PrimaryGpuTextureRenderElement>>, + Offscreen = RelocateRenderElement<RescaleRenderElement<OffscreenRenderElement>>, Shader = ShaderRenderElement, } } @@ -60,17 +58,18 @@ impl OpenAnimation { let progress = self.anim.value(); let clamped_progress = self.anim.clamped_value().clamp(0., 1.); - let (buffer, _sync_point, offset) = self + let (elem, _sync_point) = self .buffer .render(renderer, scale, elements) .context("error rendering to offscreen buffer")?; - // OffscreenBuffer renders with Transform::Normal and the scale that we passed, so we can - // assume that below. - let texture = buffer.texture(); - let texture_size = buffer.logical_size(); - if Shaders::get(renderer).program(ProgramType::Open).is_some() { + // OffscreenBuffer renders with Transform::Normal and the scale that we passed, so we + // can assume that below. + let offset = elem.offset(); + let texture = elem.texture(); + let texture_size = elem.logical_size(); + let mut area = Rectangle::new(location + offset, texture_size); // Expand the area a bit to allow for more varied effects. @@ -119,27 +118,18 @@ impl OpenAnimation { .into()); } - let elem = TextureRenderElement::from_texture_buffer( - buffer, - Point::from((0., 0.)), - clamped_progress as f32, - None, - None, - Kind::Unspecified, - ); - - let elem = PrimaryGpuTextureRenderElement(elem); + let elem = elem.with_alpha(clamped_progress as f32); let center = geo_size.to_point().downscale(2.); let elem = RescaleRenderElement::from_element( elem, - (center - offset).to_physical_precise_round(scale), + center.to_physical_precise_round(scale), (progress / 2. + 0.5).max(0.), ); let elem = RelocateRenderElement::from_element( elem, - (location + offset).to_physical_precise_round(scale), + location.to_physical_precise_round(scale), Relocate::Relative, ); diff --git a/src/render_helpers/offscreen.rs b/src/render_helpers/offscreen.rs index b6bbbc12..5d33f8e8 100644 --- a/src/render_helpers/offscreen.rs +++ b/src/render_helpers/offscreen.rs @@ -4,18 +4,26 @@ 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::element::{Element, Id, Kind, RenderElement, UnderlyingStorage}; +use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, GlesTexture}; use smithay::backend::renderer::sync::SyncPoint; -use smithay::backend::renderer::{Bind as _, Color32F, Offscreen as _, Texture as _}; -use smithay::utils::{Logical, Point, Scale, Transform}; +use smithay::backend::renderer::utils::{ + CommitCounter, DamageBag, DamageSet, DamageSnapshot, OpaqueRegions, +}; +use smithay::backend::renderer::{ + Bind as _, Color32F, Frame as _, Offscreen as _, Renderer, Texture as _, +}; +use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform}; use super::encompassing_geo; -use super::texture::TextureBuffer; +use super::renderer::AsGlesFrame as _; +use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError}; /// Buffer for offscreen rendering. #[derive(Debug)] pub struct OffscreenBuffer { + id: Id, + /// The cached texture buffer. /// /// Lazily created when `render` is called. Recreated when necessary. @@ -24,8 +32,28 @@ pub struct OffscreenBuffer { #[derive(Debug)] struct Inner { - buffer: TextureBuffer<GlesTexture>, + /// The texture with offscreened contents. + texture: GlesTexture, + /// Id of the renderer that the texture comes from. + renderer_id: usize, + /// Scale of the texture. + scale: Scale<f64>, + /// Damage tracker for drawing to the texture. damage: OutputDamageTracker, + /// Damage of this offscreen element itself facing outside. + outer_damage: DamageBag<i32, Buffer>, +} + +#[derive(Debug, Clone)] +pub struct OffscreenRenderElement { + id: Id, + texture: GlesTexture, + renderer_id: usize, + scale: Scale<f64>, + damage: DamageSnapshot<i32, Buffer>, + offset: Point<f64, Logical>, + alpha: f32, + kind: Kind, } impl OffscreenBuffer { @@ -34,7 +62,7 @@ impl OffscreenBuffer { renderer: &mut GlesRenderer, scale: Scale<f64>, elements: &[impl RenderElement<GlesRenderer>], - ) -> anyhow::Result<(TextureBuffer<GlesTexture>, SyncPoint, Point<f64, Logical>)> { + ) -> anyhow::Result<(OffscreenRenderElement, SyncPoint)> { let _span = tracy_client::span!("OffscreenBuffer::render"); let geo = encompassing_geo(scale, elements.iter()); @@ -50,8 +78,13 @@ impl OffscreenBuffer { // Check if we need to create or recreate the texture. let size_string; let mut reason = ""; - if let Some(Inner { buffer, .. }) = inner.as_mut() { - let old_size = buffer.texture().size(); + if let Some(Inner { + texture, + renderer_id, + .. + }) = inner.as_mut() + { + let old_size = texture.size(); if old_size != buffer_size { size_string = format!( "size changed from {} × {} to {} × {}", @@ -60,10 +93,14 @@ impl OffscreenBuffer { reason = &size_string; *inner = None; - } else if !buffer.is_texture_reference_unique() { + } else if !texture.is_unique_reference() { reason = "not unique"; *inner = None; + } else if *renderer_id != renderer.id() { + reason = "renderer id changed"; + + *inner = None; } } else { reason = "first render"; @@ -80,30 +117,29 @@ impl OffscreenBuffer { .create_buffer(Fourcc::Abgr8888, buffer_size) .context("error creating 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 }) + inner.insert(Inner { + texture, + renderer_id: renderer.id(), + scale, + damage, + outer_damage: DamageBag::default(), + }) }; // 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); + if inner.scale != scale { + inner.scale = scale; trace!("recreating damage tracker due to scale change"); inner.damage = OutputDamageTracker::new(geo.size, scale, Transform::Normal); + inner.outer_damage = DamageBag::default(); } let res = { - let mut texture = inner.buffer.texture().clone(); - let mut target = renderer.bind(&mut texture)?; + let mut target = renderer.bind(&mut inner.texture)?; inner.damage.render_output( renderer, &mut target, @@ -113,12 +149,29 @@ impl OffscreenBuffer { )? }; - if res.damage.is_some() { - // Increment the commit counter if some contents updated. - inner.buffer.increment_commit_counter(); + // Add the resulting damage to the outer tracker. + if let Some(damage) = res.damage { + // OutputDamageTracker gives us Physical coordinate space, but it's actually the Buffer + // space because we were rendering to a texture. + let size = geo.size.to_logical(1); + let damage = damage + .iter() + .map(|rect| rect.to_logical(1).to_buffer(1, Transform::Normal, &size)); + inner.outer_damage.add(damage); } - Ok((inner.buffer.clone(), res.sync, offset)) + let elem = OffscreenRenderElement { + id: self.id.clone(), + texture: inner.texture.clone(), + renderer_id: inner.renderer_id, + scale, + damage: inner.outer_damage.snapshot(), + offset, + alpha: 1., + kind: Kind::Unspecified, + }; + + Ok((elem, res.sync)) } } @@ -126,6 +179,145 @@ impl Default for OffscreenBuffer { fn default() -> Self { OffscreenBuffer { inner: RefCell::new(None), + id: Id::new(), + } + } +} + +impl OffscreenRenderElement { + pub fn texture(&self) -> &GlesTexture { + &self.texture + } + + pub fn offset(&self) -> Point<f64, Logical> { + self.offset + } + + pub fn with_alpha(mut self, alpha: f32) -> Self { + self.alpha = alpha; + self + } + + pub fn logical_size(&self) -> Size<f64, Logical> { + self.texture + .size() + .to_f64() + .to_logical(self.scale, Transform::Normal) + } + + fn damage_since(&self, commit: Option<CommitCounter>) -> DamageSet<i32, Buffer> { + self.damage + .damage_since(commit) + .unwrap_or_else(|| DamageSet::from_slice(&[Rectangle::from_size(self.texture.size())])) + } +} + +impl Element for OffscreenRenderElement { + fn id(&self) -> &Id { + &self.id + } + + fn current_commit(&self) -> CommitCounter { + self.damage.current_commit() + } + + fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> { + let logical_geo = Rectangle::new(self.offset, self.logical_size()); + logical_geo.to_physical_precise_round(scale) + } + + fn transform(&self) -> Transform { + Transform::Normal + } + + fn src(&self) -> Rectangle<f64, Buffer> { + Rectangle::from_size(self.texture.size()).to_f64() + } + + fn damage_since( + &self, + scale: Scale<f64>, + commit: Option<CommitCounter>, + ) -> DamageSet<i32, Physical> { + let texture_size = self.texture.size().to_f64(); + + self.damage_since(commit) + .into_iter() + .map(|rect| { + rect.to_f64() + .to_logical(self.scale, Transform::Normal, &texture_size) + .to_physical_precise_up(scale) + }) + .collect() + } + + fn opaque_regions(&self, _scale: Scale<f64>) -> OpaqueRegions<i32, Physical> { + OpaqueRegions::default() + } + + fn alpha(&self) -> f32 { + self.alpha + } + + fn kind(&self) -> Kind { + self.kind + } +} + +impl RenderElement<GlesRenderer> for OffscreenRenderElement { + fn draw( + &self, + frame: &mut GlesFrame<'_, '_>, + src: Rectangle<f64, Buffer>, + dest: Rectangle<i32, Physical>, + damage: &[Rectangle<i32, Physical>], + opaque_regions: &[Rectangle<i32, Physical>], + ) -> Result<(), GlesError> { + if frame.id() != self.renderer_id { + warn!("trying to render texture from different renderer"); + return Ok(()); } + + frame.render_texture_from_to( + &self.texture, + src, + dest, + damage, + opaque_regions, + Transform::Normal, + self.alpha, + None, + &[], + ) + } + + fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> { + // If scanout for things other than Wayland buffers is implemented, this will need to take + // the target GPU into account. + None + } +} + +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(); + RenderElement::<GlesRenderer>::draw(&self, gles_frame, src, dst, damage, opaque_regions)?; + Ok(()) + } + + fn underlying_storage( + &self, + _renderer: &mut TtyRenderer<'render>, + ) -> Option<UnderlyingStorage> { + // If scanout for things other than Wayland buffers is implemented, this will need to take + // the target GPU into account. + None } } diff --git a/src/render_helpers/texture.rs b/src/render_helpers/texture.rs index e88ac0e3..2967c361 100644 --- a/src/render_helpers/texture.rs +++ b/src/render_helpers/texture.rs @@ -105,10 +105,6 @@ 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> { |
