diff --git a/src/backend/drm/surface/gbm.rs b/src/backend/drm/surface/gbm.rs index 292dd54..b6b8be2 100644 --- a/src/backend/drm/surface/gbm.rs +++ b/src/backend/drm/surface/gbm.rs @@ -17,22 +17,16 @@ use crate::backend::SwapBuffersError; use slog::{debug, error, o, trace, warn}; /// Simplified abstraction of a swapchain for gbm-buffers displayed on a [`DrmSurface`]. +#[derive(Debug)] pub struct GbmBufferedSurface { - buffers: Buffers, + current_fb: Slot>, + pending_fb: Option>>, + queued_fb: Option>>, + next_fb: Option>>, swapchain: Swapchain, BufferObject<()>>, drm: Arc>, } -// TODO: Replace with #[derive(Debug)] once gbm::BufferObject implements debug -impl std::fmt::Debug for GbmBufferedSurface { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("GbmBufferedSurface") - .field("buffers", &self.buffers) - .field("drm", &self.drm) - .finish_non_exhaustive() - } -} - impl GbmBufferedSurface where D: AsRawFd + 'static, @@ -154,9 +148,11 @@ where match drm.test_buffer(handle, &mode, true) { Ok(_) => { debug!(logger, "Choosen format: {:?}", format); - let buffers = Buffers::new(drm.clone(), buffer); Ok(GbmBufferedSurface { - buffers, + current_fb: buffer, + pending_fb: None, + queued_fb: None, + next_fb: None, swapchain, drm, }) @@ -176,7 +172,24 @@ where /// *Note*: This function can be called multiple times and /// will return the same buffer until it is queued (see [`GbmBufferedSurface::queue_buffer`]). pub fn next_buffer(&mut self) -> Result { - self.buffers.next(&mut self.swapchain) + if self.next_fb.is_none() { + let slot = self.swapchain.acquire()?.ok_or(Error::NoFreeSlotsError)?; + + let maybe_buffer = slot.userdata().get::().cloned(); + if maybe_buffer.is_none() { + let dmabuf = slot.export().map_err(Error::AsDmabufError)?; + let fb_handle = attach_framebuffer(&self.drm, &*slot)?; + + let userdata = slot.userdata(); + userdata.insert_if_missing(|| dmabuf); + userdata.insert_if_missing(|| fb_handle); + } + + self.next_fb = Some(slot); + } + + let slot = self.next_fb.as_ref().unwrap(); + Ok(slot.userdata().get::().unwrap().clone()) } /// Queues the current buffer for rendering. @@ -185,7 +198,11 @@ where /// when a vblank event is received, that denotes successful scanout of the buffer. /// Otherwise the underlying swapchain will eventually run out of buffers. pub fn queue_buffer(&mut self) -> Result<(), Error> { - self.buffers.queue() + self.queued_fb = self.next_fb.take(); + if self.pending_fb.is_none() && self.queued_fb.is_some() { + self.submit()?; + } + Ok(()) } /// Marks the current frame as submitted. @@ -194,7 +211,30 @@ where /// was received after calling [`GbmBufferedSurface::queue_buffer`] on this surface. /// Otherwise the underlying swapchain will run out of buffers eventually. pub fn frame_submitted(&mut self) -> Result<(), Error> { - self.buffers.submitted() + if let Some(mut pending) = self.pending_fb.take() { + std::mem::swap(&mut pending, &mut self.current_fb); + if self.queued_fb.is_some() { + self.submit()?; + } + } + + Ok(()) + } + + fn submit(&mut self) -> Result<(), Error> { + // yes it does not look like it, but both of these lines should be safe in all cases. + let slot = self.queued_fb.take().unwrap(); + let fb = slot.userdata().get::>().unwrap().fb; + + let flip = if self.drm.commit_pending() { + self.drm.commit([(fb, self.drm.plane())].iter(), true) + } else { + self.drm.page_flip([(fb, self.drm.plane())].iter(), true) + }; + if flip.is_ok() { + self.pending_fb = Some(slot); + } + flip.map_err(Error::DrmError) } /// Returns the underlying [`crtc`](drm::control::crtc) of this surface @@ -285,99 +325,6 @@ impl Drop for FbHandle { } } -struct Buffers { - drm: Arc>, - _current_fb: Slot>, - pending_fb: Option>>, - queued_fb: Option>>, - next_fb: Option>>, -} - -// TODO: Replace with #[derive(Debug)] once gbm::BufferObject implements debug -impl std::fmt::Debug for Buffers { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Buffers") - .field("drm", &self.drm) - .finish_non_exhaustive() - } -} - -impl Buffers -where - D: AsRawFd + 'static, -{ - pub fn new(drm: Arc>, slot: Slot>) -> Buffers { - Buffers { - drm, - _current_fb: slot, - pending_fb: None, - queued_fb: None, - next_fb: None, - } - } - - pub fn next( - &mut self, - swapchain: &mut Swapchain, BufferObject<()>>, - ) -> Result { - if self.next_fb.is_none() { - let slot = swapchain.acquire()?.ok_or(Error::NoFreeSlotsError)?; - - let maybe_buffer = slot.userdata().get::().clone(); - if maybe_buffer.is_none() { - let dmabuf = slot.export().map_err(Error::AsDmabufError)?; - let fb_handle = attach_framebuffer(&self.drm, &*slot)?; - - let userdata = slot.userdata(); - userdata.insert_if_missing(|| dmabuf); - userdata.insert_if_missing(|| fb_handle); - } - - self.next_fb = Some(slot); - } - - let slot = self.next_fb.as_ref().unwrap(); - Ok(slot.userdata().get::().unwrap().clone()) - } - - pub fn queue(&mut self) -> Result<(), Error> { - self.queued_fb = self.next_fb.take(); - if self.pending_fb.is_none() && self.queued_fb.is_some() { - self.submit() - } else { - Ok(()) - } - } - - pub fn submitted(&mut self) -> Result<(), Error> { - if self.pending_fb.is_none() { - return Ok(()); - } - self._current_fb = self.pending_fb.take().unwrap(); - if self.queued_fb.is_some() { - self.submit() - } else { - Ok(()) - } - } - - fn submit(&mut self) -> Result<(), Error> { - // yes it does not look like it, but both of these lines should be safe in all cases. - let slot = self.queued_fb.take().unwrap(); - let fb = slot.userdata().get::>().unwrap().fb; - - let flip = if self.drm.commit_pending() { - self.drm.commit([(fb, self.drm.plane())].iter(), true) - } else { - self.drm.page_flip([(fb, self.drm.plane())].iter(), true) - }; - if flip.is_ok() { - self.pending_fb = Some(slot); - } - flip.map_err(Error::DrmError) - } -} - fn attach_framebuffer(drm: &Arc>, bo: &BufferObject<()>) -> Result, Error> where A: AsRawFd + 'static,