eglstream: initial backend implementation

This commit is contained in:
Victor Brekenfeld 2019-05-31 12:50:10 +02:00
parent eb1dc5de4f
commit 69c1116d82
11 changed files with 1482 additions and 2 deletions

View File

@ -45,12 +45,13 @@ slog-term = "2.3"
gl_generator = { version = "0.14", optional = true } gl_generator = { version = "0.14", optional = true }
[features] [features]
default = ["backend_winit", "backend_drm_legacy", "backend_drm_atomic", "backend_drm_gbm", "backend_drm_egl", "backend_libinput", "backend_udev", "backend_session_logind", "renderer_glium", "xwayland", "wayland_frontend"] default = ["backend_winit", "backend_drm_legacy", "backend_drm_atomic", "backend_drm_gbm", "backend_drm_eglstream", "backend_drm_egl", "backend_libinput", "backend_udev", "backend_session_logind", "renderer_glium", "xwayland", "wayland_frontend"]
backend_winit = ["winit", "wayland-server/dlopen", "backend_egl", "wayland-egl", "renderer_gl", "use_system_lib"] backend_winit = ["winit", "wayland-server/dlopen", "backend_egl", "wayland-egl", "renderer_gl", "use_system_lib"]
backend_drm = ["drm", "failure"] backend_drm = ["drm", "failure"]
backend_drm_atomic = ["backend_drm"] backend_drm_atomic = ["backend_drm"]
backend_drm_legacy = ["backend_drm"] backend_drm_legacy = ["backend_drm"]
backend_drm_gbm = ["backend_drm", "gbm", "image"] backend_drm_gbm = ["backend_drm", "gbm", "image"]
backend_drm_eglstream = ["backend_drm", "backend_egl"]
backend_drm_egl = ["backend_drm", "backend_egl"] backend_drm_egl = ["backend_drm", "backend_egl"]
backend_egl = ["gl_generator"] backend_egl = ["gl_generator"]
backend_libinput = ["input"] backend_libinput = ["input"]

View File

@ -32,6 +32,17 @@ fn main() {
"EGL_EXT_platform_wayland", "EGL_EXT_platform_wayland",
"EGL_EXT_platform_device", "EGL_EXT_platform_device",
"EGL_KHR_image_base", "EGL_KHR_image_base",
"EGL_EXT_output_base",
"EGL_EXT_output_drm",
"EGL_EXT_device_drm",
"EGL_EXT_device_enumeration",
"EGL_EXT_device_query",
"EGL_KHR_stream",
"EGL_KHR_stream_producer_eglsurface",
"EGL_EXT_stream_consumer_egloutput",
"EGL_KHR_stream_fifo",
"EGL_NV_output_drm_flip_event",
"EGL_NV_stream_attrib",
], ],
) )
.write_bindings(gl_generator::GlobalGenerator, &mut file) .write_bindings(gl_generator::GlobalGenerator, &mut file)

175
examples/raw_nvidia.rs Normal file
View File

