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