From 39b1ac295556a914d7606ec86084b67afb7270d9 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 5 Jun 2025 13:40:47 -0700 Subject: [PATCH] kms/surface: Split off part of `redraw` into a `send_screencopy_result` This should not change behavior in any way. --- src/backend/kms/surface/mod.rs | 525 +++++++++++++++++---------------- 1 file changed, 265 insertions(+), 260 deletions(-) diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index 78b94f00..82a25d53 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -22,10 +22,18 @@ use anyhow::{Context, Result}; use calloop::channel::Channel; use smithay::{ backend::{ - allocator::{format::FormatSet, gbm::GbmAllocator, Fourcc}, + allocator::{ + format::FormatSet, + gbm::{GbmAllocator, GbmBuffer}, + Fourcc, + }, drm::{ - compositor::{BlitFrameResultError, FrameError, FrameFlags, PrimaryPlaneElement}, + compositor::{ + BlitFrameResultError, FrameError, FrameFlags, PrimaryPlaneElement, + RenderFrameResult, + }, exporter::gbm::GbmFramebufferExporter, + gbm::GbmFramebuffer, output::DrmOutput, DrmDeviceFd, DrmEventMetadata, DrmEventTime, DrmNode, VrrSupport, }, @@ -223,6 +231,14 @@ pub enum SurfaceCommand { RenderStates(RenderElementStates), } +#[derive(Debug, Default)] +struct PrePostprocessData { + states: Option, + texture: Option, + cursor_texture: Option, + cursor_geometry: Option>, +} + impl Surface { pub fn new( output: &Output, @@ -1107,14 +1123,6 @@ impl SurfaceThreadState { || !self.screen_filter.is_noop() }); - #[derive(Debug, Default)] - struct PrePostprocessData { - states: Option, - texture: Option, - cursor_texture: Option, - cursor_geometry: Option>, - } - let mut pre_postprocess_data = PrePostprocessData::default(); let res = if let Some(source_output) = source_output { @@ -1454,257 +1462,18 @@ impl SurfaceThreadState { }; } - for (session, frame, res) in frames { - let damage = match res { - Ok((damage, _)) => damage, - Err(err) => { - tracing::warn!(?err, "Failed to screencopy"); - session - .user_data() - .get::() - .unwrap() - .lock() - .unwrap() - .reset(); - frame.fail(FailureReason::Unknown); - continue; - } - }; - - let mut sync = SyncPoint::default(); - let mut dmabuf_clone; - let mut render_buffer; - let buffer = frame.buffer(); - let mut shm_buffer = false; - let mut fb = if let Ok(dmabuf) = get_dmabuf(&buffer) { - dmabuf_clone = dmabuf.clone(); - Some(renderer - .bind(&mut dmabuf_clone) - .map_err(RenderError::<::Error>::Rendering)?) - } else { - shm_buffer = true; - let size = buffer_dimensions(&buffer).ok_or(RenderError::< - ::Error, - >::Rendering( - MultiError::ImportFailed, - ))?; - let format = - with_buffer_contents(&buffer, |_, _, data| shm_format_to_fourcc(data.format)) - .map_err(|_| OutputNoMode)? // eh, we have to do some error - .expect("We should be able to convert all hardcoded shm screencopy formats"); - - if pre_postprocess_data - .texture - .as_ref() - .is_some_and(|tex| tex.format() == Some(format)) - && (session.draw_cursor() == false - || pre_postprocess_data.cursor_texture.is_none()) - { - None - } else { - render_buffer = - Offscreen::::create_buffer( - &mut renderer, - format, - size, - ) - .map_err(RenderError::<::Error>::Rendering)?; - Some(renderer - .bind(&mut render_buffer) - .map_err(RenderError::<::Error>::Rendering)?) - } - }; - - if let Some(ref damage) = damage { - let (output_size, output_scale, output_transform) = ( - self.output.current_mode().ok_or(OutputNoMode)?.size, - self.output.current_scale().fractional_scale(), - self.output.current_transform(), - ); - - let filter = (!session.draw_cursor()) - .then(|| { - elements.iter().filter_map(|elem| { - if let CosmicElement::Cursor(_) = elem { - Some(elem.id().clone()) - } else { - None - } - }) - }) - .into_iter() - .flatten(); - - // If the screen is rotated, we must convert damage to match output. - let adjusted = damage - .iter() - .copied() - .map(|mut d| { - d.size = d - .size - .to_logical(1) - .to_buffer(1, output_transform) - .to_logical(1, Transform::Normal) - .to_physical(1); - d - }) - .collect::>(); - - if let Some(tex) = pre_postprocess_data.texture.as_mut() { - let mut tex_fb = renderer.bind(tex).map_err(RenderError::<::Error>::Rendering)?; - - if let Some(fb) = fb.as_mut() { - for rect in adjusted.iter().copied() { - renderer - .blit( - &mut tex_fb, - fb, - rect, - rect, - TextureFilter::Linear, - ) - .map_err( - RenderError::< - ::Error, - >::Rendering, - )?; - } - if let Some(cursor_geometry) = pre_postprocess_data - .cursor_geometry - .as_ref() - .filter(|_| session.draw_cursor()) - { - let cursor_damage = adjusted - .iter() - .filter_map(|rect| { - cursor_geometry.intersection(*rect) - }) - .map(|rect| { - Rectangle::new( - rect.loc - cursor_geometry.loc, - rect.size, - ) - }) - .collect::>(); - let mut frame = renderer - .render(fb, output_size, output_transform) - .map_err( - RenderError::< - ::Error, - >::Rendering, - )?; - frame - .as_mut() - .render_texture_from_to( - pre_postprocess_data - .cursor_texture - .as_ref() - .unwrap(), - Rectangle::new( - Point::from((0., 0.)), - cursor_geometry - .size - .to_logical(1) - .to_buffer(1, Transform::Normal) - .to_f64(), - ), - *cursor_geometry, - &cursor_damage, - &[*cursor_geometry], - Transform::Normal, - 1.0, - ) - .map_err(GlMultiError::Render) - .map_err( - RenderError::< - ::Error, - >::Rendering, - )?; - let sync = frame.finish().map_err( - RenderError::< - ::Error, - >::Rendering, - )?; - renderer.wait(&sync).map_err( - RenderError::< - ::Error, - >::Rendering, - )?; - } - } else { - fb = Some(tex_fb); - } - } else { - match frame_result - .blit_frame_result( - output_size, - output_transform, - output_scale, - &mut renderer, - fb.as_mut().unwrap(), - adjusted, - filter, - ) - .map_err(|err| match err { - BlitFrameResultError::Rendering(err) => RenderError::< - ::Error, - >::Rendering( - err - ), - BlitFrameResultError::Export(_) => RenderError::< - ::Error, - >::Rendering( - MultiError::DeviceMissing, - ), - }) { - Ok(new_sync) => { - sync = new_sync; - } - Err(err) => { - tracing::warn!(?err, "Failed to screencopy"); - session - .user_data() - .get::() - .unwrap() - .lock() - .unwrap() - .reset(); - frame.fail(FailureReason::Unknown); - continue; - } - }; - }; - } - - let transform = self.output.current_transform(); - - match submit_buffer( - frame, + let now = self.clock.now(); + for frame in frames { + send_screencopy_result( &mut renderer, - shm_buffer.then_some(fb.as_mut().unwrap()), - transform, - damage.as_deref(), - sync, - ) { - Ok(Some((frame, damage))) => { - if frame_result.is_empty { - frame.success(transform, damage, self.clock.now()); - } else { - let _ = tx.send((frame, damage)); - } - } - Ok(None) => {} - Err(err) => { - session - .user_data() - .get::() - .unwrap() - .lock() - .unwrap() - .reset(); - tracing::warn!(?err, "Failed to screencopy"); - } - } + &self.output, + &mut pre_postprocess_data, + &tx, + &frame_result, + &elements, + frame, + now.into(), + )?; } if self.mirroring.is_none() { @@ -1941,3 +1710,239 @@ fn get_surface_dmabuf_feedback( primary_scanout_feedback, } } + +fn send_screencopy_result<'a>( + renderer: &mut GlMultiRenderer<'a>, + output: &Output, + pre_postprocess_data: &mut PrePostprocessData, + tx: &std::sync::mpsc::Sender<(ScreencopyFrame, Vec>)>, + frame_result: &RenderFrameResult>>, + elements: &[CosmicElement], + (session, frame, res): ( + ScreencopySessionRef, + ScreencopyFrame, + Result<(Option>>, RenderElementStates), OutputNoMode>, + ), + presentation_time: Duration, +) -> Result<()> { + let damage = match res { + Ok((damage, _)) => damage, + Err(err) => { + tracing::warn!(?err, "Failed to screencopy"); + session + .user_data() + .get::() + .unwrap() + .lock() + .unwrap() + .reset(); + frame.fail(FailureReason::Unknown); + return Ok(()); + } + }; + + let mut sync = SyncPoint::default(); + let mut dmabuf_clone; + let mut render_buffer; + let buffer = frame.buffer(); + let mut shm_buffer = false; + let mut fb = if let Ok(dmabuf) = get_dmabuf(&buffer) { + dmabuf_clone = dmabuf.clone(); + Some( + renderer + .bind(&mut dmabuf_clone) + .map_err(RenderError::<::Error>::Rendering)?, + ) + } else { + shm_buffer = true; + let size = + buffer_dimensions(&buffer).ok_or(RenderError::< + ::Error, + >::Rendering(MultiError::ImportFailed))?; + let format = with_buffer_contents(&buffer, |_, _, data| shm_format_to_fourcc(data.format)) + .map_err(|_| OutputNoMode)? // eh, we have to do some error + .expect("We should be able to convert all hardcoded shm screencopy formats"); + + if pre_postprocess_data + .texture + .as_ref() + .is_some_and(|tex| tex.format() == Some(format)) + && (session.draw_cursor() == false || pre_postprocess_data.cursor_texture.is_none()) + { + None + } else { + render_buffer = Offscreen::::create_buffer(renderer, format, size) + .map_err(RenderError::<::Error>::Rendering)?; + Some( + renderer + .bind(&mut render_buffer) + .map_err(RenderError::<::Error>::Rendering)?, + ) + } + }; + + if let Some(ref damage) = damage { + let (output_size, output_scale, output_transform) = ( + output.current_mode().ok_or(OutputNoMode)?.size, + output.current_scale().fractional_scale(), + output.current_transform(), + ); + + let filter = (!session.draw_cursor()) + .then(|| { + elements.iter().filter_map(|elem| { + if let CosmicElement::Cursor(_) = elem { + Some(elem.id().clone()) + } else { + None + } + }) + }) + .into_iter() + .flatten(); + + // If the screen is rotated, we must convert damage to match output. + let adjusted = damage + .iter() + .copied() + .map(|mut d| { + d.size = d + .size + .to_logical(1) + .to_buffer(1, output_transform) + .to_logical(1, Transform::Normal) + .to_physical(1); + d + }) + .collect::>(); + + if let Some(tex) = pre_postprocess_data.texture.as_mut() { + let mut tex_fb = renderer + .bind(tex) + .map_err(RenderError::<::Error>::Rendering)?; + + if let Some(fb) = fb.as_mut() { + for rect in adjusted.iter().copied() { + renderer + .blit(&mut tex_fb, fb, rect, rect, TextureFilter::Linear) + .map_err( + RenderError::<::Error>::Rendering, + )?; + } + if let Some(cursor_geometry) = pre_postprocess_data + .cursor_geometry + .as_ref() + .filter(|_| session.draw_cursor()) + { + let cursor_damage = adjusted + .iter() + .filter_map(|rect| cursor_geometry.intersection(*rect)) + .map(|rect| Rectangle::new(rect.loc - cursor_geometry.loc, rect.size)) + .collect::>(); + let mut frame = renderer.render(fb, output_size, output_transform).map_err( + RenderError::<::Error>::Rendering, + )?; + frame + .as_mut() + .render_texture_from_to( + pre_postprocess_data.cursor_texture.as_ref().unwrap(), + Rectangle::new( + Point::from((0., 0.)), + cursor_geometry + .size + .to_logical(1) + .to_buffer(1, Transform::Normal) + .to_f64(), + ), + *cursor_geometry, + &cursor_damage, + &[*cursor_geometry], + Transform::Normal, + 1.0, + ) + .map_err(GlMultiError::Render) + .map_err( + RenderError::<::Error>::Rendering, + )?; + let sync = frame.finish().map_err( + RenderError::<::Error>::Rendering, + )?; + renderer.wait(&sync).map_err( + RenderError::<::Error>::Rendering, + )?; + } + } else { + fb = Some(tex_fb); + } + } else { + match frame_result + .blit_frame_result( + output_size, + output_transform, + output_scale, + renderer, + fb.as_mut().unwrap(), + adjusted, + filter, + ) + .map_err(|err| match err { + BlitFrameResultError::Rendering(err) => { + RenderError::<::Error>::Rendering(err) + } + BlitFrameResultError::Export(_) => { + RenderError::<::Error>::Rendering( + MultiError::DeviceMissing, + ) + } + }) { + Ok(new_sync) => { + sync = new_sync; + } + Err(err) => { + tracing::warn!(?err, "Failed to screencopy"); + session + .user_data() + .get::() + .unwrap() + .lock() + .unwrap() + .reset(); + frame.fail(FailureReason::Unknown); + return Ok(()); + } + }; + }; + } + + let transform = output.current_transform(); + + match submit_buffer( + frame, + renderer, + shm_buffer.then_some(fb.as_mut().unwrap()), + transform, + damage.as_deref(), + sync, + ) { + Ok(Some((frame, damage))) => { + if frame_result.is_empty { + frame.success(transform, damage, presentation_time); + } else { + let _ = tx.send((frame, damage)); + } + } + Ok(None) => {} + Err(err) => { + session + .user_data() + .get::() + .unwrap() + .lock() + .unwrap() + .reset(); + tracing::warn!(?err, "Failed to screencopy"); + } + } + + Ok(()) +}