Merge pull request #209 from Smithay/fix/egl_glium_errors

Rework rendering loop
This commit is contained in:
Victor Brekenfeld 2020-05-14 00:45:20 +02:00 committed by GitHub
commit 1a39c208c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 466 additions and 279 deletions

View File

@ -50,7 +50,7 @@ jobs:
override: true
- name: System dependencies
run: sudo apt-get install libudev-dev libgbm-dev libxkbcommon-dev libegl1-mesa-dev libwayland-dev libinput-dev libsystemd-dev libdbus-1-dev
run: sudo apt-get update; sudo apt-get install libudev-dev libgbm-dev libxkbcommon-dev libegl1-mesa-dev libwayland-dev libinput-dev libsystemd-dev libdbus-1-dev
- name: Test features
if: matrix.features != 'all'
@ -100,7 +100,7 @@ jobs:
override: true
- name: System dependencies
run: sudo apt-get install libudev-dev libgbm-dev libxkbcommon-dev libegl1-mesa-dev libwayland-dev libinput-dev libsystemd-dev libdbus-1-dev
run: sudo apt-get update; sudo apt-get install libudev-dev libgbm-dev libxkbcommon-dev libegl1-mesa-dev libwayland-dev libinput-dev libsystemd-dev libdbus-1-dev
- name: Test features
if: matrix.features != 'all'
@ -128,7 +128,7 @@ jobs:
components: rustfmt, clippy
- name: System dependencies
run: sudo apt-get install libudev-dev libgbm-dev libxkbcommon-dev libegl1-mesa-dev libwayland-dev libinput-dev libsystemd-dev libdbus-1-dev
run: sudo apt-get update; sudo apt-get install libudev-dev libgbm-dev libxkbcommon-dev libegl1-mesa-dev libwayland-dev libinput-dev libsystemd-dev libdbus-1-dev
- name: Cargo fmt
run: cargo fmt --all -- --check

View File

@ -19,9 +19,13 @@ use smithay::{
graphics::{
gl::GLGraphicsBackend,
glium::{Frame, GliumGraphicsBackend},
SwapBuffersError,
},
},
reexports::wayland_server::protocol::{wl_buffer, wl_surface},
reexports::{
calloop::LoopHandle,
wayland_server::protocol::{wl_buffer, wl_surface},
},
wayland::{
compositor::{roles::Role, SubsurfaceRole, TraversalAction},
data_device::DnDIconRole,
@ -457,3 +461,23 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
self.draw_surface_tree(frame, surface, (x, y), token, screen_dimensions);
}
}
pub fn schedule_initial_render<F: GLGraphicsBackend + 'static, Data: 'static>(
renderer: Rc<GliumDrawer<F>>,
evt_handle: &LoopHandle<Data>,
) {
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
warn!(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),
}
}
}

View File

@ -25,20 +25,27 @@ 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::{
connector::{Info as ConnectorInfo, State as ConnectorState},
crtc,
encoder::Info as EncoderInfo,
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,
@ -57,7 +64,7 @@ use smithay::{
};
use crate::buffer_utils::BufferUtils;
use crate::glium_drawer::GliumDrawer;
use crate::glium_drawer::{schedule_initial_render, GliumDrawer};
use crate::shell::{MyWindowMap, Roles};
use crate::state::AnvilState;
@ -242,8 +249,9 @@ pub fn run_udev(
struct BackendData<S: SessionNotifier> {
id: S::Id,
restart_id: S::Id,
event_source: Source<Generic<RenderDevice>>,
surfaces: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<RenderSurface>>>>,
surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>>>>,
}
struct UdevHandlerImpl<S: SessionNotifier, Data: 'static> {
@ -270,7 +278,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
device: &mut RenderDevice,
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
logger: &::slog::Logger,
) -> HashMap<crtc::Handle, GliumDrawer<RenderSurface>> {
) -> HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>> {
// Get a set of all modesetting resource handles (excluding planes):
let res_handles = device.resource_handles().unwrap();
@ -304,7 +312,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
logger.clone(),
);
entry.insert(renderer);
entry.insert(Rc::new(renderer));
break;
}
}
@ -318,7 +326,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
pub fn scan_connectors(
device: &mut RenderDevice,
logger: &::slog::Logger,
) -> HashMap<crtc::Handle, GliumDrawer<RenderSurface>> {
) -> HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>> {
// Get a set of all modesetting resource handles (excluding planes):
let res_handles = device.resource_handles().unwrap();
@ -347,7 +355,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
let renderer =
GliumDrawer::init(device.create_surface(crtc).unwrap(), logger.clone());
backends.insert(crtc, renderer);
backends.insert(crtc, Rc::new(renderer));
break;
}
}
@ -372,7 +380,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
|fd| match FallbackDevice::new(SessionFd(fd), true, self.logger.clone()) {
Ok(drm) => Some(drm),
Err(err) => {
error!(self.logger, "Skipping drm device, because of error: {}", err);
warn!(self.logger, "Skipping drm device, because of error: {}", err);
None
}
},
@ -380,14 +388,14 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
.and_then(|drm| match GbmDevice::new(drm, self.logger.clone()) {
Ok(gbm) => Some(gbm),
Err(err) => {
error!(self.logger, "Skipping gbm device, because of error: {}", err);
warn!(self.logger, "Skipping gbm device, because of error: {}", err);
None
}
})
.and_then(|gbm| match EglDevice::new(gbm, self.logger.clone()) {
Ok(egl) => Some(egl),
Err(err) => {
error!(self.logger, "Skipping egl device, because of error: {}", err);
warn!(self.logger, "Skipping egl device, because of error: {}", err);
None
}
})
@ -417,7 +425,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
// 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 +434,14 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
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 +457,14 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
.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 +478,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
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 +500,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
.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 +524,48 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
}
self.notifier.unregister(backend_data.id);
self.notifier.unregister(backend_data.restart_id);
debug!(self.logger, "Dropping device");
}
}
}
pub struct DrmHandlerImpl {
pub struct DrmHandlerImpl<Data: 'static> {
renderer: Rc<DrmRenderer>,
loop_handle: LoopHandle<Data>,
}
impl<Data: 'static> DeviceHandler for DrmHandlerImpl<Data> {
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: <RenderSurface as Surface>::Error) {
error!(self.renderer.logger, "{:?}", error);
}
}
pub struct DrmRendererSessionListener<Data: 'static> {
renderer: Rc<DrmRenderer>,
loop_handle: LoopHandle<Data>,
}
impl<Data: 'static> SessionObserver for DrmRendererSessionListener<Data> {
fn pause(&mut self, _device: Option<(u32, u32)>) {}
fn activate(&mut self, _device: Option<(u32, u32, Option<RawFd>)>) {
// 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<Roles>,
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<RenderSurface>>>>,
backends: Rc<RefCell<HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>>>>,
window_map: Rc<RefCell<MyWindowMap>>,
pointer_location: Rc<RefCell<(f64, f64)>>,
cursor_status: Arc<Mutex<CursorImageStatus>>,
@ -529,10 +573,18 @@ 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<Data: 'static>(self: Rc<Self>, evt_handle: Option<&LoopHandle<Data>>) {
for crtc in self.backends.borrow().keys() {
self.clone().render(*crtc, None, evt_handle);
}
}
fn render<Data: 'static>(
self: Rc<Self>,
crtc: crtc::Handle,
timer: Option<TimerHandle<(std::rc::Weak<DrmRenderer>, crtc::Handle)>>,
evt_handle: Option<&LoopHandle<Data>>,
) {
if let Some(drawer) = self.backends.borrow().get(&crtc) {
{
let (x, y) = *self.pointer_location.borrow();
@ -577,16 +629,67 @@ 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 {
warn!(self.logger, "Error during rendering: {:?}", err);
let reschedule = match err {
SwapBuffersError::AlreadySwapped => false,
SwapBuffersError::TemporaryFailure(err) => {
match err.downcast_ref::<smithay::backend::drm::common::Error>() {
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<Data>>::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: <RenderSurface as Surface>::Error) {
error!(self.logger, "{:?}", error);
}
}

View File

@ -389,29 +389,21 @@ impl<A: AsRawFd + 'static> Device for AtomicDrmDevice<A> {
for event in events {
if let Event::PageFlip(event) = event {
trace!(self.logger, "Got a page-flip event for crtc ({:?})", event.crtc);
if self.active.load(Ordering::SeqCst) {
if self
.backends
.borrow()
.get(&event.crtc)
.iter()
.flat_map(|x| x.upgrade())
.next()
.is_some()
{
trace!(self.logger, "Handling event for backend {:?}", event.crtc);
if let Some(handler) = self.handler.as_ref() {
handler.borrow_mut().vblank(event.crtc);
}
} else {
self.backends.borrow_mut().remove(&event.crtc);
if self
.backends
.borrow()
.get(&event.crtc)
.iter()
.flat_map(|x| x.upgrade())
.next()
.is_some()
{
trace!(self.logger, "Handling event for backend {:?}", event.crtc);
if let Some(handler) = self.handler.as_ref() {
handler.borrow_mut().vblank(event.crtc);
}
} else {
debug!(
self.logger,
"Device ({:?}) not active. Ignoring PageFlip",
self.dev_path()
);
self.backends.borrow_mut().remove(&event.crtc);
}
} else {
trace!(

View File

@ -16,7 +16,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use super::{AtomicDrmDevice, AtomicDrmSurfaceInternal, Dev};
use crate::backend::drm::{common::Error, DevPath};
use crate::backend::drm::{common::Error, DevPath, Surface};
use crate::backend::session::{AsSessionObserver, SessionObserver};
/// [`SessionObserver`](SessionObserver)
@ -164,6 +164,18 @@ impl<A: AsRawFd + 'static> AtomicDrmDeviceObserver<A> {
// lets force a non matching state
current.connectors.clear();
current.mode = unsafe { std::mem::zeroed() };
// recreate property blob
let mode = {
let pending = surface.pending.read().unwrap();
pending.mode
};
surface.use_mode(mode)?;
// drop cursor state
surface.cursor.position.set(None);
surface.cursor.hotspot.set((0, 0));
surface.cursor.framebuffer.set(None);
}
}
}

View File

@ -20,9 +20,9 @@ use crate::backend::graphics::CursorBackend;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CursorState {
position: Cell<Option<(u32, u32)>>,
hotspot: Cell<(u32, u32)>,
framebuffer: Cell<Option<framebuffer::Info>>,
pub position: Cell<Option<(u32, u32)>>,
pub hotspot: Cell<(u32, u32)>,
pub framebuffer: Cell<Option<framebuffer::Handle>>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
@ -66,6 +66,11 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
connectors: &[connector::Handle],
logger: ::slog::Logger,
) -> Result<Self, Error> {
info!(
logger,
"Initializing drm surface with mode {:?} and connectors {:?}", mode, connectors
);
let crtc_info = dev.get_crtc(crtc).compat().map_err(|source| Error::Access {
errmsg: "Error loading crtc info",
dev: dev.dev_path(),
@ -454,7 +459,7 @@ impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurfaceInternal<A> {
}
let mut current = self.state.write().unwrap();
let mut pending = self.pending.write().unwrap();
let pending = self.pending.write().unwrap();
debug!(
self.logger,
@ -510,25 +515,14 @@ impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurfaceInternal<A> {
self.logger,
"New screen configuration invalid!:\n\t{:#?}\n\t{}\n", req, err
);
info!(self.logger, "Reverting back to last know good state");
*pending = current.clone();
self.build_request(
&mut [].iter(),
&mut [].iter(),
&self.planes,
Some(framebuffer),
Some(current.mode),
Some(current.blob),
)?
return Err(err);
} else {
if current.mode != pending.mode {
if let Err(err) = self.dev.destroy_property_blob(current.blob.into()) {
warn!(self.logger, "Failed to destory old mode property blob: {}", err);
}
}
*current = pending.clone();
// new config
req
@ -536,22 +530,27 @@ impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurfaceInternal<A> {
};
debug!(self.logger, "Setting screen: {:?}", req);
self.atomic_commit(
&[
AtomicCommitFlags::PageFlipEvent,
AtomicCommitFlags::AllowModeset,
AtomicCommitFlags::Nonblock,
],
req,
)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error setting crtc",
dev: self.dev_path(),
source,
})?;
let result = self
.atomic_commit(
&[
AtomicCommitFlags::PageFlipEvent,
AtomicCommitFlags::AllowModeset,
AtomicCommitFlags::Nonblock,
],
req,
)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error setting crtc",
dev: self.dev_path(),
source,
});
Ok(())
if result.is_ok() {
*current = pending.clone();
}
result
}
fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), Error> {
@ -568,7 +567,7 @@ impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurfaceInternal<A> {
None,
)?;
trace!(self.logger, "Queueing page flip: {:#?}", req);
trace!(self.logger, "Queueing page flip: {:?}", req);
self.atomic_commit(
&[AtomicCommitFlags::PageFlipEvent, AtomicCommitFlags::Nonblock],
req,
@ -610,23 +609,17 @@ impl<A: AsRawFd + 'static> CursorBackend for AtomicDrmSurfaceInternal<A> {
trace!(self.logger, "Setting the new imported cursor");
if let Some(fb) = self.cursor.framebuffer.get().take() {
let _ = self.destroy_framebuffer(fb.handle());
let _ = self.destroy_framebuffer(fb);
}
self.cursor.framebuffer.set(Some(
self.get_framebuffer(self.add_planar_framebuffer(buffer, &[0; 4], 0).compat().map_err(
|source| Error::Access {
self.add_planar_framebuffer(buffer, &[0; 4], 0)
.compat()
.map_err(|source| Error::Access {
errmsg: "Failed to import cursor",
dev: self.dev_path(),
source,
},
)?)
.compat()
.map_err(|source| Error::Access {
errmsg: "Failed to get framebuffer info",
dev: self.dev_path(),
source,
})?,
})?,
));
self.cursor.hotspot.set(hotspot);
@ -803,58 +796,70 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
let cursor_fb = self.cursor.framebuffer.get();
if let (Some(pos), Some(fb)) = (cursor_pos, cursor_fb) {
let hotspot = self.cursor.hotspot.get();
match self.get_framebuffer(fb).compat().map_err(|source| Error::Access {
errmsg: "Error getting cursor fb",
dev: self.dev_path(),
source,
}) {
Ok(cursor_info) => {
let hotspot = self.cursor.hotspot.get();
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_ID")?,
property::Value::CRTC(Some(self.crtc)),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_X")?,
property::Value::UnsignedRange(0),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_Y")?,
property::Value::UnsignedRange(0),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_W")?,
property::Value::UnsignedRange((fb.size().0 as u64) << 16),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_H")?,
property::Value::UnsignedRange((fb.size().1 as u64) << 16),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_X")?,
property::Value::SignedRange(pos.0 as i64 - (hotspot.0 as i64)),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_Y")?,
property::Value::SignedRange(pos.1 as i64 - (hotspot.1 as i64)),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_W")?,
property::Value::UnsignedRange(fb.size().0 as u64),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_H")?,
property::Value::UnsignedRange(fb.size().1 as u64),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "FB_ID")?,
property::Value::Framebuffer(Some(fb.handle())),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_ID")?,
property::Value::CRTC(Some(self.crtc)),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_X")?,
property::Value::UnsignedRange(0),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_Y")?,
property::Value::UnsignedRange(0),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_W")?,
property::Value::UnsignedRange((cursor_info.size().0 as u64) << 16),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_H")?,
property::Value::UnsignedRange((cursor_info.size().1 as u64) << 16),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_X")?,
property::Value::SignedRange(pos.0 as i64 - (hotspot.0 as i64)),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_Y")?,
property::Value::SignedRange(pos.1 as i64 - (hotspot.1 as i64)),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_W")?,
property::Value::UnsignedRange(cursor_info.size().0 as u64),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_H")?,
property::Value::UnsignedRange(cursor_info.size().1 as u64),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "FB_ID")?,
property::Value::Framebuffer(Some(fb)),
);
}
Err(err) => {
warn!(self.logger, "Cursor FB invalid: {}. Skipping.", err);
self.cursor.framebuffer.set(None);
}
}
}
Ok(req)

View File

@ -191,7 +191,7 @@ impl<A: AsRawFd + Clone + 'static> FallbackDevice<AtomicDrmDevice<A>, LegacyDrmD
match AtomicDrmDevice::new(fd.clone(), disable_connectors, log.clone()) {
Ok(dev) => Ok(FallbackDevice::Preference(dev)),
Err(err) => {
error!(log, "Failed to initialize preferred AtomicDrmDevice: {}", err);
warn!(log, "Failed to initialize preferred AtomicDrmDevice: {}", err);
info!(log, "Falling back to fallback LegacyDrmDevice");
Ok(FallbackDevice::Fallback(LegacyDrmDevice::new(
fd,

View File

@ -76,18 +76,20 @@ impl Into<SwapBuffersError> for Error {
fn into(self) -> SwapBuffersError {
match self {
x @ Error::DeviceInactive => SwapBuffersError::TemporaryFailure(Box::new(x)),
Error::Access { source, .. }
if match source.get_ref() {
drm::SystemError::Unknown {
errno: nix::errno::Errno::EBUSY,
} => true,
drm::SystemError::Unknown {
errno: nix::errno::Errno::EINTR,
} => true,
_ => false,
} =>
Error::Access {
errmsg, dev, source, ..
} if match source.get_ref() {
drm::SystemError::PermissionDenied => true,
drm::SystemError::Unknown {
errno: nix::errno::Errno::EBUSY,
} => true,
drm::SystemError::Unknown {
errno: nix::errno::Errno::EINTR,
} => true,
_ => false,
} =>
{
SwapBuffersError::TemporaryFailure(Box::new(source))
SwapBuffersError::TemporaryFailure(Box::new(Error::Access { errmsg, dev, source }))
}
x => SwapBuffersError::ContextLost(Box::new(x)),
}

View File

@ -11,7 +11,10 @@
use drm::control::{connector, crtc, encoder, framebuffer, plane, Mode, ResourceHandles};
use drm::SystemError as DrmError;
use nix::libc::dev_t;
use std::cell::RefCell;
use std::collections::HashMap;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak};
#[cfg(feature = "use_system_lib")]
use wayland_server::Display;
@ -44,6 +47,7 @@ pub enum Error<U: std::error::Error + std::fmt::Debug + std::fmt::Display + 'sta
}
type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>);
type BackendRef<D> = Weak<EglSurfaceInternal<<D as Device>::Surface>>;
/// Representation of an egl device to create egl rendering surfaces
pub struct EglDevice<B, D>
@ -58,6 +62,7 @@ where
logger: ::slog::Logger,
default_attributes: GlAttributes,
default_requirements: PixelFormatRequirements,
backends: Rc<RefCell<HashMap<crtc::Handle, BackendRef<D>>>>,
}
impl<B, D> AsRawFd for EglDevice<B, D>
@ -125,6 +130,7 @@ where
dev: EGLDisplay::new(dev, log.clone()).map_err(Error::EGL)?,
default_attributes,
default_requirements,
backends: Rc::new(RefCell::new(HashMap::new())),
logger: log,
})
}
@ -208,7 +214,9 @@ where
SurfaceCreationError::NativeSurfaceCreationFailed(err) => Error::Underlying(err),
})?;
Ok(EglSurface { context, surface })
let backend = Rc::new(EglSurfaceInternal { context, surface });
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend));
Ok(EglSurface(backend))
}
fn process_events(&mut self) {

View File

@ -4,21 +4,28 @@
//!
use drm::control::{connector, crtc, Mode};
use std::cell::RefCell;
use std::collections::HashMap;
use std::os::unix::io::RawFd;
use std::rc::{Rc, Weak};
use super::EglDevice;
use super::{EglDevice, EglSurfaceInternal};
use crate::backend::drm::{Device, Surface};
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use crate::backend::egl::{
ffi,
native::{Backend, NativeDisplay, NativeSurface},
};
use crate::backend::session::{AsSessionObserver, SessionObserver};
/// [`SessionObserver`](SessionObserver)
/// linked to the [`EglDevice`](EglDevice) it was
/// created from.
pub struct EglDeviceObserver<S: SessionObserver + 'static> {
pub struct EglDeviceObserver<S: SessionObserver + 'static, N: NativeSurface + Surface> {
observer: S,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglSurfaceInternal<N>>>>>,
}
impl<S, B, D> AsSessionObserver<EglDeviceObserver<S>> for EglDevice<B, D>
impl<S, B, D> AsSessionObserver<EglDeviceObserver<S, <D as Device>::Surface>> for EglDevice<B, D>
where
S: SessionObserver + 'static,
B: Backend<Surface = <D as Device>::Surface> + 'static,
@ -31,19 +38,32 @@ where
+ 'static,
<D as Device>::Surface: NativeSurface,
{
fn observer(&mut self) -> EglDeviceObserver<S> {
fn observer(&mut self) -> EglDeviceObserver<S, <D as Device>::Surface> {
EglDeviceObserver {
observer: self.dev.borrow_mut().observer(),
backends: Rc::downgrade(&self.backends),
}
}
}
impl<S: SessionObserver + 'static> SessionObserver for EglDeviceObserver<S> {
impl<S: SessionObserver + 'static, N: NativeSurface + Surface> SessionObserver for EglDeviceObserver<S, N> {
fn pause(&mut self, devnum: Option<(u32, u32)>) {
self.observer.pause(devnum);
}
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
self.observer.activate(devnum);
if let Some(backends) = self.backends.upgrade() {
for (_crtc, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() {
let old_surface = backend.surface.surface.replace(std::ptr::null());
if !old_surface.is_null() {
unsafe {
ffi::egl::DestroySurface(**backend.surface.display, old_surface as *const _);
}
}
}
}
}
}
}

