diff options
| -rw-r--r-- | src/render_helpers/mod.rs | 1 | ||||
| -rw-r--r-- | src/render_helpers/offscreen.rs | 216 |
2 files changed, 217 insertions, 0 deletions
diff --git a/src/render_helpers/mod.rs b/src/render_helpers/mod.rs index 47853856..c05877f9 100644 --- a/src/render_helpers/mod.rs +++ b/src/render_helpers/mod.rs @@ -6,6 +6,7 @@ use smithay::backend::renderer::sync::SyncPoint; use smithay::backend::renderer::{Bind, ExportMem, Frame, Offscreen, Renderer}; use smithay::utils::{Physical, Rectangle, Scale, Size, Transform}; +pub mod offscreen; pub mod primary_gpu_texture; pub mod render_elements; pub mod renderer; diff --git a/src/render_helpers/offscreen.rs b/src/render_helpers/offscreen.rs new file mode 100644 index 00000000..35e38bfb --- /dev/null +++ b/src/render_helpers/offscreen.rs @@ -0,0 +1,216 @@ +use smithay::backend::allocator::Fourcc; +use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement}; +use smithay::backend::renderer::element::texture::{TextureBuffer, TextureRenderElement}; +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; +use smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform}; + +use super::primary_gpu_texture::PrimaryGpuTextureRenderElement; +use super::render_to_texture; +use super::renderer::AsGlesFrame; +use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError}; + +/// Renders elements into an off-screen buffer. +#[derive(Debug)] +pub struct OffscreenRenderElement { + // The texture, if rendering succeeded. + texture: Option<PrimaryGpuTextureRenderElement>, + // The fallback buffer in case the rendering fails. + fallback: SolidColorRenderElement, +} + +impl OffscreenRenderElement { + pub fn new( + renderer: &mut GlesRenderer, + scale: i32, + elements: &[impl RenderElement<GlesRenderer>], + result_alpha: f32, + ) -> Self { + 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, + ); + + let elements = elements.iter().rev().map(|ele| { + RelocateRenderElement::from_element(ele, (-geo.loc.x, -geo.loc.y), Relocate::Relative) + }); + + match render_to_texture( + renderer, + geo.size, + Scale::from(scale as f64), + Fourcc::Abgr8888, + elements, + ) { + Ok((texture, _sync_point)) => { + let buffer = + TextureBuffer::from_texture(renderer, texture, scale, Transform::Normal, None); + let element = TextureRenderElement::from_texture_buffer( + geo.loc.to_f64(), + &buffer, + Some(result_alpha), + None, + None, + Kind::Unspecified, + ); + Self { + texture: Some(PrimaryGpuTextureRenderElement(element)), + fallback, + } + } + Err(err) => { + warn!("error off-screening elements: {err:?}"); + Self { + texture: None, + fallback, + } + } + } + } +} + +impl Element for OffscreenRenderElement { + fn id(&self) -> &Id { + if let Some(texture) = &self.texture { + texture.id() + } else { + self.fallback.id() + } + } + + fn current_commit(&self) -> CommitCounter { + if let Some(texture) = &self.texture { + texture.current_commit() + } else { + self.fallback.current_commit() + } + } + + fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> { + if let Some(texture) = &self.texture { + texture.geometry(scale) + } else { + self.fallback.geometry(scale) + } + } + + fn transform(&self) -> Transform { + if let Some(texture) = &self.texture { + texture.transform() + } else { + self.fallback.transform() + } + } + + fn src(&self) -> Rectangle<f64, Buffer> { + if let Some(texture) = &self.texture { + texture.src() + } else { + self.fallback.src() + } + } + + fn damage_since( + &self, + scale: Scale<f64>, + commit: Option<CommitCounter>, + ) -> Vec<Rectangle<i32, Physical>> { + if let Some(texture) = &self.texture { + texture.damage_since(scale, commit) + } else { + self.fallback.damage_since(scale, commit) + } + } + + fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> { + if let Some(texture) = &self.texture { + texture.opaque_regions(scale) + } else { + self.fallback.opaque_regions(scale) + } + } + + fn alpha(&self) -> f32 { + if let Some(texture) = &self.texture { + texture.alpha() + } else { + self.fallback.alpha() + } + } + + fn kind(&self) -> Kind { + if let Some(texture) = &self.texture { + texture.kind() + } else { + self.fallback.kind() + } + } +} + +impl RenderElement<GlesRenderer> for OffscreenRenderElement { + fn draw( + &self, + frame: &mut GlesFrame<'_>, + src: Rectangle<f64, Buffer>, + dst: Rectangle<i32, Physical>, + damage: &[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)?; + } else { + RenderElement::<GlesRenderer>::draw(&self.fallback, gles_frame, src, dst, damage)?; + } + Ok(()) + } + + 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) + } + } +} + +impl<'render, 'alloc> RenderElement<TtyRenderer<'render, 'alloc>> for OffscreenRenderElement { + fn draw( + &self, + frame: &mut TtyFrame<'_, '_, '_>, + src: Rectangle<f64, Buffer>, + dst: Rectangle<i32, Physical>, + damage: &[Rectangle<i32, Physical>], + ) -> Result<(), TtyRendererError<'render, 'alloc>> { + let gles_frame = frame.as_gles_frame(); + if let Some(texture) = &self.texture { + RenderElement::<GlesRenderer>::draw(texture, gles_frame, src, dst, damage)?; + } else { + RenderElement::<GlesRenderer>::draw(&self.fallback, gles_frame, src, dst, damage)?; + } + Ok(()) + } + + fn underlying_storage( + &self, + renderer: &mut TtyRenderer<'render, 'alloc>, + ) -> Option<UnderlyingStorage> { + if let Some(texture) = &self.texture { + texture.underlying_storage(renderer) + } else { + self.fallback.underlying_storage(renderer) + } + } +} |
