Refactor EGL backend

This commit is contained in:
Chandler Newman 2020-04-15 12:01:01 +01:00
parent 8ab34f0081
commit a684f5d8d6
11 changed files with 930 additions and 793 deletions

View File

@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc};
use slog::Logger; use slog::Logger;
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
use smithay::backend::egl::EGLDisplay; use smithay::backend::egl::display::WaylandEGLDisplay;
use smithay::{ use smithay::{
reexports::wayland_server::protocol::wl_buffer::WlBuffer, reexports::wayland_server::protocol::wl_buffer::WlBuffer,
wayland::shm::with_buffer_contents as shm_buffer_contents, wayland::shm::with_buffer_contents as shm_buffer_contents,
@ -13,14 +13,14 @@ use smithay::{
#[derive(Clone)] #[derive(Clone)]
pub struct BufferUtils { pub struct BufferUtils {
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
egl_display: Rc<RefCell<Option<EGLDisplay>>>, egl_display: Rc<RefCell<Option<WaylandEGLDisplay>>>,
log: Logger, log: Logger,
} }
impl BufferUtils { impl BufferUtils {
/// Creates a new `BufferUtils`. /// Creates a new `BufferUtils`.
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
pub fn new(egl_display: Rc<RefCell<Option<EGLDisplay>>>, log: Logger) -> Self { pub fn new(egl_display: Rc<RefCell<Option<WaylandEGLDisplay>>>, log: Logger) -> Self {
Self { egl_display, log } Self { egl_display, log }
} }

View File

@ -12,7 +12,7 @@ use glium::{
use slog::Logger; use slog::Logger;
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
use smithay::backend::egl::EGLDisplay; use smithay::backend::egl::display::WaylandEGLDisplay;
use smithay::{ use smithay::{
backend::{ backend::{
egl::{BufferAccessError, EGLImages, Format}, egl::{BufferAccessError, EGLImages, Format},
@ -44,7 +44,7 @@ pub struct GliumDrawer<F: GLGraphicsBackend + 'static> {
index_buffer: glium::IndexBuffer<u16>, index_buffer: glium::IndexBuffer<u16>,
programs: [glium::Program; shaders::FRAGMENT_COUNT], programs: [glium::Program; shaders::FRAGMENT_COUNT],
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
egl_display: Rc<RefCell<Option<EGLDisplay>>>, egl_display: Rc<RefCell<Option<WaylandEGLDisplay>>>,
log: Logger, log: Logger,
} }
@ -56,7 +56,11 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
impl<T: Into<GliumGraphicsBackend<T>> + GLGraphicsBackend + 'static> GliumDrawer<T> { impl<T: Into<GliumGraphicsBackend<T>> + GLGraphicsBackend + 'static> GliumDrawer<T> {
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
pub fn init(backend: T, egl_display: Rc<RefCell<Option<EGLDisplay>>>, log: Logger) -> GliumDrawer<T> { pub fn init(
backend: T,
egl_display: Rc<RefCell<Option<WaylandEGLDisplay>>>,
log: Logger,
) -> GliumDrawer<T> {
let display = backend.into(); let display = backend.into();
// building the vertex buffer, which contains all the vertices that we will draw // building the vertex buffer, which contains all the vertices that we will draw

View File

@ -15,7 +15,7 @@ use glium::Surface as GliumSurface;
use slog::Logger; use slog::Logger;
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
use smithay::backend::egl::{EGLDisplay, EGLGraphicsBackend}; use smithay::backend::egl::{display::WaylandEGLDisplay, EGLGraphicsBackend};
use smithay::{ use smithay::{
backend::{ backend::{
drm::{ drm::{
@ -66,6 +66,7 @@ use crate::glium_drawer::GliumDrawer;
use crate::input_handler::AnvilInputHandler; use crate::input_handler::AnvilInputHandler;
use crate::shell::{init_shell, MyWindowMap, Roles}; use crate::shell::{init_shell, MyWindowMap, Roles};
use crate::AnvilState; use crate::AnvilState;
use smithay::backend::drm::gbm::GbmSurface;
pub struct SessionFd(RawFd); pub struct SessionFd(RawFd);
impl AsRawFd for SessionFd { impl AsRawFd for SessionFd {
@ -76,8 +77,7 @@ impl AsRawFd for SessionFd {
type RenderDevice = type RenderDevice =
EglDevice<EglGbmBackend<LegacyDrmDevice<SessionFd>>, GbmDevice<LegacyDrmDevice<SessionFd>>>; EglDevice<EglGbmBackend<LegacyDrmDevice<SessionFd>>, GbmDevice<LegacyDrmDevice<SessionFd>>>;
type RenderSurface = type RenderSurface = EglSurface<GbmSurface<LegacyDrmDevice<SessionFd>>>;
EglSurface<EglGbmBackend<LegacyDrmDevice<SessionFd>>, GbmDevice<LegacyDrmDevice<SessionFd>>>;
pub fn run_udev(mut display: Display, mut event_loop: EventLoop<AnvilState>, log: Logger) -> Result<(), ()> { pub fn run_udev(mut display: Display, mut event_loop: EventLoop<AnvilState>, log: Logger) -> Result<(), ()> {
let name = display.add_socket_auto().unwrap().into_string().unwrap(); let name = display.add_socket_auto().unwrap().into_string().unwrap();
@ -294,7 +294,7 @@ struct BackendData<S: SessionNotifier> {
struct UdevHandlerImpl<S: SessionNotifier, Data: 'static> { struct UdevHandlerImpl<S: SessionNotifier, Data: 'static> {
compositor_token: CompositorToken<Roles>, compositor_token: CompositorToken<Roles>,
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
active_egl_context: Rc<RefCell<Option<EGLDisplay>>>, active_egl_context: Rc<RefCell<Option<WaylandEGLDisplay>>>,
session: AutoSession, session: AutoSession,
backends: HashMap<dev_t, BackendData<S>>, backends: HashMap<dev_t, BackendData<S>>,
display: Rc<RefCell<Display>>, display: Rc<RefCell<Display>>,
@ -313,7 +313,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
pub fn scan_connectors( pub fn scan_connectors(
device: &mut RenderDevice, device: &mut RenderDevice,
egl_display: Rc<RefCell<Option<EGLDisplay>>>, egl_display: Rc<RefCell<Option<WaylandEGLDisplay>>>,
logger: &::slog::Logger, logger: &::slog::Logger,
) -> HashMap<crtc::Handle, GliumDrawer<RenderSurface>> { ) -> HashMap<crtc::Handle, GliumDrawer<RenderSurface>> {
// Get a set of all modesetting resource handles (excluding planes): // Get a set of all modesetting resource handles (excluding planes):

View File

@ -12,20 +12,19 @@ use drm::control::{connector, crtc, encoder, framebuffer, plane, ResourceHandles
use drm::SystemError as DrmError; use drm::SystemError as DrmError;
use nix::libc::dev_t; use nix::libc::dev_t;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc;
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
use wayland_server::Display; use wayland_server::Display;
use super::{Device, DeviceHandler, Surface}; use super::{Device, DeviceHandler, Surface};
use crate::backend::egl::context::GlAttributes;
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use crate::backend::egl::EGLContext;
use crate::backend::egl::Error as EGLError; use crate::backend::egl::Error as EGLError;
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
use crate::backend::egl::{EGLDisplay, EGLGraphicsBackend}; use crate::backend::egl::{display::WaylandEGLDisplay, EGLGraphicsBackend};
mod surface; mod surface;
pub use self::surface::*; pub use self::surface::*;
use crate::backend::egl::context::GlAttributes;
use crate::backend::egl::display::EGLDisplay;
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
pub mod session; pub mod session;
@ -48,7 +47,7 @@ where
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static, D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
dev: Rc<EGLContext<B, D>>, dev: EGLDisplay<B, D>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -73,31 +72,7 @@ where
/// ///
/// Returns an error if the file is no valid device or context /// Returns an error if the file is no valid device or context
/// creation was not successful. /// creation was not successful.
pub fn new<L>(dev: D, logger: L) -> Result<Self, Error<<<D as Device>::Surface as Surface>::Error>> pub fn new<L>(mut dev: D, logger: L) -> Result<Self, Error<<<D as Device>::Surface as Surface>::Error>>
where
L: Into<Option<::slog::Logger>>,
{
EglDevice::new_with_gl_attr(
dev,
GlAttributes {
version: None,
profile: None,
debug: cfg!(debug_assertions),
vsync: true,
},
logger,
)
}
/// Create a new [`EglDevice`] from an open device and given [`GlAttributes`]
///
/// Returns an error if the file is no valid device or context
/// creation was not successful.
pub fn new_with_gl_attr<L>(
mut dev: D,
attributes: GlAttributes,
logger: L,
) -> Result<Self, Error<<<D as Device>::Surface as Surface>::Error>>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -107,10 +82,7 @@ where
debug!(log, "Creating egl context from device"); debug!(log, "Creating egl context from device");
Ok(EglDevice { Ok(EglDevice {
// Open the gbm device from the drm device and create a context based on that dev: EGLDisplay::new(dev, log.clone()).map_err(Error::EGL)?,
dev: Rc::new(
EGLContext::new(dev, attributes, Default::default(), log.clone()).map_err(Error::EGL)?,
),
logger: log, logger: log,
}) })
} }
@ -147,7 +119,7 @@ where
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static, D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
type Surface = EglSurface<B, D>; type Surface = EglSurface<<D as Device>::Surface>;
fn device_id(&self) -> dev_t { fn device_id(&self) -> dev_t {
self.dev.borrow().device_id() self.dev.borrow().device_id()
@ -166,15 +138,30 @@ where
fn create_surface( fn create_surface(
&mut self, &mut self,
crtc: crtc::Handle, crtc: crtc::Handle,
) -> Result<EglSurface<B, D>, <Self::Surface as Surface>::Error> { ) -> Result<Self::Surface, <Self::Surface as Surface>::Error> {
info!(self.logger, "Initializing EglSurface"); info!(self.logger, "Initializing EglSurface");
let surface = self.dev.create_surface(crtc).map_err(Error::EGL)?; // Device trait is unaware of opengl, so using sensible defaults
let attributes = GlAttributes {
version: None,
profile: None,
debug: cfg!(debug_assertions),
vsync: true,
};
let reqs = Default::default();
Ok(EglSurface { let context = self.dev.create_context(attributes, reqs).map_err(Error::EGL)?;
dev: self.dev.clone(), let surface = self
surface, .dev
}) .create_surface(
context.get_pixel_format(),
reqs.double_buffer,
context.get_config_id(),
crtc,
)
.map_err(Error::EGL)?;
Ok(EglSurface { context, surface })
} }
fn process_events(&mut self) { fn process_events(&mut self) {
@ -212,7 +199,7 @@ where
D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static, D: Device + NativeDisplay<B, Arguments = crtc::Handle> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay, EGLError> { fn bind_wl_display(&self, display: &Display) -> Result<WaylandEGLDisplay, EGLError> {
self.dev.bind_wl_display(display) self.dev.bind_wl_display(display)
} }
} }

View File

@ -1,11 +1,10 @@
use drm::control::{connector, crtc, Mode}; use drm::control::{connector, crtc, Mode};
use nix::libc::c_void; use nix::libc::c_void;
use std::rc::Rc;
use super::Error; use super::Error;
use crate::backend::drm::{Device, Surface}; use crate::backend::drm::Surface;
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; use crate::backend::egl::native::NativeSurface;
use crate::backend::egl::{EGLContext, EGLSurface}; use crate::backend::egl::{get_proc_address, native, EGLContext, EGLSurface};
#[cfg(feature = "renderer_gl")] #[cfg(feature = "renderer_gl")]
use crate::backend::graphics::gl::GLGraphicsBackend; use crate::backend::graphics::gl::GLGraphicsBackend;
#[cfg(feature = "renderer_gl")] #[cfg(feature = "renderer_gl")]
@ -13,24 +12,20 @@ use crate::backend::graphics::PixelFormat;
use crate::backend::graphics::{CursorBackend, SwapBuffersError}; use crate::backend::graphics::{CursorBackend, SwapBuffersError};
/// Egl surface for rendering /// Egl surface for rendering
pub struct EglSurface<B, D> pub struct EglSurface<N>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, N: native::NativeSurface + Surface,
D: Device + NativeDisplay<B> + 'static,
<D as Device>::Surface: NativeSurface,
{ {
pub(super) dev: Rc<EGLContext<B, D>>, pub(super) context: EGLContext,
pub(super) surface: EGLSurface<B::Surface>, pub(super) surface: EGLSurface<N>,
} }
impl<B, D> Surface for EglSurface<B, D> impl<N> Surface for EglSurface<N>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, N: NativeSurface + Surface,
D: Device + NativeDisplay<B> + 'static,
<D as Device>::Surface: NativeSurface,
{ {
type Error = Error<<<D as Device>::Surface as Surface>::Error>; type Connectors = <N as Surface>::Connectors;
type Connectors = <<D as Device>::Surface as Surface>::Connectors; type Error = Error<<N as Surface>::Error>;
fn crtc(&self) -> crtc::Handle { fn crtc(&self) -> crtc::Handle {
(*self.surface).crtc() (*self.surface).crtc()
@ -67,14 +62,12 @@ where
} }
} }
impl<'a, B, D> CursorBackend<'a> for EglSurface<B, D> impl<'a, N> CursorBackend<'a> for EglSurface<N>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, N: NativeSurface + Surface + CursorBackend<'a>,
D: Device + NativeDisplay<B> + 'static,
<D as Device>::Surface: NativeSurface + CursorBackend<'a>,
{ {
type CursorFormat = <D::Surface as CursorBackend<'a>>::CursorFormat; type CursorFormat = <N as CursorBackend<'a>>::CursorFormat;
type Error = <D::Surface as CursorBackend<'a>>::Error; type Error = <N as CursorBackend<'a>>::Error;
fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> { fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> {
self.surface.set_cursor_position(x, y) self.surface.set_cursor_position(x, y)
@ -93,18 +86,16 @@ where
} }
#[cfg(feature = "renderer_gl")] #[cfg(feature = "renderer_gl")]
impl<B, D> GLGraphicsBackend for EglSurface<B, D> impl<N> GLGraphicsBackend for EglSurface<N>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, N: native::NativeSurface + Surface,
D: Device + NativeDisplay<B> + 'static,
<D as Device>::Surface: NativeSurface,
{ {
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.surface.swap_buffers() self.surface.swap_buffers()
} }
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
self.dev.get_proc_address(symbol) get_proc_address(symbol)
} }
fn get_framebuffer_dimensions(&self) -> (u32, u32) { fn get_framebuffer_dimensions(&self) -> (u32, u32) {
@ -113,14 +104,14 @@ where
} }
fn is_current(&self) -> bool { fn is_current(&self) -> bool {
self.dev.is_current() && self.surface.is_current() self.context.is_current() && self.surface.is_current()
} }
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.surface.make_current() self.context.make_current_with_surface(&self.surface)
} }
fn get_pixel_format(&self) -> PixelFormat { fn get_pixel_format(&self) -> PixelFormat {
self.dev.get_pixel_format() self.surface.get_pixel_format()
} }
} }

View File

@ -1,76 +1,38 @@
//! EGL context related structs //! EGL context related structs
use super::{ffi, native, EGLSurface, Error}; use super::{ffi, Error};
use crate::backend::graphics::PixelFormat; use crate::backend::egl::display::EGLDisplay;
use nix::libc::{c_int, c_void}; use crate::backend::egl::native::NativeSurface;
use crate::backend::egl::{native, EGLSurface};
use crate::backend::graphics::{PixelFormat, SwapBuffersError};
use slog; use slog;
use std::{ use std::ptr;
cell::{Ref, RefCell, RefMut}, use std::sync::{Arc, Weak};
ffi::{CStr, CString},
marker::PhantomData,
mem::MaybeUninit,
ptr,
rc::Rc,
};
/// EGL context for rendering /// EGL context for rendering
pub struct EGLContext<B: native::Backend, N: native::NativeDisplay<B>> { pub struct EGLContext {
native: RefCell<N>, context: Arc<ffi::egl::types::EGLContext>,
pub(crate) context: Rc<ffi::egl::types::EGLContext>, display: Weak<ffi::egl::types::EGLDisplay>,
pub(crate) display: Rc<ffi::egl::types::EGLDisplay>, config_id: ffi::egl::types::EGLConfig,
pub(crate) config_id: ffi::egl::types::EGLConfig,
pub(crate) surface_attributes: Vec<c_int>,
pixel_format: PixelFormat, pixel_format: PixelFormat,
pub(crate) wl_drm_support: bool,
logger: slog::Logger, logger: slog::Logger,
_backend: PhantomData<B>,
} }
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> { impl EGLContext {
/// Create a new [`EGLContext`] from a given [`NativeDisplay`](native::NativeDisplay) /// Create a new [`EGLContext`] from a given [`NativeDisplay`](native::NativeDisplay)
pub fn new<L>( pub(crate) fn new<B, N, L>(
native: N, display: &EGLDisplay<B, N>,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
logger: L,
) -> Result<EGLContext<B, N>, Error>
where
L: Into<Option<::slog::Logger>>,
{
let log = crate::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl"));
let ptr = native.ptr()?;
let (context, display, config_id, surface_attributes, pixel_format, wl_drm_support) =
unsafe { EGLContext::<B, N>::new_internal(ptr, attributes, reqs, log.clone()) }?;
Ok(EGLContext {
native: RefCell::new(native),
context,
display,
config_id,
surface_attributes,
pixel_format,
wl_drm_support,
logger: log,
_backend: PhantomData,
})
}
unsafe fn new_internal(
ptr: ffi::NativeDisplayType,
mut attributes: GlAttributes, mut attributes: GlAttributes,
reqs: PixelFormatRequirements, reqs: PixelFormatRequirements,
log: ::slog::Logger, log: L,
) -> Result< ) -> Result<EGLContext, Error>
( where
Rc<ffi::egl::types::EGLContext>, L: Into<Option<::slog::Logger>>,
Rc<ffi::egl::types::EGLDisplay>, B: native::Backend,
ffi::egl::types::EGLConfig, N: native::NativeDisplay<B>,
Vec<c_int>, {
PixelFormat, let log = crate::slog_or_stdlog(log.into()).new(o!("smithay_module" => "renderer_egl"));
bool,
),
Error,
> {
// If no version is given, try OpenGLES 3.0, if available, // If no version is given, try OpenGLES 3.0, if available,
// fallback to 2.0 otherwise // fallback to 2.0 otherwise
let version = match attributes.version { let version = match attributes.version {
@ -79,13 +41,13 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
None => { None => {
debug!(log, "Trying to initialize EGL with OpenGLES 3.0"); debug!(log, "Trying to initialize EGL with OpenGLES 3.0");
attributes.version = Some((3, 0)); attributes.version = Some((3, 0));
match EGLContext::<B, N>::new_internal(ptr, attributes, reqs, log.clone()) { match EGLContext::new(display, attributes, reqs, log.clone()) {
Ok(x) => return Ok(x), Ok(x) => return Ok(x),
Err(err) => { Err(err) => {
warn!(log, "EGL OpenGLES 3.0 Initialization failed with {}", err); warn!(log, "EGL OpenGLES 3.0 Initialization failed with {}", err);
debug!(log, "Trying to initialize EGL with OpenGLES 2.0"); debug!(log, "Trying to initialize EGL with OpenGLES 2.0");
attributes.version = Some((2, 0)); attributes.version = Some((2, 0));
return EGLContext::<B, N>::new_internal(ptr, attributes, reqs, log); return EGLContext::new(display, attributes, reqs, log.clone());
} }
} }
} }
@ -102,265 +64,11 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
} }
}; };
ffi::egl::LOAD.call_once(|| { let (pixel_format, config_id) = unsafe { display.choose_config(version, reqs)? };
fn constrain<F>(f: F) -> F
where
F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void,
{
f
};
ffi::egl::load_with(|sym| {
let name = CString::new(sym).unwrap();
let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes());
match symbol {
Ok(x) => *x as *const _,
Err(_) => ptr::null(),
}
});
let proc_address = constrain(|sym| {
let addr = CString::new(sym).unwrap();
let addr = addr.as_ptr();
ffi::egl::GetProcAddress(addr) as *const _
});
ffi::egl::load_with(&proc_address);
ffi::egl::BindWaylandDisplayWL::load_with(&proc_address);
ffi::egl::UnbindWaylandDisplayWL::load_with(&proc_address);
ffi::egl::QueryWaylandBufferWL::load_with(&proc_address);
});
// the first step is to query the list of extensions without any display, if supported
let dp_extensions = {
let p = ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32);
// 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);
let display = B::get_display(ptr, |e: &str| dp_extensions.iter().any(|s| s == e), log.clone());
if display == ffi::egl::NO_DISPLAY {
error!(log, "EGL Display is not valid");
return Err(Error::DisplayNotSupported);
}
let egl_version = {
let mut major: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
let mut minor: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
if ffi::egl::Initialize(display, major.as_mut_ptr(), minor.as_mut_ptr()) == 0 {
return Err(Error::InitFailed);
}
let major = major.assume_init();
let minor = minor.assume_init();
info!(log, "EGL Initialized");
info!(log, "EGL Version: {:?}", (major, minor));
(major, minor)
};
// the list of extensions supported by the client once initialized is different from the
// list of extensions obtained earlier
let extensions = if egl_version >= (1, 2) {
let p = CStr::from_ptr(ffi::egl::QueryString(display, 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<_>>()
} else {
vec![]
};
info!(log, "EGL Extensions: {:?}", extensions);
if egl_version >= (1, 2) && ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) == 0 {
error!(log, "OpenGLES not supported by the underlying EGL implementation");
return Err(Error::OpenGlesNotSupported);
}
let descriptor = {
let mut out: Vec<c_int> = Vec::with_capacity(37);
if egl_version >= (1, 2) {
trace!(log, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER");
out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int);
out.push(ffi::egl::RGB_BUFFER as c_int);
}
trace!(log, "Setting SURFACE_TYPE to WINDOW");
out.push(ffi::egl::SURFACE_TYPE as c_int);
// TODO: Some versions of Mesa report a BAD_ATTRIBUTE error
// if we ask for PBUFFER_BIT as well as WINDOW_BIT
out.push((ffi::egl::WINDOW_BIT) as c_int);
match version {
(3, _) => {
if egl_version < (1, 3) {
error!(
log,
"OpenglES 3.* is not supported on EGL Versions lower then 1.3"
);
return Err(Error::NoAvailablePixelFormat);
}
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES3");
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
trace!(log, "Setting CONFORMANT to OPENGL_ES3");
out.push(ffi::egl::CONFORMANT as c_int);
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
}
(2, _) => {
if egl_version < (1, 3) {
error!(
log,
"OpenglES 2.* is not supported on EGL Versions lower then 1.3"
);
return Err(Error::NoAvailablePixelFormat);
}
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES2");
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
trace!(log, "Setting CONFORMANT to OPENGL_ES2");
out.push(ffi::egl::CONFORMANT as c_int);
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
}
(_, _) => unreachable!(),
};
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
out.push(ffi::egl::CONFIG_CAVEAT as c_int);
out.push(if hardware_accelerated {
trace!(log, "Setting CONFIG_CAVEAT to NONE");
ffi::egl::NONE as c_int
} else {
trace!(log, "Setting CONFIG_CAVEAT to SLOW_CONFIG");
ffi::egl::SLOW_CONFIG as c_int
});
}
if let Some(color) = reqs.color_bits {
trace!(log, "Setting RED_SIZE to {}", color / 3);
out.push(ffi::egl::RED_SIZE as c_int);
out.push((color / 3) as c_int);
trace!(
log,
"Setting GREEN_SIZE to {}",
color / 3 + if color % 3 != 0 { 1 } else { 0 }
);
out.push(ffi::egl::GREEN_SIZE as c_int);
out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
trace!(
log,
"Setting BLUE_SIZE to {}",
color / 3 + if color % 3 == 2 { 1 } else { 0 }
);
out.push(ffi::egl::BLUE_SIZE as c_int);
out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
}
if let Some(alpha) = reqs.alpha_bits {
trace!(log, "Setting ALPHA_SIZE to {}", alpha);
out.push(ffi::egl::ALPHA_SIZE as c_int);
out.push(alpha as c_int);
}
if let Some(depth) = reqs.depth_bits {
trace!(log, "Setting DEPTH_SIZE to {}", depth);
out.push(ffi::egl::DEPTH_SIZE as c_int);
out.push(depth as c_int);
}
if let Some(stencil) = reqs.stencil_bits {
trace!(log, "Setting STENCIL_SIZE to {}", stencil);
out.push(ffi::egl::STENCIL_SIZE as c_int);
out.push(stencil as c_int);
}
if let Some(multisampling) = reqs.multisampling {
trace!(log, "Setting SAMPLES to {}", multisampling);
out.push(ffi::egl::SAMPLES as c_int);
out.push(multisampling as c_int);
}
if reqs.stereoscopy {
error!(log, "Stereoscopy is currently unsupported (sorry!)");
return Err(Error::NoAvailablePixelFormat);
}
out.push(ffi::egl::NONE as c_int);
out
};
// calling `eglChooseConfig`
let mut config_id = MaybeUninit::uninit();
let mut num_configs = MaybeUninit::uninit();
if ffi::egl::ChooseConfig(
display,
descriptor.as_ptr(),
config_id.as_mut_ptr(),
1,
num_configs.as_mut_ptr(),
) == 0
{
return Err(Error::ConfigFailed);
}
let config_id = config_id.assume_init();
let num_configs = num_configs.assume_init();
if num_configs == 0 {
error!(log, "No matching color format found");
return Err(Error::NoAvailablePixelFormat);
}
// analyzing each config
macro_rules! attrib {
($display:expr, $config:expr, $attr:expr) => {{
let mut value = MaybeUninit::uninit();
let res = ffi::egl::GetConfigAttrib(
$display,
$config,
$attr as ffi::egl::types::EGLint,
value.as_mut_ptr(),
);
if res == 0 {
return Err(Error::ConfigFailed);
}
value.assume_init()
}};
};
let desc = PixelFormat {
hardware_accelerated: attrib!(display, config_id, ffi::egl::CONFIG_CAVEAT)
!= ffi::egl::SLOW_CONFIG as i32,
color_bits: attrib!(display, config_id, ffi::egl::RED_SIZE) as u8
+ attrib!(display, config_id, ffi::egl::BLUE_SIZE) as u8
+ attrib!(display, config_id, ffi::egl::GREEN_SIZE) as u8,
alpha_bits: attrib!(display, config_id, ffi::egl::ALPHA_SIZE) as u8,
depth_bits: attrib!(display, config_id, ffi::egl::DEPTH_SIZE) as u8,
stencil_bits: attrib!(display, config_id, ffi::egl::STENCIL_SIZE) as u8,
stereoscopy: false,
double_buffer: true,
multisampling: match attrib!(display, config_id, ffi::egl::SAMPLES) {
0 | 1 => None,
a => Some(a as u16),
},
srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that
};
info!(log, "Selected color format: {:?}", desc);
let mut context_attributes = Vec::with_capacity(10); let mut context_attributes = Vec::with_capacity(10);
if egl_version >= (1, 5) || extensions.iter().any(|s| *s == "EGL_KHR_create_context") { if display.egl_version >= (1, 5) || display.extensions.iter().any(|s| s == "EGL_KHR_create_context") {
trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0); trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
context_attributes.push(version.0 as i32); context_attributes.push(version.0 as i32);
@ -368,7 +76,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
context_attributes.push(version.1 as i32); context_attributes.push(version.1 as i32);
if attributes.debug && egl_version >= (1, 5) { if attributes.debug && display.egl_version >= (1, 5) {
trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE"); trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE");
context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
context_attributes.push(ffi::egl::TRUE as i32); context_attributes.push(ffi::egl::TRUE as i32);
@ -376,7 +84,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
context_attributes.push(0); context_attributes.push(0);
} else if egl_version >= (1, 3) { } else if display.egl_version >= (1, 3) {
trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0); trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
context_attributes.push(version.0 as i32); context_attributes.push(version.0 as i32);
@ -385,74 +93,99 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
context_attributes.push(ffi::egl::NONE as i32); context_attributes.push(ffi::egl::NONE as i32);
trace!(log, "Creating EGL context..."); trace!(log, "Creating EGL context...");
let context = ffi::egl::CreateContext(display, config_id, ptr::null(), context_attributes.as_ptr()); // TODO: Support shared contexts
let context = unsafe {
ffi::egl::CreateContext(
*display.display,
config_id,
ptr::null(),
context_attributes.as_ptr(),
)
};
if context.is_null() { if context.is_null() {
match ffi::egl::GetError() as u32 { match unsafe { ffi::egl::GetError() } as u32 {
ffi::egl::BAD_ATTRIBUTE => return Err(Error::CreationFailed), ffi::egl::BAD_ATTRIBUTE => return Err(Error::CreationFailed),
err_no => return Err(Error::Unknown(err_no)), err_no => return Err(Error::Unknown(err_no)),
} }
} }
debug!(log, "EGL context successfully created");
let surface_attributes = {
let mut out: Vec<c_int> = Vec::with_capacity(3);
match reqs.double_buffer {
Some(true) => {
trace!(log, "Setting RENDER_BUFFER to BACK_BUFFER");
out.push(ffi::egl::RENDER_BUFFER as c_int);
out.push(ffi::egl::BACK_BUFFER as c_int);
}
Some(false) => {
trace!(log, "Setting RENDER_BUFFER to SINGLE_BUFFER");
out.push(ffi::egl::RENDER_BUFFER as c_int);
out.push(ffi::egl::SINGLE_BUFFER as c_int);
}
None => {}
}
out.push(ffi::egl::NONE as i32);
out
};
info!(log, "EGL context created"); info!(log, "EGL context created");
// make current and get list of gl extensions Ok(EGLContext {
ffi::egl::MakeCurrent(display as *const _, ptr::null(), ptr::null(), context as *const _); context: Arc::new(context as _),
display: Arc::downgrade(&display.display),
Ok((
Rc::new(context as *const _),
Rc::new(display as *const _),
config_id, config_id,
surface_attributes, pixel_format,
desc, logger: log,
extensions.iter().any(|s| *s == "EGL_WL_bind_wayland_display"),
))
}
/// Creates a surface for rendering
pub fn create_surface(&self, args: N::Arguments) -> Result<EGLSurface<B::Surface>, Error> {
trace!(self.logger, "Creating EGL window surface.");
let surface = self.native.borrow_mut().create_surface(args).map_err(|e| {
error!(self.logger, "EGL surface creation failed: {}", e);
Error::SurfaceCreationFailed
})?;
EGLSurface::new(self, surface).map(|x| {
debug!(self.logger, "EGL surface successfully created");
x
}) })
} }
/// Returns the address of an OpenGL function. /// Makes the OpenGL context the current context in the current thread with a surface to
/// read/write to.
/// ///
/// # Safety /// # Safety
/// ///
/// The context must have been made current before this function is called. /// This function is marked unsafe, because the context cannot be made current
pub unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { /// on multiple threads.
let addr = CString::new(symbol.as_bytes()).unwrap(); pub unsafe fn make_current_with_surface<N>(
let addr = addr.as_ptr(); &self,
ffi::egl::GetProcAddress(addr) as *const _ surface: &EGLSurface<N>,
) -> ::std::result::Result<(), SwapBuffersError>
where
N: NativeSurface,
{
if let Some(display) = self.display.upgrade() {
let surface_ptr = surface.surface.get();
let ret = ffi::egl::MakeCurrent(
(*display) as *const _,
surface_ptr as *const _,
surface_ptr as *const _,
(*self.context) as *const _,
);
if ret == 0 {
match ffi::egl::GetError() as u32 {
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err),
}
} else {
Ok(())
}
} else {
Err(SwapBuffersError::ContextLost)
}
}
/// Makes the OpenGL context the current context in the current thread with no surface bound.
///
/// # Safety
///
/// This function is marked unsafe, because the context cannot be made current
/// on multiple threads.
pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
if let Some(display) = self.display.upgrade() {
let surface_ptr = ptr::null();
let ret = ffi::egl::MakeCurrent(
(*display) as *const _,
surface_ptr as *const _,
surface_ptr as *const _,
(*self.context) as *const _,
);
if ret == 0 {
match ffi::egl::GetError() as u32 {
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err),
}
} else {
Ok(())
}
} else {
Err(SwapBuffersError::ContextLost)
}
} }
/// Returns true if the OpenGL context is the current one in the thread. /// Returns true if the OpenGL context is the current one in the thread.
@ -460,41 +193,25 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
unsafe { ffi::egl::GetCurrentContext() == (*self.context) as *const _ } unsafe { ffi::egl::GetCurrentContext() == (*self.context) as *const _ }
} }
/// Returns the egl config for this context
pub fn get_config_id(&self) -> ffi::egl::types::EGLConfig {
self.config_id
}
/// Returns the pixel format of the main framebuffer of the context. /// Returns the pixel format of the main framebuffer of the context.
pub fn get_pixel_format(&self) -> PixelFormat { pub fn get_pixel_format(&self) -> PixelFormat {
self.pixel_format self.pixel_format
} }
/// Borrow the underlying native display.
///
/// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell).
/// Multiple read-only borrows are possible. Borrowing the
/// backend while there is a mutable reference will panic.
pub fn borrow(&self) -> Ref<'_, N> {
self.native.borrow()
} }
/// Borrow the underlying native display mutably. impl Drop for EGLContext {
///
/// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell).
/// Holding any other borrow while trying to borrow the backend
/// mutably will panic. Note that EGL will borrow the display
/// mutably during surface creation.
pub fn borrow_mut(&self) -> RefMut<'_, N> {
self.native.borrow_mut()
}
}
unsafe impl<B: native::Backend, N: native::NativeDisplay<B> + Send> Send for EGLContext<B, N> {}
unsafe impl<B: native::Backend, N: native::NativeDisplay<B> + Sync> Sync for EGLContext<B, N> {}
impl<B: native::Backend, N: native::NativeDisplay<B>> Drop for EGLContext<B, N> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
// we don't call MakeCurrent(0, 0) because we are not sure that the context // we don't call MakeCurrent(0, 0) because we are not sure that the context
// is still the current one // is still the current one
ffi::egl::DestroyContext((*self.display) as *const _, (*self.context) as *const _); if let Some(display) = self.display.upgrade() {
ffi::egl::Terminate((*self.display) as *const _); ffi::egl::DestroyContext((*display) as *const _, (*self.context) as *const _);
}
} }
} }
} }

618
src/backend/egl/display.rs Normal file
View File

@ -0,0 +1,618 @@
//! Type safe native types for safe egl initialisation
#[cfg(feature = "use_system_lib")]
use crate::backend::egl::EGLGraphicsBackend;
use crate::backend::egl::{
ffi, get_proc_address, native, BufferAccessError, EGLContext, EGLImages, EGLSurface, Error, Format,
};
use std::sync::{Arc, Weak};
use std::ptr;
use nix::libc::{c_int, c_void};
#[cfg(feature = "wayland_frontend")]
use wayland_server::{protocol::wl_buffer::WlBuffer, Display};
#[cfg(feature = "use_system_lib")]
use wayland_sys::server::wl_display;
use crate::backend::egl::context::{GlAttributes, PixelFormatRequirements};
#[cfg(feature = "renderer_gl")]
use crate::backend::graphics::gl::ffi as gl_ffi;
use crate::backend::graphics::PixelFormat;
use std::cell::{Ref, RefCell, RefMut};
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
/// [`EGLDisplay`] represents an initialised EGL environment
pub struct EGLDisplay<B: native::Backend, N: native::NativeDisplay<B>> {
native: RefCell<N>,
pub(crate) display: Arc<ffi::egl::types::EGLDisplay>,
pub(crate) egl_version: (i32, i32),
pub(crate) extensions: Vec<String>,
logger: slog::Logger,
_backend: PhantomData<B>,
}
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
/// Create a new [`EGLDisplay`] from a given [`NativeDisplay`](native::NativeDisplay)
pub fn new<L>(native: N, logger: L) -> Result<EGLDisplay<B, N>, Error>
where
L: Into<Option<::slog::Logger>>,
{
let log = crate::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl"));
let ptr = native.ptr()?;
ffi::egl::LOAD.call_once(|| unsafe {
fn constrain<F>(f: F) -> F
where
F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void,
{
f
};
ffi::egl::load_with(|sym| {
let name = CString::new(sym).unwrap();
let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes());
match symbol {
Ok(x) => *x as *const _,
Err(_) => ptr::null(),
}
});
let proc_address = constrain(|sym| get_proc_address(sym));
ffi::egl::load_with(&proc_address);
ffi::egl::BindWaylandDisplayWL::load_with(&proc_address);
ffi::egl::UnbindWaylandDisplayWL::load_with(&proc_address);
ffi::egl::QueryWaylandBufferWL::load_with(&proc_address);
});
// the first step is to query the list of extensions without any display, if supported
let dp_extensions = unsafe {
let p = ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32);
// 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);
let display =
unsafe { B::get_display(ptr, |e: &str| dp_extensions.iter().any(|s| s == e), log.clone()) };
if display == ffi::egl::NO_DISPLAY {
error!(log, "EGL Display is not valid");
return Err(Error::DisplayNotSupported);
}
let egl_version = {
let mut major: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
let mut minor: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
if unsafe { ffi::egl::Initialize(display, major.as_mut_ptr(), minor.as_mut_ptr()) } == 0 {
return Err(Error::InitFailed);
}
let major = unsafe { major.assume_init() };
let minor = unsafe { minor.assume_init() };
info!(log, "EGL Initialized");
info!(log, "EGL Version: {:?}", (major, minor));
(major, minor)
};
// the list of extensions supported by the client once initialized is different from the
// list of extensions obtained earlier
let extensions = if egl_version >= (1, 2) {
let p = unsafe { CStr::from_ptr(ffi::egl::QueryString(display, 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<_>>()
} else {
vec![]
};
info!(log, "EGL Extensions: {:?}", extensions);
if egl_version >= (1, 2) && unsafe { ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) } == 0 {
error!(log, "OpenGLES not supported by the underlying EGL implementation");
return Err(Error::OpenGlesNotSupported);
}
Ok(EGLDisplay {
native: RefCell::new(native),
display: Arc::new(display as *const _),
egl_version,
extensions,
logger: log,
_backend: PhantomData,
})
}
/// Finds a compatible [`EGLConfig`] for a given set of requirements
pub unsafe fn choose_config(
&self,
version: (u8, u8),
reqs: PixelFormatRequirements,
) -> Result<(PixelFormat, ffi::egl::types::EGLConfig), Error> {
let descriptor = {
let mut out: Vec<c_int> = Vec::with_capacity(37);
if self.egl_version >= (1, 2) {
trace!(self.logger, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER");
out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int);
out.push(ffi::egl::RGB_BUFFER as c_int);
}
trace!(self.logger, "Setting SURFACE_TYPE to WINDOW");
out.push(ffi::egl::SURFACE_TYPE as c_int);
// TODO: Some versions of Mesa report a BAD_ATTRIBUTE error
// if we ask for PBUFFER_BIT as well as WINDOW_BIT
out.push((ffi::egl::WINDOW_BIT) as c_int);
match version {
(3, _) => {
if self.egl_version < (1, 3) {
error!(
self.logger,
"OpenglES 3.* is not supported on EGL Versions lower then 1.3"
);
return Err(Error::NoAvailablePixelFormat);
}
trace!(self.logger, "Setting RENDERABLE_TYPE to OPENGL_ES3");
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
trace!(self.logger, "Setting CONFORMANT to OPENGL_ES3");
out.push(ffi::egl::CONFORMANT as c_int);
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
}
(2, _) => {
if self.egl_version < (1, 3) {
error!(
self.logger,
"OpenglES 2.* is not supported on EGL Versions lower then 1.3"
);
return Err(Error::NoAvailablePixelFormat);
}
trace!(self.logger, "Setting RENDERABLE_TYPE to OPENGL_ES2");
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
trace!(self.logger, "Setting CONFORMANT to OPENGL_ES2");
out.push(ffi::egl::CONFORMANT as c_int);
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
}
(_, _) => unreachable!(),
};
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
out.push(ffi::egl::CONFIG_CAVEAT as c_int);
out.push(if hardware_accelerated {
trace!(self.logger, "Setting CONFIG_CAVEAT to NONE");
ffi::egl::NONE as c_int
} else {
trace!(self.logger, "Setting CONFIG_CAVEAT to SLOW_CONFIG");
ffi::egl::SLOW_CONFIG as c_int
});
}
if let Some(color) = reqs.color_bits {
trace!(self.logger, "Setting RED_SIZE to {}", color / 3);
out.push(ffi::egl::RED_SIZE as c_int);
out.push((color / 3) as c_int);
trace!(
self.logger,
"Setting GREEN_SIZE to {}",
color / 3 + if color % 3 != 0 { 1 } else { 0 }
);
out.push(ffi::egl::GREEN_SIZE as c_int);
out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
trace!(
self.logger,
"Setting BLUE_SIZE to {}",
color / 3 + if color % 3 == 2 { 1 } else { 0 }
);
out.push(ffi::egl::BLUE_SIZE as c_int);
out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
}
if let Some(alpha) = reqs.alpha_bits {
trace!(self.logger, "Setting ALPHA_SIZE to {}", alpha);
out.push(ffi::egl::ALPHA_SIZE as c_int);
out.push(alpha as c_int);
}
if let Some(depth) = reqs.depth_bits {
trace!(self.logger, "Setting DEPTH_SIZE to {}", depth);
out.push(ffi::egl::DEPTH_SIZE as c_int);
out.push(depth as c_int);
}
if let Some(stencil) = reqs.stencil_bits {
trace!(self.logger, "Setting STENCIL_SIZE to {}", stencil);
out.push(ffi::egl::STENCIL_SIZE as c_int);
out.push(stencil as c_int);
}
if let Some(multisampling) = reqs.multisampling {
trace!(self.logger, "Setting SAMPLES to {}", multisampling);
out.push(ffi::egl::SAMPLES as c_int);
out.push(multisampling as c_int);
}
if reqs.stereoscopy {
error!(self.logger, "Stereoscopy is currently unsupported (sorry!)");
return Err(Error::NoAvailablePixelFormat);
}
out.push(ffi::egl::NONE as c_int);
out
};
// calling `eglChooseConfig`
let mut config_id = MaybeUninit::uninit();
let mut num_configs = MaybeUninit::uninit();
if ffi::egl::ChooseConfig(
*self.display,
descriptor.as_ptr(),
config_id.as_mut_ptr(),
1,
num_configs.as_mut_ptr(),
) == 0
{
return Err(Error::ConfigFailed);
}
let config_id = config_id.assume_init();
let num_configs = num_configs.assume_init();
if num_configs == 0 {
error!(self.logger, "No matching color format found");
return Err(Error::NoAvailablePixelFormat);
}
// TODO: Filter configs for matching vsync property
// analyzing each config
macro_rules! attrib {
($display:expr, $config:expr, $attr:expr) => {{
let mut value = MaybeUninit::uninit();
let res = ffi::egl::GetConfigAttrib(
*$display,
$config,
$attr as ffi::egl::types::EGLint,
value.as_mut_ptr(),
);
if res == 0 {
return Err(Error::ConfigFailed);
}
value.assume_init()
}};
};
let desc = PixelFormat {
hardware_accelerated: attrib!(self.display, config_id, ffi::egl::CONFIG_CAVEAT)
!= ffi::egl::SLOW_CONFIG as i32,
color_bits: attrib!(self.display, config_id, ffi::egl::RED_SIZE) as u8
+ attrib!(self.display, config_id, ffi::egl::BLUE_SIZE) as u8
+ attrib!(self.display, config_id, ffi::egl::GREEN_SIZE) as u8,
alpha_bits: attrib!(self.display, config_id, ffi::egl::ALPHA_SIZE) as u8,
depth_bits: attrib!(self.display, config_id, ffi::egl::DEPTH_SIZE) as u8,
stencil_bits: attrib!(self.display, config_id, ffi::egl::STENCIL_SIZE) as u8,
stereoscopy: false,
multisampling: match attrib!(self.display, config_id, ffi::egl::SAMPLES) {
0 | 1 => None,
a => Some(a as u16),
},
srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that
};
info!(self.logger, "Selected color format: {:?}", desc);
Ok((desc, config_id))
}
/// Create a new [`EGLContext`](::backend::egl::EGLContext)
pub fn create_context(
&self,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
) -> Result<EGLContext, Error> {
EGLContext::new(&self, attributes, reqs, self.logger.clone())
}
/// Creates a surface for rendering
pub fn create_surface(
&self,
pixel_format: PixelFormat,
double_buffer: Option<bool>,
config: ffi::egl::types::EGLConfig,
args: N::Arguments,
) -> Result<EGLSurface<B::Surface>, Error> {
trace!(self.logger, "Creating EGL window surface.");
let surface = self.native.borrow_mut().create_surface(args).map_err(|e| {
error!(self.logger, "EGL surface creation failed: {}", e);
Error::SurfaceCreationFailed
})?;
EGLSurface::new(
&self.display,
pixel_format,
double_buffer,
config,
surface,
self.logger.clone(),
)
.map(|x| {
debug!(self.logger, "EGL surface successfully created");
x
})
}
/// Returns the runtime egl version of this display
pub fn get_egl_version(&self) -> (i32, i32) {
self.egl_version
}
/// Returns the supported extensions of this display
pub fn get_extensions(&self) -> Vec<String> {
self.extensions.clone()
}
/// Borrow the underlying native display.
///
/// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell).
/// Multiple read-only borrows are possible. Borrowing the
/// backend while there is a mutable reference will panic.
pub fn borrow(&self) -> Ref<'_, N> {
self.native.borrow()
}
/// Borrow the underlying native display mutably.
///
/// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell).
/// Holding any other borrow while trying to borrow the backend
/// mutably will panic. Note that EGL will borrow the display
/// mutably during surface creation.
pub fn borrow_mut(&self) -> RefMut<'_, N> {
self.native.borrow_mut()
}
}
impl<B: native::Backend, N: native::NativeDisplay<B>> Drop for EGLDisplay<B, N> {
fn drop(&mut self) {
unsafe {
ffi::egl::Terminate((*self.display) as *const _);
}
}
}
#[cfg(feature = "use_system_lib")]
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLGraphicsBackend for EGLDisplay<B, N> {
/// Binds this EGL display to the given Wayland display.
///
/// This will allow clients to utilize EGL to create hardware-accelerated
/// surfaces. The server will need to be able to handle EGL-[`WlBuffer`]s.
///
/// ## Errors
///
/// This might return [`EglExtensionNotSupported`](ErrorKind::EglExtensionNotSupported)
/// if binding is not supported by the EGL implementation.
///
/// This might return [`OtherEGLDisplayAlreadyBound`](ErrorKind::OtherEGLDisplayAlreadyBound)
/// if called for the same [`Display`] multiple times, as only one egl display may be bound at any given time.
fn bind_wl_display(&self, display: &Display) -> Result<WaylandEGLDisplay, Error> {
if !self.extensions.iter().any(|s| s == "EGL_WL_bind_wayland_display") {
return Err(Error::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
}
let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.c_ptr() as *mut _) };
if res == 0 {
return Err(Error::OtherEGLDisplayAlreadyBound);
}
Ok(WaylandEGLDisplay::new(
Arc::downgrade(&self.display),
display.c_ptr(),
&self.extensions,
))
}
}
/// Type to receive [`EGLImages`] for EGL-based [`WlBuffer`]s.
///
/// Can be created by using [`EGLGraphicsBackend::bind_wl_display`].
#[cfg(feature = "use_system_lib")]
pub struct WaylandEGLDisplay {
display: Weak<ffi::egl::types::EGLDisplay>,
wayland: *mut wl_display,
#[cfg(feature = "renderer_gl")]
gl: gl_ffi::Gles2,
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: bool,
}
#[cfg(feature = "use_system_lib")]
impl WaylandEGLDisplay {
fn new(
display: Weak<ffi::egl::types::EGLDisplay>,
wayland: *mut wl_display,
extensions: &Vec<String>,
) -> Self {
#[cfg(feature = "renderer_gl")]
let gl = gl_ffi::Gles2::load_with(|s| unsafe { get_proc_address(s) as *const _ });
Self {
display,
wayland,
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: extensions
.iter()
.any(|s| s == "GL_OES_EGL_image" || s == "GL_OES_EGL_image_base"),
#[cfg(feature = "renderer_gl")]
gl,
}
}
/// Try to receive [`EGLImages`] from a given [`WlBuffer`].
///
/// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm))
/// a [`BufferAccessError::NotManaged`](::backend::egl::BufferAccessError::NotManaged) is returned with the original buffer
/// to render it another way.
pub fn egl_buffer_contents(
&self,
buffer: WlBuffer,
) -> ::std::result::Result<EGLImages, BufferAccessError> {
if let Some(display) = self.display.upgrade() {
let mut format: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::EGL_TEXTURE_FORMAT,
&mut format as *mut _,
) == 0
} {
return Err(BufferAccessError::NotManaged(buffer));
}
let format = match format {
x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB,
x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA,
ffi::egl::TEXTURE_EXTERNAL_WL => Format::External,
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"),
};
let mut width: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::WIDTH as i32,
&mut width as *mut _,
) == 0
} {
return Err(BufferAccessError::NotManaged(buffer));
}
let mut height: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::HEIGHT as i32,
&mut height as *mut _,
) == 0
} {
return Err(BufferAccessError::NotManaged(buffer));
}
let mut inverted: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::WAYLAND_Y_INVERTED_WL,
&mut inverted as *mut _,
) != 0
} {
inverted = 1;
}
let mut images = Vec::with_capacity(format.num_planes());
for i in 0..format.num_planes() {
let mut out = Vec::with_capacity(3);
out.push(ffi::egl::WAYLAND_PLANE_WL as i32);
out.push(i as i32);
out.push(ffi::egl::NONE as i32);
images.push({
let image = unsafe {
ffi::egl::CreateImageKHR(
*display,
ffi::egl::NO_CONTEXT,
ffi::egl::WAYLAND_BUFFER_WL,
buffer.as_ref().c_ptr() as *mut _,
out.as_ptr(),
)
};
if image == ffi::egl::NO_IMAGE_KHR {
return Err(BufferAccessError::EGLImageCreationFailed);
} else {
image
}
});
}
Ok(EGLImages {
display: Arc::downgrade(&display),
width: width as u32,
height: height as u32,
y_inverted: inverted != 0,
format,
images,
buffer,
#[cfg(feature = "renderer_gl")]
gl: self.gl.clone(),
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: self.egl_to_texture_support,
})
} else {
Err(BufferAccessError::ContextLost)
}
}
/// Try to receive the dimensions of a given [`WlBuffer`].
///
/// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm)) or the
/// context has been lost, `None` is returned.
pub fn egl_buffer_dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
if let Some(display) = self.display.upgrade() {
let mut width: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::WIDTH as i32,
&mut width as *mut _,
) == 0
} {
return None;
}
let mut height: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::HEIGHT as i32,
&mut height as *mut _,
) == 0
} {
return None;
}
Some((width, height))
} else {
None
}
}
}
#[cfg(feature = "use_system_lib")]
impl Drop for WaylandEGLDisplay {
fn drop(&mut self) {
if let Some(display) = self.display.upgrade() {
if !self.wayland.is_null() {
unsafe {
ffi::egl::UnbindWaylandDisplayWL(*display, self.wayland as *mut _);
}
}
}
}
}