@ -0,0 +1,175 @@
#![warn(rust_2018_idioms)]
#[macro_use]
extern crate slog;
use glium::Surface as GliumSurface;
use slog::Drain;
use smithay::{
backend::{
drm::{
atomic::AtomicDrmDevice,
common::fallback::{EitherError, FallbackDevice},
common::Error as DrmError,
device_bind,
egl::{EglDevice, EglSurface, Error as EglError},
eglstream::{
egl::EglStreamDeviceBackend, EglStreamDevice, EglStreamSurface, Error as EglStreamError,
},
legacy::LegacyDrmDevice,
Device, DeviceHandler,
},
graphics::glium::GliumGraphicsBackend,
},
reexports::{
calloop::EventLoop,
drm::control::{connector::State as ConnectorState, crtc},
},
};
use std::{
fs::{File, OpenOptions},
io::Error as IoError,
os::unix::io::{AsRawFd, RawFd},
rc::Rc,
sync::Mutex,
};
pub struct ClonableFile {
file: File,
}
impl AsRawFd for ClonableFile {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
impl Clone for ClonableFile {
fn clone(&self) -> Self {
ClonableFile {
file: self.file.try_clone().expect("Unable to clone file"),
}
}
}
fn main() {
let log = slog::Logger::root(Mutex::new(slog_term::term_full().fuse()).fuse(), o!());
/*
* Initialize the drm backend
*/
// "Find" a suitable drm device
let mut options = OpenOptions::new();
options.read(true);
options.write(true);
let file = ClonableFile {
file: options.open("/dev/dri/card1").expect("Failed to open card1"),
};
let mut device = EglDevice::new(
EglStreamDevice::new(
FallbackDevice::<AtomicDrmDevice<_>, LegacyDrmDevice<_>>::new(file, true, log.clone())
.expect("Failed to initialize drm device"),
log.clone(),
)
.expect("Failed to initialize egl stream device"),
log.clone(),
)
.expect("Failed to initialize egl device");
// Get a set of all modesetting resource handles (excluding planes):
let res_handles = Device::resource_handles(&device).unwrap();
// Use first connected connector
let connector_info = res_handles
.connectors()
.iter()
.map(|conn| Device::get_connector_info(&device, *conn).unwrap())
.find(|conn| conn.state() == ConnectorState::Connected)
.unwrap();
println!("Conn: {:?}", connector_info.interface());
// Use the first encoder
let encoder_info =
Device::get_encoder_info(&device, connector_info.encoders()[0].expect("expected encoder")).unwrap();
// use the connected crtc if any
let crtc = encoder_info
.crtc()
// or use the first one that is compatible with the encoder
.unwrap_or_else(|| {
*res_handles
.filter_crtcs(encoder_info.possible_crtcs())
.iter()
.next()
.unwrap()
});
println!("Crtc {:?}", crtc);
// Assuming we found a good connector and loaded the info into `connector_info`
let mode = connector_info.modes()[0]; // Use first mode (usually highest resolution, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
println!("Mode: {:?}", mode);
// Initialize the hardware backend
let surface = device
.create_surface(crtc, mode, &[connector_info.handle()])
.expect("Failed to create surface");
let backend: Rc<GliumGraphicsBackend<_>> = Rc::new(surface.into());
device.set_handler(DrmHandlerImpl {
surface: backend.clone(),
});
/*
* Register the DrmDevice on the EventLoop
*/
let mut event_loop = EventLoop::<()>::new().unwrap();
let _source = device_bind(&event_loop.handle(), device)
.map_err(|err| -> IoError { err.into() })
.unwrap();
// Start rendering
{
if let Err(smithay::backend::graphics::SwapBuffersError::ContextLost(err)) = backend.draw().finish() {
println!("{}", err);
return;
};
}
// Run
event_loop.run(None, &mut (), |_| {}).unwrap();
}
pub struct DrmHandlerImpl {
surface: Rc<
GliumGraphicsBackend<
EglSurface<
EglStreamSurface<
FallbackDevice<AtomicDrmDevice<ClonableFile>, LegacyDrmDevice<ClonableFile>>,
>,
>,
>,
>,
}
impl DeviceHandler for DrmHandlerImpl {
type Device = EglDevice<
EglStreamDeviceBackend<FallbackDevice<AtomicDrmDevice<ClonableFile>, LegacyDrmDevice<ClonableFile>>>,
EglStreamDevice<FallbackDevice<AtomicDrmDevice<ClonableFile>, LegacyDrmDevice<ClonableFile>>>,
>;
fn vblank(&mut self, _crtc: crtc::Handle) {
{
println!("Vblank");
let mut frame = self.surface.draw();
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
if let Err(smithay::backend::graphics::SwapBuffersError::ContextLost(err)) = frame.finish() {
panic!("Failed to swap: {}", err);
}
}
}
fn error(&mut self, error: EglError<EglStreamError<EitherError<DrmError, DrmError>>>) {
panic!("{:?}", error);
}
}

View File

@ -40,7 +40,7 @@ pub struct Planes {
pub(in crate::backend::drm) struct AtomicDrmSurfaceInternal<A: AsRawFd + 'static> { pub(in crate::backend::drm) struct AtomicDrmSurfaceInternal<A: AsRawFd + 'static> {
pub(super) dev: Rc<Dev<A>>, pub(super) dev: Rc<Dev<A>>,
pub(super) crtc: crtc::Handle, pub(in crate::backend::drm) crtc: crtc::Handle,
pub(super) cursor: CursorState, pub(super) cursor: CursorState,
pub(in crate::backend::drm) planes: Planes, pub(in crate::backend::drm) planes: Planes,
pub(super) state: RwLock<State>, pub(super) state: RwLock<State>,

View File

@ -0,0 +1,238 @@
//!
//! Egl [`NativeDisplay`](::backend::egl::native::NativeDisplay) and
//! [`NativeSurface`](::backend::egl::native::NativeSurface) support for
//! [`EglStreamDevice`](EglStreamDevice) and [`EglStreamSurface`](EglStreamSurface).
//!
#[cfg(feature = "backend_drm_atomic")]
use crate::backend::drm::atomic::AtomicDrmDevice;
#[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))]
use crate::backend::drm::common::fallback::{EitherError, FallbackDevice, FallbackSurface};
#[cfg(any(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))]
use crate::backend::drm::common::Error as DrmError;
#[cfg(feature = "backend_drm_legacy")]
use crate::backend::drm::legacy::LegacyDrmDevice;
use crate::backend::drm::{Device, RawDevice, RawSurface, Surface};
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use crate::backend::egl::{
display::EGLDisplayHandle, ffi, wrap_egl_call, EGLError, Error as EglBackendError, SurfaceCreationError,
SwapBuffersError,
};
use super::Error;
use super::{EglStreamDevice, EglStreamSurface};
use drm::control::{connector, crtc, Device as ControlDevice, Mode};
use nix::libc::{c_int, c_void};
use std::marker::PhantomData;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;
/// Egl Device backend type
///
/// See [`Backend`](::backend::egl::native::Backend).
pub struct EglStreamDeviceBackend<D: RawDevice + 'static> {
_userdata: PhantomData<D>,
}
impl<D: RawDevice + 'static> Backend for EglStreamDeviceBackend<D>
where
EglStreamSurface<D>: NativeSurface<Error = Error<<<D as Device>::Surface as Surface>::Error>>,
{
type Surface = EglStreamSurface<D>;
type Error = Error<<<D as Device>::Surface as Surface>::Error>;
unsafe fn get_display<F>(
display: ffi::NativeDisplayType,
attribs: &[ffi::EGLint],
has_dp_extension: F,
log: ::slog::Logger,
) -> Result<ffi::egl::types::EGLDisplay, EGLError>
where
F: Fn(&str) -> bool,
{
if has_dp_extension("EGL_EXT_platform_device") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
debug!(
log,
"EGL Display Initialization via EGL_EXT_platform_device with {:?}", display
);
wrap_egl_call(|| {
ffi::egl::GetPlatformDisplayEXT(
ffi::egl::PLATFORM_DEVICE_EXT,
display as *mut _,
attribs.as_ptr() as *const _,
)
})
} else {
Ok(ffi::egl::NO_DISPLAY)
}
}
}
unsafe impl<D: RawDevice + ControlDevice + 'static> NativeDisplay<EglStreamDeviceBackend<D>>
for EglStreamDevice<D>
where
EglStreamSurface<D>: NativeSurface<Error = Error<<<D as Device>::Surface as Surface>::Error>>,
{
type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>);
fn is_backend(&self) -> bool {
true
}
fn ptr(&self) -> Result<ffi::NativeDisplayType, EglBackendError> {
Ok(self.dev as *const _)
}
fn attributes(&self) -> Vec<ffi::EGLint> {
vec![
ffi::egl::DRM_MASTER_FD_EXT as ffi::EGLint,
self.raw.as_raw_fd(),
ffi::egl::NONE as i32,
]
}
fn surface_type(&self) -> ffi::EGLint {
ffi::egl::STREAM_BIT_KHR as ffi::EGLint
}
fn create_surface(
&mut self,
args: Self::Arguments,
) -> Result<EglStreamSurface<D>, Error<<<D as Device>::Surface as Surface>::Error>> {
Device::create_surface(self, args.0, args.1, &args.2)
}
}
#[cfg(feature = "backend_drm_atomic")]
unsafe impl<A: AsRawFd + 'static> NativeSurface for EglStreamSurface<AtomicDrmDevice<A>> {
type Error = Error<DrmError>;
unsafe fn create(
&self,
display: &Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig,
surface_attribs: &[c_int],
) -> Result<*const c_void, SurfaceCreationError<Self::Error>> {
let output_attributes = {
let mut out: Vec<isize> = Vec::with_capacity(3);
out.push(ffi::egl::DRM_PLANE_EXT as isize);
out.push(Into::<u32>::into(self.0.crtc.0.planes.primary) as isize);
out.push(ffi::egl::NONE as isize);
out
};
self.create_surface(display, config_id, surface_attribs, &output_attributes)
.map_err(SurfaceCreationError::NativeSurfaceCreationFailed)
}
fn needs_recreation(&self) -> bool {
self.0.crtc.commit_pending()
}
fn swap_buffers(
&self,
display: &Arc<EGLDisplayHandle>,
surface: ffi::egl::types::EGLSurface,
) -> Result<(), SwapBuffersError<Error<DrmError>>> {
if let Some((buffer, fb)) = self.0.commit_buffer.take() {
let _ = self.0.crtc.destroy_framebuffer(fb);
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
}
self.flip(self.0.crtc.0.crtc, display, surface)
}
}
#[cfg(feature = "backend_drm_legacy")]
unsafe impl<A: AsRawFd + 'static> NativeSurface for EglStreamSurface<LegacyDrmDevice<A>> {
type Error = Error<DrmError>;
unsafe fn create(
&self,
display: &Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig,
surface_attribs: &[c_int],
) -> Result<*const c_void, SurfaceCreationError<Self::Error>> {
let output_attributes = {
let mut out: Vec<isize> = Vec::with_capacity(3);
out.push(ffi::egl::DRM_CRTC_EXT as isize);
out.push(Into::<u32>::into(self.0.crtc.0.crtc) as isize);
out.push(ffi::egl::NONE as isize);
out
};
self.create_surface(display, config_id, surface_attribs, &output_attributes)
.map_err(SurfaceCreationError::NativeSurfaceCreationFailed)
}
fn needs_recreation(&self) -> bool {
self.0.crtc.commit_pending()
}
fn swap_buffers(
&self,
display: &Arc<EGLDisplayHandle>,
surface: ffi::egl::types::EGLSurface,
) -> Result<(), SwapBuffersError<Error<DrmError>>> {
if let Some((buffer, fb)) = self.0.commit_buffer.take() {
let _ = self.0.crtc.destroy_framebuffer(fb);
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
}
self.flip(self.0.crtc.0.crtc, display, surface)
}
}
#[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))]
unsafe impl<A: AsRawFd + 'static> NativeSurface
for EglStreamSurface<FallbackDevice<AtomicDrmDevice<A>, LegacyDrmDevice<A>>>
{
type Error = Error<EitherError<DrmError, DrmError>>;
unsafe fn create(
&self,
display: &Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig,
surface_attribs: &[c_int],
) -> Result<*const c_void, SurfaceCreationError<Self::Error>> {
let output_attributes = {
let mut out: Vec<isize> = Vec::with_capacity(3);
match &self.0.crtc {
FallbackSurface::Preference(dev) => {
out.push(ffi::egl::DRM_PLANE_EXT as isize);
out.push(Into::<u32>::into(dev.0.planes.primary) as isize);
} //AtomicDrmSurface
FallbackSurface::Fallback(dev) => {
out.push(ffi::egl::DRM_CRTC_EXT as isize);
out.push(Into::<u32>::into(dev.0.crtc) as isize);
} // LegacyDrmSurface
}
out.push(ffi::egl::NONE as isize);
out
};
self.create_surface(display, config_id, surface_attribs, &output_attributes)
.map_err(SurfaceCreationError::NativeSurfaceCreationFailed)
}
fn needs_recreation(&self) -> bool {
self.0.crtc.commit_pending()
}
fn swap_buffers(
&self,
display: &Arc<EGLDisplayHandle>,
surface: ffi::egl::types::EGLSurface,
) -> Result<(), SwapBuffersError<Self::Error>> {
if let Some((buffer, fb)) = self.0.commit_buffer.take() {
let _ = self.0.crtc.destroy_framebuffer(fb);
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
}
let crtc = match &self.0.crtc {
FallbackSurface::Preference(dev) => dev.0.crtc,
FallbackSurface::Fallback(dev) => dev.0.crtc,
};
self.flip(crtc, display, surface)
}
}

