Rework `DrmRenderSurface` into `GbmBufferedSurface`.
Removes the renderer from the `DrmRenderSurface` allowing anvil to use just one renderer per backend. Since the old `DrmRenderSurface` was dependant on gbm anyway to import buffers, the new `GbmBufferedSurface` does now only supports gbm as an allocator, which hugely simplifies the code and also skips some unnecessary imports/exports.
This commit is contained in:
parent
6bd0d71ebc
commit
ce3b2d1eab
|
@ -12,19 +12,15 @@ use std::{
|
|||
use image::{ImageBuffer, Rgba};
|
||||
use slog::Logger;
|
||||
|
||||
#[cfg(feature = "egl")]
|
||||
use smithay::{
|
||||
backend::{drm::DevPath, egl::display::EGLBufferReader, renderer::ImportDma, udev::primary_gpu},
|
||||
wayland::dmabuf::init_dmabuf_global,
|
||||
};
|
||||
use smithay::{
|
||||
backend::{
|
||||
drm::{DrmDevice, DrmError, DrmEvent, DrmRenderSurface},
|
||||
allocator::dmabuf::Dmabuf,
|
||||
drm::{DrmDevice, DrmError, DrmEvent, GbmBufferedSurface},
|
||||
egl::{EGLContext, EGLDisplay},
|
||||
libinput::{LibinputInputBackend, LibinputSessionInterface},
|
||||
renderer::{
|
||||
gles2::{Gles2Renderer, Gles2Texture},
|
||||
Frame, Renderer, Transform,
|
||||
Bind, Frame, Renderer, Transform,
|
||||
},
|
||||
session::{auto::AutoSession, Session, Signal as SessionSignal},
|
||||
udev::{UdevBackend, UdevEvent},
|
||||
|
@ -44,7 +40,7 @@ use smithay::{
|
|||
Device as ControlDevice,
|
||||
},
|
||||
},
|
||||
gbm::{BufferObject as GbmBuffer, Device as GbmDevice},
|
||||
gbm::Device as GbmDevice,
|
||||
input::Libinput,
|
||||
nix::{fcntl::OFlag, sys::stat::dev_t},
|
||||
wayland_server::{
|
||||
|
@ -60,6 +56,11 @@ use smithay::{
|
|||
seat::CursorImageStatus,
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "egl")]
|
||||
use smithay::{
|
||||
backend::{drm::DevPath, egl::display::EGLBufferReader, renderer::ImportDma, udev::primary_gpu},
|
||||
wayland::dmabuf::init_dmabuf_global,
|
||||
};
|
||||
|
||||
use crate::drawing::*;
|
||||
use crate::shell::{MyWindowMap, Roles};
|
||||
|
@ -189,10 +190,7 @@ pub fn run_udev(
|
|||
{
|
||||
let mut formats = Vec::new();
|
||||
for backend_data in state.backend_data.backends.values() {
|
||||
let surfaces = backend_data.surfaces.borrow_mut();
|
||||
if let Some(surface) = surfaces.values().next() {
|
||||
formats.extend(surface.borrow_mut().renderer().dmabuf_formats().cloned());
|
||||
}
|
||||
formats.extend(backend_data.renderer.borrow().dmabuf_formats().cloned());
|
||||
}
|
||||
|
||||
init_dmabuf_global(
|
||||
|
@ -201,13 +199,10 @@ pub fn run_udev(
|
|||
|buffer, mut ddata| {
|
||||
let anvil_state = ddata.get::<AnvilState<UdevData>>().unwrap();
|
||||
for backend_data in anvil_state.backend_data.backends.values() {
|
||||
let surfaces = backend_data.surfaces.borrow_mut();
|
||||
if let Some(surface) = surfaces.values().next() {
|
||||
if surface.borrow_mut().renderer().import_dmabuf(buffer).is_ok() {
|
||||
if backend_data.renderer.borrow_mut().import_dmabuf(buffer).is_ok() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
},
|
||||
log.clone(),
|
||||
|
@ -318,25 +313,23 @@ impl Drop for MyOutput {
|
|||
}
|
||||
}
|
||||
|
||||
pub type RenderSurface = DrmRenderSurface<SessionFd, GbmDevice<SessionFd>, Gles2Renderer, GbmBuffer<()>>;
|
||||
pub type RenderSurface = GbmBufferedSurface<SessionFd>;
|
||||
|
||||
struct BackendData {
|
||||
_restart_token: SignalToken,
|
||||
surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>>>>,
|
||||
context: EGLContext,
|
||||
egl: EGLDisplay,
|
||||
pointer_image: Gles2Texture,
|
||||
renderer: Rc<RefCell<Gles2Renderer>>,
|
||||
gbm: GbmDevice<SessionFd>,
|
||||
registration_token: RegistrationToken,
|
||||
event_dispatcher: Dispatcher<'static, DrmDevice<SessionFd>, AnvilState<UdevData>>,
|
||||
dev_id: u64,
|
||||
pointer_image: Gles2Texture,
|
||||
}
|
||||
|
||||
pub fn scan_connectors(
|
||||
device: &mut DrmDevice<SessionFd>,
|
||||
gbm: &GbmDevice<SessionFd>,
|
||||
egl: &EGLDisplay,
|
||||
context: &EGLContext,
|
||||
renderer: &mut Gles2Renderer,
|
||||
display: &mut Display,
|
||||
output_map: &mut Vec<MyOutput>,
|
||||
signaler: &Signaler<SessionSignal>,
|
||||
|
@ -374,20 +367,6 @@ pub fn scan_connectors(
|
|||
connector_info.interface_id(),
|
||||
crtc,
|
||||
);
|
||||
let context = match EGLContext::new_shared(egl, context, logger.clone()) {
|
||||
Ok(context) => context,
|
||||
Err(err) => {
|
||||
warn!(logger, "Failed to create EGLContext: {}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let renderer = match unsafe { Gles2Renderer::new(context, logger.clone()) } {
|
||||
Ok(renderer) => renderer,
|
||||
Err(err) => {
|
||||
warn!(logger, "Failed to create Gles2 Renderer: {}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut surface = match device.create_surface(
|
||||
crtc,
|
||||
connector_info.modes()[0],
|
||||
|
@ -400,7 +379,12 @@ pub fn scan_connectors(
|
|||
}
|
||||
};
|
||||
surface.link(signaler.clone());
|
||||
let renderer = match DrmRenderSurface::new(surface, gbm.clone(), renderer, logger.clone())
|
||||
|
||||
let renderer_formats =
|
||||
Bind::<Dmabuf>::supported_formats(renderer).expect("Dmabuf renderer without formats");
|
||||
|
||||
let renderer =
|
||||
match GbmBufferedSurface::new(surface, gbm.clone(), renderer_formats, logger.clone())
|
||||
{
|
||||
Ok(renderer) => renderer,
|
||||
Err(err) => {
|
||||
|
@ -498,26 +482,24 @@ impl AnvilState<UdevData> {
|
|||
}
|
||||
};
|
||||
|
||||
let renderer = Rc::new(RefCell::new(unsafe {
|
||||
Gles2Renderer::new(context, self.log.clone()).unwrap()
|
||||
}));
|
||||
|
||||
let backends = Rc::new(RefCell::new(scan_connectors(
|
||||
&mut device,
|
||||
&gbm,
|
||||
&egl,
|
||||
&context,
|
||||
&mut *renderer.borrow_mut(),
|
||||
&mut *self.display.borrow_mut(),
|
||||
&mut self.backend_data.output_map,
|
||||
&self.backend_data.signaler,
|
||||
&self.log,
|
||||
)));
|
||||
|
||||
// we leak this texture (we would need to call `destroy_texture` on Drop of DrmRenderer),
|
||||
// but only on shutdown anyway, because we do not support hot-pluggin, so it does not really matter.
|
||||
let pointer_image = {
|
||||
let context = EGLContext::new_shared(&egl, &context, self.log.clone()).unwrap();
|
||||
let mut renderer = unsafe { Gles2Renderer::new(context, self.log.clone()).unwrap() };
|
||||
renderer
|
||||
let pointer_image = renderer
|
||||
.borrow_mut()
|
||||
.import_bitmap(&self.backend_data.pointer_image)
|
||||
.expect("Failed to load pointer")
|
||||
};
|
||||
.expect("Failed to load pointer");
|
||||
|
||||
let dev_id = device.device_id();
|
||||
let handle = self.handle.clone();
|
||||
|
@ -544,7 +526,7 @@ impl AnvilState<UdevData> {
|
|||
for backend in backends.borrow_mut().values() {
|
||||
// render first frame
|
||||
trace!(self.log, "Scheduling frame");
|
||||
schedule_initial_render(backend.clone(), &self.handle, self.log.clone());
|
||||
schedule_initial_render(backend.clone(), renderer.clone(), &self.handle, self.log.clone());
|
||||
}
|
||||
|
||||
self.backend_data.backends.insert(
|
||||
|
@ -554,8 +536,7 @@ impl AnvilState<UdevData> {
|
|||
registration_token,
|
||||
event_dispatcher,
|
||||
surfaces: backends,
|
||||
egl,
|
||||
context,
|
||||
renderer,
|
||||
gbm,
|
||||
pointer_image,
|
||||
dev_id,
|
||||
|
@ -580,8 +561,7 @@ impl AnvilState<UdevData> {
|
|||
*backends = scan_connectors(
|
||||
&mut *source,
|
||||
&backend_data.gbm,
|
||||
&backend_data.egl,
|
||||
&backend_data.context,
|
||||
&mut *backend_data.renderer.borrow_mut(),
|
||||
&mut *display,
|
||||
&mut self.backend_data.output_map,
|
||||
&signaler,
|
||||
|
@ -591,7 +571,12 @@ impl AnvilState<UdevData> {
|
|||
for renderer in backends.values() {
|
||||
let logger = logger.clone();
|
||||
// render first frame
|
||||
schedule_initial_render(renderer.clone(), &loop_handle, logger);
|
||||
schedule_initial_render(
|
||||
renderer.clone(),
|
||||
backend_data.renderer.clone(),
|
||||
&loop_handle,
|
||||
logger,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -651,6 +636,7 @@ impl AnvilState<UdevData> {
|
|||
for (&crtc, surface) in to_render_iter {
|
||||
let result = render_surface(
|
||||
&mut *surface.borrow_mut(),
|
||||
&mut *device_backend.renderer.borrow_mut(),
|
||||
#[cfg(feature = "egl")]
|
||||
self.egl_reader.as_ref(),
|
||||
device_backend.dev_id,
|
||||
|
@ -700,6 +686,7 @@ impl AnvilState<UdevData> {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
fn render_surface(
|
||||
surface: &mut RenderSurface,
|
||||
renderer: &mut Gles2Renderer,
|
||||
#[cfg(feature = "egl")] egl_buffer_reader: Option<&EGLBufferReader>,
|
||||
device_id: dev_t,
|
||||
crtc: crtc::Handle,
|
||||
|
@ -725,9 +712,15 @@ fn render_surface(
|
|||
.map(|output| output.size)
|
||||
.unwrap_or((0, 0)); // in this case the output will be removed.
|
||||
|
||||
// and draw in sync with our monitor
|
||||
surface
|
||||
.render(|renderer, frame| {
|
||||
let dmabuf = surface.next_buffer()?;
|
||||
renderer.bind(dmabuf)?;
|
||||
// and draw to our buffer
|
||||
match renderer
|
||||
.render(
|
||||
width,
|
||||
height,
|
||||
Transform::Flipped180, // Scanout is rotated
|
||||
|renderer, frame| {
|
||||
frame.clear([0.8, 0.8, 0.9, 1.0])?;
|
||||
// draw the surfaces
|
||||
draw_windows(
|
||||
|
@ -799,28 +792,27 @@ fn render_surface(
|
|||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
)
|
||||
.map_err(Into::<SwapBuffersError>::into)
|
||||
.and_then(|x| x)
|
||||
.map_err(Into::<SwapBuffersError>::into)
|
||||
{
|
||||
Ok(()) => surface.queue_buffer().map_err(Into::<SwapBuffersError>::into),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
fn schedule_initial_render<Data: 'static>(
|
||||
renderer: Rc<RefCell<RenderSurface>>,
|
||||
surface: Rc<RefCell<RenderSurface>>,
|
||||
renderer: Rc<RefCell<Gles2Renderer>>,
|
||||
evt_handle: &LoopHandle<'static, Data>,
|
||||
logger: ::slog::Logger,
|
||||
) {
|
||||
let result = {
|
||||
let mut surface = surface.borrow_mut();
|
||||
let mut renderer = renderer.borrow_mut();
|
||||
// Does not matter if we render an empty frame
|
||||
renderer
|
||||
.render(|_, frame| {
|
||||
frame
|
||||
.clear([0.8, 0.8, 0.9, 1.0])
|
||||
.map_err(Into::<SwapBuffersError>::into)
|
||||
})
|
||||
.map_err(Into::<SwapBuffersError>::into)
|
||||
.and_then(|x| x.map_err(Into::<SwapBuffersError>::into))
|
||||
initial_render(&mut *surface, &mut *renderer)
|
||||
};
|
||||
if let Err(err) = result {
|
||||
match err {
|
||||
|
@ -829,9 +821,25 @@ fn schedule_initial_render<Data: 'static>(
|
|||
// TODO dont reschedule after 3(?) retries
|
||||
warn!(logger, "Failed to submit page_flip: {}", err);
|
||||
let handle = evt_handle.clone();
|
||||
evt_handle.insert_idle(move |_| schedule_initial_render(renderer, &handle, logger));
|
||||
evt_handle.insert_idle(move |_| schedule_initial_render(surface, renderer, &handle, logger));
|
||||
}
|
||||
SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn initial_render(surface: &mut RenderSurface, renderer: &mut Gles2Renderer) -> Result<(), SwapBuffersError> {
|
||||
let dmabuf = surface.next_buffer()?;
|
||||
renderer.bind(dmabuf)?;
|
||||
// Does not matter if we render an empty frame
|
||||
renderer
|
||||
.render(1, 1, Transform::Normal, |_, frame| {
|
||||
frame
|
||||
.clear([0.8, 0.8, 0.9, 1.0])
|
||||
.map_err(Into::<SwapBuffersError>::into)
|
||||
})
|
||||
.map_err(Into::<SwapBuffersError>::into)
|
||||
.and_then(|x| x.map_err(Into::<SwapBuffersError>::into))?;
|
||||
surface.queue_buffer()?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -62,8 +62,6 @@
|
|||
|
||||
pub(crate) mod device;
|
||||
pub(self) mod error;
|
||||
#[cfg(feature = "backend_gbm")]
|
||||
mod render;
|
||||
#[cfg(feature = "backend_session")]
|
||||
pub(self) mod session;
|
||||
pub(self) mod surface;
|
||||
|
@ -71,7 +69,7 @@ pub(self) mod surface;
|
|||
pub use device::{DevPath, DrmDevice, DrmEvent};
|
||||
pub use error::Error as DrmError;
|
||||
#[cfg(feature = "backend_gbm")]
|
||||
pub use render::{DrmRenderSurface, Error as DrmRenderError};
|
||||
pub use surface::gbm::{Error as GbmBufferedSurfaceError, GbmBufferedSurface};
|
||||
pub use surface::DrmSurface;
|
||||
|
||||
use drm::control::{crtc, plane, Device as ControlDevice, PlaneType};
|
||||
|
|
|
@ -4,14 +4,14 @@ use std::sync::Arc;
|
|||
|
||||
use drm::buffer::PlanarBuffer;
|
||||
use drm::control::{connector, crtc, framebuffer, plane, Device, Mode};
|
||||
use gbm::{BufferObject, BufferObjectFlags, Device as GbmDevice};
|
||||
use gbm::{BufferObject, Device as GbmDevice};
|
||||
|
||||
use super::{device::DevPath, surface::DrmSurfaceInternal, DrmError, DrmSurface};
|
||||
use crate::backend::allocator::{
|
||||
dmabuf::{AsDmabuf, Dmabuf},
|
||||
Allocator, Buffer, Format, Fourcc, Modifier, Slot, Swapchain,
|
||||
gbm::GbmConvertError,
|
||||
Format, Fourcc, Modifier, Slot, Swapchain,
|
||||
};
|
||||
use crate::backend::renderer::{Bind, Frame, Renderer, Transform};
|
||||
use crate::backend::drm::{device::DevPath, surface::DrmSurfaceInternal, DrmError, DrmSurface};
|
||||
use crate::backend::SwapBuffersError;
|
||||
|
||||
/// Simplified by limited abstraction to link single [`DrmSurface`]s to renderers.
|
||||
|
@ -21,22 +21,15 @@ use crate::backend::SwapBuffersError;
|
|||
/// In some scenarios it might be enough to use of a drm-surface as the one and only target
|
||||
/// of a single renderer. In these cases `DrmRenderSurface` provides a way to quickly
|
||||
/// get up and running without manually handling and binding buffers.
|
||||
pub struct DrmRenderSurface<D: AsRawFd + 'static, A: Allocator<B>, R: Bind<Dmabuf>, B: Buffer> {
|
||||
buffers: Buffers<D, B>,
|
||||
swapchain: Swapchain<A, B, (Dmabuf, BufferObject<FbHandle<D>>)>,
|
||||
renderer: R,
|
||||
pub struct GbmBufferedSurface<D: AsRawFd + 'static> {
|
||||
buffers: Buffers<D>,
|
||||
swapchain: Swapchain<GbmDevice<D>, BufferObject<()>, (Dmabuf, FbHandle<D>)>,
|
||||
drm: Arc<DrmSurface<D>>,
|
||||
}
|
||||
|
||||
impl<D, A, B, R, E1, E2, E3> DrmRenderSurface<D, A, R, B>
|
||||
impl<D> GbmBufferedSurface<D>
|
||||
where
|
||||
D: AsRawFd + 'static,
|
||||
A: Allocator<B, Error = E1>,
|
||||
B: Buffer + AsDmabuf<Error = E2>,
|
||||
R: Bind<Dmabuf> + Renderer<Error = E3>,
|
||||
E1: std::error::Error + 'static,
|
||||
E2: std::error::Error + 'static,
|
||||
E3: std::error::Error + 'static,
|
||||
{
|
||||
/// Create a new `DrmRendererSurface` from a given compatible combination
|
||||
/// of a surface, an allocator and a renderer.
|
||||
|
@ -48,12 +41,15 @@ where
|
|||
/// The function will futhermore check for compatibility by enumerating
|
||||
/// supported pixel formats and choosing an appropriate one.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn new<L: Into<Option<::slog::Logger>>>(
|
||||
pub fn new<L>(
|
||||
drm: DrmSurface<D>,
|
||||
allocator: A,
|
||||
mut renderer: R,
|
||||
allocator: GbmDevice<D>,
|
||||
mut renderer_formats: HashSet<Format>,
|
||||
log: L,
|
||||
) -> Result<DrmRenderSurface<D, A, R, B>, Error<E1, E2, E3>> {
|
||||
) -> Result<GbmBufferedSurface<D>, Error>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
// we cannot simply pick the first supported format of the intersection of *all* formats, because:
|
||||
// - we do not want something like Abgr4444, which looses color information
|
||||
// - some formats might perform terribly
|
||||
|
@ -73,15 +69,10 @@ where
|
|||
.filter(|fmt| fmt.code == code)
|
||||
.cloned()
|
||||
.collect::<HashSet<_>>();
|
||||
let renderer_formats = Bind::<Dmabuf>::supported_formats(&renderer)
|
||||
.expect("Dmabuf renderer without formats")
|
||||
.iter()
|
||||
.filter(|fmt| fmt.code == code)
|
||||
.cloned()
|
||||
.collect::<HashSet<_>>();
|
||||
renderer_formats.retain(|fmt| fmt.code == code);
|
||||
|
||||
debug!(logger, "Remaining plane formats: {:?}", plane_formats);
|
||||
debug!(logger, "Remaining renderer formats: {:?}", renderer_formats);
|
||||
trace!(logger, "Plane formats: {:?}", plane_formats);
|
||||
trace!(logger, "Renderer formats: {:?}", renderer_formats);
|
||||
debug!(
|
||||
logger,
|
||||
"Remaining intersected formats: {:?}",
|
||||
|
@ -95,6 +86,7 @@ where
|
|||
} else if renderer_formats.is_empty() {
|
||||
return Err(Error::NoSupportedRendererFormat);
|
||||
}
|
||||
|
||||
let formats = {
|
||||
// Special case: if a format supports explicit LINEAR (but no implicit Modifiers)
|
||||
// and the other doesn't support any modifier, force Implicit.
|
||||
|
@ -127,8 +119,7 @@ where
|
|||
|
||||
let mode = drm.pending_mode();
|
||||
|
||||
let gbm = unsafe { GbmDevice::new_from_fd(drm.as_raw_fd())? };
|
||||
let mut swapchain = Swapchain::new(
|
||||
let mut swapchain: Swapchain<GbmDevice<D>, BufferObject<()>, (Dmabuf, FbHandle<D>)> = Swapchain::new(
|
||||
allocator,
|
||||
mode.size().0 as u32,
|
||||
mode.size().1 as u32,
|
||||
|
@ -137,116 +128,63 @@ where
|
|||
);
|
||||
|
||||
// Test format
|
||||
let buffer = swapchain.acquire().map_err(Error::SwapchainError)?.unwrap();
|
||||
let dmabuf = buffer.export().map_err(Error::AsDmabufError)?;
|
||||
let buffer = swapchain.acquire()?.unwrap();
|
||||
let format = Format {
|
||||
code,
|
||||
modifier: buffer.format().modifier, // no guarantee
|
||||
modifier: buffer.modifier().unwrap(), // no guarantee
|
||||
// that this is stable across allocations, but
|
||||
// we want to print that here for debugging proposes.
|
||||
// It has no further use.
|
||||
};
|
||||
|
||||
match renderer
|
||||
.bind(dmabuf.clone())
|
||||
.map_err(Error::<E1, E2, E3>::RenderError)
|
||||
.and_then(|_| {
|
||||
renderer
|
||||
.render(
|
||||
mode.size().0 as u32,
|
||||
mode.size().1 as u32,
|
||||
Transform::Normal,
|
||||
|_, frame| frame.clear([0.0, 0.0, 0.0, 1.0]),
|
||||
)
|
||||
.map_err(Error::RenderError)
|
||||
})
|
||||
.and_then(|_| renderer.unbind().map_err(Error::RenderError))
|
||||
{
|
||||
Ok(_) => {
|
||||
let bo = import_dmabuf(&drm, &gbm, &dmabuf)?;
|
||||
let fb = bo.userdata().unwrap().unwrap().fb;
|
||||
*buffer.userdata() = Some((dmabuf, bo));
|
||||
let fb = attach_framebuffer(&drm, &*buffer)?;
|
||||
let dmabuf = buffer.export()?;
|
||||
let handle = fb.fb;
|
||||
*buffer.userdata() = Some((dmabuf, fb));
|
||||
|
||||
match drm.test_buffer(fb, &mode, true) {
|
||||
match drm.test_buffer(handle, &mode, true) {
|
||||
Ok(_) => {
|
||||
debug!(logger, "Success, choosen format: {:?}", format);
|
||||
let buffers = Buffers::new(drm.clone(), gbm, buffer);
|
||||
Ok(DrmRenderSurface {
|
||||
debug!(logger, "Choosen format: {:?}", format);
|
||||
let buffers = Buffers::new(drm.clone(), buffer);
|
||||
Ok(GbmBufferedSurface {
|
||||
buffers,
|
||||
swapchain,
|
||||
renderer,
|
||||
drm,
|
||||
})
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
logger,
|
||||
"Mode-setting failed with automatically selected buffer format {:?}: {}",
|
||||
format,
|
||||
err
|
||||
"Mode-setting failed with automatically selected buffer format {:?}: {}", format, err
|
||||
);
|
||||
Err(err).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
logger,
|
||||
"Rendering failed with automatically selected format {:?}: {}", format, err
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the next buffer to be rendered into.
|
||||
///
|
||||
/// *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<Dmabuf, Error> {
|
||||
self.buffers.next(&mut self.swapchain)
|
||||
}
|
||||
|
||||
/// Access the underlying renderer
|
||||
pub fn renderer(&mut self) -> &mut R {
|
||||
&mut self.renderer
|
||||
}
|
||||
|
||||
/// Shortcut to [`Renderer::render`] with the pending mode as dimensions
|
||||
/// and this surface set a the rendering target.
|
||||
pub fn render<F, S>(&mut self, rendering: F) -> Result<S, Error<E1, E2, E3>>
|
||||
where
|
||||
F: FnOnce(&mut R, &mut <R as Renderer>::Frame) -> S,
|
||||
{
|
||||
let mode = self.drm.pending_mode();
|
||||
let (width, height) = (mode.size().0 as u32, mode.size().1 as u32);
|
||||
let slot = self
|
||||
.swapchain
|
||||
.acquire()
|
||||
.map_err(Error::SwapchainError)?
|
||||
.ok_or(Error::NoFreeSlotsError)?;
|
||||
let dmabuf = match &*slot.userdata() {
|
||||
Some((buf, _)) => buf.clone(),
|
||||
None => (*slot).export().map_err(Error::AsDmabufError)?,
|
||||
};
|
||||
self.renderer.bind(dmabuf.clone()).map_err(Error::RenderError)?;
|
||||
|
||||
let result = self.renderer
|
||||
.render(
|
||||
width, height,
|
||||
Transform::Flipped180 /* TODO: add Add<Transform> implementation to add and correct _transform here */,
|
||||
rendering,
|
||||
)
|
||||
.map_err(Error::RenderError)?;
|
||||
|
||||
match self.buffers.queue::<E1, E2, E3>(slot, dmabuf) {
|
||||
Ok(()) => {}
|
||||
Err(Error::DrmError(drm)) => return Err(drm.into()),
|
||||
Err(Error::GbmError(err)) => return Err(err.into()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
/// Queues the current buffer for rendering.
|
||||
///
|
||||
/// *Note*: This function needs to be followed up with [`GbmBufferedSurface::frame_submitted`]
|
||||
/// 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()
|
||||
}
|
||||
|
||||
/// Marks the current frame as submitted.
|
||||
///
|
||||
/// Needs to be called, after the vblank event of the matching [`DrmDevice`](super::DrmDevice)
|
||||
/// was received after calling [`DrmRenderSurface::render`] on this surface.
|
||||
/// Otherwise the rendering will run out of buffers eventually.
|
||||
pub fn frame_submitted(&mut self) -> Result<(), Error<E1, E2, E3>> {
|
||||
/// *Note*: Needs to be called, after the vblank event of the matching [`DrmDevice`](super::DrmDevice)
|
||||
/// 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()
|
||||
}
|
||||
|
||||
|
@ -266,7 +204,7 @@ where
|
|||
}
|
||||
|
||||
/// Returns the pending [`connector`](drm::control::connector)s
|
||||
/// used for the next frame in [`render`](DrmRenderSurface::render)
|
||||
/// used for the next frame queued via [`queue_buffer`](GbmBufferedSurface::queue_buffer).
|
||||
pub fn pending_connectors(&self) -> impl IntoIterator<Item = connector::Handle> {
|
||||
self.drm.pending_connectors()
|
||||
}
|
||||
|
@ -283,13 +221,13 @@ where
|
|||
/// (e.g. no suitable [`encoder`](drm::control::encoder) may be found)
|
||||
/// or is not compatible with the currently pending
|
||||
/// [`Mode`](drm::control::Mode).
|
||||
pub fn add_connector(&self, connector: connector::Handle) -> Result<(), Error<E1, E2, E3>> {
|
||||
pub fn add_connector(&self, connector: connector::Handle) -> Result<(), Error> {
|
||||
self.drm.add_connector(connector).map_err(Error::DrmError)
|
||||
}
|
||||
|
||||
/// Tries to mark a [`connector`](drm::control::connector)
|
||||
/// for removal on the next commit.
|
||||
pub fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error<E1, E2, E3>> {
|
||||
pub fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> {
|
||||
self.drm.remove_connector(connector).map_err(Error::DrmError)
|
||||
}
|
||||
|
||||
|
@ -299,7 +237,7 @@ where
|
|||
/// (e.g. no suitable [`encoder`](drm::control::encoder) may be found)
|
||||
/// or is not compatible with the currently pending
|
||||
/// [`Mode`](drm::control::Mode).
|
||||
pub fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error<E1, E2, E3>> {
|
||||
pub fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error> {
|
||||
self.drm.set_connectors(connectors).map_err(Error::DrmError)
|
||||
}
|
||||
|
||||
|
@ -321,7 +259,7 @@ where
|
|||
/// Fails if the mode is not compatible with the underlying
|
||||
/// [`crtc`](drm::control::crtc) or any of the
|
||||
/// pending [`connector`](drm::control::connector)s.
|
||||
pub fn use_mode(&self, mode: Mode) -> Result<(), Error<E1, E2, E3>> {
|
||||
pub fn use_mode(&self, mode: Mode) -> Result<(), Error> {
|
||||
self.drm.use_mode(mode).map_err(Error::DrmError)
|
||||
}
|
||||
}
|
||||
|
@ -337,65 +275,66 @@ impl<A: AsRawFd + 'static> Drop for FbHandle<A> {
|
|||
}
|
||||
}
|
||||
|
||||
type DmabufSlot<B, D> = Slot<B, (Dmabuf, BufferObject<FbHandle<D>>)>;
|
||||
type DmabufSlot<D> = Slot<BufferObject<()>, (Dmabuf, FbHandle<D>)>;
|
||||
|
||||
struct Buffers<D: AsRawFd + 'static, B: Buffer> {
|
||||
gbm: GbmDevice<gbm::FdWrapper>,
|
||||
struct Buffers<D: AsRawFd + 'static> {
|
||||
drm: Arc<DrmSurface<D>>,
|
||||
_current_fb: DmabufSlot<B, D>,
|
||||
pending_fb: Option<DmabufSlot<B, D>>,
|
||||
queued_fb: Option<DmabufSlot<B, D>>,
|
||||
_current_fb: DmabufSlot<D>,
|
||||
pending_fb: Option<DmabufSlot<D>>,
|
||||
queued_fb: Option<DmabufSlot<D>>,
|
||||
next_fb: Option<DmabufSlot<D>>,
|
||||
}
|
||||
|
||||
impl<D, B> Buffers<D, B>
|
||||
impl<D> Buffers<D>
|
||||
where
|
||||
B: Buffer + AsDmabuf,
|
||||
D: AsRawFd + 'static,
|
||||
{
|
||||
pub fn new(
|
||||
drm: Arc<DrmSurface<D>>,
|
||||
gbm: GbmDevice<gbm::FdWrapper>,
|
||||
slot: Slot<B, (Dmabuf, BufferObject<FbHandle<D>>)>,
|
||||
) -> Buffers<D, B> {
|
||||
pub fn new(drm: Arc<DrmSurface<D>>, slot: DmabufSlot<D>) -> Buffers<D> {
|
||||
Buffers {
|
||||
drm,
|
||||
gbm,
|
||||
_current_fb: slot,
|
||||
pending_fb: None,
|
||||
queued_fb: None,
|
||||
next_fb: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue<E1, E2, E3>(
|
||||
pub fn next(
|
||||
&mut self,
|
||||
slot: Slot<B, (Dmabuf, BufferObject<FbHandle<D>>)>,
|
||||
dmabuf: Dmabuf,
|
||||
) -> Result<(), Error<E1, E2, E3>>
|
||||
where
|
||||
B: AsDmabuf<Error = E2>,
|
||||
E1: std::error::Error + 'static,
|
||||
E2: std::error::Error + 'static,
|
||||
E3: std::error::Error + 'static,
|
||||
{
|
||||
if slot.userdata().is_none() {
|
||||
let bo = import_dmabuf(&self.drm, &self.gbm, &dmabuf)?;
|
||||
*slot.userdata() = Some((dmabuf, bo));
|
||||
swapchain: &mut Swapchain<GbmDevice<D>, BufferObject<()>, (Dmabuf, FbHandle<D>)>,
|
||||
) -> Result<Dmabuf, Error> {
|
||||
if let Some(slot) = self.next_fb.as_ref() {
|
||||
return Ok(slot.userdata().as_ref().unwrap().0.clone());
|
||||
}
|
||||
|
||||
self.queued_fb = Some(slot);
|
||||
if self.pending_fb.is_none() {
|
||||
let slot = swapchain.acquire()?.ok_or(Error::NoFreeSlotsError)?;
|
||||
|
||||
let maybe_buffer = slot.userdata().as_ref().map(|(buf, _)| buf.clone());
|
||||
let dmabuf = match maybe_buffer {
|
||||
Some(buf) => buf.clone(),
|
||||
None => {
|
||||
let dmabuf = slot.export()?;
|
||||
let fb_handle = attach_framebuffer(&self.drm, &*slot)?;
|
||||
*slot.userdata() = Some((dmabuf.clone(), fb_handle));
|
||||
dmabuf
|
||||
}
|
||||
};
|
||||
|
||||
self.next_fb = Some(slot);
|
||||
|
||||
Ok(dmabuf)
|
||||
}
|
||||
|
||||
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<E1, E2, E3>(&mut self) -> Result<(), Error<E1, E2, E3>>
|
||||
where
|
||||
E1: std::error::Error + 'static,
|
||||
E2: std::error::Error + 'static,
|
||||
E3: std::error::Error + 'static,
|
||||
{
|
||||
pub fn submitted(&mut self) -> Result<(), Error> {
|
||||
if self.pending_fb.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -407,23 +346,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn submit<E1, E2, E3>(&mut self) -> Result<(), Error<E1, E2, E3>>
|
||||
where
|
||||
E1: std::error::Error + 'static,
|
||||
E2: std::error::Error + 'static,
|
||||
E3: std::error::Error + 'static,
|
||||
{
|
||||
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()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.1
|
||||
.userdata()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.fb;
|
||||
let fb = slot.userdata().as_ref().unwrap().1.fb;
|
||||
|
||||
let flip = if self.drm.commit_pending() {
|
||||
self.drm.commit([(fb, self.drm.plane())].iter(), true)
|
||||
|
@ -437,19 +363,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn import_dmabuf<A, E1, E2, E3>(
|
||||
drm: &Arc<DrmSurface<A>>,
|
||||
gbm: &GbmDevice<gbm::FdWrapper>,
|
||||
buffer: &Dmabuf,
|
||||
) -> Result<BufferObject<FbHandle<A>>, Error<E1, E2, E3>>
|
||||
fn attach_framebuffer<A>(drm: &Arc<DrmSurface<A>>, bo: &BufferObject<()>) -> Result<FbHandle<A>, Error>
|
||||
where
|
||||
A: AsRawFd + 'static,
|
||||
E1: std::error::Error + 'static,
|
||||
E2: std::error::Error + 'static,
|
||||
E3: std::error::Error + 'static,
|
||||
{
|
||||
// TODO check userdata and return early
|
||||
let mut bo = buffer.import_to(&gbm, BufferObjectFlags::SCANOUT)?;
|
||||
let modifier = match bo.modifier().unwrap() {
|
||||
Modifier::Invalid => None,
|
||||
x => Some(x),
|
||||
|
@ -468,15 +385,15 @@ where
|
|||
if num > 2 { modifier } else { None },
|
||||
if num > 3 { modifier } else { None },
|
||||
];
|
||||
drm.add_planar_framebuffer(&bo, &modifiers, drm_ffi::DRM_MODE_FB_MODIFIERS)
|
||||
drm.add_planar_framebuffer(bo, &modifiers, drm_ffi::DRM_MODE_FB_MODIFIERS)
|
||||
} else {
|
||||
drm.add_planar_framebuffer(&bo, &[None, None, None, None], 0)
|
||||
drm.add_planar_framebuffer(bo, &[None, None, None, None], 0)
|
||||
} {
|
||||
Ok(fb) => fb,
|
||||
Err(source) => {
|
||||
// We only support this as a fallback of last resort for ARGB8888 visuals,
|
||||
// like xf86-video-modesetting does.
|
||||
if drm::buffer::Buffer::format(&bo) != Fourcc::Argb8888 || bo.handles()[1].is_some() {
|
||||
if drm::buffer::Buffer::format(bo) != Fourcc::Argb8888 || bo.handles()[1].is_some() {
|
||||
return Err(Error::DrmError(DrmError::Access {
|
||||
errmsg: "Failed to add framebuffer",
|
||||
dev: drm.dev_path(),
|
||||
|
@ -484,7 +401,7 @@ where
|
|||
}));
|
||||
}
|
||||
debug!(logger, "Failed to add framebuffer, trying legacy method");
|
||||
drm.add_framebuffer(&bo, 32, 32)
|
||||
drm.add_framebuffer(bo, 32, 32)
|
||||
.map_err(|source| DrmError::Access {
|
||||
errmsg: "Failed to add framebuffer",
|
||||
dev: drm.dev_path(),
|
||||
|
@ -492,19 +409,12 @@ where
|
|||
})?
|
||||
}
|
||||
};
|
||||
bo.set_userdata(FbHandle { drm: drm.clone(), fb }).unwrap();
|
||||
|
||||
Ok(bo)
|
||||
Ok(FbHandle { drm: drm.clone(), fb })
|
||||
}
|
||||
|
||||
/// Errors thrown by a [`DrmRenderSurface`]
|
||||
/// Errors thrown by a [`GbmBufferedSurface`]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error<E1, E2, E3>
|
||||
where
|
||||
E1: std::error::Error + 'static,
|
||||
E2: std::error::Error + 'static,
|
||||
E3: std::error::Error + 'static,
|
||||
{
|
||||
pub enum Error {
|
||||
/// No supported pixel format for the given plane could be determined
|
||||
#[error("No supported plane buffer format found")]
|
||||
NoSupportedPlaneFormat,
|
||||
|
@ -526,24 +436,13 @@ where
|
|||
/// Error importing the rendered buffer to libgbm for scan-out
|
||||
#[error("The underlying gbm device encounted an error: {0}")]
|
||||
GbmError(#[from] std::io::Error),
|
||||
/// Error allocating or converting newly created buffers
|
||||
#[error("The swapchain encounted an error: {0}")]
|
||||
SwapchainError(#[source] E1),
|
||||
/// Error exporting as Dmabuf
|
||||
#[error("The allocated buffer could not be exported as a dmabuf: {0}")]
|
||||
AsDmabufError(#[source] E2),
|
||||
/// Error during rendering
|
||||
#[error("The renderer encounted an error: {0}")]
|
||||
RenderError(#[source] E3),
|
||||
AsDmabufError(#[from] GbmConvertError),
|
||||
}
|
||||
|
||||
impl<
|
||||
E1: std::error::Error + 'static,
|
||||
E2: std::error::Error + 'static,
|
||||
E3: std::error::Error + Into<SwapBuffersError> + 'static,
|
||||
> From<Error<E1, E2, E3>> for SwapBuffersError
|
||||
{
|
||||
fn from(err: Error<E1, E2, E3>) -> SwapBuffersError {
|
||||
impl From<Error> for SwapBuffersError {
|
||||
fn from(err: Error) -> SwapBuffersError {
|
||||
match err {
|
||||
x @ Error::NoSupportedPlaneFormat
|
||||
| x @ Error::NoSupportedRendererFormat
|
||||
|
@ -552,9 +451,7 @@ impl<
|
|||
x @ Error::NoFreeSlotsError => SwapBuffersError::TemporaryFailure(Box::new(x)),
|
||||
Error::DrmError(err) => err.into(),
|
||||
Error::GbmError(err) => SwapBuffersError::ContextLost(Box::new(err)),
|
||||
Error::SwapchainError(err) => SwapBuffersError::ContextLost(Box::new(err)),
|
||||
Error::AsDmabufError(err) => SwapBuffersError::ContextLost(Box::new(err)),
|
||||
Error::RenderError(err) => err.into(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ use drm::{Device as BasicDevice, DriverCapability};
|
|||
use nix::libc::dev_t;
|
||||
|
||||
pub(super) mod atomic;
|
||||
#[cfg(feature = "backend_gbm")]
|
||||
pub(super) mod gbm;
|
||||
pub(super) mod legacy;
|
||||
use super::{device::DevPath, error::Error, plane_type, planes, PlaneType, Planes};
|
||||
use crate::backend::allocator::{Format, Fourcc, Modifier};
|
||||
|
|
Loading…
Reference in New Issue