From 0761401650660c632b41715418b57bf2a40a9da3 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Wed, 7 Feb 2024 11:30:33 +0400 Subject: Add OffscreenRenderElement --- src/render_helpers/mod.rs | 1 + src/render_helpers/offscreen.rs | 216 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 src/render_helpers/offscreen.rs 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, + // The fallback buffer in case the rendering fails. + fallback: SolidColorRenderElement, +} + +impl OffscreenRenderElement { + pub fn new( + renderer: &mut GlesRenderer, + scale: i32, + elements: &[impl RenderElement], + 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) -> Rectangle { + 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 { + if let Some(texture) = &self.texture { + texture.src() + } else { + self.fallback.src() + } + } + + fn damage_since( + &self, + scale: Scale, + commit: Option, + ) -> Vec> { + if let Some(texture) = &self.texture { + texture.damage_since(scale, commit) + } else { + self.fallback.damage_since(scale, commit) + } + } + + fn opaque_regions(&self, scale: Scale) -> Vec> { + 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 for OffscreenRenderElement { + fn draw( + &self, + frame: &mut GlesFrame<'_>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + ) -> Result<(), GlesError> { + let gles_frame = frame.as_gles_frame(); + if let Some(texture) = &self.texture { + RenderElement::::draw(texture, gles_frame, src, dst, damage)?; + } else { + RenderElement::::draw(&self.fallback, gles_frame, src, dst, damage)?; + } + Ok(()) + } + + fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option { + if let Some(texture) = &self.texture { + texture.underlying_storage(renderer) + } else { + self.fallback.underlying_storage(renderer) + } + } +} + +impl<'render, 'alloc> RenderElement> for OffscreenRenderElement { + fn draw( + &self, + frame: &mut TtyFrame<'_, '_, '_>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + ) -> Result<(), TtyRendererError<'render, 'alloc>> { + let gles_frame = frame.as_gles_frame(); + if let Some(texture) = &self.texture { + RenderElement::::draw(texture, gles_frame, src, dst, damage)?; + } else { + RenderElement::::draw(&self.fallback, gles_frame, src, dst, damage)?; + } + Ok(()) + } + + fn underlying_storage( + &self, + renderer: &mut TtyRenderer<'render, 'alloc>, + ) -> Option { + if let Some(texture) = &self.texture { + texture.underlying_storage(renderer) + } else { + self.fallback.underlying_storage(renderer) + } + } +} -- cgit