image-copy: Store offscreen buffer for shm capture in session

Avoid allocating a GPU buffer every frame, and avoid re-rendering
everything.
This commit is contained in:
Ian Douglas Scott 2026-03-11 19:49:02 -07:00 committed by Victoria Brekenfeld
parent e5954de6cd
commit 40c7eb26cd
2 changed files with 48 additions and 24 deletions

View file

@ -5,8 +5,8 @@ use smithay::{
backend::{ backend::{
allocator::{Buffer, Fourcc, format::get_transparent}, allocator::{Buffer, Fourcc, format::get_transparent},
renderer::{ renderer::{
BufferType, Color32F, ExportMem, ImportAll, ImportMem, Offscreen, buffer_dimensions, BufferType, Color32F, ExportMem, ImportAll, ImportMem, Offscreen, Renderer,
buffer_type, buffer_dimensions, buffer_type,
damage::{Error as DTError, OutputDamageTracker, RenderOutputResult}, damage::{Error as DTError, OutputDamageTracker, RenderOutputResult},
element::{ element::{
AsRenderElements, RenderElement, AsRenderElements, RenderElement,
@ -204,37 +204,53 @@ where
Vec<Rectangle<i32, BufferCoords>>, Vec<Rectangle<i32, BufferCoords>>,
) -> Result<RenderOutputResult<'d>, DTError<R::Error>>, ) -> Result<RenderOutputResult<'d>, DTError<R::Error>>,
{ {
let mut session_damage_tracking = session.lock().unwrap(); let mut session_user_data = session.lock().unwrap();
let buffer = frame.buffer(); let buffer = frame.buffer();
let mut offscreen = matches!(buffer_type(&buffer), Some(BufferType::Shm))
.then(|| {
let size = buffer_dimensions(&buffer).ok_or(DTError::OutputNoMode(OutputNoMode))?;
let format = with_buffer_contents(&buffer, |_, _, data| {
shm_format_to_fourcc(data.format)
.expect("We should be able to convert all hardcoded shm screencopy formats")
})
.map_err(|_| DTError::OutputNoMode(OutputNoMode))?;
Offscreen::<GlesRenderbuffer>::create_buffer(renderer, format, size)
.map_err(DTError::Rendering)
})
.transpose()?;
let age = if offscreen.is_some() { let mut age = 1;
// TODO re-use offscreen buffer to damage track screencopy to shm if matches!(buffer_type(&buffer), Some(BufferType::Shm)) {
0 let size = buffer_dimensions(&buffer).ok_or(DTError::OutputNoMode(OutputNoMode))?;
let format = with_buffer_contents(&buffer, |_, _, data| {
shm_format_to_fourcc(data.format)
.expect("We should be able to convert all hardcoded shm screencopy formats")
})
.map_err(|_| DTError::OutputNoMode(OutputNoMode))?;
// Re-allocate if context id, size, or format are different
session_user_data
.offscreen
.take_if(|(context_id, renderbuffer)| {
renderer.glow_renderer().context_id() != *context_id
|| renderbuffer.size() != size
|| renderbuffer.format() != Some(format)
});
if session_user_data.offscreen.is_none() {
let renderbuffer = Offscreen::<GlesRenderbuffer>::create_buffer(renderer, format, size)
.map_err(DTError::Rendering)?;
session_user_data.offscreen =
Some((renderer.glow_renderer().context_id(), renderbuffer));
// If we're allocating a new offscreen buffer, we need to re-render everything
// (or copy the contexts of the shm buffer)
age = 0;
}
} else { } else {
1 // If for some reason a capture session is used for shm, but then changes to dmabuf capture,
}; // remove the offscreen buffer.
session_user_data.offscreen = None;
}
let SessionUserData { dt, offscreen } = &mut *session_user_data;
let mut fb = offscreen let mut fb = offscreen
.as_mut() .as_mut()
.map(|tex| renderer.bind(tex).map_err(DTError::Rendering)) .map(|(_, tex)| renderer.bind(tex).map_err(DTError::Rendering))
.transpose()?; .transpose()?;
let res = render_fn( let res = render_fn(
&frame.buffer(), &frame.buffer(),
renderer, renderer,
fb.as_mut(), fb.as_mut(),
&mut session_damage_tracking.dt, dt,
age, age,
frame.damage(), frame.damage(),
); );

View file

@ -3,7 +3,11 @@
use std::{cell::RefCell, sync::Mutex}; use std::{cell::RefCell, sync::Mutex};
use smithay::{ use smithay::{
backend::renderer::damage::OutputDamageTracker, backend::renderer::{
ContextId,
damage::OutputDamageTracker,
gles::{GlesRenderbuffer, GlesTexture},
},
output::Output, output::Output,
wayland::image_copy_capture::{ wayland::image_copy_capture::{
CursorSession, CursorSessionRef, Frame, FrameRef, Session, SessionRef, CursorSession, CursorSessionRef, Frame, FrameRef, Session, SessionRef,
@ -19,11 +23,15 @@ pub type SessionData = Mutex<SessionUserData>;
pub struct SessionUserData { pub struct SessionUserData {
pub dt: OutputDamageTracker, pub dt: OutputDamageTracker,
pub offscreen: Option<(ContextId<GlesTexture>, GlesRenderbuffer)>,
} }
impl SessionUserData { impl SessionUserData {
pub fn new(tracker: OutputDamageTracker) -> SessionUserData { pub fn new(tracker: OutputDamageTracker) -> SessionUserData {
SessionUserData { dt: tracker } SessionUserData {
dt: tracker,
offscreen: None,
}
} }
} }