View File

@ -21,28 +21,29 @@
#[cfg(feature = "renderer_gl")] #[cfg(feature = "renderer_gl")]
use crate::backend::graphics::gl::ffi as gl_ffi; use crate::backend::graphics::gl::ffi as gl_ffi;
use nix::libc::c_uint; use nix::libc::c_uint;
use std::{ use std::fmt;
ffi::CStr,
fmt,
rc::{Rc, Weak},
};
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
use wayland_server::{protocol::wl_buffer::WlBuffer, Display}; use wayland_server::{protocol::wl_buffer::WlBuffer, Display};
#[cfg(feature = "use_system_lib")]
use wayland_sys::server::wl_display;
pub mod context; pub mod context;
pub use self::context::EGLContext; pub use self::context::EGLContext;
mod error; mod error;
pub use self::error::Error; pub use self::error::Error;
use nix::libc::c_void;
#[allow(non_camel_case_types, dead_code, unused_mut, non_upper_case_globals)] #[allow(non_camel_case_types, dead_code, unused_mut, non_upper_case_globals)]
pub mod ffi; pub mod ffi;
use self::ffi::egl::types::EGLImage; use self::ffi::egl::types::EGLImage;
pub mod display;
pub mod native; pub mod native;
pub mod surface; pub mod surface;
pub use self::surface::EGLSurface; pub use self::surface::EGLSurface;
#[cfg(feature = "use_system_lib")]
use crate::backend::egl::display::WaylandEGLDisplay;
use std::ffi::CString;
use std::sync::Weak;
/// Error that can happen on optional EGL features /// Error that can happen on optional EGL features
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -61,6 +62,15 @@ impl fmt::Display for EglExtensionNotSupportedError {
impl ::std::error::Error for EglExtensionNotSupportedError {} impl ::std::error::Error for EglExtensionNotSupportedError {}
/// Returns the address of an OpenGL function.
///
/// Result is independent of displays and does not guarantee an extension is actually supported at runtime.
pub unsafe fn get_proc_address(symbol: &str) -> *const c_void {
let addr = CString::new(symbol.as_bytes()).unwrap();
let addr = addr.as_ptr();
ffi::egl::GetProcAddress(addr) as *const _
}
/// Error that can occur when accessing an EGL buffer /// Error that can occur when accessing an EGL buffer
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
#[derive(thiserror::Error)] #[derive(thiserror::Error)]
@ -234,7 +244,7 @@ impl Drop for EGLImages {
} }
/// Trait any backend type may implement that allows binding a [`Display`](wayland_server::Display) /// Trait any backend type may implement that allows binding a [`Display`](wayland_server::Display)
/// to create an [`EGLDisplay`] for EGL-based [`WlBuffer`]s. /// to create an [`WaylandDisplay`](display::WaylandDisplay) for EGL-based [`WlBuffer`]s.
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
pub trait EGLGraphicsBackend { pub trait EGLGraphicsBackend {
/// Binds this EGL context to the given Wayland display. /// Binds this EGL context to the given Wayland display.
@ -249,226 +259,5 @@ pub trait EGLGraphicsBackend {
/// ///
/// This might return [`OtherEGLDisplayAlreadyBound`](ErrorKind::OtherEGLDisplayAlreadyBound) /// This might return [`OtherEGLDisplayAlreadyBound`](ErrorKind::OtherEGLDisplayAlreadyBound)
/// if called for the same [`Display`] multiple times, as only one context may be bound at any given time. /// if called for the same [`Display`] multiple times, as only one context may be bound at any given time.
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay, Error>; fn bind_wl_display(&self, display: &Display) -> Result<WaylandEGLDisplay, Error>;
}
/// Type to receive [`EGLImages`] for EGL-based [`WlBuffer`]s.
///
/// Can be created by using [`EGLGraphicsBackend::bind_wl_display`].
#[cfg(feature = "use_system_lib")]
pub struct EGLDisplay {
egl: Weak<ffi::egl::types::EGLDisplay>,
wayland: *mut wl_display,
#[cfg(feature = "renderer_gl")]
gl: gl_ffi::Gles2,
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: bool,
}
#[cfg(feature = "use_system_lib")]
impl EGLDisplay {
fn new<B: native::Backend, N: native::NativeDisplay<B>>(
context: &EGLContext<B, N>,
display: *mut wl_display,
) -> EGLDisplay {
#[cfg(feature = "renderer_gl")]
let gl = gl_ffi::Gles2::load_with(|s| unsafe { context.get_proc_address(s) as *const _ });
EGLDisplay {
egl: Rc::downgrade(&context.display),
wayland: display,
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: {
// the list of gl extensions supported by the context
let data = unsafe { CStr::from_ptr(gl.GetString(gl_ffi::EXTENSIONS) as *const _) }
.to_bytes()
.to_vec();
let list = String::from_utf8(data).unwrap();
list.split(' ')
.any(|s| s == "GL_OES_EGL_image" || s == "GL_OES_EGL_image_base")
},
#[cfg(feature = "renderer_gl")]
gl,
}
}
/// Try to receive [`EGLImages`] from a given [`WlBuffer`].
///
/// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm))
/// a [`BufferAccessError::NotManaged`](::backend::egl::BufferAccessError::NotManaged) is returned with the original buffer
/// to render it another way.
pub fn egl_buffer_contents(
&self,
buffer: WlBuffer,
) -> ::std::result::Result<EGLImages, BufferAccessError> {
if let Some(display) = self.egl.upgrade() {
let mut format: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::EGL_TEXTURE_FORMAT,
&mut format as *mut _,
) == 0
} {
return Err(BufferAccessError::NotManaged(buffer));
}
let format = match format {
x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB,
x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA,
ffi::egl::TEXTURE_EXTERNAL_WL => Format::External,
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"),
};
let mut width: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::WIDTH as i32,
&mut width as *mut _,
) == 0
} {
return Err(BufferAccessError::NotManaged(buffer));
}
let mut height: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::HEIGHT as i32,
&mut height as *mut _,
) == 0
} {
return Err(BufferAccessError::NotManaged(buffer));
}
let mut inverted: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::WAYLAND_Y_INVERTED_WL,
&mut inverted as *mut _,
) != 0
} {
inverted = 1;
}
let mut images = Vec::with_capacity(format.num_planes());
for i in 0..format.num_planes() {
let mut out = Vec::with_capacity(3);
out.push(ffi::egl::WAYLAND_PLANE_WL as i32);
out.push(i as i32);
out.push(ffi::egl::NONE as i32);
images.push({
let image = unsafe {
ffi::egl::CreateImageKHR(
*display,
ffi::egl::NO_CONTEXT,
ffi::egl::WAYLAND_BUFFER_WL,
buffer.as_ref().c_ptr() as *mut _,
out.as_ptr(),
)
};
if image == ffi::egl::NO_IMAGE_KHR {
return Err(BufferAccessError::EGLImageCreationFailed);
} else {
image
}
});
}
Ok(EGLImages {
display: Rc::downgrade(&display),
width: width as u32,
height: height as u32,
y_inverted: inverted != 0,
format,
images,
buffer,
#[cfg(feature = "renderer_gl")]
gl: self.gl.clone(),
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: self.egl_to_texture_support,
})
} else {
Err(BufferAccessError::ContextLost)
}
}
/// Try to receive the dimensions of a given [`WlBuffer`].
///
/// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm)) or the
/// context has been lost, `None` is returned.
pub fn egl_buffer_dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
if let Some(display) = self.egl.upgrade() {
let mut width: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::WIDTH as i32,
&mut width as *mut _,
) == 0
} {
return None;
}
let mut height: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::HEIGHT as i32,
&mut height as *mut _,
) == 0
} {
return None;
}
Some((width, height))
} else {
None
}
}
}
#[cfg(feature = "use_system_lib")]
impl Drop for EGLDisplay {
fn drop(&mut self) {
if let Some(display) = self.egl.upgrade() {
if !self.wayland.is_null() {
unsafe {
ffi::egl::UnbindWaylandDisplayWL(*display, self.wayland as *mut _);
}
}
}
}
}
#[cfg(feature = "use_system_lib")]
impl<E: EGLGraphicsBackend> EGLGraphicsBackend for Rc<E> {
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay, Error> {
(**self).bind_wl_display(display)
}
}
#[cfg(feature = "use_system_lib")]
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLGraphicsBackend for EGLContext<B, N> {
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay, Error> {
if !self.wl_drm_support {
return Err(Error::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
}
let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.c_ptr() as *mut _) };
if res == 0 {
return Err(Error::OtherEGLDisplayAlreadyBound);
}
Ok(EGLDisplay::new(self, display.c_ptr()))
}
} }

