diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 7130bf70..78a01341 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -114,6 +114,14 @@ pub enum MetalError { MissingDevModifier(&'static str), #[error("Device GFX API cannot read any buffers writable by the render GFX API (format {0})")] MissingRenderModifier(&'static str), + #[error("Could not render the frame")] + RenderFrame(#[source] GfxError), + #[error("Could not copy frame to output device")] + CopyToOutput(#[source] GfxError), + #[error("Could not perform atomic commit")] + Commit(#[source] DrmError), + #[error("Could not clear framebuffer")] + Clear(#[source] GfxError), } pub struct MetalBackend { diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index df5a1bbb..630a17b4 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -376,7 +376,9 @@ impl MetalConnector { async fn present_loop(self: Rc) { loop { self.present_trigger.triggered().await; - let _ = self.present(true); + if let Err(e) = self.present(true) { + log::error!("Could not present: {}", ErrorFmt(e)); + } } } @@ -585,7 +587,7 @@ impl MetalConnector { plane: &Rc, output: &OutputNode, try_direct_scanout: bool, - ) -> PresentFb { + ) -> Result { self.trim_scanout_cache(); let buffer_fb = buffer.render_fb(); let render_hw_cursor = !self.cursor_enabled.get(); @@ -630,23 +632,23 @@ impl MetalConnector { } let fb = match &direct_scanout_data { None => { + buffer_fb + .perform_render_pass(pass) + .map_err(MetalError::RenderFrame)?; + buffer.copy_to_dev()?; self.next_buffer.fetch_add(1); - buffer_fb.perform_render_pass(pass); - if let Some(tex) = &buffer.dev_tex { - buffer.dev_fb.copy_texture(tex, 0, 0); - } output.perform_screencopies(&buffer.render_tex, !render_hw_cursor, 0, 0, None); buffer.drm.clone() } Some(dsd) => dsd.fb.clone(), }; - PresentFb { + Ok(PresentFb { fb, direct_scanout_data, - } + }) } - pub fn present(&self, try_direct_scanout: bool) -> Result<(), ()> { + pub fn present(&self, try_direct_scanout: bool) -> Result<(), MetalError> { let crtc = match self.crtc.get() { Some(crtc) => crtc, _ => return Ok(()), @@ -676,7 +678,7 @@ impl MetalConnector { let buffer = &buffers[self.next_buffer.get() % buffers.len()]; let mut rr = self.render_result.borrow_mut(); let fb = - self.prepare_present_fb(&mut rr, buffer, &plane, &node, try_direct_scanout); + self.prepare_present_fb(&mut rr, buffer, &plane, &node, try_direct_scanout)?; rr.dispatch_frame_requests(); let (crtc_x, crtc_y, crtc_w, crtc_h, src_width, src_height) = match &fb.direct_scanout_data { @@ -721,9 +723,7 @@ impl MetalConnector { let buffers = self.cursor_buffers.get().unwrap(); let buffer = &buffers[front_buffer % buffers.len()]; if cursor_swap_buffer { - if let Some(tex) = &buffer.dev_tex { - buffer.dev_fb.copy_texture(tex, 0, 0); - } + buffer.copy_to_dev()?; } let (width, height) = buffer.dev_fb.physical_size(); changes.change_object(plane.id, |c| { @@ -746,32 +746,28 @@ impl MetalConnector { } } if let Err(e) = changes.commit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, 0) { - match e { - DrmError::Atomic(OsError(c::EACCES)) => { - log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master"); - } - _ => 'handle_failure: { - if let Some(fb) = &new_fb { - if let Some(dsd) = &fb.direct_scanout_data { - if self.present(false).is_ok() { - let mut cache = self.scanout_buffers.borrow_mut(); - if let Some(buffer) = cache.remove(&dsd.dma_buf_id) { - cache.insert( - dsd.dma_buf_id, - DirectScanoutCache { - tex: buffer.tex, - fb: None, - }, - ); - } - break 'handle_failure; - } + if let DrmError::Atomic(OsError(c::EACCES)) = e { + log::debug!("Could not perform atomic commit, likely because we're no longer the DRM master"); + return Ok(()); + } + if let Some(fb) = &new_fb { + if let Some(dsd) = &fb.direct_scanout_data { + if self.present(false).is_ok() { + let mut cache = self.scanout_buffers.borrow_mut(); + if let Some(buffer) = cache.remove(&dsd.dma_buf_id) { + cache.insert( + dsd.dma_buf_id, + DirectScanoutCache { + tex: buffer.tex, + fb: None, + }, + ); } + return Ok(()); } - log::error!("Could not set plane framebuffer: {}", ErrorFmt(e)); } } - Err(()) + Err(MetalError::Commit(e)) } else { if let Some(fb) = new_fb { self.next_framebuffer.set(Some(fb)); @@ -2160,7 +2156,7 @@ impl MetalBackend { Ok(fb) => fb, Err(e) => return Err(MetalError::ImportFb(e)), }; - dev_fb.clear(); + dev_fb.clear().map_err(MetalError::Clear)?; let (dev_tex, render_tex, render_fb) = if dev.id == render_ctx.dev_id { let render_tex = match dev_img.to_texture() { Ok(fb) => fb, @@ -2209,7 +2205,7 @@ impl MetalBackend { Ok(fb) => fb, Err(e) => return Err(MetalError::ImportFb(e)), }; - render_fb.clear(); + render_fb.clear().map_err(MetalError::Clear)?; let render_tex = match render_img.to_texture() { Ok(fb) => fb, Err(e) => return Err(MetalError::ImportTexture(e)), @@ -2429,6 +2425,15 @@ impl RenderBuffer { .clone() .unwrap_or_else(|| self.dev_fb.clone()) } + + fn copy_to_dev(&self) -> Result<(), MetalError> { + let Some(tex) = &self.dev_tex else { + return Ok(()); + }; + self.dev_fb + .copy_texture(tex, 0, 0) + .map_err(MetalError::CopyToOutput) + } } fn modes_equal(a: &DrmModeInfo, b: &DrmModeInfo) -> bool { diff --git a/src/backends/x.rs b/src/backends/x.rs index 77a35c81..1130e32c 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -738,13 +738,17 @@ impl XBackend { image.last_serial.set(serial); if let Some(node) = self.state.root.outputs.get(&output.id) { - self.state.present_output( + let res = self.state.present_output( &node, &image.fb.get(), &image.tex.get(), &mut self.render_result.borrow_mut(), true, ); + if let Err(e) = res { + log::error!("Could not render screen: {}", ErrorFmt(e)); + return; + } } let pp = PresentPixmap { diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 75fcf245..1d411222 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -189,7 +189,7 @@ pub trait GfxFramebuffer: Debug { fn physical_size(&self) -> (i32, i32); - fn render(&self, ops: Vec, clear: Option<&Color>); + fn render(&self, ops: Vec, clear: Option<&Color>) -> Result<(), GfxError>; fn copy_to_shm( self: Rc, @@ -206,13 +206,13 @@ pub trait GfxFramebuffer: Debug { } impl dyn GfxFramebuffer { - pub fn clear(&self) { - self.clear_with(0.0, 0.0, 0.0, 0.0); + pub fn clear(&self) -> Result<(), GfxError> { + self.clear_with(0.0, 0.0, 0.0, 0.0) } - pub fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) { + pub fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) -> Result<(), GfxError> { let ops = self.take_render_ops(); - self.render(ops, Some(&Color { r, g, b, a })); + self.render(ops, Some(&Color { r, g, b, a })) } pub fn logical_size(&self, transform: Transform) -> (i32, i32) { @@ -237,13 +237,18 @@ impl dyn GfxFramebuffer { } } - pub fn copy_texture(&self, texture: &Rc, x: i32, y: i32) { + pub fn copy_texture( + &self, + texture: &Rc, + x: i32, + y: i32, + ) -> Result<(), GfxError> { let mut ops = self.take_render_ops(); let scale = Scale::from_int(1); let mut renderer = self.renderer_base(&mut ops, scale, Transform::None); renderer.render_texture(texture, x, y, None, None, scale, None, None); let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT); - self.render(ops, clear); + self.render(ops, clear) } pub fn render_custom( @@ -251,11 +256,11 @@ impl dyn GfxFramebuffer { scale: Scale, clear: Option<&Color>, f: &mut dyn FnMut(&mut RendererBase), - ) { + ) -> Result<(), GfxError> { let mut ops = self.take_render_ops(); let mut renderer = self.renderer_base(&mut ops, scale, Transform::None); f(&mut renderer); - self.render(ops, clear); + self.render(ops, clear) } pub fn create_render_pass( @@ -326,7 +331,7 @@ impl dyn GfxFramebuffer { } } - pub fn perform_render_pass(&self, pass: GfxRenderPass) { + pub fn perform_render_pass(&self, pass: GfxRenderPass) -> Result<(), GfxError> { self.render(pass.ops, pass.clear.as_ref()) } @@ -338,7 +343,7 @@ impl dyn GfxFramebuffer { result: Option<&mut RenderResult>, scale: Scale, render_hardware_cursor: bool, - ) { + ) -> Result<(), GfxError> { self.render_node( node, state, @@ -361,7 +366,7 @@ impl dyn GfxFramebuffer { render_hardware_cursor: bool, black_background: bool, transform: Transform, - ) { + ) -> Result<(), GfxError> { let pass = self.create_render_pass( node, state, @@ -372,7 +377,7 @@ impl dyn GfxFramebuffer { black_background, transform, ); - self.perform_render_pass(pass); + self.perform_render_pass(pass) } pub fn render_hardware_cursor( @@ -381,7 +386,7 @@ impl dyn GfxFramebuffer { state: &State, scale: Scale, transform: Transform, - ) { + ) -> Result<(), GfxError> { let mut ops = self.take_render_ops(); let mut renderer = Renderer { base: self.renderer_base(&mut ops, scale, transform), @@ -394,7 +399,7 @@ impl dyn GfxFramebuffer { }, }; cursor.render_hardware_cursor(&mut renderer); - self.render(ops, Some(&Color::TRANSPARENT)); + self.render(ops, Some(&Color::TRANSPARENT)) } } diff --git a/src/gfx_apis/gl/renderer/framebuffer.rs b/src/gfx_apis/gl/renderer/framebuffer.rs index 427f71f1..b409118c 100644 --- a/src/gfx_apis/gl/renderer/framebuffer.rs +++ b/src/gfx_apis/gl/renderer/framebuffer.rs @@ -10,6 +10,7 @@ use { renderer::context::GlRenderContext, run_ops, sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA}, + RenderError, }, theme::Color, }, @@ -64,9 +65,9 @@ impl Framebuffer { }); } - pub fn render(&self, ops: Vec, clear: Option<&Color>) { + pub fn render(&self, ops: Vec, clear: Option<&Color>) -> Result<(), RenderError> { let gles = self.ctx.ctx.dpy.gles; - let _ = self.ctx.ctx.with_current(|| { + let res = self.ctx.ctx.with_current(|| { unsafe { (gles.glBindFramebuffer)(GL_FRAMEBUFFER, self.gl.fbo); (gles.glViewport)(0, 0, self.gl.width, self.gl.height); @@ -83,6 +84,7 @@ impl Framebuffer { Ok(()) }); *self.ctx.gfx_ops.borrow_mut() = ops; + res } } @@ -101,8 +103,8 @@ impl GfxFramebuffer for Framebuffer { (self.gl.width, self.gl.height) } - fn render(&self, ops: Vec, clear: Option<&Color>) { - self.render(ops, clear); + fn render(&self, ops: Vec, clear: Option<&Color>) -> Result<(), GfxError> { + self.render(ops, clear).map_err(|e| e.into()) } fn copy_to_shm( diff --git a/src/gfx_apis/vulkan.rs b/src/gfx_apis/vulkan.rs index 83d3ecd2..3429d028 100644 --- a/src/gfx_apis/vulkan.rs +++ b/src/gfx_apis/vulkan.rs @@ -173,6 +173,8 @@ pub enum VulkanError { height: i32, stride: i32, }, + #[error(transparent)] + GfxError(GfxError), } impl From for GfxError { diff --git a/src/gfx_apis/vulkan/image.rs b/src/gfx_apis/vulkan/image.rs index 9385fb1c..53cb0351 100644 --- a/src/gfx_apis/vulkan/image.rs +++ b/src/gfx_apis/vulkan/image.rs @@ -525,8 +525,10 @@ impl GfxFramebuffer for VulkanImage { (self.width as _, self.height as _) } - fn render(&self, ops: Vec, clear: Option<&Color>) { - self.renderer.execute(self, &ops, clear).unwrap(); + fn render(&self, ops: Vec, clear: Option<&Color>) -> Result<(), GfxError> { + self.renderer + .execute(self, &ops, clear) + .map_err(|e| e.into()) } fn copy_to_shm( diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 39727560..6b4cf5f4 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -698,7 +698,9 @@ impl VulkanRenderer { &[], true, )?; - (&*tmp_tex as &dyn GfxFramebuffer).copy_texture(&(tex.clone() as _), x, y); + (&*tmp_tex as &dyn GfxFramebuffer) + .copy_texture(&(tex.clone() as _), x, y) + .map_err(VulkanError::GfxError)?; self.read_all_pixels(&tmp_tex, stride, dst) } diff --git a/src/ifs/jay_screencast.rs b/src/ifs/jay_screencast.rs index f3a7a791..4e655d83 100644 --- a/src/ifs/jay_screencast.rs +++ b/src/ifs/jay_screencast.rs @@ -173,7 +173,7 @@ impl JayScreencast { let mut buffer = self.buffers.borrow_mut(); for (idx, buffer) in buffer.deref_mut().iter_mut().enumerate() { if buffer.free { - self.client.state.perform_screencopy( + let res = self.client.state.perform_screencopy( texture, &buffer.fb, on.global.pos.get(), @@ -183,12 +183,20 @@ impl JayScreencast { size, on.global.persistent.transform.get(), ); - self.client.event(Ready { - self_id: self.id, - idx: idx as _, - }); - buffer.free = false; - return; + match res { + Ok(_) => { + self.client.event(Ready { + self_id: self.id, + idx: idx as _, + }); + buffer.free = false; + return; + } + Err(e) => { + log::error!("Could not perform screencopy: {}", ErrorFmt(e)); + break; + } + } } } self.missed_frame.set(true); diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index 8fb3f1cc..37d626ea 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -18,6 +18,7 @@ use { buffd::{MsgParser, MsgParserError}, clonecell::CloneCell, copyhashmap::CopyHashMap, + errorfmt::ErrorFmt, linkedlist::LinkedList, transform_ext::TransformExt, }, @@ -243,7 +244,7 @@ impl WlOutputGlobal { if let Some(WlBufferStorage::Shm { mem, stride }) = wl_buffer.storage.borrow_mut().deref() { - self.state.perform_shm_screencopy( + let res = self.state.perform_shm_screencopy( tex, self.pos.get(), x_off, @@ -255,6 +256,11 @@ impl WlOutputGlobal { wl_buffer.format, Transform::None, ); + if let Err(e) = res { + log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e)); + capture.send_failed(); + continue; + } } else { let fb = match wl_buffer.famebuffer.get() { Some(fb) => fb, @@ -264,7 +270,7 @@ impl WlOutputGlobal { continue; } }; - self.state.perform_screencopy( + let res = self.state.perform_screencopy( tex, &fb, self.pos.get(), @@ -274,6 +280,11 @@ impl WlOutputGlobal { size, Transform::None, ); + if let Err(e) = res { + log::warn!("Could not perform screencopy: {}", ErrorFmt(e)); + capture.send_failed(); + continue; + } } if capture.with_damage.get() { capture.send_damage(); diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 3edf241b..4e18cf6e 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -299,13 +299,20 @@ impl WlSeatGlobal { if extents.intersects(&Rect::new_sized(-x_rel, -y_rel, width, height).unwrap()) { if render { let buffer = hc.get_buffer(); - buffer.render_hardware_cursor( + let res = buffer.render_hardware_cursor( cursor.deref(), &self.state, scale, transform, ); - hc.swap_buffer(); + match res { + Ok(_) => { + hc.swap_buffer(); + } + Err(e) => { + log::error!("Could not render hardware cursor: {}", ErrorFmt(e)); + } + } } hc.set_enabled(true); let mode = output.global.mode.get(); diff --git a/src/portal/ptr_gui.rs b/src/portal/ptr_gui.rs index 28190beb..570d671c 100644 --- a/src/portal/ptr_gui.rs +++ b/src/portal/ptr_gui.rs @@ -628,6 +628,19 @@ impl WindowData { } return; }; + + let res = buf + .fb + .render_custom(self.scale.get(), Some(&Color::from_gray(0)), &mut |r| { + if let Some(content) = self.content.get() { + content.render_at(r, 0.0, 0.0) + } + }); + if let Err(e) = res { + log::error!("Could not render frame: {}", ErrorFmt(e)); + return; + } + self.frame_missed.set(false); self.surface.frame({ @@ -643,13 +656,6 @@ impl WindowData { self.have_frame.set(false); buf.free.set(false); - buf.fb - .render_custom(self.scale.get(), Some(&Color::from_gray(0)), &mut |r| { - if let Some(content) = self.content.get() { - content.render_at(r, 0.0, 0.0) - } - }); - self.surface.attach(&buf.wl); self.surface.commit(); } diff --git a/src/screenshoter.rs b/src/screenshoter.rs index ec4b3f66..acfbaab4 100644 --- a/src/screenshoter.rs +++ b/src/screenshoter.rs @@ -78,7 +78,7 @@ pub fn take_screenshot(state: &State) -> Result true, false, Transform::None, - ); + )?; let drm = gbm.drm.dup_render()?.fd().clone(); Ok(Screenshot { drm, bo }) } diff --git a/src/state.rs b/src/state.rs index 78849dfe..2ac63154 100644 --- a/src/state.rs +++ b/src/state.rs @@ -81,6 +81,7 @@ use { sync::Arc, time::Duration, }, + thiserror::Error, }; pub struct State { @@ -769,7 +770,7 @@ impl State { tex: &Rc, rr: &mut RenderResult, render_hw_cursor: bool, - ) { + ) -> Result<(), GfxError> { fb.render_output( output, self, @@ -777,9 +778,10 @@ impl State { Some(rr), output.global.persistent.scale.get(), render_hw_cursor, - ); + )?; output.perform_screencopies(tex, !render_hw_cursor, 0, 0, None); rr.dispatch_frame_requests(); + Ok(()) } pub fn perform_screencopy( @@ -792,7 +794,7 @@ impl State { y_off: i32, size: Option<(i32, i32)>, transform: Transform, - ) { + ) -> Result<(), GfxError> { let mut ops = target.take_render_ops(); let mut renderer = Renderer { base: target.renderer_base(&mut ops, Scale::from_int(1), Transform::None), @@ -828,7 +830,7 @@ impl State { } } } - target.render(ops, Some(&Color::SOLID_BLACK)); + target.render(ops, Some(&Color::SOLID_BLACK)) } fn have_hardware_cursor(&self) -> bool { @@ -854,7 +856,7 @@ impl State { stride: i32, format: &'static Format, transform: Transform, - ) { + ) -> Result<(), ShmScreencopyError> { let (src_width, src_height) = src.size(); let mut needs_copy = capture.rect.x1() < x_off || capture.rect.x2() > x_off + src_width @@ -869,20 +871,11 @@ impl State { } let acc = if needs_copy { let Some(ctx) = self.render_ctx.get() else { - log::warn!("Cannot perform shm screencopy because there is no render context"); - return; + return Err(ShmScreencopyError::NoRenderContext); }; - let fb = - match ctx.create_fb(capture.rect.width(), capture.rect.height(), stride, format) { - Ok(f) => f, - Err(e) => { - log::warn!( - "Could not create temporary fb for screencopy: {}", - ErrorFmt(e) - ); - return; - } - }; + let fb = ctx + .create_fb(capture.rect.width(), capture.rect.height(), stride, format) + .map_err(ShmScreencopyError::CreateTemporaryFb)?; self.perform_screencopy( src, &fb, @@ -892,7 +885,8 @@ impl State { y_off - capture.rect.y1(), size, transform, - ); + ) + .map_err(ShmScreencopyError::CopyToTemporary)?; mem.access(|mem| { fb.copy_to_shm( 0, @@ -917,16 +911,12 @@ impl State { ) }) }; - let res = match acc { - Ok(res) => res, + match acc { + Ok(res) => res.map_err(ShmScreencopyError::ReadPixels), Err(e) => { capture.client.error(e); - return; + Ok(()) } - }; - if let Err(e) = res { - log::warn!("Could not read texture to memory: {}", ErrorFmt(e)); - capture.send_failed(); } } @@ -937,3 +927,15 @@ impl State { seat } } + +#[derive(Debug, Error)] +pub enum ShmScreencopyError { + #[error("There is no render context")] + NoRenderContext, + #[error("Could not create a bridge framebuffer")] + CreateTemporaryFb(#[source] GfxError), + #[error("Could not copy texture to bridge framebuffer")] + CopyToTemporary(#[source] GfxError), + #[error("Could not read pixels from texture")] + ReadPixels(#[source] GfxError), +}