Merge pull request #188 from csnewman/egl-refactor
Refactor EGL backend
This commit is contained in:
commit
8678738d01
|
@ -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::EGLBufferReader;
|
||||||
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,15 +13,18 @@ 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_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
||||||
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_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>, log: Logger) -> Self {
|
||||||
Self { egl_display, log }
|
Self {
|
||||||
|
egl_buffer_reader,
|
||||||
|
log,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `BufferUtils`.
|
/// Creates a new `BufferUtils`.
|
||||||
|
@ -34,7 +37,7 @@ impl BufferUtils {
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
|
pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
|
||||||
// Try to retrieve the EGL dimensions of this buffer, and, if that fails, the shm dimensions.
|
// Try to retrieve the EGL dimensions of this buffer, and, if that fails, the shm dimensions.
|
||||||
self.egl_display
|
self.egl_buffer_reader
|
||||||
.borrow()
|
.borrow()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|display| display.egl_buffer_dimensions(buffer))
|
.and_then(|display| display.egl_buffer_dimensions(buffer))
|
||||||
|
|
|
@ -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::EGLBufferReader;
|
||||||
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_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
||||||
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_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
||||||
|
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
|
||||||
|
@ -94,7 +98,7 @@ impl<T: Into<GliumGraphicsBackend<T>> + GLGraphicsBackend + 'static> GliumDrawer
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
index_buffer,
|
index_buffer,
|
||||||
programs,
|
programs,
|
||||||
egl_display,
|
egl_buffer_reader,
|
||||||
log,
|
log,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +151,7 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
pub fn texture_from_buffer(&self, buffer: wl_buffer::WlBuffer) -> Result<TextureMetadata, ()> {
|
pub fn texture_from_buffer(&self, buffer: wl_buffer::WlBuffer) -> Result<TextureMetadata, ()> {
|
||||||
// try to retrieve the egl contents of this buffer
|
// try to retrieve the egl contents of this buffer
|
||||||
let images = if let Some(display) = &self.egl_display.borrow().as_ref() {
|
let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() {
|
||||||
display.egl_buffer_contents(buffer)
|
display.egl_buffer_contents(buffer)
|
||||||
} else {
|
} else {
|
||||||
Err(BufferAccessError::NotManaged(buffer))
|
Err(BufferAccessError::NotManaged(buffer))
|
||||||
|
|
|
@ -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::EGLBufferReader, 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();
|
||||||
|
@ -85,10 +85,10 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<AnvilState>, log
|
||||||
::std::env::set_var("WAYLAND_DISPLAY", name);
|
::std::env::set_var("WAYLAND_DISPLAY", name);
|
||||||
|
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
let active_egl_context = Rc::new(RefCell::new(None));
|
let egl_buffer_reader = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
let buffer_utils = BufferUtils::new(active_egl_context.clone(), log.clone());
|
let buffer_utils = BufferUtils::new(egl_buffer_reader.clone(), log.clone());
|
||||||
#[cfg(not(feature = "egl"))]
|
#[cfg(not(feature = "egl"))]
|
||||||
let buffer_utils = BufferUtils::new(log.clone());
|
let buffer_utils = BufferUtils::new(log.clone());
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop<AnvilState>, log
|
||||||
UdevHandlerImpl {
|
UdevHandlerImpl {
|
||||||
compositor_token,
|
compositor_token,
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
active_egl_context,
|
egl_buffer_reader,
|
||||||
session: session.clone(),
|
session: session.clone(),
|
||||||
backends: HashMap::new(),
|
backends: HashMap::new(),
|
||||||
display: display.clone(),
|
display: display.clone(),
|
||||||
|
@ -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>>>,
|
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
||||||
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_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
||||||
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):
|
||||||
|
@ -343,7 +343,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
|
||||||
if let Entry::Vacant(entry) = backends.entry(crtc) {
|
if let Entry::Vacant(entry) = backends.entry(crtc) {
|
||||||
let renderer = GliumDrawer::init(
|
let renderer = GliumDrawer::init(
|
||||||
device.create_surface(crtc).unwrap(),
|
device.create_surface(crtc).unwrap(),
|
||||||
egl_display.clone(),
|
egl_buffer_reader.clone(),
|
||||||
logger.clone(),
|
logger.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -419,7 +419,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandler for UdevHandlerImpl<S, Data>
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
{
|
{
|
||||||
if path.canonicalize().ok() == self.primary_gpu {
|
if path.canonicalize().ok() == self.primary_gpu {
|
||||||
*self.active_egl_context.borrow_mut() =
|
*self.egl_buffer_reader.borrow_mut() =
|
||||||
device.bind_wl_display(&*self.display.borrow()).ok();
|
device.bind_wl_display(&*self.display.borrow()).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,7 +427,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandler for UdevHandlerImpl<S, Data>
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
let backends = Rc::new(RefCell::new(UdevHandlerImpl::<S, Data>::scan_connectors(
|
let backends = Rc::new(RefCell::new(UdevHandlerImpl::<S, Data>::scan_connectors(
|
||||||
&mut device,
|
&mut device,
|
||||||
self.active_egl_context.clone(),
|
self.egl_buffer_reader.clone(),
|
||||||
&self.logger,
|
&self.logger,
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
@ -491,7 +491,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandler for UdevHandlerImpl<S, Data>
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
let new_backends = UdevHandlerImpl::<S, Data>::scan_connectors(
|
let new_backends = UdevHandlerImpl::<S, Data>::scan_connectors(
|
||||||
&mut (*evented).0,
|
&mut (*evented).0,
|
||||||
self.active_egl_context.clone(),
|
self.egl_buffer_reader.clone(),
|
||||||
&self.logger,
|
&self.logger,
|
||||||
);
|
);
|
||||||
#[cfg(not(feature = "egl"))]
|
#[cfg(not(feature = "egl"))]
|
||||||
|
@ -532,7 +532,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandler for UdevHandlerImpl<S, Data>
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
{
|
{
|
||||||
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
||||||
*self.active_egl_context.borrow_mut() = None;
|
*self.egl_buffer_reader.borrow_mut() = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,10 +37,10 @@ pub fn run_winit(
|
||||||
let (renderer, mut input) = winit::init(log.clone()).map_err(|_| ())?;
|
let (renderer, mut input) = winit::init(log.clone()).map_err(|_| ())?;
|
||||||
|
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
let egl_display = Rc::new(RefCell::new(
|
let egl_buffer_reader = Rc::new(RefCell::new(
|
||||||
if let Ok(egl_display) = renderer.bind_wl_display(&display) {
|
if let Ok(egl_buffer_reader) = renderer.bind_wl_display(&display) {
|
||||||
info!(log, "EGL hardware-acceleration enabled");
|
info!(log, "EGL hardware-acceleration enabled");
|
||||||
Some(egl_display)
|
Some(egl_buffer_reader)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
@ -48,12 +48,12 @@ pub fn run_winit(
|
||||||
|
|
||||||
let (w, h) = renderer.get_framebuffer_dimensions();
|
let (w, h) = renderer.get_framebuffer_dimensions();
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
let drawer = GliumDrawer::init(renderer, egl_display.clone(), log.clone());
|
let drawer = GliumDrawer::init(renderer, egl_buffer_reader.clone(), log.clone());
|
||||||
#[cfg(not(feature = "egl"))]
|
#[cfg(not(feature = "egl"))]
|
||||||
let drawer = GliumDrawer::init(renderer, log.clone());
|
let drawer = GliumDrawer::init(renderer, log.clone());
|
||||||
|
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
let buffer_utils = BufferUtils::new(egl_display, log.clone());
|
let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone());
|
||||||
#[cfg(not(feature = "egl"))]
|
#[cfg(not(feature = "egl"))]
|
||||||
let buffer_utils = BufferUtils::new(log.clone());
|
let buffer_utils = BufferUtils::new(log.clone());
|
||||||
|
|
||||||
|
|
|
@ -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::EGLBufferReader, EGLGraphicsBackend};
|
||||||
|
|
||||||
mod surface;
|
mod surface;
|
||||||
pub use self::surface::*;
|
pub use self::surface::*;
|
||||||
|
use crate::backend::egl::context::{GlAttributes, PixelFormatRequirements};
|
||||||
|
use crate::backend::egl::display::EGLDisplay;
|
||||||
|
|
||||||
#[cfg(feature = "backend_session")]
|
#[cfg(feature = "backend_session")]
|
||||||
pub mod session;
|
pub mod session;
|
||||||
|
@ -48,8 +47,10 @@ 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,
|
||||||
|
default_attributes: GlAttributes,
|
||||||
|
default_requirements: PixelFormatRequirements,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B, D> AsRawFd for EglDevice<B, D>
|
impl<B, D> AsRawFd for EglDevice<B, D>
|
||||||
|
@ -77,7 +78,7 @@ where
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
EglDevice::new_with_gl_attr(
|
EglDevice::new_with_defaults(
|
||||||
dev,
|
dev,
|
||||||
GlAttributes {
|
GlAttributes {
|
||||||
version: None,
|
version: None,
|
||||||
|
@ -85,17 +86,20 @@ where
|
||||||
debug: cfg!(debug_assertions),
|
debug: cfg!(debug_assertions),
|
||||||
vsync: true,
|
vsync: true,
|
||||||
},
|
},
|
||||||
|
Default::default(),
|
||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`EglDevice`] from an open device and given [`GlAttributes`]
|
/// Try to create a new [`EglDevice`] from an open device with the given attributes and
|
||||||
|
/// requirements as defaults for new surfaces.
|
||||||
///
|
///
|
||||||
/// 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_with_gl_attr<L>(
|
pub fn new_with_defaults<L>(
|
||||||
mut dev: D,
|
mut dev: D,
|
||||||
attributes: GlAttributes,
|
default_attributes: GlAttributes,
|
||||||
|
default_requirements: PixelFormatRequirements,
|
||||||
logger: L,
|
logger: L,
|
||||||
) -> Result<Self, Error<<<D as Device>::Surface as Surface>::Error>>
|
) -> Result<Self, Error<<<D as Device>::Surface as Surface>::Error>>
|
||||||
where
|
where
|
||||||
|
@ -107,10 +111,9 @@ 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(
|
default_attributes,
|
||||||
EGLContext::new(dev, attributes, Default::default(), log.clone()).map_err(Error::EGL)?,
|
default_requirements,
|
||||||
),
|
|
||||||
logger: log,
|
logger: log,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -147,7 +150,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 +169,24 @@ 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)?;
|
let context = self
|
||||||
|
.dev
|
||||||
|
.create_context(self.default_attributes, self.default_requirements)
|
||||||
|
.map_err(Error::EGL)?;
|
||||||
|
let surface = self
|
||||||
|
.dev
|
||||||
|
.create_surface(
|
||||||
|
context.get_pixel_format(),
|
||||||
|
self.default_requirements.double_buffer,
|
||||||
|
context.get_config_id(),
|
||||||
|
crtc,
|
||||||
|
)
|
||||||
|
.map_err(Error::EGL)?;
|
||||||
|
|
||||||
Ok(EglSurface {
|
Ok(EglSurface { context, surface })
|
||||||
dev: self.dev.clone(),
|
|
||||||
surface,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_events(&mut self) {
|
fn process_events(&mut self) {
|
||||||
|
@ -212,7 +224,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<EGLBufferReader, EGLError> {
|
||||||
self.dev.bind_wl_display(display)
|
self.dev.bind_wl_display(display)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,76 +1,37 @@
|
||||||
//! 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, EGLDisplayHandle};
|
||||||
use nix::libc::{c_int, c_void};
|
use crate::backend::egl::native::NativeSurface;
|
||||||
use slog;
|
use crate::backend::egl::{native, EGLSurface};
|
||||||
use std::{
|
use crate::backend::graphics::{PixelFormat, SwapBuffersError};
|
||||||
cell::{Ref, RefCell, RefMut},
|
use std::os::raw::c_int;
|
||||||
ffi::{CStr, CString},
|
use std::ptr;
|
||||||
marker::PhantomData,
|
use std::sync::Arc;
|
||||||
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: ffi::egl::types::EGLContext,
|
||||||
pub(crate) context: Rc<ffi::egl::types::EGLContext>,
|
display: Arc<EGLDisplayHandle>,
|
||||||
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,
|
|
||||||
_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 +40,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 +63,11 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ffi::egl::LOAD.call_once(|| {
|
let (pixel_format, config_id) = display.choose_config(attributes, 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 +75,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 +83,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,116 +92,105 @@ 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,
|
||||||
|
display: display.display.clone(),
|
||||||
Ok((
|
|
||||||
Rc::new(context as *const _),
|
|
||||||
Rc::new(display as *const _),
|
|
||||||
config_id,
|
config_id,
|
||||||
surface_attributes,
|
pixel_format,
|
||||||
desc,
|
|
||||||
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,
|
||||||
|
{
|
||||||
|
let surface_ptr = surface.surface.get();
|
||||||
|
|
||||||
|
let ret = ffi::egl::MakeCurrent(**self.display, surface_ptr, surface_ptr, self.context);
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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> {
|
||||||
|
let ret = ffi::egl::MakeCurrent(**self.display, ptr::null(), ptr::null(), self.context);
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
pub fn is_current(&self) -> bool {
|
pub fn is_current(&self) -> bool {
|
||||||
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 need to ensure the context is unbound, otherwise it egl stalls the destroy call
|
||||||
// is still the current one
|
if ffi::egl::GetCurrentContext() == self.context as *const _ {
|
||||||
ffi::egl::DestroyContext((*self.display) as *const _, (*self.context) as *const _);
|
ffi::egl::MakeCurrent(ptr::null(), ptr::null(), ptr::null(), ptr::null());
|
||||||
ffi::egl::Terminate((*self.display) as *const _);
|
}
|
||||||
|
|
||||||
|
ffi::egl::DestroyContext(**self.display, self.context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -571,3 +267,70 @@ impl Default for PixelFormatRequirements {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PixelFormatRequirements {
|
||||||
|
/// Append the requirements to the given attribute list
|
||||||
|
pub fn create_attributes(&self, out: &mut Vec<c_int>, logger: &slog::Logger) -> Result<(), Error> {
|
||||||
|
if let Some(hardware_accelerated) = self.hardware_accelerated {
|
||||||
|
out.push(ffi::egl::CONFIG_CAVEAT as c_int);
|
||||||
|
out.push(if hardware_accelerated {
|
||||||
|
trace!(logger, "Setting CONFIG_CAVEAT to NONE");
|
||||||
|
ffi::egl::NONE as c_int
|
||||||
|
} else {
|
||||||
|
trace!(logger, "Setting CONFIG_CAVEAT to SLOW_CONFIG");
|
||||||
|
ffi::egl::SLOW_CONFIG as c_int
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(color) = self.color_bits {
|
||||||
|
trace!(logger, "Setting RED_SIZE to {}", color / 3);
|
||||||
|
out.push(ffi::egl::RED_SIZE as c_int);
|
||||||
|
out.push((color / 3) as c_int);
|
||||||
|
trace!(
|
||||||
|
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!(
|
||||||
|
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) = self.alpha_bits {
|
||||||
|
trace!(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) = self.depth_bits {
|
||||||
|
trace!(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) = self.stencil_bits {
|
||||||
|
trace!(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) = self.multisampling {
|
||||||
|
trace!(logger, "Setting SAMPLES to {}", multisampling);
|
||||||
|
out.push(ffi::egl::SAMPLES as c_int);
|
||||||
|
out.push(multisampling as c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.stereoscopy {
|
||||||
|
error!(logger, "Stereoscopy is currently unsupported (sorry!)");
|
||||||
|
return Err(Error::NoAvailablePixelFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,620 @@
|
||||||
|
//! 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
/// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed
|
||||||
|
/// once all resources bound to it have been dropped.
|
||||||
|
pub(crate) struct EGLDisplayHandle {
|
||||||
|
handle: ffi::egl::types::EGLDisplay,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for EGLDisplayHandle {
|
||||||
|
type Target = ffi::egl::types::EGLDisplay;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EGLDisplayHandle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
ffi::egl::Terminate(self.handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [`EGLDisplay`] represents an initialised EGL environment
|
||||||
|
pub struct EGLDisplay<B: native::Backend, N: native::NativeDisplay<B>> {
|
||||||
|
native: RefCell<N>,
|
||||||
|
pub(crate) display: Arc<EGLDisplayHandle>,
|
||||||
|
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(EGLDisplayHandle { handle: display }),
|
||||||
|
egl_version,
|
||||||
|
extensions,
|
||||||
|
logger: log,
|
||||||
|
_backend: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds a compatible [`EGLConfig`] for a given set of requirements
|
||||||
|
pub fn choose_config(
|
||||||
|
&self,
|
||||||
|
attributes: GlAttributes,
|
||||||
|
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 attributes.version {
|
||||||
|
Some((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);
|
||||||
|
}
|
||||||
|
Some((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);
|
||||||
|
}
|
||||||
|
Some(ver) => {
|
||||||
|
return Err(Error::OpenGlVersionNotSupported(ver));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(Error::OpenGlVersionNotSupported((0, 0)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reqs.create_attributes(&mut out, &self.logger)?;
|
||||||
|
|
||||||
|
out.push(ffi::egl::NONE as c_int);
|
||||||
|
out
|
||||||
|
};
|
||||||
|
|
||||||
|
// calling `eglChooseConfig`
|
||||||
|
let mut num_configs = unsafe { std::mem::zeroed() };
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::ChooseConfig(
|
||||||
|
**self.display,
|
||||||
|
descriptor.as_ptr(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
0,
|
||||||
|
&mut num_configs,
|
||||||
|
)
|
||||||
|
} == 0
|
||||||
|
{
|
||||||
|
return Err(Error::ConfigFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if num_configs == 0 {
|
||||||
|
return Err(Error::NoAvailablePixelFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut config_ids = Vec::with_capacity(num_configs as usize);
|
||||||
|
config_ids.resize_with(num_configs as usize, || unsafe { std::mem::zeroed() });
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::ChooseConfig(
|
||||||
|
**self.display,
|
||||||
|
descriptor.as_ptr(),
|
||||||
|
config_ids.as_mut_ptr(),
|
||||||
|
num_configs,
|
||||||
|
&mut num_configs,
|
||||||
|
)
|
||||||
|
} == 0
|
||||||
|
{
|
||||||
|
return Err(Error::ConfigFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Deeper swap intervals might have some uses
|
||||||
|
let desired_swap_interval = if attributes.vsync { 1 } else { 0 };
|
||||||
|
|
||||||
|
let config_ids = config_ids
|
||||||
|
.into_iter()
|
||||||
|
.filter(|&config| unsafe {
|
||||||
|
let mut min_swap_interval = 0;
|
||||||
|
ffi::egl::GetConfigAttrib(
|
||||||
|
**self.display,
|
||||||
|
config,
|
||||||
|
ffi::egl::MIN_SWAP_INTERVAL as ffi::egl::types::EGLint,
|
||||||
|
&mut min_swap_interval,
|
||||||
|
);
|
||||||
|
|
||||||
|
if desired_swap_interval < min_swap_interval {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut max_swap_interval = 0;
|
||||||
|
ffi::egl::GetConfigAttrib(
|
||||||
|
**self.display,
|
||||||
|
config,
|
||||||
|
ffi::egl::MAX_SWAP_INTERVAL as ffi::egl::types::EGLint,
|
||||||
|
&mut max_swap_interval,
|
||||||
|
);
|
||||||
|
|
||||||
|
if desired_swap_interval > max_swap_interval {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if config_ids.is_empty() {
|
||||||
|
return Err(Error::NoAvailablePixelFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Improve config selection
|
||||||
|
let config_id = config_ids[0];
|
||||||
|
|
||||||
|
// 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 = unsafe {
|
||||||
|
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.clone(),
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<EGLBufferReader, 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(EGLBufferReader::new(
|
||||||
|
self.display.clone(),
|
||||||
|
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 EGLBufferReader {
|
||||||
|
display: Arc<EGLDisplayHandle>,
|
||||||
|
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 EGLBufferReader {
|
||||||
|
fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display, extensions: &[String]) -> Self {
|
||||||
|
#[cfg(feature = "renderer_gl")]
|
||||||
|
let gl = gl_ffi::Gles2::load_with(|s| 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> {
|
||||||
|
let mut format: i32 = 0;
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::QueryWaylandBufferWL(
|
||||||
|
**self.display,
|
||||||
|
buffer.as_ref().c_ptr() as _,
|
||||||
|
ffi::egl::EGL_TEXTURE_FORMAT,
|
||||||
|
&mut format,
|
||||||
|
) == 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(
|
||||||
|
**self.display,
|
||||||
|
buffer.as_ref().c_ptr() as _,
|
||||||
|
ffi::egl::WIDTH as i32,
|
||||||
|
&mut width,
|
||||||
|
) == 0
|
||||||
|
} {
|
||||||
|
return Err(BufferAccessError::NotManaged(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut height: i32 = 0;
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::QueryWaylandBufferWL(
|
||||||
|
**self.display,
|
||||||
|
buffer.as_ref().c_ptr() as _,
|
||||||
|
ffi::egl::HEIGHT as i32,
|
||||||
|
&mut height,
|
||||||
|
) == 0
|
||||||
|
} {
|
||||||
|
return Err(BufferAccessError::NotManaged(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut inverted: i32 = 0;
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::QueryWaylandBufferWL(
|
||||||
|
**self.display,
|
||||||
|
buffer.as_ref().c_ptr() as _,
|
||||||
|
ffi::egl::WAYLAND_Y_INVERTED_WL,
|
||||||
|
&mut inverted,
|
||||||
|
) != 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(
|
||||||
|
**self.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: self.display.clone(),
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)> {
|
||||||
|
let mut width: i32 = 0;
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::QueryWaylandBufferWL(
|
||||||
|
**self.display,
|
||||||
|
buffer.as_ref().c_ptr() as _,
|
||||||
|
ffi::egl::WIDTH as _,
|
||||||
|
&mut width,
|
||||||
|
) == 0
|
||||||
|
} {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut height: i32 = 0;
|
||||||
|
if unsafe {
|
||||||
|
ffi::egl::QueryWaylandBufferWL(
|
||||||
|
**self.display,
|
||||||
|
buffer.as_ref().c_ptr() as _,
|
||||||
|
ffi::egl::HEIGHT as _,
|
||||||
|
&mut height,
|
||||||
|
) == 0
|
||||||
|
} {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((width, height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "use_system_lib")]
|
||||||
|
impl Drop for EGLBufferReader {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.wayland.is_null() {
|
||||||
|
unsafe {
|
||||||
|
ffi::egl::UnbindWaylandDisplayWL(**self.display, self.wayland as _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,28 +21,30 @@
|
||||||
#[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::EGLBufferReader;
|
||||||
|
use crate::backend::egl::display::EGLDisplayHandle;
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// 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 +63,17 @@ 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 fn get_proc_address(symbol: &str) -> *const c_void {
|
||||||
|
unsafe {
|
||||||
|
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)]
|
||||||
|
@ -155,7 +168,7 @@ impl Format {
|
||||||
/// Images of the EGL-based [`WlBuffer`].
|
/// Images of the EGL-based [`WlBuffer`].
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
pub struct EGLImages {
|
pub struct EGLImages {
|
||||||
display: Weak<ffi::egl::types::EGLDisplay>,
|
display: Arc<EGLDisplayHandle>,
|
||||||
/// Width in pixels
|
/// Width in pixels
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
/// Height in pixels
|
/// Height in pixels
|
||||||
|
@ -192,7 +205,6 @@ impl EGLImages {
|
||||||
plane: usize,
|
plane: usize,
|
||||||
tex_id: c_uint,
|
tex_id: c_uint,
|
||||||
) -> ::std::result::Result<(), TextureCreationError> {
|
) -> ::std::result::Result<(), TextureCreationError> {
|
||||||
if self.display.upgrade().is_some() {
|
|
||||||
if !self.egl_to_texture_support {
|
if !self.egl_to_texture_support {
|
||||||
return Err(TextureCreationError::GLExtensionNotSupported("GL_OES_EGL_image"));
|
return Err(TextureCreationError::GLExtensionNotSupported("GL_OES_EGL_image"));
|
||||||
}
|
}
|
||||||
|
@ -213,20 +225,15 @@ impl EGLImages {
|
||||||
};
|
};
|
||||||
self.gl.BindTexture(gl_ffi::TEXTURE_2D, old_tex_id as u32);
|
self.gl.BindTexture(gl_ffi::TEXTURE_2D, old_tex_id as u32);
|
||||||
res
|
res
|
||||||
} else {
|
|
||||||
Err(TextureCreationError::ContextLost)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wayland_frontend")]
|
#[cfg(feature = "wayland_frontend")]
|
||||||
impl Drop for EGLImages {
|
impl Drop for EGLImages {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(display) = self.display.upgrade() {
|
|
||||||
for image in self.images.drain(..) {
|
for image in self.images.drain(..) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::egl::DestroyImageKHR(*display, image);
|
ffi::egl::DestroyImageKHR(**self.display, image);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.buffer.release();
|
self.buffer.release();
|
||||||
|
@ -234,7 +241,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 [`EGLBufferReader`](display::EGLBufferReader) 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 +256,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<EGLBufferReader, 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()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
//! 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::egl::display::EGLDisplayHandle;
|
||||||
|
use crate::backend::graphics::{PixelFormat, SwapBuffersError};
|
||||||
use nix::libc::c_int;
|
use nix::libc::c_int;
|
||||||
|
use std::sync::Arc;
|
||||||
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: Arc<EGLDisplayHandle>,
|
||||||
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 +34,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<EGLDisplayHandle>,
|
||||||
|
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 +77,12 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EGLSurface {
|
Ok(EGLSurface {
|
||||||
context: Rc::downgrade(&context.context),
|
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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +91,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
||||||
let surface = self.surface.get();
|
let surface = self.surface.get();
|
||||||
|
|
||||||
if !surface.is_null() {
|
if !surface.is_null() {
|
||||||
if let Some(display) = self.display.upgrade() {
|
let ret = unsafe { ffi::egl::SwapBuffers(**self.display, surface as *const _) };
|
||||||
let ret = unsafe { ffi::egl::SwapBuffers((*display) as *const _, surface as *const _) };
|
|
||||||
|
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
match unsafe { ffi::egl::GetError() } as u32 {
|
match unsafe { ffi::egl::GetError() } as u32 {
|
||||||
|
@ -76,75 +101,46 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
||||||
} else {
|
} else {
|
||||||
self.native.swap_buffers()?;
|
self.native.swap_buffers()?;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return Err(SwapBuffersError::ContextLost);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.native.needs_recreation() || surface.is_null() {
|
if self.native.needs_recreation() || surface.is_null() {
|
||||||
if let Some(display) = self.display.upgrade() {
|
|
||||||
self.native.recreate();
|
self.native.recreate();
|
||||||
self.surface.set(unsafe {
|
self.surface.set(unsafe {
|
||||||
ffi::egl::CreateWindowSurface(
|
ffi::egl::CreateWindowSurface(
|
||||||
*display,
|
**self.display,
|
||||||
self.config_id,
|
self.config_id,
|
||||||
self.native.ptr(),
|
self.native.ptr(),
|
||||||
self.surface_attributes.as_ptr(),
|
self.surface_attributes.as_ptr(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: native::NativeSurface> Drop for EGLSurface<N> {
|
impl<N: native::NativeSurface> Drop for EGLSurface<N> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(display) = self.display.upgrade() {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::egl::DestroySurface((*display) as *const _, self.surface.get() as *const _);
|
ffi::egl::DestroySurface(**self.display, self.surface.get() as *const _);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -18,11 +18,7 @@ pub trait GLGraphicsBackend {
|
||||||
fn swap_buffers(&self) -> Result<(), SwapBuffersError>;
|
fn swap_buffers(&self) -> Result<(), SwapBuffersError>;
|
||||||
|
|
||||||
/// Returns the address of an OpenGL function.
|
/// Returns the address of an OpenGL function.
|
||||||
///
|
fn get_proc_address(&self, symbol: &str) -> *const c_void;
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The context must have been made current before this function is called.
|
|
||||||
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void;
|
|
||||||
|
|
||||||
/// Returns the dimensions of the window, or screen, etc in points.
|
/// Returns the dimensions of the window, or screen, etc in points.
|
||||||
///
|
///
|
||||||
|
@ -51,5 +47,5 @@ pub trait GLGraphicsBackend {
|
||||||
/// and may only be used in combination with the backend. Using this with any
|
/// and may only be used in combination with the backend. Using this with any
|
||||||
/// other gl context or after the backend was dropped *may* cause undefined behavior.
|
/// other gl context or after the backend was dropped *may* cause undefined behavior.
|
||||||
pub fn load_raw_gl<B: GLGraphicsBackend>(backend: &B) -> Gles2 {
|
pub fn load_raw_gl<B: GLGraphicsBackend>(backend: &B) -> Gles2 {
|
||||||
Gles2::load_with(|s| unsafe { backend.get_proc_address(s) as *const _ })
|
Gles2::load_with(|s| backend.get_proc_address(s) as *const _)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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::EGLBufferReader, 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);
|
||||||
},
|
},
|
||||||
|
@ -263,12 +285,9 @@ impl GLGraphicsBackend for WinitGraphicsBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
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<EGLBufferReader, 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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue