From 7518f8c0f7cd455a175e8b2c2db26a223af566c6 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Fri, 1 May 2020 01:48:52 +0200 Subject: [PATCH] anvil: retry initial renderings --- anvil/src/glium_drawer.rs | 30 ++++++-- anvil/src/udev.rs | 143 ++++++++++++++++++++++++++++---------- 2 files changed, 132 insertions(+), 41 deletions(-) diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs index 25a03d3..53f77b2 100644 --- a/anvil/src/glium_drawer.rs +++ b/anvil/src/glium_drawer.rs @@ -16,12 +16,12 @@ use smithay::backend::egl::display::EGLBufferReader; use smithay::{ backend::{ egl::{BufferAccessError, EGLImages, Format}, - graphics::{ - gl::GLGraphicsBackend, - glium::{Frame, GliumGraphicsBackend}, - }, + graphics::{gl::GLGraphicsBackend, glium::{GliumGraphicsBackend, Frame}, SwapBuffersError}, + }, + reexports::{ + calloop::LoopHandle, + wayland_server::protocol::{wl_buffer, wl_surface}, }, - reexports::wayland_server::protocol::{wl_buffer, wl_surface}, wayland::{ compositor::{roles::Role, SubsurfaceRole, TraversalAction}, data_device::DnDIconRole, @@ -457,3 +457,23 @@ impl GliumDrawer { self.draw_surface_tree(frame, surface, (x, y), token, screen_dimensions); } } + +pub fn schedule_initial_render(renderer: Rc>, evt_handle: &LoopHandle) { + let mut frame = renderer.draw(); + frame.clear_color(0.8, 0.8, 0.9, 1.0); + if let Err(err) = frame.set_finish() { + match err { + SwapBuffersError::AlreadySwapped => {}, + SwapBuffersError::TemporaryFailure(err) => { + // TODO dont reschedule after 3(?) retries + error!( + renderer.log, + "Failed to submit page_flip: {}", err + ); + let handle = evt_handle.clone(); + evt_handle.insert_idle(move |_| schedule_initial_render(renderer, &handle)); + }, + SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err), + } + } +} \ No newline at end of file diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index 008c44a..b567e77 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -25,21 +25,25 @@ use smithay::{ legacy::LegacyDrmDevice, DevPath, Device, DeviceHandler, Surface, }, - graphics::CursorBackend, + graphics::{CursorBackend, SwapBuffersError}, libinput::{LibinputInputBackend, LibinputSessionInterface}, session::{ auto::{auto_session_bind, AutoSession}, - notify_multiplexer, AsSessionObserver, Session, SessionNotifier, + notify_multiplexer, AsSessionObserver, Session, SessionNotifier, SessionObserver, }, udev::{primary_gpu, UdevBackend, UdevEvent}, }, reexports::{ - calloop::{generic::Generic, EventLoop, LoopHandle, Source}, - drm::control::{ + calloop::{ + generic::Generic, + timer::{Timer, TimerHandle}, + EventLoop, LoopHandle, Source, + }, + drm::{self, control::{ connector::{Info as ConnectorInfo, State as ConnectorState}, crtc, encoder::Info as EncoderInfo, - }, + }}, image::{ImageBuffer, Rgba}, input::Libinput, nix::{fcntl::OFlag, sys::stat::dev_t}, @@ -57,7 +61,7 @@ use smithay::{ }; use crate::buffer_utils::BufferUtils; -use crate::glium_drawer::GliumDrawer; +use crate::glium_drawer::{GliumDrawer, schedule_initial_render}; use crate::shell::{MyWindowMap, Roles}; use crate::state::AnvilState; @@ -242,8 +246,9 @@ pub fn run_udev( struct BackendData { id: S::Id, + restart_id: S::Id, event_source: Source>, - surfaces: Rc>>>, + surfaces: Rc>>>>, } struct UdevHandlerImpl { @@ -270,7 +275,7 @@ impl UdevHandlerImpl { device: &mut RenderDevice, egl_buffer_reader: Rc>>, logger: &::slog::Logger, - ) -> HashMap> { + ) -> HashMap>> { // Get a set of all modesetting resource handles (excluding planes): let res_handles = device.resource_handles().unwrap(); @@ -304,7 +309,7 @@ impl UdevHandlerImpl { logger.clone(), ); - entry.insert(renderer); + entry.insert(Rc::new(renderer)); break; } } @@ -318,7 +323,7 @@ impl UdevHandlerImpl { pub fn scan_connectors( device: &mut RenderDevice, logger: &::slog::Logger, - ) -> HashMap> { + ) -> HashMap>> { // Get a set of all modesetting resource handles (excluding planes): let res_handles = device.resource_handles().unwrap(); @@ -347,7 +352,7 @@ impl UdevHandlerImpl { let renderer = GliumDrawer::init(device.create_surface(crtc).unwrap(), logger.clone()); - backends.insert(crtc, renderer); + backends.insert(crtc, Rc::new(renderer)); break; } } @@ -417,7 +422,7 @@ impl UdevHandlerImpl { // Set the handler. // Note: if you replicate this (very simple) structure, it is rather easy // to introduce reference cycles with Rc. Be sure about your drop order - device.set_handler(DrmHandlerImpl { + let renderer = Rc::new(DrmRenderer { compositor_token: self.compositor_token, backends: backends.clone(), window_map: self.window_map.clone(), @@ -426,6 +431,8 @@ impl UdevHandlerImpl { dnd_icon: self.dnd_icon.clone(), logger: self.logger.clone(), }); + let restart_id = self.notifier.register(DrmRendererSessionListener { renderer: renderer.clone(), loop_handle: self.loop_handle.clone() }); + device.set_handler(DrmHandlerImpl { renderer, loop_handle: self.loop_handle.clone() }); let device_session_id = self.notifier.register(device.observer()); let dev_id = device.device_id(); @@ -441,17 +448,14 @@ impl UdevHandlerImpl { .unwrap(); // render first frame - { - let mut frame = renderer.draw(); - frame.clear_color(0.8, 0.8, 0.9, 1.0); - frame.finish().unwrap(); - } + schedule_initial_render(renderer.clone(), &self.loop_handle); } self.backends.insert( dev_id, BackendData { id: device_session_id, + restart_id, event_source, surfaces: backends, }, @@ -465,6 +469,7 @@ impl UdevHandlerImpl { let logger = &self.logger; let pointer_image = &self.pointer_image; let egl_buffer_reader = self.egl_buffer_reader.clone(); + let loop_handle = self.loop_handle.clone(); self.loop_handle .with_source(&backend_data.event_source, |source| { let mut backends = backend_data.surfaces.borrow_mut(); @@ -486,11 +491,7 @@ impl UdevHandlerImpl { .unwrap(); // render first frame - { - let mut frame = renderer.draw(); - frame.clear_color(0.8, 0.8, 0.9, 1.0); - frame.finish().unwrap(); - } + schedule_initial_render(renderer.clone(), &loop_handle); } }); } @@ -514,14 +515,48 @@ impl UdevHandlerImpl { } self.notifier.unregister(backend_data.id); + self.notifier.unregister(backend_data.restart_id); debug!(self.logger, "Dropping device"); } } } -pub struct DrmHandlerImpl { +pub struct DrmHandlerImpl { + renderer: Rc, + loop_handle: LoopHandle, +} + +impl DeviceHandler for DrmHandlerImpl { + type Device = RenderDevice; + + fn vblank(&mut self, crtc: crtc::Handle) { + self.renderer.clone().render(crtc, None, Some(&self.loop_handle)) + } + + fn error(&mut self, error: ::Error) { + error!(self.renderer.logger, "{:?}", error); + } +} + +pub struct DrmRendererSessionListener { + renderer: Rc, + loop_handle: LoopHandle, +} + +impl SessionObserver for DrmRendererSessionListener { + fn pause(&mut self, _device: Option<(u32, u32)>) {} + fn activate(&mut self, _device: Option<(u32, u32, Option)>) { + // we want to be called, after all session handling is done (TODO this is not so nice) + let renderer = self.renderer.clone(); + let handle = self.loop_handle.clone(); + self.loop_handle.insert_idle(move |_| renderer.render_all(Some(&handle))); + } +} + + +pub struct DrmRenderer { compositor_token: CompositorToken, - backends: Rc>>>, + backends: Rc>>>>, window_map: Rc>, pointer_location: Rc>, cursor_status: Arc>, @@ -529,10 +564,13 @@ pub struct DrmHandlerImpl { logger: ::slog::Logger, } -impl DeviceHandler for DrmHandlerImpl { - type Device = RenderDevice; - - fn vblank(&mut self, crtc: crtc::Handle) { +impl DrmRenderer { + fn render_all(self: Rc, evt_handle: Option<&LoopHandle>) { + for crtc in self.backends.borrow().keys() { + self.clone().render(*crtc, None, evt_handle); + } + } + fn render(self: Rc, crtc: crtc::Handle, timer: Option, crtc::Handle)>>, evt_handle: Option<&LoopHandle>) { if let Some(drawer) = self.backends.borrow().get(&crtc) { { let (x, y) = *self.pointer_location.borrow(); @@ -577,16 +615,49 @@ impl DeviceHandler for DrmHandlerImpl { } } - if let Err(err) = frame.finish() { - error!(self.logger, "Error during rendering: {:?}", err); + let result = frame.finish(); + if result.is_ok() { + // Send frame events so that client start drawing their next frame + self.window_map.borrow().send_frames(SCOUNTER.next_serial()); } - // Send frame events so that client start drawing their next frame - self.window_map.borrow().send_frames(SCOUNTER.next_serial()); + if let Err(err) = result { + error!(self.logger, "Error during rendering: {:?}", err); + let reschedule = match err { + SwapBuffersError::AlreadySwapped => false, + SwapBuffersError::TemporaryFailure(err) => match err.downcast_ref::() { + Some(&smithay::backend::drm::common::Error::DeviceInactive) => false, + Some(&smithay::backend::drm::common::Error::Access { ref source, .. }) if match source.get_ref() { + drm::SystemError::PermissionDenied => true, + _ => false, + } => false, + _ => true + }, + SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err), + }; + + if reschedule { + match (timer, evt_handle) { + (Some(handle), _) => { + let _ = handle.add_timeout(Duration::from_millis(1000 /*a seconds*/ / 60 /*refresh rate*/), (Rc::downgrade(&self), crtc)); + }, + (None, Some(evt_handle)) => { + let timer = Timer::new().unwrap(); + let handle = timer.handle(); + let _ = handle.add_timeout(Duration::from_millis(1000 /*a seconds*/ / 60 /*refresh rate*/), (Rc::downgrade(&self), crtc)); + evt_handle.insert_source(timer, |(renderer, crtc), handle, _data| { + if let Some(renderer) = renderer.upgrade() { + renderer.render(crtc, Some(handle.clone()), Option::<&LoopHandle>::None); + } + }).unwrap(); + }, + _ => unreachable!(), + } + } + } else { + // Send frame events so that client start drawing their next frame + self.window_map.borrow().send_frames(SCOUNTER.next_serial()); + } } } - - fn error(&mut self, error: ::Error) { - error!(self.logger, "{:?}", error); - } }