//! Utilities to attach [`framebuffer::Handle`]s to dumb buffers use drm_fourcc::{DrmFormat, DrmModifier}; use drm::{ buffer::{Buffer as DrmBuffer, PlanarBuffer}, control::{framebuffer, Device, FbCmd2Flags}, }; use tracing::{trace, warn}; use crate::utils::DevPath; use crate::{ backend::{ allocator::{ dumb::DumbBuffer, format::{get_bpp, get_depth, get_opaque}, Buffer, Fourcc, }, drm::DrmDeviceFd, }, utils::{Buffer as BufferCoords, Size}, }; use super::{error::AccessError, warn_legacy_fb_export, Framebuffer}; /// A GBM backed framebuffer #[derive(Debug)] pub struct DumbFramebuffer { fb: framebuffer::Handle, format: drm_fourcc::DrmFormat, drm: DrmDeviceFd, } impl Drop for DumbFramebuffer { fn drop(&mut self) { trace!(fb = ?self.fb, "destroying framebuffer"); if let Err(err) = self.drm.destroy_framebuffer(self.fb) { warn!(fb = ?self.fb, ?err, "failed to destroy framebuffer"); } } } impl AsRef for DumbFramebuffer { #[inline] fn as_ref(&self) -> &framebuffer::Handle { &self.fb } } impl Framebuffer for DumbFramebuffer { #[inline] fn format(&self) -> drm_fourcc::DrmFormat { self.format } } struct PlanarDumbBuffer<'a>(&'a DumbBuffer); impl Buffer for PlanarDumbBuffer<'_> { #[inline] fn size(&self) -> Size { self.0.size() } #[inline] fn format(&self) -> DrmFormat { self.0.format() } } impl PlanarBuffer for PlanarDumbBuffer<'_> { #[inline] fn size(&self) -> (u32, u32) { let size = self.0.size(); (size.w as u32, size.h as u32) } #[inline] fn format(&self) -> Fourcc { self.0.format().code } #[inline] fn modifier(&self) -> Option { Some(self.0.format().modifier) } #[inline] fn pitches(&self) -> [u32; 4] { [self.0.handle().pitch(), 0, 0, 0] } #[inline] fn handles(&self) -> [Option; 4] { [Some(self.0.handle().handle()), None, None, None] } #[inline] fn offsets(&self) -> [u32; 4] { [0, 0, 0, 0] } } struct OpaqueBufferWrapper<'a, B>(&'a B); impl PlanarBuffer for OpaqueBufferWrapper<'_, B> where B: PlanarBuffer, { #[inline] fn size(&self) -> (u32, u32) { self.0.size() } #[inline] fn format(&self) -> Fourcc { let fmt = self.0.format(); get_opaque(fmt).unwrap_or(fmt) } #[inline] fn modifier(&self) -> Option { self.0.modifier() } #[inline] fn pitches(&self) -> [u32; 4] { self.0.pitches() } #[inline] fn handles(&self) -> [Option; 4] { self.0.handles() } #[inline] fn offsets(&self) -> [u32; 4] { self.0.offsets() } } /// Attach a [`framebuffer::Handle`] to an [`DumbBuffer`] #[profiling::function] pub fn framebuffer_from_dumb_buffer( drm: &DrmDeviceFd, buffer: &DumbBuffer, use_opaque: bool, ) -> Result { let buffer = PlanarDumbBuffer(buffer); let format = Buffer::format(&buffer); let modifier = buffer.modifier(); let flags = if modifier.is_some() { FbCmd2Flags::MODIFIERS } else { FbCmd2Flags::empty() }; let ret = if use_opaque { let opaque_wrapper = OpaqueBufferWrapper(&buffer); drm.add_planar_framebuffer(&opaque_wrapper, flags).map(|fb| { ( fb, drm_fourcc::DrmFormat { code: opaque_wrapper.format(), modifier: modifier.unwrap_or(DrmModifier::Invalid), }, ) }) } else { drm.add_planar_framebuffer(&buffer, flags).map(|fb| (fb, format)) }; let (fb, format) = match ret { Ok(fb) => fb, Err(source) => { warn_legacy_fb_export(); let fourcc = format.code; let (depth, bpp) = get_depth(fourcc) .and_then(|d| get_bpp(fourcc).map(|b| (d, b))) .ok_or_else(|| AccessError { errmsg: "Unknown format for legacy framebuffer", dev: drm.dev_path(), source, })?; let fb = drm .add_framebuffer(buffer.0.handle(), depth as u32, bpp as u32) .map_err(|source| AccessError { errmsg: "Failed to add framebuffer", dev: drm.dev_path(), source, })?; (fb, format) } }; Ok(DumbFramebuffer { fb, format, drm: drm.clone(), }) }