diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 8a2fd8f2..29fecb53 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -53,6 +53,7 @@ use smithay::{ element::PixelShaderElement, GlesError, GlesPixelProgram, GlesRenderer, Uniform, UniformName, UniformType, }, + glow::GlowRenderer, multigpu::{Error as MultiError, MultiFrame, MultiRenderer}, sync::SyncPoint, Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, TextureFilter, @@ -86,6 +87,29 @@ pub type GlMultiFrame<'a, 'frame> = MultiFrame<'a, 'a, 'frame, GbmGlowBackend, GbmGlowBackend>; pub type GlMultiError = MultiError, GbmGlowBackend>; +pub enum RendererRef<'a> { + Glow(&'a mut GlowRenderer), + GlMulti(GlMultiRenderer<'a>), +} + +impl<'a> AsRef for RendererRef<'a> { + fn as_ref(&self) -> &GlowRenderer { + match self { + Self::Glow(renderer) => renderer, + Self::GlMulti(renderer) => renderer.as_ref(), + } + } +} + +impl<'a> AsMut for RendererRef<'a> { + fn as_mut(&mut self) -> &mut GlowRenderer { + match self { + Self::Glow(renderer) => renderer, + Self::GlMulti(renderer) => renderer.as_mut(), + } + } +} + pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0]; pub static OUTLINE_SHADER: &str = include_str!("./shaders/rounded_outline.frag"); pub static RECTANGLE_SHADER: &str = include_str!("./shaders/rounded_rectangle.frag"); diff --git a/src/state.rs b/src/state.rs index d16065fc..5fb0070c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - backend::{kms::KmsState, winit::WinitState, x11::X11State}, + backend::{ + kms::KmsState, + render::{GlMultiError, RendererRef}, + winit::WinitState, + x11::X11State, + }, config::{Config, OutputConfig, OutputState}, input::gestures::GestureState, shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell}, @@ -26,7 +31,7 @@ use once_cell::sync::Lazy; use rust_embed::RustEmbed; use smithay::{ backend::{ - allocator::dmabuf::Dmabuf, + allocator::{dmabuf::Dmabuf, Fourcc}, drm::DrmNode, renderer::{ element::{ @@ -380,6 +385,50 @@ impl BackendData { }; Ok(None) } + + /// Get an offscreen renderer for screen capture / screenshot rendering + /// + /// `kms_node_cb` callback use used to determine nodes to render with when using kms backend. + /// Panics if this returns `None`. + pub fn offscreen_renderer, F: FnOnce(&mut KmsState) -> Option>( + &mut self, + kms_node_cb: F, + ) -> Result { + match self { + BackendData::Kms(kms) => { + let KmsNodes { + render_node, + target_node, + copy_format, + } = kms_node_cb(kms).expect("No Software Rendering").into(); + Ok(RendererRef::GlMulti(kms.api.renderer( + &render_node, + &target_node, + copy_format, + )?)) + } + BackendData::Winit(winit) => Ok(RendererRef::Glow(winit.backend.renderer())), + BackendData::X11(x11) => Ok(RendererRef::Glow(&mut x11.renderer)), + _ => unreachable!("No backend set when getting offscreen renderer"), + } + } +} + +pub struct KmsNodes { + pub render_node: DrmNode, + pub target_node: DrmNode, + pub copy_format: Fourcc, +} + +impl From for KmsNodes { + fn from(node: DrmNode) -> Self { + KmsNodes { + render_node: node, + target_node: node, + // Ignored if render == target + copy_format: Fourcc::Abgr8888, + } + } } pub fn client_has_no_security_context(client: &Client) -> bool { diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index 222f7da7..500efeaf 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -16,8 +16,9 @@ use smithay::{ use tracing::warn; use crate::{ + backend::render::RendererRef, shell::element::CosmicSurface, - state::{advertised_node_for_surface, BackendData, State}, + state::{advertised_node_for_surface, State}, }; pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { @@ -96,27 +97,21 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { } if let Some(wl_surface) = surface.wl_surface() { - let res = match &mut state.backend { - BackendData::Kms(kms) => { - let node = advertised_node_for_surface(&wl_surface, &state.common.display_handle) - .unwrap_or(kms.primary_node.expect("No Software Rendering")); - kms.api - .single_renderer(&node) - .with_context(|| "Failed to get renderer for screenshot") - .and_then(|mut multirenderer| { - render_window(&mut multirenderer, surface, &state.common.local_offset) - }) - } - BackendData::Winit(winit) => render_window( - winit.backend.renderer(), - surface, - &state.common.local_offset, - ), - BackendData::X11(x11) => { - render_window(&mut x11.renderer, surface, &state.common.local_offset) - } - BackendData::Unset => unreachable!(), - }; + let res = state + .backend + .offscreen_renderer(|kms| { + advertised_node_for_surface(&wl_surface, &state.common.display_handle) + .or(kms.primary_node) + }) + .with_context(|| "Failed to get renderer for screenshot") + .and_then(|renderer| match renderer { + RendererRef::Glow(renderer) => { + render_window(renderer, surface, &state.common.local_offset) + } + RendererRef::GlMulti(mut renderer) => { + render_window(&mut renderer, surface, &state.common.local_offset) + } + }); if let Err(err) = res { warn!(?err, "Failed to take screenshot") } diff --git a/src/wayland/handlers/screencopy/mod.rs b/src/wayland/handlers/screencopy/mod.rs index 19b04bf3..6bb221bd 100644 --- a/src/wayland/handlers/screencopy/mod.rs +++ b/src/wayland/handlers/screencopy/mod.rs @@ -351,21 +351,10 @@ fn constraints_for_output(output: &Output, backend: &mut BackendData) -> Option< } }; - let mut _kms_renderer = None; - let renderer = match backend { - BackendData::Kms(ref mut kms) => { - let node = kms - .target_node_for_output(&output) - .unwrap_or(kms.primary_node.expect("No Software Rendering")); - _kms_renderer = Some(kms.api.single_renderer(&node).unwrap()); - _kms_renderer.as_mut().unwrap().as_mut() - } - BackendData::Winit(ref mut winit) => winit.backend.renderer(), - BackendData::X11(ref mut x11) => &mut x11.renderer, - _ => unreachable!(), - }; - - Some(constraints_for_renderer(mode, renderer)) + let mut renderer = backend + .offscreen_renderer(|kms| kms.target_node_for_output(&output).or(kms.primary_node)) + .unwrap(); + Some(constraints_for_renderer(mode, renderer.as_mut())) } fn constraints_for_toplevel( @@ -375,9 +364,8 @@ fn constraints_for_toplevel( let size = surface.geometry().size.to_buffer(1, Transform::Normal); let wl_surface = surface.wl_surface()?; - let mut _kms_renderer = None; - let renderer = match backend { - BackendData::Kms(ref mut kms) => { + let mut renderer = backend + .offscreen_renderer(|kms| { let dma_node = with_renderer_surface_state(&wl_surface, |state| { let buffer = state.buffer()?; let dmabuf = get_dmabuf(&*buffer).ok()?; @@ -385,16 +373,11 @@ fn constraints_for_toplevel( }) .flatten(); - let node = dma_node.unwrap_or(kms.primary_node.expect("No Software Rendering")); - _kms_renderer = Some(kms.api.single_renderer(&node).unwrap()); - _kms_renderer.as_mut().unwrap().as_mut() - } - BackendData::Winit(ref mut winit) => winit.backend.renderer(), - BackendData::X11(ref mut x11) => &mut x11.renderer, - _ => unreachable!(), - }; + dma_node.or(kms.primary_node) + }) + .unwrap(); - Some(constraints_for_renderer(size, renderer)) + Some(constraints_for_renderer(size, renderer.as_mut())) } fn constraints_for_renderer( diff --git a/src/wayland/handlers/screencopy/render.rs b/src/wayland/handlers/screencopy/render.rs index ab1f95ea..592406fe 100644 --- a/src/wayland/handlers/screencopy/render.rs +++ b/src/wayland/handlers/screencopy/render.rs @@ -36,10 +36,10 @@ use crate::{ backend::render::{ cursor, element::{AsGlowRenderer, CosmicElement, DamageElement, FromGlesError}, - render_workspace, CursorMode, ElementFilter, CLEAR_COLOR, + render_workspace, CursorMode, ElementFilter, RendererRef, CLEAR_COLOR, }, shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement}, - state::{BackendData, Common, State}, + state::{Common, KmsNodes, State}, utils::prelude::SeatExt, wayland::{ handlers::screencopy::{ @@ -326,39 +326,39 @@ pub fn render_workspace_to_buffer( let transform = output.current_transform(); let common = &mut state.common; - let result = match &mut state.backend { - BackendData::Kms(kms) => { - let render_node = kms - .target_node_for_output(&output) - .unwrap_or(kms.primary_node.expect("No Software Rendering")); - let target_node = get_dmabuf(&buffer) - .ok() - .and_then(|dma| dma.node()) - .unwrap_or(render_node); + let renderer = match state.backend.offscreen_renderer(|kms| { + let render_node = kms.target_node_for_output(&output).or(kms.primary_node)?; + let target_node = get_dmabuf(&buffer) + .ok() + .and_then(|dma| dma.node()) + .unwrap_or(render_node); - let buffer_format = match buffer_type(&buffer) { - Some(BufferType::Dma) => Some(get_dmabuf(&buffer).unwrap().format().code), - Some(BufferType::Shm) => { - with_buffer_contents(&buffer, |_, _, data| shm_format_to_fourcc(data.format)) - .unwrap() - } - _ => None, - }; - let mut multirenderer = match kms.api.renderer( - &render_node, - &target_node, - buffer_format.unwrap_or(Fourcc::Abgr8888), - ) { - Ok(renderer) => renderer, - Err(err) => { - warn!(?err, "Couldn't use nodes for screencopy"); - frame.fail(FailureReason::Unknown); - return; - } - }; + let buffer_format = match buffer_type(&buffer) { + Some(BufferType::Dma) => Some(get_dmabuf(&buffer).unwrap().format().code), + Some(BufferType::Shm) => { + with_buffer_contents(&buffer, |_, _, data| shm_format_to_fourcc(data.format)) + .unwrap() + } + _ => None, + }; + Some(KmsNodes { + render_node, + target_node, + copy_format: buffer_format.unwrap_or(Fourcc::Abgr8888), + }) + }) { + Ok(renderer) => renderer, + Err(err) => { + warn!(?err, "Couldn't use node for screencopy"); + frame.fail(FailureReason::Unknown); + return; + } + }; + let result = match renderer { + RendererRef::Glow(renderer) => { match render_session::<_, _>( - &mut multirenderer, + renderer, session.user_data().get::().unwrap(), frame, transform, @@ -383,57 +383,33 @@ pub fn render_workspace_to_buffer( } } } - BackendData::Winit(winit) => match render_session::<_, _>( - winit.backend.renderer(), - session.user_data().get::().unwrap(), - frame, - transform, - |buffer, renderer, dt, age, additional_damage| { - render_fn( - buffer, - renderer, - dt, - age, - additional_damage, - draw_cursor, - common, - &output, - (handle, idx), - ) - }, - ) { - Ok(frame) => frame, - Err(err) => { - tracing::warn!(?err, "Failed to render to screencopy buffer"); - None + RendererRef::GlMulti(mut renderer) => { + match render_session::<_, _>( + &mut renderer, + session.user_data().get::().unwrap(), + frame, + transform, + |buffer, renderer, dt, age, additional_damage| { + render_fn( + buffer, + renderer, + dt, + age, + additional_damage, + draw_cursor, + common, + &output, + (handle, idx), + ) + }, + ) { + Ok(frame) => frame, + Err(err) => { + tracing::warn!(?err, "Failed to render to screencopy buffer"); + None + } } - }, - BackendData::X11(x11) => match render_session::<_, _>( - &mut x11.renderer, - session.user_data().get::().unwrap(), - frame, - transform, - |buffer, renderer, dt, age, additional_damage| { - render_fn( - buffer, - renderer, - dt, - age, - additional_damage, - draw_cursor, - common, - &output, - (handle, idx), - ) - }, - ) { - Ok(frame) => frame, - Err(err) => { - tracing::warn!(?err, "Failed to render to screencopy buffer"); - None - } - }, - _ => unreachable!(), + } }; if let Some((frame, damage)) = result { @@ -601,61 +577,34 @@ pub fn render_window_to_buffer( let common = &mut state.common; let draw_cursor = session.draw_cursor(); - let result = match &mut state.backend { - BackendData::Kms(kms) => { - let node = get_dmabuf(&buffer) - .ok() - .and_then(|dmabuf| dmabuf.node()) - .or_else(|| { - toplevel - .wl_surface() - .and_then(|wl_surface| { - with_renderer_surface_state(&wl_surface, |state| { - let buffer = state.buffer()?; - let dmabuf = get_dmabuf(&*buffer).ok()?; - dmabuf.node() - }) + let renderer = match state.backend.offscreen_renderer(|kms| { + get_dmabuf(&buffer) + .ok() + .and_then(|dmabuf| dmabuf.node()) + .or_else(|| { + toplevel + .wl_surface() + .and_then(|wl_surface| { + with_renderer_surface_state(&wl_surface, |state| { + let buffer = state.buffer()?; + let dmabuf = get_dmabuf(&*buffer).ok()?; + dmabuf.node() }) - .flatten() - }) - .unwrap_or(kms.primary_node.expect("No Software Rendering")); - - let mut multirenderer = match kms.api.single_renderer(&node) { - Ok(renderer) => renderer, - Err(err) => { - warn!(?err, "Couldn't use node for screencopy"); - frame.fail(FailureReason::Unknown); - return; - } - }; - match render_session::<_, _>( - &mut multirenderer, - session.user_data().get::().unwrap(), - frame, - Transform::Normal, - |buffer, renderer, dt, age, additional_damage| { - render_fn( - buffer, - renderer, - dt, - age, - additional_damage, - draw_cursor, - common, - toplevel, - geometry, - ) - }, - ) { - Ok(frame) => frame, - Err(err) => { - tracing::warn!(?err, "Failed to render to screencopy buffer"); - None - } - } + }) + .flatten() + }) + .or(kms.primary_node) + }) { + Ok(renderer) => renderer, + Err(err) => { + warn!(?err, "Couldn't use node for screencopy"); + frame.fail(FailureReason::Unknown); + return; } - BackendData::Winit(winit) => match render_session::<_, _>( - winit.backend.renderer(), + }; + let result = match renderer { + RendererRef::Glow(renderer) => match render_session::<_, _>( + renderer, session.user_data().get::().unwrap(), frame, Transform::Normal, @@ -679,8 +628,8 @@ pub fn render_window_to_buffer( None } }, - BackendData::X11(x11) => match render_session::<_, _>( - &mut x11.renderer, + RendererRef::GlMulti(mut renderer) => match render_session::<_, _>( + &mut renderer, session.user_data().get::().unwrap(), frame, Transform::Normal, @@ -704,7 +653,6 @@ pub fn render_window_to_buffer( None } }, - _ => unreachable!(), }; if let Some((frame, damage)) = result { @@ -811,21 +759,18 @@ pub fn render_cursor_to_buffer( } let common = &mut state.common; - let result = match &mut state.backend { - BackendData::Kms(kms) => { - let mut multirenderer = match kms - .api - .single_renderer(&kms.primary_node.expect("No Software Rendering")) - { - Ok(renderer) => renderer, - Err(err) => { - warn!(?err, "Couldn't use node for screencopy"); - frame.fail(FailureReason::Unknown); - return; - } - }; + let renderer = match state.backend.offscreen_renderer(|kms| kms.primary_node) { + Ok(renderer) => renderer, + Err(err) => { + warn!(?err, "Couldn't use node for screencopy"); + frame.fail(FailureReason::Unknown); + return; + } + }; + let result = match renderer { + RendererRef::Glow(renderer) => { match render_session::<_, _>( - &mut multirenderer, + renderer, session.user_data().get::().unwrap(), frame, Transform::Normal, @@ -840,37 +785,23 @@ pub fn render_cursor_to_buffer( } } } - BackendData::Winit(winit) => match render_session::<_, _>( - winit.backend.renderer(), - session.user_data().get::().unwrap(), - frame, - Transform::Normal, - |buffer, renderer, dt, age, additional_damage| { - render_fn(buffer, renderer, dt, age, additional_damage, common, seat) - }, - ) { - Ok(frame) => frame, - Err(err) => { - tracing::warn!(?err, "Failed to render to screencopy buffer"); - None + RendererRef::GlMulti(mut renderer) => { + match render_session::<_, _>( + &mut renderer, + session.user_data().get::().unwrap(), + frame, + Transform::Normal, + |buffer, renderer, dt, age, additional_damage| { + render_fn(buffer, renderer, dt, age, additional_damage, common, seat) + }, + ) { + Ok(frame) => frame, + Err(err) => { + tracing::warn!(?err, "Failed to render to screencopy buffer"); + None + } } - }, - BackendData::X11(x11) => match render_session::<_, _>( - &mut x11.renderer, - session.user_data().get::().unwrap(), - frame, - Transform::Normal, - |buffer, renderer, dt, age, additional_damage| { - render_fn(buffer, renderer, dt, age, additional_damage, common, seat) - }, - ) { - Ok(frame) => frame, - Err(err) => { - tracing::warn!(?err, "Failed to render to screencopy buffer"); - None - } - }, - _ => unreachable!(), + } }; if let Some((frame, damage)) = result {