Migrate the winit backend to the new egl and renderer apis.

This commit is contained in:
Victor Brekenfeld 2021-04-07 01:09:48 +02:00
parent f3f10242e9
commit 3a2e4ddf61
1 changed files with 135 additions and 214 deletions

View File

@ -1,10 +1,12 @@
//! 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::display::EGLDisplay;
use crate::backend::egl::get_proc_address;
use crate::backend::{ use crate::backend::{
egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError, SurfaceCreationError}, egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError},
graphics::{gl::GLGraphicsBackend, CursorBackend, PixelFormat, SwapBuffersError}, renderer::{
Renderer, Bind, Transform, Frame,
gles2::{Gles2Renderer, Gles2Error, Gles2Texture, Gles2Frame},
},
input::{ input::{
Axis, AxisSource, Event as BackendEvent, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, Axis, AxisSource, Event as BackendEvent, InputBackend, InputEvent, KeyState, KeyboardKeyEvent,
MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent,
@ -12,16 +14,16 @@ use crate::backend::{
UnusedEvent, UnusedEvent,
}, },
}; };
use nix::libc::c_void;
use std::{ use std::{
cell::{Ref, RefCell}, cell::RefCell,
convert::TryInto,
fmt,
rc::Rc, rc::Rc,
time::Instant, time::Instant,
}; };
use cgmath::Matrix3;
use wayland_egl as wegl; use wayland_egl as wegl;
use wayland_server::Display; use wayland_server::Display;
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_shm, wl_buffer};
use winit::{ use winit::{
dpi::{LogicalPosition, LogicalSize, PhysicalSize}, dpi::{LogicalPosition, LogicalSize, PhysicalSize},
event::{ event::{
@ -29,12 +31,13 @@ use winit::{
TouchPhase, WindowEvent, TouchPhase, WindowEvent,
}, },
event_loop::{ControlFlow, EventLoop}, event_loop::{ControlFlow, EventLoop},
platform::run_return::EventLoopExtRunReturn, platform::desktop::EventLoopExtDesktop,
window::{CursorIcon, Window as WinitWindow, WindowBuilder}, platform::unix::WindowExtUnix,
window::{Window as WinitWindow, WindowBuilder},
}; };
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
use crate::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend}; use crate::backend::egl::{display::EGLBufferReader};
/// Errors thrown by the `winit` backends /// Errors thrown by the `winit` backends
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
@ -47,71 +50,32 @@ pub enum Error {
NotSupported, NotSupported,
/// EGL error /// EGL error
#[error("EGL error: {0}")] #[error("EGL error: {0}")]
EGL(#[from] EGLError), Egl(#[from] EGLError),
/// Surface Creation failed /// Renderer initialization failed
#[error("Surface creation failed: {0}")] #[error("Renderer creation failed: {0}")]
SurfaceCreationError(#[from] SurfaceCreationError<EGLError>), RendererCreationError(#[from] Gles2Error),
} }
enum Window { #[derive(Debug, Clone)]
Wayland { pub struct WindowSize {
display: EGLDisplay<native::Wayland, WinitWindow>, pub physical_size: PhysicalSize<u32>,
context: EGLContext, pub scale_factor: f64,
surface: EGLSurface<wegl::WlEglSurface>,
},
X11 {
display: EGLDisplay<native::X11, WinitWindow>,
context: EGLContext,
surface: EGLSurface<native::XlibWindow>,
},
}
// WlEglSurface does not implement debug, so we have to impl Debug manually
impl fmt::Debug for Window {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Window::Wayland { display, context, .. } => f
.debug_struct("Window::Wayland")
.field("display", &display)
.field("context", &context)
.field("surface", &"...")
.finish(),
Window::X11 {
display,
context,
surface,
} => f
.debug_struct("Window::X11")
.field("display", &display)
.field("context", &context)
.field("surface", &surface)
.finish(),
}
}
}
impl Window {
fn window(&self) -> Ref<'_, WinitWindow> {
match *self {
Window::Wayland { ref display, .. } => display.borrow(),
Window::X11 { ref display, .. } => display.borrow(),
}
}
}
#[derive(Debug)]
struct WindowSize {
physical_size: PhysicalSize<u32>,
scale_factor: f64,
} }
/// Window with an active EGL Context created by `winit`. Implements the /// Window with an active EGL Context created by `winit`. Implements the
/// [`EGLGraphicsBackend`] and [`GLGraphicsBackend`] graphics backend trait /// [`EGLGraphicsBackend`] and [`GLGraphicsBackend`] graphics backend trait
#[derive(Debug)] #[derive(Debug)]
pub struct WinitGraphicsBackend { pub struct WinitGraphicsBackend {
window: Rc<Window>, renderer: Gles2Renderer,
display: EGLDisplay,
egl: Rc<EGLSurface>,
window: Rc<WinitWindow>,
size: Rc<RefCell<WindowSize>>, size: Rc<RefCell<WindowSize>>,
logger: ::slog::Logger, }
pub struct WinitFrame {
frame: Gles2Frame,
egl: Rc<EGLSurface>,
} }
/// Abstracted event loop of a [`WinitWindow`] implementing the [`InputBackend`] trait /// Abstracted event loop of a [`WinitWindow`] implementing the [`InputBackend`] trait
@ -120,8 +84,9 @@ pub struct WinitGraphicsBackend {
/// periodically to receive any events. /// periodically to receive any events.
#[derive(Debug)] #[derive(Debug)]
pub struct WinitInputBackend { pub struct WinitInputBackend {
egl: Rc<EGLSurface>,
window: Rc<WinitWindow>,
events_loop: EventLoop<()>, events_loop: EventLoop<()>,
window: Rc<Window>,
time: Instant, time: Instant,
key_counter: u32, key_counter: u32,
seat: Seat, seat: Seat,
@ -158,7 +123,7 @@ where
init_from_builder_with_gl_attr( init_from_builder_with_gl_attr(
builder, builder,
GlAttributes { GlAttributes {
version: None, version: (3, 0),
profile: None, profile: None,
debug: cfg!(debug_assertions), debug: cfg!(debug_assertions),
vsync: true, vsync: true,
@ -188,54 +153,53 @@ where
debug!(log, "Window created"); debug!(log, "Window created");
let reqs = Default::default(); let reqs = Default::default();
let window = Rc::new( let (display, context, surface) = {
if native::NativeDisplay::<native::Wayland>::is_backend(&winit_window) { let display = EGLDisplay::new(&winit_window, log.clone())?;
let display = EGLDisplay::<native::Wayland, WinitWindow>::new(winit_window, log.clone())?; let context = EGLContext::new_with_config(&display, attributes, reqs, log.clone())?;
let context = display.create_context(attributes, reqs)?;
let surface = display.create_surface( let surface = if let Some(wl_surface) = winit_window.wayland_surface() {
context.get_pixel_format(), debug!(log, "Winit backend: Wayland");
reqs.double_buffer, let size = winit_window.inner_size();
context.get_config_id(), let surface = unsafe {
(), wegl::WlEglSurface::new_from_raw(wl_surface as *mut _, size.width as i32, size.height as i32)
)?; };
Window::Wayland { EGLSurface::new(&display, context.pixel_format().unwrap(), reqs.double_buffer, context.config_id(), surface, log.clone())
display, .map_err(EGLError::CreationFailed)?
context, } else if let Some(xlib_window) = winit_window.xlib_window().map(native::XlibWindow) {
surface, debug!(log, "Winit backend: X11");
} EGLSurface::new(&display, context.pixel_format().unwrap(), reqs.double_buffer, context.config_id(), xlib_window, log.clone())
} else if native::NativeDisplay::<native::X11>::is_backend(&winit_window) { .map_err(EGLError::CreationFailed)?
let display = EGLDisplay::<native::X11, WinitWindow>::new(winit_window, log.clone())?;
let context = display.create_context(attributes, reqs)?;
let surface = display.create_surface(
context.get_pixel_format(),
reqs.double_buffer,
context.get_config_id(),
(),
)?;
Window::X11 {
display,
context,
surface,
}
} else { } else {
return Err(Error::NotSupported); unreachable!("No backends for winit other then Wayland and X11 are supported")
}, };
);
(
display,
context,
surface,
)
};
let size = Rc::new(RefCell::new(WindowSize { let size = Rc::new(RefCell::new(WindowSize {
physical_size: window.window().inner_size(), // TODO: original code check if window is alive or not using inner_size().expect() physical_size: winit_window.inner_size(), // TODO: original code check if window is alive or not using inner_size().expect()
scale_factor: window.window().scale_factor(), scale_factor: winit_window.scale_factor(),
})); }));
let window = Rc::new(winit_window);
let egl = Rc::new(surface);
Ok(( Ok((
WinitGraphicsBackend { WinitGraphicsBackend {
window: window.clone(), window: window.clone(),
display,
egl: egl.clone(),
renderer: Gles2Renderer::new(context, log.clone())?,
size: size.clone(), size: size.clone(),
logger: log.new(o!("smithay_winit_component" => "graphics")),
}, },
WinitInputBackend { WinitInputBackend {
events_loop, events_loop,
window, window,
egl,
time: Instant::now(), time: Instant::now(),
key_counter: 0, key_counter: 0,
seat: Seat::new( seat: Seat::new(
@ -269,111 +233,71 @@ pub enum WinitEvent {
Refresh, Refresh,
} }
#[cfg(feature = "use_system_lib")]
impl WinitGraphicsBackend { impl WinitGraphicsBackend {
/// Get a reference to the internally used [`WinitWindow`] pub fn bind_wl_display(&self, wl_display: &Display) -> Result<EGLBufferReader, EGLError> {
pub fn winit_window(&self) -> Ref<'_, WinitWindow> { self.display.bind_wl_display(wl_display)
self.window.window()
}
}
impl CursorBackend for WinitGraphicsBackend {
type CursorFormat = CursorIcon;
type Error = ();
fn set_cursor_position(&self, _x: u32, _y: u32) -> ::std::result::Result<(), ()> {
// With other backends, we read events from input devices and then have to position the
// mouse cursor on screen accordingly. Here, there is already a windowing system that deals
// with the position on screen. If we "force" out idea of the cursor position here, that
// breaks and e.g. the cursor on X11 becomes stuck and cannot move. (Us forcing a cursor
// position generates a mouse move event and thus we force the same cursor position again.)
Ok(())
} }
fn set_cursor_representation( pub fn window_size(&self) -> WindowSize {
&self, self.size.borrow().clone()
cursor: &Self::CursorFormat,
_hotspot: (u32, u32),
) -> ::std::result::Result<(), ()> {
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be
debug!(self.logger, "Changing cursor representation");
self.window.window().set_cursor_icon(*cursor);
self.window.window().set_cursor_visible(true);
Ok(())
} }
fn clear_cursor_representation(&self) -> ::std::result::Result<(), ()> { pub fn window(&self) -> &WinitWindow {
self.window.window().set_cursor_visible(false); &*self.window
Ok(())
}
}
impl GLGraphicsBackend for WinitGraphicsBackend {
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
trace!(self.logger, "Swapping buffers");
match *self.window {
Window::Wayland { ref surface, .. } => {
surface.swap_buffers().map_err(|err| err.try_into().unwrap())
}
Window::X11 { ref surface, .. } => surface.swap_buffers().map_err(|err| err.try_into().unwrap()),
}
} }
fn get_proc_address(&self, symbol: &str) -> *const c_void { pub fn begin(&mut self) -> Result<WinitFrame, Gles2Error> {
trace!(self.logger, "Getting symbol for {:?}", symbol); let (width, height) = {
get_proc_address(symbol)
}
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
let size = self.size.borrow(); let size = self.size.borrow();
size.physical_size.into() size.physical_size.into()
} };
Renderer::begin(self, width, height, Transform::Normal)
fn is_current(&self) -> bool {
match *self.window {
Window::Wayland {
ref context,
ref surface,
..
} => context.is_current() && surface.is_current(),
Window::X11 {
ref context,
ref surface,
..
} => context.is_current() && surface.is_current(),
}
}
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
trace!(self.logger, "Setting EGL context to be the current context");
match *self.window {
Window::Wayland {
ref surface,
ref context,
..
} => context.make_current_with_surface(surface).map_err(Into::into),
Window::X11 {
ref surface,
ref context,
..
} => context.make_current_with_surface(surface).map_err(Into::into),
}
}
fn get_pixel_format(&self) -> PixelFormat {
match *self.window {
Window::Wayland { ref surface, .. } => surface.get_pixel_format(),
Window::X11 { ref surface, .. } => surface.get_pixel_format(),
}
} }
} }
#[cfg(feature = "use_system_lib")] impl Renderer for WinitGraphicsBackend {
impl EGLGraphicsBackend for WinitGraphicsBackend { type Error = Gles2Error;
fn bind_wl_display(&self, wl_display: &Display) -> Result<EGLBufferReader, EGLError> { type Texture = Gles2Texture;
match *self.window { type Frame = WinitFrame;
Window::Wayland { ref display, .. } => display.bind_wl_display(wl_display),
Window::X11 { ref display, .. } => display.bind_wl_display(wl_display), fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<Self::Frame, Self::Error> {
self.renderer.bind(&*self.egl)?;
let frame = self.renderer.begin(width, height, transform)?;
Ok(WinitFrame {
frame,
egl: self.egl.clone(),
})
} }
#[cfg(feature = "wayland_frontend")]
fn shm_formats(&self) -> &[wl_shm::Format] {
self.renderer.shm_formats()
}
#[cfg(feature = "wayland_frontend")]
fn import_shm(&self, buffer: &wl_buffer::WlBuffer) -> Result<Self::Texture, Self::Error> {
self.renderer.import_shm(buffer)
}
}
impl Frame for WinitFrame {
type Error = Gles2Error;
type Texture = Gles2Texture;
fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> {
self.frame.clear(color)
}
fn render_texture(&mut self, texture: &Self::Texture, matrix: Matrix3<f32>, alpha: f32) -> Result<(), Self::Error> {
self.frame.render_texture(texture, matrix, alpha)
}
fn finish(self) -> Result<(), crate::backend::SwapBuffersError> {
self.frame.finish()?;
self.egl.swap_buffers()?;
Ok(())
} }
} }
@ -703,6 +627,7 @@ impl InputBackend for WinitInputBackend {
let time = &self.time; let time = &self.time;
let seat = &self.seat; let seat = &self.seat;
let window = &self.window; let window = &self.window;
let egl = &self.egl;
let logger = &self.logger; let logger = &self.logger;
let window_size = &self.size; let window_size = &self.size;
let mut callback = move |event| callback(event, &mut WinitInputConfig); let mut callback = move |event| callback(event, &mut WinitInputConfig);
@ -722,13 +647,11 @@ impl InputBackend for WinitInputBackend {
match event { match event {
WindowEvent::Resized(psize) => { WindowEvent::Resized(psize) => {
trace!(logger, "Resizing window to {:?}", psize); trace!(logger, "Resizing window to {:?}", psize);
let scale_factor = window.window().scale_factor(); let scale_factor = window.scale_factor();
let mut wsize = window_size.borrow_mut(); let mut wsize = window_size.borrow_mut();
wsize.physical_size = psize; wsize.physical_size = psize;
wsize.scale_factor = scale_factor; wsize.scale_factor = scale_factor;
if let Window::Wayland { ref surface, .. } = **window { egl.resize(psize.width as i32, psize.height as i32, 0, 0);
surface.resize(psize.width as i32, psize.height as i32, 0, 0);
}
callback(InputEvent::Special(WinitEvent::Resized { callback(InputEvent::Special(WinitEvent::Resized {
size: psize.into(), size: psize.into(),
scale_factor, scale_factor,
@ -744,9 +667,7 @@ impl InputBackend for WinitInputBackend {
} => { } => {
let mut wsize = window_size.borrow_mut(); let mut wsize = window_size.borrow_mut();
wsize.scale_factor = scale_factor; wsize.scale_factor = scale_factor;
if let Window::Wayland { ref surface, .. } = **window { egl.resize(new_psize.width as i32, new_psize.height as i32, 0, 0);
surface.resize(new_psize.width as i32, new_psize.height as i32, 0, 0);
}
let psize_f64: (f64, f64) = (new_psize.width.into(), new_psize.height.into()); let psize_f64: (f64, f64) = (new_psize.width.into(), new_psize.height.into());
callback(InputEvent::Special(WinitEvent::Resized { callback(InputEvent::Special(WinitEvent::Resized {
size: psize_f64, size: psize_f64,