View File

@ -0,0 +1,364 @@
//!
//! [`Device`](Device) and [`Surface`](Surface) implementations using
//! the proposed EGLStream api for efficient rendering.
//! Currently this api is only implemented by the proprietary `nvidia` driver.
//!
//! Usually this implementation will be wrapped into a [`EglDevice`](::backend::drm::egl::EglDevice).
//!
//! To use these types standalone, you will need to render via egl yourself as page flips
//! are driven via `eglSwapBuffers`.
//!
//! To use this api in place of GBM for nvidia cards take a look at
//! [`FallbackDevice`](::backend::drm::common::fallback::FallbackDevice).
//! Take a look at `anvil`s source code for an example of this.
//!
use super::{Device, DeviceHandler, RawDevice, Surface};
use drm::buffer::format::PixelFormat;
use drm::control::{
connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Mode, ResourceHandles,
};
use drm::SystemError as DrmError;
use failure::ResultExt;
use nix::libc::dev_t;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::ffi::CStr;
use std::os::unix::fs::MetadataExt;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak};
use std::{fs, ptr};
mod surface;
pub use self::surface::EglStreamSurface;
use self::surface::EglStreamSurfaceInternal;
pub mod egl;
#[cfg(feature = "backend_session")]
pub mod session;
use crate::backend::egl::ffi::{self, egl::types::EGLDeviceEXT};
use crate::backend::egl::{wrap_egl_call, EGLError as RawEGLError, Error as EglError};
use crate::backend::graphics::SwapBuffersError;
/// Errors thrown by the [`EglStreamDevice`](::backend::drm::eglstream::EglStreamDevice)
/// and [`EglStreamSurface`](::backend::drm::eglstream::EglStreamSurface).
#[derive(thiserror::Error, Debug)]
pub enum Error<U: std::error::Error + std::fmt::Debug + std::fmt::Display + 'static> {
/// `eglInitialize` returned an error
#[error("Failed to initialize EGL: {0:}")]
InitFailed(#[source] RawEGLError),
/// Failed to enumerate EGL_EXT_drm_device
#[error("Failed to enumerate EGL_EXT_drm_device: {0:}")]
FailedToEnumerateDevices(#[source] RawEGLError),
/// Device is not compatible with EGL_EXT_drm_device extension
#[error("Device is not compatible with EGL_EXT_drm_device extension")]
DeviceIsNoEGLStreamDevice,
/// Device has not suitable output layer
#[error("Device has no suitable output layer")]
DeviceNoOutputLayer,
/// Device was unable to create an EGLStream
#[error("EGLStream creation failed")]
DeviceStreamCreationFailed,
/// Underlying backend error
#[error("Underlying error: {0}")]
Underlying(#[source] U),
/// Buffer creation failed
#[error("Buffer creation failed")]
BufferCreationFailed(#[source] failure::Compat<DrmError>),
/// Buffer write failed
#[error("Buffer write failed")]
BufferWriteFailed(#[source] failure::Compat<DrmError>),
/// Stream flip failed
#[error("Stream flip failed ({0})")]
StreamFlipFailed(#[source] RawEGLError),
}
/// Representation of an open egl stream device to create egl rendering surfaces
pub struct EglStreamDevice<D: RawDevice + ControlDevice + 'static> {
pub(self) dev: EGLDeviceEXT,
raw: D,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>,
logger: ::slog::Logger,
}
impl<D: RawDevice + ControlDevice + 'static> EglStreamDevice<D> {
/// Try to create a new [`EglStreamDevice`] from an open device.
///
/// Returns an error if the underlying device would not support the required EGL extensions.
pub fn new<L>(mut raw: D, logger: L) -> Result<Self, Error<<<D as Device>::Surface as Surface>::Error>>
where
L: Into<Option<::slog::Logger>>,
{
let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_eglstream"));
raw.clear_handler();
debug!(log, "Creating egl stream device");
ffi::make_sure_egl_is_loaded();
fn has_extensions(exts: &[String], check: &'static [&'static str]) -> Result<(), EglError> {
check
.iter()
.map(|ext| {
if exts.iter().any(|s| *s == *ext) {
Ok(())
} else {
Err(EglError::EglExtensionNotSupported(check))
}
})
.collect()
}
let device = unsafe {
// the first step is to query the list of extensions without any display, if supported
let dp_extensions = {
let p = wrap_egl_call(|| {
ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32)
})
.map_err(Error::InitFailed)?;
// this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise
// `eglQueryString` returns an error
if p.is_null() {
vec![]
} else {
let p = CStr::from_ptr(p);
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
}
};
debug!(log, "EGL No-Display Extensions: {:?}", dp_extensions);
// we need either EGL_EXT_device_base or EGL_EXT_device_enumeration &_query
if let Err(_err) = has_extensions(&dp_extensions, &["EGL_EXT_device_base"]) {
has_extensions(
&dp_extensions,
&["EGL_EXT_device_enumeration", "EGL_EXT_device_query"],
)
.map_err(|_| Error::DeviceIsNoEGLStreamDevice)?;
}
let mut num_devices = 0;
wrap_egl_call(|| ffi::egl::QueryDevicesEXT(0, ptr::null_mut(), &mut num_devices))
.map_err(Error::FailedToEnumerateDevices)?;
if num_devices == 0 {
return Err(Error::DeviceIsNoEGLStreamDevice);
}
let mut devices = Vec::with_capacity(num_devices as usize);
wrap_egl_call(|| ffi::egl::QueryDevicesEXT(num_devices, devices.as_mut_ptr(), &mut num_devices))
.map_err(Error::FailedToEnumerateDevices)?;
devices.set_len(num_devices as usize);
debug!(log, "Devices: {:#?}, Count: {}", devices, num_devices);
devices
.into_iter()
.find(|device| {
*device != ffi::egl::NO_DEVICE_EXT
&& {
let device_extensions = {
let p = ffi::egl::QueryDeviceStringEXT(*device, ffi::egl::EXTENSIONS as i32);
if p.is_null() {
vec![]
} else {
let p = CStr::from_ptr(p);
let list = String::from_utf8(p.to_bytes().to_vec())
.unwrap_or_else(|_| String::new());
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
}
};
device_extensions.iter().any(|s| *s == "EGL_EXT_device_drm")
}
&& {
let path = {
let p = ffi::egl::QueryDeviceStringEXT(
*device,
ffi::egl::DRM_DEVICE_FILE_EXT as i32,
);
if p.is_null() {
String::new()
} else {
let p = CStr::from_ptr(p);
String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new())
}
};
match fs::metadata(&path) {
Ok(metadata) => metadata.rdev() == raw.device_id(),
Err(_) => false,
}
}
})
.ok_or(Error::DeviceIsNoEGLStreamDevice)?
};
Ok(EglStreamDevice {
dev: device,
raw,
backends: Rc::new(RefCell::new(HashMap::new())),
logger: log,
})
}
}
struct InternalDeviceHandler<D: RawDevice + ControlDevice + 'static> {
handler: Box<dyn DeviceHandler<Device = EglStreamDevice<D>> + 'static>,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>,
logger: ::slog::Logger,
}
impl<D: RawDevice + ControlDevice + 'static> DeviceHandler for InternalDeviceHandler<D> {
type Device = D;
fn vblank(&mut self, crtc: crtc::Handle) {
if let Some(backends) = self.backends.upgrade() {
if let Some(surface) = backends.borrow().get(&crtc) {
if surface.upgrade().is_some() {
self.handler.vblank(crtc);
}
} else {
warn!(
self.logger,
"Surface ({:?}) not managed by egl stream, event not handled.", crtc
);
}
}
}
fn error(&mut self, error: <<D as Device>::Surface as Surface>::Error) {
self.handler.error(Error::Underlying(error))
}
}
impl<D: RawDevice + ControlDevice + 'static> Device for EglStreamDevice<D> {
type Surface = EglStreamSurface<D>;
fn device_id(&self) -> dev_t {
self.raw.device_id()
}
fn set_handler(&mut self, handler: impl DeviceHandler<Device = Self> + 'static) {
self.raw.set_handler(InternalDeviceHandler {
handler: Box::new(handler),
backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(),
});
}
fn clear_handler(&mut self) {
self.raw.clear_handler();
}
fn create_surface(
&mut self,
crtc: crtc::Handle,
mode: Mode,
connectors: &[connector::Handle],
) -> Result<EglStreamSurface<D>, Error<<<D as Device>::Surface as Surface>::Error>> {
info!(self.logger, "Initializing EglStreamSurface");
let drm_surface =
Device::create_surface(&mut self.raw, crtc, mode, connectors).map_err(Error::Underlying)?;
// initialize a buffer for the cursor image
let cursor = Cell::new(Some((
self.raw
.create_dumb_buffer((1, 1), PixelFormat::ARGB8888)
.compat()
.map_err(Error::BufferCreationFailed)?,
(0, 0),
)));
let backend = Rc::new(EglStreamSurfaceInternal {
crtc: drm_surface,
cursor,
stream: RefCell::new(None),
commit_buffer: Cell::new(None),
logger: self.logger.new(o!("crtc" => format!("{:?}", crtc))),
locked: std::sync::atomic::AtomicBool::new(false),
});
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend));
Ok(EglStreamSurface(backend))
}
fn process_events(&mut self) {
self.raw.process_events()
}
fn resource_handles(&self) -> Result<ResourceHandles, Error<<<D as Device>::Surface as Surface>::Error>> {
Device::resource_handles(&self.raw).map_err(Error::Underlying)
}
fn get_connector_info(&self, conn: connector::Handle) -> Result<connector::Info, DrmError> {
self.raw.get_connector_info(conn)
}
fn get_crtc_info(&self, crtc: crtc::Handle) -> Result<crtc::Info, DrmError> {
self.raw.get_crtc_info(crtc)
}
fn get_encoder_info(&self, enc: encoder::Handle) -> Result<encoder::Info, DrmError> {
self.raw.get_encoder_info(enc)
}
fn get_framebuffer_info(&self, fb: framebuffer::Handle) -> Result<framebuffer::Info, DrmError> {
self.raw.get_framebuffer_info(fb)
}
fn get_plane_info(&self, plane: plane::Handle) -> Result<plane::Info, DrmError> {
self.raw.get_plane_info(plane)
}
}
impl<D: RawDevice + ControlDevice + 'static> AsRawFd for EglStreamDevice<D> {
fn as_raw_fd(&self) -> RawFd {
self.raw.as_raw_fd()
}
}
impl<D: RawDevice + ControlDevice + 'static> Drop for EglStreamDevice<D> {
fn drop(&mut self) {
self.clear_handler();
}
}
impl<E> Into<SwapBuffersError> for Error<E>
where
E: std::error::Error + Into<SwapBuffersError> + 'static,
{
fn into(self) -> SwapBuffersError {
match self {
Error::BufferCreationFailed(x)
if match x.get_ref() {
drm::SystemError::Unknown {
errno: nix::errno::Errno::EBUSY,
} => true,
drm::SystemError::Unknown {
errno: nix::errno::Errno::EINTR,
} => true,
_ => false,
} =>
{
SwapBuffersError::TemporaryFailure(Box::new(Error::<E>::BufferCreationFailed(x)))
}
Error::BufferWriteFailed(x)
if match x.get_ref() {
drm::SystemError::Unknown {
errno: nix::errno::Errno::EBUSY,
} => true,
drm::SystemError::Unknown {
errno: nix::errno::Errno::EINTR,
} => true,
_ => false,
} =>
{
SwapBuffersError::TemporaryFailure(Box::new(Error::<E>::BufferCreationFailed(x)))
}
Error::StreamFlipFailed(x @ RawEGLError::ResourceBusy) => {
SwapBuffersError::TemporaryFailure(Box::new(Error::<E>::StreamFlipFailed(x)))
}
Error::Underlying(x) => x.into(),
x => SwapBuffersError::ContextLost(Box::new(x)),
}
}
}

View File

@ -0,0 +1,91 @@
//!
//! Support to register a [`EglStreamDevice`](EglStreamDevice)
//! to an open [`Session`](::backend::session::Session).
//!
use super::{EglStreamDevice, EglStreamSurfaceInternal};
use crate::backend::drm::{RawDevice, Surface};
use crate::backend::egl::ffi;
use crate::backend::session::{AsSessionObserver, SessionObserver};
use std::cell::RefCell;
use std::collections::HashMap;
use std::os::unix::io::RawFd;
use std::rc::{Rc, Weak};
use drm::control::{crtc, Device as ControlDevice};
/// [`SessionObserver`](SessionObserver)
/// linked to the [`EglStreamDevice`](EglStreamDevice) it was
/// created from.
pub struct EglStreamDeviceObserver<
O: SessionObserver + 'static,
D: RawDevice + AsSessionObserver<O> + 'static,
> {
observer: O,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>,
logger: ::slog::Logger,
}
impl<O: SessionObserver + 'static, D: RawDevice + ControlDevice + AsSessionObserver<O> + 'static>
AsSessionObserver<EglStreamDeviceObserver<O, D>> for EglStreamDevice<D>
{
fn observer(&mut self) -> EglStreamDeviceObserver<O, D> {
EglStreamDeviceObserver {
observer: self.raw.observer(),
backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(),
}
}
}
impl<O: SessionObserver + 'static, D: RawDevice + AsSessionObserver<O> + 'static> SessionObserver
for EglStreamDeviceObserver<O, D>
{
fn pause(&mut self, devnum: Option<(u32, u32)>) {
if let Some(backends) = self.backends.upgrade() {
for (_, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() {
// destroy/disable the streams so it will not submit any pending frames
if let Some((display, stream)) = backend.stream.replace(None) {
unsafe {
ffi::egl::DestroyStreamKHR(display.handle, stream);
}
}
// framebuffers will be likely not valid anymore, lets just recreate those after activation.
if let Some((buffer, fb)) = backend.commit_buffer.take() {
let _ = backend.crtc.destroy_framebuffer(fb);
let _ = backend.crtc.destroy_dumb_buffer(buffer);
}
}
}
}
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 (_, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() {
if let Some((cursor, hotspot)) = backend.cursor.get() {
if backend
.crtc
.set_cursor2(
backend.crtc.crtc(),
Some(&cursor),
(hotspot.0 as i32, hotspot.1 as i32),
)
.is_err()
{
if let Err(err) = backend.crtc.set_cursor(backend.crtc.crtc(), Some(&cursor)) {
warn!(self.logger, "Failed to reset cursor: {}", err)
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,528 @@
use super::super::{Device, RawDevice, RawSurface, Surface};
use super::Error;
use drm::buffer::format::PixelFormat;
use drm::control::{connector, crtc, dumbbuffer::DumbBuffer, framebuffer, Device as ControlDevice, Mode};
#[cfg(feature = "backend_drm")]
use failure::ResultExt;
#[cfg(feature = "backend_drm")]
use image::{ImageBuffer, Rgba};
use nix::libc::{c_int, c_void};
use std::cell::{Cell, RefCell};
use std::ffi::CStr;
use std::ptr;
use std::rc::Rc;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
#[cfg(feature = "backend_drm")]
use crate::backend::drm::common::Error as DrmError;
#[cfg(feature = "backend_drm")]
use crate::backend::drm::DevPath;
use crate::backend::egl::ffi::{
self,
egl::{self, types::EGLStreamKHR},
};
use crate::backend::egl::{display::EGLDisplayHandle, wrap_egl_call, EGLError, SwapBuffersError};
#[cfg(feature = "backend_drm")]
use crate::backend::graphics::CursorBackend;
pub(in crate::backend::drm) struct EglStreamSurfaceInternal<D: RawDevice + 'static> {
pub(in crate::backend::drm) crtc: <D as Device>::Surface,
pub(in crate::backend::drm) cursor: Cell<Option<(DumbBuffer, (u32, u32))>>,
pub(in crate::backend::drm) stream: RefCell<Option<(Arc<EGLDisplayHandle>, EGLStreamKHR)>>,
pub(in crate::backend::drm) commit_buffer: Cell<Option<(DumbBuffer, framebuffer::Handle)>>,
pub(in crate::backend::drm) locked: AtomicBool,
pub(in crate::backend::drm) logger: ::slog::Logger,
}
impl<D: RawDevice + 'static> Surface for EglStreamSurfaceInternal<D> {
type Connectors = <<D as Device>::Surface as Surface>::Connectors;
type Error = Error<<<D as Device>::Surface as Surface>::Error>;
fn crtc(&self) -> crtc::Handle {
self.crtc.crtc()
}
fn current_connectors(&self) -> Self::Connectors {
self.crtc.current_connectors()
}
fn pending_connectors(&self) -> Self::Connectors {
self.crtc.pending_connectors()
}
fn add_connector(&self, connector: connector::Handle) -> Result<(), Self::Error> {
self.crtc.add_connector(connector).map_err(Error::Underlying)
}
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error> {
self.crtc.remove_connector(connector).map_err(Error::Underlying)
}
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.crtc.set_connectors(connectors).map_err(Error::Underlying)
}
fn current_mode(&self) -> Mode {
self.crtc.current_mode()
}
fn pending_mode(&self) -> Mode {
self.crtc.pending_mode()
}
fn use_mode(&self, mode: Mode) -> Result<(), Error<<<D as Device>::Surface as Surface>::Error>> {
self.crtc.use_mode(mode).map_err(Error::Underlying)
}
}
impl<D: RawDevice + 'static> Drop for EglStreamSurfaceInternal<D> {
fn drop(&mut self) {
if let Some((buffer, _)) = self.cursor.get() {
let _ = self.crtc.destroy_dumb_buffer(buffer);
}
if let Some((buffer, fb)) = self.commit_buffer.take() {
let _ = self.crtc.destroy_framebuffer(fb);
let _ = self.crtc.destroy_dumb_buffer(buffer);
}
if let Some((display, stream)) = self.stream.replace(None) {
unsafe {
egl::DestroyStreamKHR(display.handle, stream);
}
}
}
}
// Conceptionally EglStream is a weird api.
// It does modesetting on its own, bypassing our `RawSurface::commit` function.
// As a result, we cannot easily sync any other planes to the commit without more
// experimental egl extensions to do this via the EglStream-API.
//
// So instead we leverage the fact, that all drm-drivers still support the legacy
// `drmModeSetCursor` and `drmModeMoveCursor` functions, that (mostly) implicitly sync to the primary plane.
// That way we can use hardware cursors at least on all drm-backends (including atomic), although
// this is a little hacky. Overlay planes however are completely out of question for now.
//
// Note that this might still appear a little chuppy, we should just use software cursors
// on eglstream devices by default and only use this, if the user really wants it.
#[cfg(feature = "backend_drm")]
impl<D: RawDevice + 'static> CursorBackend for EglStreamSurfaceInternal<D> {
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
type Error = Error<DrmError>;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> {
trace!(self.logger, "Move the cursor to {},{}", x, y);
self.crtc
.move_cursor(self.crtc.crtc(), (x as i32, y as i32))
.compat()
.map_err(|source| DrmError::Access {
errmsg: "Error moving cursor",
dev: self.crtc.dev_path(),
source,
})
.map_err(Error::Underlying)
}
fn set_cursor_representation(
&self,
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
hotspot: (u32, u32),
) -> Result<(), Self::Error> {
let (w, h) = buffer.dimensions();
debug!(self.logger, "Importing cursor");
// import the cursor into a buffer we can render
let mut cursor = self
.crtc
.create_dumb_buffer((w, h), PixelFormat::ARGB8888)
.compat()
.map_err(Error::BufferCreationFailed)?;
{
let mut mapping = self
.crtc
.map_dumb_buffer(&mut cursor)
.compat()
.map_err(Error::BufferWriteFailed)?;
mapping.as_mut().copy_from_slice(buffer);
}
trace!(self.logger, "Setting the new imported cursor");
trace!(self.logger, "Setting the new imported cursor");
if self
.crtc
.set_cursor2(
self.crtc.crtc(),
Some(&cursor),
(hotspot.0 as i32, hotspot.1 as i32),
)
.is_err()
{
self.crtc
.set_cursor(self.crtc.crtc(), Some(&cursor))
.compat()
.map_err(|source| DrmError::Access {
errmsg: "Failed to set cursor",
dev: self.crtc.dev_path(),
source,
})
.map_err(Error::Underlying)?;
}
// and store it
if let Some((old, _)) = self.cursor.replace(Some((cursor, hotspot))) {
if self.crtc.destroy_dumb_buffer(old).is_err() {
warn!(self.logger, "Failed to free old cursor");
}
}
Ok(())
}
}
/// egl stream surface for rendering
pub struct EglStreamSurface<D: RawDevice + 'static>(
pub(in crate::backend::drm) Rc<EglStreamSurfaceInternal<D>>,
);
impl<D: RawDevice + 'static> EglStreamSurface<D> {
/// Check if underlying gbm resources need to be recreated.
pub fn needs_recreation(&self) -> bool {
self.0.crtc.commit_pending() || self.0.stream.borrow().is_none()
}
pub(super) fn create_stream(
&self,
display: &Arc<EGLDisplayHandle>,
output_attribs: &[isize],
) -> Result<EGLStreamKHR, Error<<<D as Device>::Surface as Surface>::Error>> {
// drop old steam, if it already exists
if let Some((display, stream)) = self.0.stream.replace(None) {
// ignore result
unsafe {
ffi::egl::DestroyStreamKHR(display.handle, stream);
}
}
// because we are re-creating there might be a new mode. if there is? -> commit it
if self.0.crtc.commit_pending() {
let (w, h) = self.pending_mode().size();
// but we need a buffer to commit...
// well lets create one and clean it up once the stream is running
if let Ok(buffer) = self
.0
.crtc
.create_dumb_buffer((w as u32, h as u32), PixelFormat::ARGB8888)
{
if let Ok(fb) = self.0.crtc.add_framebuffer(&buffer) {
if let Some((buffer, fb)) = self.0.commit_buffer.replace(Some((buffer, fb))) {
let _ = self.0.crtc.destroy_framebuffer(fb);
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
}
self.0.crtc.commit(fb).map_err(Error::Underlying)?;
}
}
}
let extensions = {
let p =
unsafe { CStr::from_ptr(ffi::egl::QueryString(display.handle, ffi::egl::EXTENSIONS as i32)) };
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
};
if !extensions.iter().any(|s| *s == "EGL_EXT_output_base")
|| !extensions.iter().any(|s| *s == "EGL_EXT_output_drm")
|| !extensions.iter().any(|s| *s == "EGL_KHR_stream")
|| !extensions
.iter()
.any(|s| *s == "EGL_EXT_stream_consumer_egloutput")
|| !extensions
.iter()
.any(|s| *s == "EGL_KHR_stream_producer_eglsurface")
{
error!(self.0.logger, "Extension for EGLStream surface creation missing");
return Err(Error::DeviceIsNoEGLStreamDevice);
}
if cfg!(debug_assertions) {
// TEST START
let mut num_layers = 0;
if unsafe {
ffi::egl::GetOutputLayersEXT(
display.handle,
ptr::null(),
ptr::null_mut(),
10,
&mut num_layers,
)
} == 0
{
error!(self.0.logger, "Failed to get any! output layer");
}
if num_layers == 0 {
error!(self.0.logger, "Failed to find any! output layer");
}
let mut layers = Vec::with_capacity(num_layers as usize);
if unsafe {
ffi::egl::GetOutputLayersEXT(
display.handle,
ptr::null(),
layers.as_mut_ptr(),
num_layers,
&mut num_layers,
)
} == 0
{
error!(self.0.logger, "Failed to receive Output Layers");
}
unsafe {
layers.set_len(num_layers as usize);
}
for layer in layers {
debug!(self.0.logger, "Found layer: {:?}", layer);
let mut val = 0;
if unsafe {
ffi::egl::QueryOutputLayerAttribEXT(
display.handle,
layer,
ffi::egl::DRM_CRTC_EXT as i32,
&mut val,
)
} != 0
{
info!(self.0.logger, "Possible crtc output layer: {}", val);
}
val = 0;
if unsafe {
ffi::egl::QueryOutputLayerAttribEXT(
display.handle,
layer,
ffi::egl::DRM_PLANE_EXT as i32,
&mut val,
)
} != 0
{
info!(self.0.logger, "Possible plane output layer: {}", val);
}
}
// TEST END
}
let mut num_layers = 0;
if unsafe {
ffi::egl::GetOutputLayersEXT(
display.handle,
output_attribs.as_ptr(),
ptr::null_mut(),
1,
&mut num_layers,
)
} == 0
{
error!(
self.0.logger,
"Failed to acquire Output Layer. Attributes {:?}", output_attribs
);
return Err(Error::DeviceNoOutputLayer);
}
if num_layers == 0 {
error!(self.0.logger, "Failed to find Output Layer");
return Err(Error::DeviceNoOutputLayer);
}
let mut layers = Vec::with_capacity(num_layers as usize);
if unsafe {
ffi::egl::GetOutputLayersEXT(
display.handle,
output_attribs.as_ptr(),
layers.as_mut_ptr(),
num_layers,
&mut num_layers,
)
} == 0
{
error!(self.0.logger, "Failed to get Output Layer");
return Err(Error::DeviceNoOutputLayer);
}
unsafe {
layers.set_len(num_layers as usize);
}
let layer = layers[0];
unsafe {
ffi::egl::OutputLayerAttribEXT(display.handle, layer, ffi::egl::SWAP_INTERVAL_EXT as i32, 0);
}
let stream_attributes = {
let mut out: Vec<c_int> = Vec::with_capacity(7);
out.push(ffi::egl::STREAM_FIFO_LENGTH_KHR as i32);
out.push(0);
out.push(ffi::egl::CONSUMER_AUTO_ACQUIRE_EXT as i32);
out.push(ffi::egl::FALSE as i32);
out.push(ffi::egl::CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR as i32);
out.push(0);
out.push(ffi::egl::NONE as i32);
out
};
let stream = unsafe { ffi::egl::CreateStreamKHR(display.handle, stream_attributes.as_ptr()) };
if stream == ffi::egl::NO_STREAM_KHR {
error!(self.0.logger, "Failed to create egl stream");
return Err(Error::DeviceStreamCreationFailed);
}
if unsafe { ffi::egl::StreamConsumerOutputEXT(display.handle, stream, layer) } == 0 {
error!(self.0.logger, "Failed to link Output Layer as Stream Consumer");
return Err(Error::DeviceStreamCreationFailed);
}
let _ = self.0.stream.replace(Some((display.clone(), stream)));
Ok(stream)
}
pub(super) fn create_surface(
&self,
display: &Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig,
_surface_attribs: &[c_int],
output_attribs: &[isize],
) -> Result<*const c_void, Error<<<D as Device>::Surface as Surface>::Error>> {
let stream = self.create_stream(display, output_attribs)?;
let (w, h) = self.current_mode().size();
info!(self.0.logger, "Creating stream surface with size: ({}:{})", w, h);
let surface_attributes = {
let mut out: Vec<c_int> = Vec::with_capacity(5);
out.push(ffi::egl::WIDTH as i32);
out.push(w as i32);
out.push(ffi::egl::HEIGHT as i32);
out.push(h as i32);
out.push(ffi::egl::NONE as i32);
out
};
let surface = unsafe {
ffi::egl::CreateStreamProducerSurfaceKHR(
display.handle,
config_id,
stream,
surface_attributes.as_ptr(),
)
};
if surface == ffi::egl::NO_SURFACE {
error!(self.0.logger, "Failed to create surface: 0x{:X}", unsafe {
ffi::egl::GetError()
});
}
Ok(surface)
}
pub(super) fn flip(
&self,
crtc: crtc::Handle,
display: &Arc<EGLDisplayHandle>,
surface: ffi::egl::types::EGLSurface,
) -> Result<(), SwapBuffersError<Error<<<D as Device>::Surface as Surface>::Error>>> {
if self.0.locked.load(Ordering::SeqCst) {
let acquire_attributes = [
ffi::egl::DRM_FLIP_EVENT_DATA_NV as isize,
Into::<u32>::into(crtc) as isize,
ffi::egl::NONE as isize,
];
if let Ok(stream) = self.0.stream.try_borrow() {
let res = if let Some(&(ref display, ref stream)) = stream.as_ref() {
wrap_egl_call(|| unsafe {
ffi::egl::StreamConsumerAcquireAttribNV(
display.handle,
*stream,
acquire_attributes.as_ptr(),
);
})
.map_err(Error::StreamFlipFailed)
} else {
Err(Error::StreamFlipFailed(EGLError::NotInitialized))
};
if res.is_ok() {
self.0.locked.store(false, Ordering::SeqCst);
} else {
return res.map_err(SwapBuffersError::Underlying);
}
}
}
if !self.0.locked.load(Ordering::SeqCst) {
wrap_egl_call(|| unsafe { ffi::egl::SwapBuffers(***display, surface as *const _) })
.map_err(SwapBuffersError::EGLSwapBuffers)?;
self.0.locked.store(true, Ordering::SeqCst);
}
Ok(())
}
}
impl<D: RawDevice + 'static> Surface for EglStreamSurface<D> {
type Connectors = <<D as Device>::Surface as Surface>::Connectors;
type Error = Error<<<D as Device>::Surface as Surface>::Error>;
fn crtc(&self) -> crtc::Handle {
self.0.crtc()
}
fn current_connectors(&self) -> Self::Connectors {
self.0.current_connectors()
}
fn pending_connectors(&self) -> Self::Connectors {
self.0.pending_connectors()
}
fn add_connector(&self, connector: connector::Handle) -> Result<(), Self::Error> {
self.0.add_connector(connector)
}
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error> {
self.0.remove_connector(connector)
}
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.0.set_connectors(connectors)
}
fn current_mode(&self) -> Mode {
self.0.current_mode()
}
fn pending_mode(&self) -> Mode {
self.0.pending_mode()
}
fn use_mode(&self, mode: Mode) -> Result<(), Self::Error> {
self.0.use_mode(mode)
}
}
#[cfg(feature = "backend_drm_legacy")]
impl<D: RawDevice + 'static> CursorBackend for EglStreamSurface<D> {
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
type Error = Error<DrmError>;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> {
self.0.set_cursor_position(x, y)
}
fn set_cursor_representation(
&self,
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
hotspot: (u32, u32),
) -> Result<(), Self::Error> {
self.0.set_cursor_representation(buffer, hotspot)
}
}

View File

@ -54,6 +54,8 @@ pub mod atomic;
pub mod common; pub mod common;
#[cfg(feature = "backend_drm_egl")] #[cfg(feature = "backend_drm_egl")]
pub mod egl; pub mod egl;
#[cfg(feature = "backend_drm_eglstream")]
pub mod eglstream;
#[cfg(feature = "backend_drm_gbm")] #[cfg(feature = "backend_drm_gbm")]
pub mod gbm; pub mod gbm;
#[cfg(feature = "backend_drm_legacy")] #[cfg(feature = "backend_drm_legacy")]

View File

@ -91,6 +91,10 @@ pub enum EGLError {
/// A NativeWindowType argument does not refer to a valid native window. /// A NativeWindowType argument does not refer to a valid native window.
#[error("A NativeWindowType argument does not refer to a valid native window.")] #[error("A NativeWindowType argument does not refer to a valid native window.")]
BadNativeWindow, BadNativeWindow,
#[cfg(feature = "backend_drm_eglstream")]
/// The EGL operation failed due to temporary unavailability of a requested resource, but the arguments were otherwise valid, and a subsequent attempt may succeed.
#[error("The EGL operation failed due to temporary unavailability of a requested resource, but the arguments were otherwise valid, and a subsequent attempt may succeed.")]
ResourceBusy,
/// A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering. /// A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering.
#[error("A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering.")] #[error("A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering.")]
ContextLost, ContextLost,
@ -114,6 +118,8 @@ impl From<u32> for EGLError {
ffi::egl::BAD_PARAMETER => EGLError::BadParameter, ffi::egl::BAD_PARAMETER => EGLError::BadParameter,
ffi::egl::BAD_NATIVE_PIXMAP => EGLError::BadNativePixmap, ffi::egl::BAD_NATIVE_PIXMAP => EGLError::BadNativePixmap,
ffi::egl::BAD_NATIVE_WINDOW => EGLError::BadNativeWindow, ffi::egl::BAD_NATIVE_WINDOW => EGLError::BadNativeWindow,
#[cfg(feature = "backend_drm_eglstream")]
ffi::egl::RESOURCE_BUSY_EXT => EGLError::ResourceBusy,
ffi::egl::CONTEXT_LOST => EGLError::ContextLost, ffi::egl::CONTEXT_LOST => EGLError::ContextLost,
x => EGLError::Unknown(x), x => EGLError::Unknown(x),
} }

View File

@ -37,6 +37,8 @@ pub fn make_sure_egl_is_loaded() {
egl::BindWaylandDisplayWL::load_with(&proc_address); egl::BindWaylandDisplayWL::load_with(&proc_address);
egl::UnbindWaylandDisplayWL::load_with(&proc_address); egl::UnbindWaylandDisplayWL::load_with(&proc_address);
egl::QueryWaylandBufferWL::load_with(&proc_address); egl::QueryWaylandBufferWL::load_with(&proc_address);
#[cfg(feature = "backend_drm_eglstream")]
egl::StreamConsumerAcquireAttribNV::load_with(&proc_address);
}); });
} }
@ -196,4 +198,66 @@ pub mod egl {
// Accepted in the <attribute> parameter of eglQueryWaylandBufferWL: // Accepted in the <attribute> parameter of eglQueryWaylandBufferWL:
pub const EGL_TEXTURE_FORMAT: i32 = 0x3080; pub const EGL_TEXTURE_FORMAT: i32 = 0x3080;
pub const WAYLAND_Y_INVERTED_WL: i32 = 0x31DB; pub const WAYLAND_Y_INVERTED_WL: i32 = 0x31DB;
/// nVidia support needs some implemented but only proposed egl extensions...
/// Therefor gl_generator cannot generate them and we need some constants...
/// And a function...
#[cfg(feature = "backend_drm_eglstream")]
pub const CONSUMER_AUTO_ACQUIRE_EXT: i32 = 0x332B;
#[cfg(feature = "backend_drm_eglstream")]
pub const DRM_FLIP_EVENT_DATA_NV: i32 = 0x333E;
#[cfg(feature = "backend_drm_eglstream")]
pub const CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR: i32 = 0x321E;
#[cfg(feature = "backend_drm_eglstream")]
pub const RESOURCE_BUSY_EXT: u32 = 0x3353;
#[cfg(feature = "backend_drm_eglstream")]
#[allow(non_snake_case, unused_variables, dead_code)]
#[inline]
pub unsafe fn StreamConsumerAcquireAttribNV(
dpy: types::EGLDisplay,
stream: types::EGLStreamKHR,
attrib_list: *const types::EGLAttrib,
) -> types::EGLBoolean {
__gl_imports::mem::transmute::<
_,
extern "system" fn(
types::EGLDisplay,
types::EGLStreamKHR,
*const types::EGLAttrib,
) -> types::EGLBoolean,
>(nvidia_storage::StreamConsumerAcquireAttribNV.f)(dpy, stream, attrib_list)
}
#[cfg(feature = "backend_drm_eglstream")]
mod nvidia_storage {
use super::{FnPtr, __gl_imports::raw};
pub static mut StreamConsumerAcquireAttribNV: FnPtr = FnPtr {
f: super::missing_fn_panic as *const raw::c_void,
is_loaded: false,
};
}
#[cfg(feature = "backend_drm_eglstream")]
#[allow(non_snake_case)]
pub mod StreamConsumerAcquireAttribNV {
use super::{FnPtr, __gl_imports::raw, metaloadfn, nvidia_storage};
#[inline]
#[allow(dead_code)]
pub fn is_loaded() -> bool {
unsafe { nvidia_storage::StreamConsumerAcquireAttribNV.is_loaded }
}
#[allow(dead_code)]
pub fn load_with<F>(mut loadfn: F)
where
F: FnMut(&str) -> *const raw::c_void,
{
unsafe {
nvidia_storage::StreamConsumerAcquireAttribNV =
FnPtr::new(metaloadfn(&mut loadfn, "eglStreamConsumerAcquireAttribNV", &[]))
}
}
}
} }