View File

@ -12,8 +12,12 @@ use crate::backend::graphics::gl::GLGraphicsBackend;
use crate::backend::graphics::PixelFormat;
use crate::backend::graphics::{CursorBackend, SwapBuffersError};
use std::rc::Rc;
/// Egl surface for rendering
pub struct EglSurface<N>
pub struct EglSurface<N: native::NativeSurface + Surface>(pub(super) Rc<EglSurfaceInternal<N>>);
pub(super) struct EglSurfaceInternal<N>
where
N: native::NativeSurface + Surface,
{
@ -29,41 +33,45 @@ where
type Error = Error<<N as Surface>::Error>;
fn crtc(&self) -> crtc::Handle {
(*self.surface).crtc()
(*self.0.surface).crtc()
}
fn current_connectors(&self) -> Self::Connectors {
self.surface.current_connectors()
self.0.surface.current_connectors()
}
fn pending_connectors(&self) -> Self::Connectors {
self.surface.pending_connectors()
self.0.surface.pending_connectors()
}
fn add_connector(&self, connector: connector::Handle) -> Result<(), Self::Error> {
self.surface.add_connector(connector).map_err(Error::Underlying)
self.0.surface.add_connector(connector).map_err(Error::Underlying)
}
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error> {
self.surface
self.0
.surface
.remove_connector(connector)
.map_err(Error::Underlying)
}
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.surface.set_connectors(connectors).map_err(Error::Underlying)
self.0
.surface
.set_connectors(connectors)
.map_err(Error::Underlying)
}
fn current_mode(&self) -> Mode {
self.surface.current_mode()
self.0.surface.current_mode()
}
fn pending_mode(&self) -> Mode {
self.surface.pending_mode()
self.0.surface.pending_mode()
}
fn use_mode(&self, mode: Mode) -> Result<(), Self::Error> {
self.surface.use_mode(mode).map_err(Error::Underlying)
self.0.surface.use_mode(mode).map_err(Error::Underlying)
}
}
@ -75,7 +83,7 @@ where
type Error = <N as CursorBackend>::Error;
fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> {
self.surface.set_cursor_position(x, y)
self.0.surface.set_cursor_position(x, y)
}
fn set_cursor_representation(
@ -83,7 +91,7 @@ where
buffer: &Self::CursorFormat,
hotspot: (u32, u32),
) -> ::std::result::Result<(), Self::Error> {
self.surface.set_cursor_representation(buffer, hotspot)
self.0.surface.set_cursor_representation(buffer, hotspot)
}
}
@ -94,7 +102,7 @@ where
<N as NativeSurface>::Error: Into<SwapBuffersError> + 'static,
{
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
if let Err(err) = self.surface.swap_buffers() {
if let Err(err) = self.0.surface.swap_buffers() {
Err(match err.try_into() {
Ok(x) => x,
Err(x) => x.into(),
@ -114,16 +122,17 @@ where
}
fn is_current(&self) -> bool {
self.context.is_current() && self.surface.is_current()
self.0.context.is_current() && self.0.surface.is_current()
}
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.context
.make_current_with_surface(&self.surface)
self.0
.context
.make_current_with_surface(&self.0.surface)
.map_err(Into::into)
}
fn get_pixel_format(&self) -> PixelFormat {
self.surface.get_pixel_format()
self.0.surface.get_pixel_format()
}
}

View File

@ -274,7 +274,7 @@ where
_ => false,
} =>
{
SwapBuffersError::TemporaryFailure(Box::new(x))
SwapBuffersError::TemporaryFailure(Box::new(Error::<E>::FramebufferCreationFailed(x)))
}
Error::Underlying(x) => x.into(),
x => SwapBuffersError::ContextLost(Box::new(x)),

View File

@ -58,22 +58,7 @@ impl<
if let Some(backends) = self.backends.upgrade() {
for (crtc, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() {
// restart rendering loop, if it was previously running
if let Some(current_fb) = backend.current_frame_buffer.get() {
let result = if backend.crtc.commit_pending() {
backend.crtc.commit(current_fb)
} else {
RawSurface::page_flip(&backend.crtc, current_fb)
};
if let Err(err) = result {
warn!(
self.logger,
"Failed to restart rendering loop. Re-creating resources. Error: {}", err
);
// TODO bubble up
}
}
backend.clear_framebuffers();
// reset cursor
{

View File

@ -74,18 +74,31 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
};
self.next_buffer.set(Some(next_bo));
if self.recreated.get() {
debug!(self.logger, "Commiting new state");
self.crtc.commit(fb).map_err(Error::Underlying)?;
self.recreated.set(false);
} else {
trace!(self.logger, "Queueing Page flip");
RawSurface::page_flip(&self.crtc, fb).map_err(Error::Underlying)?;
if cfg!(debug_assertions) {
if let Err(err) = self.crtc.get_framebuffer(fb) {
error!(self.logger, "Cached framebuffer invalid: {:?}: {}", fb, err);
}
}
self.current_frame_buffer.set(Some(fb));
let result = if self.recreated.get() {
debug!(self.logger, "Commiting new state");
self.crtc.commit(fb).map_err(Error::Underlying)
} else {
trace!(self.logger, "Queueing Page flip");
RawSurface::page_flip(&self.crtc, fb).map_err(Error::Underlying)
};
Ok(())
match result {
Ok(_) => {
self.recreated.set(false);
self.current_frame_buffer.set(Some(fb));
Ok(())
}
Err(err) => {
self.unlock_buffer();
Err(err)
}
}
}
pub fn recreate(&self) -> Result<(), Error<<<D as Device>::Surface as Surface>::Error>> {
@ -106,6 +119,17 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
.map_err(Error::SurfaceCreationFailed)?;
// Clean up buffers
self.clear_framebuffers();
// Drop the old surface after cleanup
*self.surface.borrow_mut() = surface;
self.recreated.set(true);
Ok(())
}
pub fn clear_framebuffers(&self) {
if let Some(Ok(Some(fb))) = self.next_buffer.take().map(|mut bo| bo.take_userdata()) {
if let Err(err) = self.crtc.destroy_framebuffer(fb) {
warn!(
@ -123,13 +147,6 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
);
}
}
// Drop the old surface after cleanup
*self.surface.borrow_mut() = surface;
self.recreated.set(true);
Ok(())
}
}
@ -228,27 +245,7 @@ impl<D: RawDevice + 'static> Drop for GbmSurfaceInternal<D> {
fn drop(&mut self) {
// Drop framebuffers attached to the userdata of the gbm surface buffers.
// (They don't implement drop, as they need the device)
if let Ok(Some(fb)) = {
if let Some(mut next) = self.next_buffer.take() {
next.take_userdata()
} else {
Ok(None)
}
} {
// ignore failure at this point
let _ = self.crtc.destroy_framebuffer(fb);
}
if let Ok(Some(fb)) = {
if let Some(mut next) = self.front_buffer.take() {
next.take_userdata()
} else {
Ok(None)
}
} {
// ignore failure at this point
let _ = self.crtc.destroy_framebuffer(fb);
}
self.clear_framebuffers();
}
}

View File

@ -306,25 +306,21 @@ impl<A: AsRawFd + 'static> Device for LegacyDrmDevice<A> {
Ok(events) => {
for event in events {
if let Event::PageFlip(event) = event {
if self.active.load(Ordering::SeqCst) {
if self
.backends
.borrow()
.get(&event.crtc)
.iter()
.flat_map(|x| x.upgrade())
.next()
.is_some()
{
trace!(self.logger, "Handling event for backend {:?}", event.crtc);
if let Some(handler) = self.handler.as_ref() {
handler.borrow_mut().vblank(event.crtc);
}
} else {
self.backends.borrow_mut().remove(&event.crtc);
if self
.backends
.borrow()
.get(&event.crtc)
.iter()
.flat_map(|x| x.upgrade())
.next()
.is_some()
{
trace!(self.logger, "Handling event for backend {:?}", event.crtc);
if let Some(handler) = self.handler.as_ref() {
handler.borrow_mut().vblank(event.crtc);
}
} else {
debug!(self.logger, "Device not active. Ignoring PageFlip");
self.backends.borrow_mut().remove(&event.crtc);
}
} else {
trace!(self.logger, "Unrelated event");

View File

@ -311,6 +311,11 @@ impl<A: AsRawFd + 'static> LegacyDrmSurfaceInternal<A> {
connectors: &[connector::Handle],
logger: ::slog::Logger,
) -> Result<LegacyDrmSurfaceInternal<A>, Error> {
info!(
logger,
"Initializing drm surface with mode {:?} and connectors {:?}", mode, connectors
);
// Try to enumarate the current state to set the initial state variable correctly
let crtc_info = dev.get_crtc(crtc).compat().map_err(|source| Error::Access {
errmsg: "Error loading crtc info",

View File

@ -150,7 +150,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
};
info!(log, "EGL Extensions: {:?}", extensions);
if egl_version >= (1, 2) {
if egl_version <= (1, 2) {
return Err(Error::OpenGlesNotSupported(None));
}
wrap_egl_call(|| unsafe { ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) })
@ -485,7 +485,7 @@ impl EGLBufferReader {
buffer: WlBuffer,
) -> ::std::result::Result<EGLImages, BufferAccessError> {
let mut format: i32 = 0;
wrap_egl_call(|| unsafe {
let query = wrap_egl_call(|| unsafe {
ffi::egl::QueryWaylandBufferWL(
**self.display,
buffer.as_ref().c_ptr() as _,
@ -494,6 +494,9 @@ impl EGLBufferReader {
)
})
.map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?;
if query == ffi::egl::FALSE {
return Err(BufferAccessError::NotManaged(buffer, EGLError::BadParameter));
}
let format = match format {
x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB,
@ -502,7 +505,7 @@ impl EGLBufferReader {
ffi::egl::TEXTURE_Y_UV_WL => Format::Y_UV,
ffi::egl::TEXTURE_Y_U_V_WL => Format::Y_U_V,
ffi::egl::TEXTURE_Y_XUXV_WL => Format::Y_XUXV,
_ => panic!("EGL returned invalid texture type"),
x => panic!("EGL returned invalid texture type: {}", x),
};
let mut width: i32 = 0;

View File

@ -12,12 +12,13 @@ use std::{
/// EGL surface of a given EGL context for rendering
pub struct EGLSurface<N: native::NativeSurface> {
display: Arc<EGLDisplayHandle>,
pub(crate) display: Arc<EGLDisplayHandle>,
native: N,
pub(crate) surface: Cell<ffi::egl::types::EGLSurface>,
config_id: ffi::egl::types::EGLConfig,
pixel_format: PixelFormat,
surface_attributes: Vec<c_int>,
logger: ::slog::Logger,
}
impl<N: native::NativeSurface> Deref for EGLSurface<N> {
@ -72,6 +73,10 @@ impl<N: native::NativeSurface> EGLSurface<N> {
ffi::egl::CreateWindowSurface(**display, config, native.ptr(), surface_attributes.as_ptr())
})?;
if surface == ffi::egl::NO_SURFACE {
return Err(EGLError::BadSurface);
}
Ok(EGLSurface {
display,
native,
@ -79,6 +84,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
config_id: config,
pixel_format,
surface_attributes,
logger: log,
})
}
@ -91,7 +97,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
.map_err(SwapBuffersError::EGLSwapBuffers)
.and_then(|_| self.native.swap_buffers().map_err(SwapBuffersError::Underlying))
} else {
Ok(())
Err(SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface))
};
// workaround for missing `PartialEq` impl
@ -117,9 +123,14 @@ impl<N: native::NativeSurface> EGLSurface<N> {
})
.map_err(SwapBuffersError::EGLCreateWindowSurface)?
});
}
result
result.map_err(|err| {
debug!(self.logger, "Hiding page-flip error *before* recreation: {}", err);
SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface)
})
} else {
result
}
}
/// Returns true if the OpenGL surface is the current one in the thread.

View File

@ -278,21 +278,36 @@ impl LogindSessionImpl {
let (major, minor, pause_type) = message.get3::<u32, u32, String>();
let major = major.ok_or(Error::UnexpectedMethodReturn)?;
let minor = minor.ok_or(Error::UnexpectedMethodReturn)?;
// From https://www.freedesktop.org/wiki/Software/systemd/logind/:
// `force` means the device got paused by logind already and this is only an
// asynchronous notification.
// `pause` means logind tries to pause the device and grants you limited amount
// of time to pause it. You must respond to this via PauseDeviceComplete().
// This synchronous pausing-mechanism is used for backwards-compatibility to VTs
// and logind is **free to not make use of it**.
// It is also free to send a forced PauseDevice if you don't respond in a timely manner
// (or for any other reason).
let pause_type = pause_type.ok_or(Error::UnexpectedMethodReturn)?;
debug!(
self.logger,
"Request of type \"{}\" to close device ({},{})", pause_type, major, minor
);
for signal in &mut *self.signals.borrow_mut() {
if let Some(ref mut signal) = signal {
signal.pause(Some((major, minor)));
// gone means the device was unplugged from the system and you will no longer get any
// notifications about it.
// This is handled via udev and is not part of our session api.
if pause_type != "gone" {
for signal in &mut *self.signals.borrow_mut() {
if let Some(ref mut signal) = signal {
signal.pause(Some((major, minor)));
}
}
}
// the other possible types are "force" or "gone" (unplugged),
// both expect no acknowledgement (note even this is not *really* necessary,
// logind would just timeout and send a "force" event. There is no way to
// keep the device.)
if &*pause_type == "pause" {
if pause_type == "pause" {
LogindSessionImpl::blocking_call(
&*self.conn.borrow(),
"org.freedesktop.login1",