View File

@ -1,21 +1,21 @@
//! EGL surface related structs //! EGL surface related structs
use super::{ffi, native, EGLContext, Error}; use super::{ffi, native, Error};
use crate::backend::graphics::SwapBuffersError; use crate::backend::graphics::{PixelFormat, SwapBuffersError};
use nix::libc::c_int; use nix::libc::c_int;
use std::sync::{Arc, Weak};
use std::{ use std::{
cell::Cell, cell::Cell,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
rc::{Rc, Weak},
}; };
/// EGL surface of a given EGL context for rendering /// EGL surface of a given EGL context for rendering
pub struct EGLSurface<N: native::NativeSurface> { pub struct EGLSurface<N: native::NativeSurface> {
context: Weak<ffi::egl::types::EGLContext>,
display: Weak<ffi::egl::types::EGLDisplay>, display: Weak<ffi::egl::types::EGLDisplay>,
native: N, native: N,
surface: Cell<ffi::egl::types::EGLSurface>, pub(crate) surface: Cell<ffi::egl::types::EGLSurface>,
config_id: ffi::egl::types::EGLConfig, config_id: ffi::egl::types::EGLConfig,
pixel_format: PixelFormat,
surface_attributes: Vec<c_int>, surface_attributes: Vec<c_int>,
} }
@ -33,17 +33,42 @@ impl<N: native::NativeSurface> DerefMut for EGLSurface<N> {
} }
impl<N: native::NativeSurface> EGLSurface<N> { impl<N: native::NativeSurface> EGLSurface<N> {
pub(crate) fn new<B: native::Backend<Surface = N>, D: native::NativeDisplay<B>>( pub(crate) fn new<L>(
context: &EGLContext<B, D>, display: &Arc<ffi::egl::types::EGLDisplay>,
pixel_format: PixelFormat,
double_buffered: Option<bool>,
config: ffi::egl::types::EGLConfig,
native: N, native: N,
) -> Result<EGLSurface<N>, Error> { log: L,
) -> Result<EGLSurface<N>, Error>
where
L: Into<Option<::slog::Logger>>,
{
let log = crate::slog_or_stdlog(log.into()).new(o!("smithay_module" => "renderer_egl"));
let surface_attributes = {
let mut out: Vec<c_int> = Vec::with_capacity(3);
match double_buffered {
Some(true) => {
trace!(log, "Setting RENDER_BUFFER to BACK_BUFFER");
out.push(ffi::egl::RENDER_BUFFER as c_int);
out.push(ffi::egl::BACK_BUFFER as c_int);
}
Some(false) => {
trace!(log, "Setting RENDER_BUFFER to SINGLE_BUFFER");
out.push(ffi::egl::RENDER_BUFFER as c_int);
out.push(ffi::egl::SINGLE_BUFFER as c_int);
}
None => {}
}
out.push(ffi::egl::NONE as i32);
out
};
let surface = unsafe { let surface = unsafe {
ffi::egl::CreateWindowSurface( ffi::egl::CreateWindowSurface(**display, config, native.ptr(), surface_attributes.as_ptr())
*context.display,
context.config_id,
native.ptr(),
context.surface_attributes.as_ptr(),
)
}; };
if surface.is_null() { if surface.is_null() {
@ -51,12 +76,12 @@ impl<N: native::NativeSurface> EGLSurface<N> {
} }
Ok(EGLSurface { Ok(EGLSurface {
context: Rc::downgrade(&context.context), display: Arc::downgrade(display),
display: Rc::downgrade(&context.display),
native, native,
surface: Cell::new(surface), surface: Cell::new(surface),
config_id: context.config_id, config_id: config,
surface_attributes: context.surface_attributes.clone(), pixel_format,
surface_attributes,
}) })
} }
@ -98,44 +123,22 @@ impl<N: native::NativeSurface> EGLSurface<N> {
Ok(()) Ok(())
} }
/// Makes the OpenGL context the current context in the current thread.
///
/// # Safety
///
/// This function is marked unsafe, because the context cannot be made current
/// on multiple threads.
pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
if let (Some(display), Some(context)) = (self.display.upgrade(), self.context.upgrade()) {
let ret = ffi::egl::MakeCurrent(
(*display) as *const _,
self.surface.get() as *const _,
self.surface.get() as *const _,
(*context) as *const _,
);
if ret == 0 {
match ffi::egl::GetError() as u32 {
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err),
}
} else {
Ok(())
}
} else {
Err(SwapBuffersError::ContextLost)
}
}
/// Returns true if the OpenGL surface is the current one in the thread. /// Returns true if the OpenGL surface is the current one in the thread.
pub fn is_current(&self) -> bool { pub fn is_current(&self) -> bool {
if self.context.upgrade().is_some() {
unsafe { unsafe {
ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _ ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _
&& ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _ && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _
} }
} else {
false
} }
/// Returns the egl config for this context
pub fn get_config_id(&self) -> ffi::egl::types::EGLConfig {
self.config_id
}
/// Returns the pixel format of the main framebuffer of the context.
pub fn get_pixel_format(&self) -> PixelFormat {
self.pixel_format
} }
} }

View File

@ -13,8 +13,6 @@ pub struct PixelFormat {
pub stencil_bits: u8, pub stencil_bits: u8,
/// is stereoscopy enabled /// is stereoscopy enabled
pub stereoscopy: bool, pub stereoscopy: bool,
/// is double buffering enabled
pub double_buffer: bool,
/// number of samples used for multisampling if enabled /// number of samples used for multisampling if enabled
pub multisampling: Option<u16>, pub multisampling: Option<u16>,
/// is srgb enabled /// is srgb enabled

View File

@ -1,10 +1,9 @@
//! Implementation of backend traits for types provided by `winit` //! Implementation of backend traits for types provided by `winit`
use crate::backend::egl::display::EGLDisplay;
use crate::backend::egl::get_proc_address;
use crate::backend::{ use crate::backend::{
egl::{ egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError},
context::GlAttributes, native, EGLContext, EGLDisplay, EGLGraphicsBackend, EGLSurface,
Error as EGLError,
},
graphics::{gl::GLGraphicsBackend, CursorBackend, PixelFormat, SwapBuffersError}, graphics::{gl::GLGraphicsBackend, CursorBackend, PixelFormat, SwapBuffersError},
input::{ input::{
Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent,
@ -33,6 +32,9 @@ use winit::{
window::{CursorIcon, Window as WinitWindow, WindowBuilder}, window::{CursorIcon, Window as WinitWindow, WindowBuilder},
}; };
#[cfg(feature = "use_system_lib")]
use crate::backend::egl::{display::WaylandEGLDisplay, EGLGraphicsBackend};
/// Errors thrown by the `winit` backends /// Errors thrown by the `winit` backends
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum Error { pub enum Error {
@ -49,11 +51,13 @@ pub enum Error {
enum Window { enum Window {
Wayland { Wayland {
context: EGLContext<native::Wayland, WinitWindow>, display: EGLDisplay<native::Wayland, WinitWindow>,
context: EGLContext,
surface: EGLSurface<wegl::WlEglSurface>, surface: EGLSurface<wegl::WlEglSurface>,
}, },
X11 { X11 {
context: EGLContext<native::X11, WinitWindow>, display: EGLDisplay<native::X11, WinitWindow>,
context: EGLContext,
surface: EGLSurface<native::XlibWindow>, surface: EGLSurface<native::XlibWindow>,
}, },
} }
@ -61,8 +65,8 @@ enum Window {
impl Window { impl Window {
fn window(&self) -> Ref<'_, WinitWindow> { fn window(&self) -> Ref<'_, WinitWindow> {
match *self { match *self {
Window::Wayland { ref context, .. } => context.borrow(), Window::Wayland { ref display, .. } => display.borrow(),
Window::X11 { ref context, .. } => context.borrow(), Window::X11 { ref display, .. } => display.borrow(),
} }
} }
} }
@ -158,15 +162,33 @@ where
let reqs = Default::default(); let reqs = Default::default();
let window = Rc::new( let window = Rc::new(
if native::NativeDisplay::<native::Wayland>::is_backend(&winit_window) { if native::NativeDisplay::<native::Wayland>::is_backend(&winit_window) {
let context = let display = EGLDisplay::<native::Wayland, WinitWindow>::new(winit_window, log.clone())?;
EGLContext::<native::Wayland, WinitWindow>::new(winit_window, attributes, reqs, log.clone())?; let context = display.create_context(attributes, reqs)?;
let surface = context.create_surface(())?; let surface = display.create_surface(
Window::Wayland { context, surface } context.get_pixel_format(),
reqs.double_buffer,
context.get_config_id(),
(),
)?;
Window::Wayland {
display,
context,
surface,
}
} else if native::NativeDisplay::<native::X11>::is_backend(&winit_window) { } else if native::NativeDisplay::<native::X11>::is_backend(&winit_window) {
let context = let display = EGLDisplay::<native::X11, WinitWindow>::new(winit_window, log.clone())?;
EGLContext::<native::X11, WinitWindow>::new(winit_window, attributes, reqs, log.clone())?; let context = display.create_context(attributes, reqs)?;
let surface = context.create_surface(())?; let surface = display.create_surface(
Window::X11 { context, surface } context.get_pixel_format(),
reqs.double_buffer,
context.get_config_id(),
(),
)?;
Window::X11 {
display,
context,
surface,
}
} else { } else {
return Err(Error::NotSupported); return Err(Error::NotSupported);
}, },
@ -265,10 +287,7 @@ impl GLGraphicsBackend for WinitGraphicsBackend {
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
trace!(self.logger, "Getting symbol for {:?}", symbol); trace!(self.logger, "Getting symbol for {:?}", symbol);
match *self.window { get_proc_address(symbol)
Window::Wayland { ref context, .. } => context.get_proc_address(symbol),
Window::X11 { ref context, .. } => context.get_proc_address(symbol),
}
} }
fn get_framebuffer_dimensions(&self) -> (u32, u32) { fn get_framebuffer_dimensions(&self) -> (u32, u32) {
@ -281,10 +300,12 @@ impl GLGraphicsBackend for WinitGraphicsBackend {
Window::Wayland { Window::Wayland {
ref context, ref context,
ref surface, ref surface,
..
} => context.is_current() && surface.is_current(), } => context.is_current() && surface.is_current(),
Window::X11 { Window::X11 {
ref context, ref context,
ref surface, ref surface,
..
} => context.is_current() && surface.is_current(), } => context.is_current() && surface.is_current(),
} }
} }
@ -292,24 +313,33 @@ impl GLGraphicsBackend for WinitGraphicsBackend {
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
trace!(self.logger, "Setting EGL context to be the current context"); trace!(self.logger, "Setting EGL context to be the current context");
match *self.window { match *self.window {
Window::Wayland { ref surface, .. } => surface.make_current(), Window::Wayland {
Window::X11 { ref surface, .. } => surface.make_current(), ref surface,
ref context,
..
} => context.make_current_with_surface(surface),
Window::X11 {
ref surface,
ref context,
..
} => context.make_current_with_surface(surface),
} }
} }
fn get_pixel_format(&self) -> PixelFormat { fn get_pixel_format(&self) -> PixelFormat {
match *self.window { match *self.window {
Window::Wayland { ref context, .. } => context.get_pixel_format(), Window::Wayland { ref surface, .. } => surface.get_pixel_format(),
Window::X11 { ref context, .. } => context.get_pixel_format(), Window::X11 { ref surface, .. } => surface.get_pixel_format(),
} }
} }
} }
#[cfg(feature = "use_system_lib")]
impl EGLGraphicsBackend for WinitGraphicsBackend { impl EGLGraphicsBackend for WinitGraphicsBackend {
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay, EGLError> { fn bind_wl_display(&self, wl_display: &Display) -> Result<WaylandEGLDisplay, EGLError> {
match *self.window { match *self.window {
Window::Wayland { ref context, .. } => context.bind_wl_display(display), Window::Wayland { ref display, .. } => display.bind_wl_display(wl_display),
Window::X11 { ref context, .. } => context.bind_wl_display(display), Window::X11 { ref display, .. } => display.bind_wl_display(wl_display),
} }
} }
} }