Merge pull request #336 from Smithay/fixes/backend

This commit is contained in:
Victor Brekenfeld 2021-07-15 21:43:11 +02:00 committed by GitHub
commit a5fca7b4ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 57 additions and 94 deletions

View File

@ -99,7 +99,7 @@ where
B: Buffer, B: Buffer,
U: 'static, U: 'static,
{ {
/// Create a new swapchain with the desired allocator and dimensions and pixel format for the created buffers. /// Create a new swapchain with the desired allocator, dimensions and pixel format for the created buffers.
pub fn new( pub fn new(
allocator: A, allocator: A,
width: u32, width: u32,

View File

@ -213,8 +213,6 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
/// - [`crtcs`](drm::control::crtc) represent scanout engines of the device pointing to one framebuffer. \ /// - [`crtcs`](drm::control::crtc) represent scanout engines of the device pointing to one framebuffer. \
/// Their responsibility is to read the data of the framebuffer and export it into an "Encoder". \ /// Their responsibility is to read the data of the framebuffer and export it into an "Encoder". \
/// The number of crtc's represent the number of independant output devices the hardware may handle. /// The number of crtc's represent the number of independant output devices the hardware may handle.
/// - [`planes`](drm::control::plane) represent a single plane on a crtc, which is composite together with
/// other planes on the same crtc to present the final image.
/// - [`mode`](drm::control::Mode) describes the resolution and rate of images produced by the crtc and \ /// - [`mode`](drm::control::Mode) describes the resolution and rate of images produced by the crtc and \
/// has to be compatible with the provided `connectors`. /// has to be compatible with the provided `connectors`.
/// - [`connectors`](drm::control::connector) - List of connectors driven by the crtc. At least one(!) connector needs to be \ /// - [`connectors`](drm::control::connector) - List of connectors driven by the crtc. At least one(!) connector needs to be \

View File

@ -28,8 +28,8 @@ pub enum Error {
/// Mode is not compatible with all given connectors /// Mode is not compatible with all given connectors
#[error("Mode `{0:?}` is not compatible with all given connectors")] #[error("Mode `{0:?}` is not compatible with all given connectors")]
ModeNotSuitable(Mode), ModeNotSuitable(Mode),
/// The given crtc is already in use by another backend /// The given crtc is already in use by another surface
#[error("Crtc `{0:?}` is already in use by another backend")] #[error("Crtc `{0:?}` is already in use by another surface")]
CrtcAlreadyInUse(crtc::Handle), CrtcAlreadyInUse(crtc::Handle),
/// This operation would result in a surface without connectors. /// This operation would result in a surface without connectors.
#[error("Surface of crtc `{0:?}` would have no connectors, which is not accepted")] #[error("Surface of crtc `{0:?}` would have no connectors, which is not accepted")]

View File

@ -20,13 +20,13 @@
//! //!
//! A [`connector`](drm::control::connector) represents a port on your computer, possibly with a connected monitor, TV, capture card, etc. //! A [`connector`](drm::control::connector) represents a port on your computer, possibly with a connected monitor, TV, capture card, etc.
//! //!
//! A [`framebuffer`](drm::control::framebuffer) represents a buffer you may be rendering to, see `Surface` below. //! A [`framebuffer`](drm::control::framebuffer) represents a buffer you may be rendering to, see `DrmSurface` below.
//! //!
//! A [`plane`](drm::control::plane) adds another layer on top of the crtcs, which allow us to layer multiple images on top of each other more efficiently //! A [`plane`](drm::control::plane) adds another layer on top of the crtcs, which allow us to layer multiple images on top of each other more efficiently
//! then by combining the rendered images in the rendering phase, e.g. via OpenGL. Planes can be explicitly used by the user. //! then by combining the rendered images in the rendering phase, e.g. via OpenGL. Planes have to be explicitly used by the user to be useful.
//! Every device has at least one primary plane used to display an image to the whole crtc. Additionally cursor and overlay planes may be present. //! Every device has at least one primary plane used to display an image to the whole crtc. Additionally cursor and overlay planes may be present.
//! Cursor planes are usually very restricted in size and meant to be used for hardware cursors, while overlay planes may //! Cursor planes are usually very restricted in size and meant to be used for hardware cursors, while overlay planes may
//! be used for performance reasons to display any overlay on top of the image, e.g. top-most windows. //! be used for performance reasons to display any overlay on top of the image, e.g. the top-most windows.
//! //!
//! The main functionality of a `Device` in smithay is to give access to all these properties for the user to //! The main functionality of a `Device` in smithay is to give access to all these properties for the user to
//! choose an appropriate rendering configuration. What that means is defined by the requirements and constraints documented //! choose an appropriate rendering configuration. What that means is defined by the requirements and constraints documented

View File

@ -16,13 +16,7 @@ use crate::backend::SwapBuffersError;
use slog::{debug, error, o, trace, warn}; use slog::{debug, error, o, trace, warn};
/// Simplified by limited abstraction to link single [`DrmSurface`]s to renderers. /// Simplified abstraction of a swapchain for gbm-buffers displayed on a [`DrmSurface`].
///
/// # Use-case
///
/// In some scenarios it might be enough to use of a drm-surface as the one and only target
/// of a single renderer. In these cases `DrmRenderSurface` provides a way to quickly
/// get up and running without manually handling and binding buffers.
pub struct GbmBufferedSurface<D: AsRawFd + 'static> { pub struct GbmBufferedSurface<D: AsRawFd + 'static> {
buffers: Buffers<D>, buffers: Buffers<D>,
swapchain: Swapchain<GbmDevice<D>, BufferObject<()>, (Dmabuf, FbHandle<D>)>, swapchain: Swapchain<GbmDevice<D>, BufferObject<()>, (Dmabuf, FbHandle<D>)>,
@ -43,15 +37,12 @@ impl<D> GbmBufferedSurface<D>
where where
D: AsRawFd + 'static, D: AsRawFd + 'static,
{ {
/// Create a new `DrmRendererSurface` from a given compatible combination /// Create a new `GbmBufferedSurface` from a given compatible combination
/// of a surface, an allocator and a renderer. /// of a surface, an allocator and renderer formats.
/// ///
/// To sucessfully call this function, you need to have a renderer, /// To sucessfully call this function, you need to have a renderer,
/// which can render into a Dmabuf, and an allocator, which can create /// which can render into a Dmabuf, and a gbm allocator that can produce
/// a buffer type, which can be converted into a Dmabuf. /// buffers of a supported format for rendering.
///
/// The function will futhermore check for compatibility by enumerating
/// supported pixel formats and choosing an appropriate one.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub fn new<L>( pub fn new<L>(
drm: DrmSurface<D>, drm: DrmSurface<D>,

View File

@ -63,7 +63,7 @@ impl<A: AsRawFd + 'static> DrmSurface<A> {
self.primary self.primary
} }
/// Currently used [`connector`](drm::control::connector)s of this `Surface` /// Currently used [`connector`](drm::control::connector)s of this surface
pub fn current_connectors(&self) -> impl IntoIterator<Item = connector::Handle> { pub fn current_connectors(&self) -> impl IntoIterator<Item = connector::Handle> {
match &*self.internal { match &*self.internal {
DrmSurfaceInternal::Atomic(surf) => surf.current_connectors(), DrmSurfaceInternal::Atomic(surf) => surf.current_connectors(),
@ -72,7 +72,7 @@ impl<A: AsRawFd + 'static> DrmSurface<A> {
} }
/// Returns the pending [`connector`](drm::control::connector)s /// Returns the pending [`connector`](drm::control::connector)s
/// used after the next [`commit`](DrmSurface::commit) of this [`DrmSurface`] /// used after the next [`commit`](DrmSurface::commit) of this surface
pub fn pending_connectors(&self) -> impl IntoIterator<Item = connector::Handle> { pub fn pending_connectors(&self) -> impl IntoIterator<Item = connector::Handle> {
match &*self.internal { match &*self.internal {
DrmSurfaceInternal::Atomic(surf) => surf.pending_connectors(), DrmSurfaceInternal::Atomic(surf) => surf.pending_connectors(),

View File

@ -171,7 +171,7 @@ impl EGLContext {
/// # Safety /// # Safety
/// ///
/// This function is marked unsafe, because the context cannot be made current /// This function is marked unsafe, because the context cannot be made current
/// on multiple threads. /// on multiple threads without being unbound again (see `unbind`).
pub unsafe fn make_current_with_surface(&self, surface: &EGLSurface) -> Result<(), MakeCurrentError> { pub unsafe fn make_current_with_surface(&self, surface: &EGLSurface) -> Result<(), MakeCurrentError> {
let surface_ptr = surface.surface.load(Ordering::SeqCst); let surface_ptr = surface.surface.load(Ordering::SeqCst);
wrap_egl_call(|| { wrap_egl_call(|| {
@ -186,7 +186,7 @@ impl EGLContext {
/// # Safety /// # Safety
/// ///
/// This function is marked unsafe, because the context cannot be made current /// This function is marked unsafe, because the context cannot be made current
/// on multiple threads without being unbound again (see `unbind`) /// on multiple threads without being unbound again (see `unbind`).
pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> { pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> {
wrap_egl_call(|| { wrap_egl_call(|| {
ffi::egl::MakeCurrent( ffi::egl::MakeCurrent(
@ -217,7 +217,7 @@ impl EGLContext {
/// Unbinds this context from the current thread, if set. /// Unbinds this context from the current thread, if set.
/// ///
/// This does nothing if this context is not the current context /// This does nothing if this context is not the current context.
pub fn unbind(&self) -> Result<(), MakeCurrentError> { pub fn unbind(&self) -> Result<(), MakeCurrentError> {
if self.is_current() { if self.is_current() {
wrap_egl_call(|| unsafe { wrap_egl_call(|| unsafe {
@ -232,12 +232,12 @@ impl EGLContext {
Ok(()) Ok(())
} }
/// Returns a list of formats for dmabufs that can be rendered to /// Returns a list of formats for dmabufs that can be rendered to.
pub fn dmabuf_render_formats(&self) -> &HashSet<DrmFormat> { pub fn dmabuf_render_formats(&self) -> &HashSet<DrmFormat> {
&self.display.dmabuf_render_formats &self.display.dmabuf_render_formats
} }
/// Returns a list of formats for dmabufs that can be used as textures /// Returns a list of formats for dmabufs that can be used as textures.
pub fn dmabuf_texture_formats(&self) -> &HashSet<DrmFormat> { pub fn dmabuf_texture_formats(&self) -> &HashSet<DrmFormat> {
&self.display.dmabuf_import_formats &self.display.dmabuf_import_formats
} }
@ -262,7 +262,7 @@ pub struct GlAttributes {
/// `(3, 0)` will request a OpenGL ES 3.0 context for example. /// `(3, 0)` will request a OpenGL ES 3.0 context for example.
/// `(2, 0)` is the minimum. /// `(2, 0)` is the minimum.
pub version: (u8, u8), pub version: (u8, u8),
/// OpenGL profile to use /// OpenGL profile to use.
pub profile: Option<GlProfile>, pub profile: Option<GlProfile>,
/// Whether to enable the debug flag of the context. /// Whether to enable the debug flag of the context.
/// ///
@ -276,7 +276,7 @@ pub struct GlAttributes {
/// Describes the requested OpenGL context profiles. /// Describes the requested OpenGL context profiles.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlProfile { pub enum GlProfile {
/// Include all the immediate more functions and definitions. /// Include all the immediate functions and definitions.
Compatibility, Compatibility,
/// Include all the future-compatible functions and definitions. /// Include all the future-compatible functions and definitions.
Core, Core,

View File

@ -389,7 +389,7 @@ impl EGLDisplay {
Ok((desc, config_id)) Ok((desc, config_id))
} }
/// Get a handle to the underlying native EGLDisplay /// Get a handle to the underlying raw EGLDisplay handle
pub fn get_display_handle(&self) -> Arc<EGLDisplayHandle> { pub fn get_display_handle(&self) -> Arc<EGLDisplayHandle> {
self.display.clone() self.display.clone()
} }
@ -404,7 +404,7 @@ impl EGLDisplay {
self.extensions.clone() self.extensions.clone()
} }
/// Imports a dmabuf as an eglimage /// Imports a [`Dmabuf`] as an [`EGLImage`]
pub fn create_image_from_dmabuf(&self, dmabuf: &Dmabuf) -> Result<EGLImage, Error> { pub fn create_image_from_dmabuf(&self, dmabuf: &Dmabuf) -> Result<EGLImage, Error> {
if !self.extensions.iter().any(|s| s == "EGL_KHR_image_base") if !self.extensions.iter().any(|s| s == "EGL_KHR_image_base")
&& !self && !self

View File

@ -39,11 +39,11 @@ pub enum Error {
/// No EGLDisplay is currently bound to this `WlDisplay` /// No EGLDisplay is currently bound to this `WlDisplay`
#[error("No EGLDisplay is currently bound to this `WlDisplay`")] #[error("No EGLDisplay is currently bound to this `WlDisplay`")]
NoEGLDisplayBound, NoEGLDisplayBound,
/// Index of plane is out of bounds for `EGLImages` /// Index of plane is out of bounds for `EGLBuffer`
#[error("Index of plane is out of bounds for `EGLImages`")] #[error("Index of plane is out of bounds for `EGLBuffer`")]
PlaneIndexOutOfBounds, PlaneIndexOutOfBounds,
/// Failed to create `EGLImages` from the buffer /// Failed to create `EGLBuffer` from the buffer
#[error("Failed to create `EGLImages` from the buffer")] #[error("Failed to create `EGLBuffer` from the buffer")]
EGLImageCreationFailed, EGLImageCreationFailed,
} }
@ -135,7 +135,8 @@ impl EGLError {
} }
} }
pub(crate) fn wrap_egl_call<R, F: FnOnce() -> R>(call: F) -> Result<R, EGLError> { /// Wraps a raw egl call and returns error codes from `eglGetError`, if it fails.
pub fn wrap_egl_call<R, F: FnOnce() -> R>(call: F) -> Result<R, EGLError> {
let res = call(); let res = call();
EGLError::from_last_call().map(|()| res) EGLError::from_last_call().map(|()| res)
} }

View File

@ -41,6 +41,8 @@ extern "system" fn egl_debug_log(
}); });
} }
/// Loads libEGL symbols, if not loaded already.
/// This normally happens automatically during [`EGLDisplay`] initialization.
pub fn make_sure_egl_is_loaded() -> Result<Vec<String>, Error> { pub fn make_sure_egl_is_loaded() -> Result<Vec<String>, Error> {
use std::{ use std::{
ffi::{CStr, CString}, ffi::{CStr, CString},
@ -107,6 +109,7 @@ pub fn make_sure_egl_is_loaded() -> Result<Vec<String>, Error> {
Ok(extensions) Ok(extensions)
} }
/// Module containing raw egl function bindings
#[allow(clippy::all, missing_debug_implementations)] #[allow(clippy::all, missing_debug_implementations)]
pub mod egl { pub mod egl {
use super::*; use super::*;

View File

@ -1,30 +1,30 @@
//! Common traits and types for egl rendering //! Common traits and types for egl rendering
//! //!
//! Large parts of this module are taken from //! This module has multiple responsibilities related to functionality provided by libEGL:
//! [glutin src/api/egl](https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/src/api/egl/) //! Initializing EGL objects to:
//! - initialize usage of EGL based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer)s via `wl_drm`.
//! - initialize OpenGL contexts from.
//! - Import/Export external resources to/from OpenGL
//! //!
//! It therefore falls under //! To use this module, you first need to create a [`EGLDisplay`] through a supported EGL platform
//! [glutin's Apache 2.0 license](https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/LICENSE) //! as inidicated by an implementation of the `native::EGLNativeDisplay` trait.
//! //!
//! Wayland specific EGL functionality - EGL based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer)s. //! You may bind the [`EGLDisplay`], that shall be used by clients for rendering (so pick one initialized by a fast platform)
//! to the [`wayland_server::Display`] of your compositor. Note only one backend may be bound to any [`Display`](wayland_server::Display) at any time.
//! //!
//! The types of this module can be used to initialize hardware acceleration rendering //! You may then use the resulting [`display::EGLBufferReader`] to receive [`EGLBuffer`]
//! based on EGL for clients as it may enabled usage of `EGLImage` based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer)s.
//!
//! To use it bind the [`EGLDisplay`] trait, that shall do the
//! rendering (so pick a fast one), to the [`wayland_server::Display`] of your compositor.
//! Note only one backend may be bound to any [`Display`](wayland_server::Display) at any time.
//!
//! You may then use the resulting [`EGLDisplay`] to receive [`EGLBuffer`]
//! of an EGL-based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer) for rendering. //! of an EGL-based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer) for rendering.
//! Renderers implementing the [`ImportEGL`](crate::backend::renderer::ImportEGL)-trait can manage the buffer reader for you.
//!
//! To create OpenGL contexts you may create [`EGLContext`]s from the display and if the context is initialized with a config
//! it may also be used to initialize an [`EGLSurface`], which can be [bound](crate::backend::renderer::Bind) to some renderers.
//!
//! Alternatively you may import [`dmabuf`](crate::backend::allocator::dmabuf)s using the display, which result
//! in an [`EGLImage`], which can be rendered into by OpenGL. This is perferrable to using surfaces as the dmabuf can be
//! passed around freely making resource-management and more complex use-cases like Multi-GPU rendering easier to manage.
//! Renderers based on EGL may support doing this for you by allowing you to [`Bind`](crate::backend::renderer::Bind) a dmabuf directly.
//!
/*
#[cfg(feature = "renderer_gl")]
use crate::backend::graphics::{
gl::{ffi as gl_ffi, GLGraphicsBackend},
SwapBuffersError as GraphicsSwapBuffersError,
};
*/
use std::fmt; use std::fmt;
pub mod context; pub mod context;
@ -92,7 +92,7 @@ pub enum BufferAccessError {
/// This buffer is not managed by the EGL buffer /// This buffer is not managed by the EGL buffer
#[error("This buffer is not managed by EGL. Err: {0:}")] #[error("This buffer is not managed by EGL. Err: {0:}")]
NotManaged(#[source] EGLError), NotManaged(#[source] EGLError),
/// Failed to create `EGLImages` from the buffer /// Failed to create `EGLBuffer` from the buffer
#[error("Failed to create EGLImages from the buffer. Err: {0:}")] #[error("Failed to create EGLImages from the buffer. Err: {0:}")]
EGLImageCreationFailed(#[source] EGLError), EGLImageCreationFailed(#[source] EGLError),
/// The required EGL extension is not supported by the underlying EGL implementation /// The required EGL extension is not supported by the underlying EGL implementation
@ -186,35 +186,6 @@ impl From<MakeCurrentError> for GraphicsSwapBuffersError {
} }
} }
/// Error that might happen when binding an `EGLImage` to a GL texture
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
pub enum TextureCreationError {
/// The given plane index is out of bounds
#[error("This buffer is not managed by EGL")]
PlaneIndexOutOfBounds,
/// The OpenGL context has been lost and needs to be recreated.
///
/// All the objects associated to it (textures, buffers, programs, etc.)
/// need to be recreated from scratch.
///
/// Operations will have no effect. Functions that read textures, buffers, etc.
/// from OpenGL will return uninitialized data instead.
///
/// A context loss usually happens on mobile devices when the user puts the
/// application on sleep and wakes it up later. However any OpenGL implementation
/// can theoretically lose the context at any time.
#[error("The context has been lost, it needs to be recreated")]
ContextLost,
/// Required OpenGL Extension for texture creation is missing
#[error("Required OpenGL Extension for texture creation is missing: {0}")]
GLExtensionNotSupported(&'static str),
/// Failed to bind the `EGLImage` to the given texture
///
/// The given argument is the GL error code
#[error("Failed to create EGLImages from the buffer (GL error code {0:x}")]
TextureBindingFailed(u32),
}
/// Texture format types /// Texture format types
#[repr(i32)] #[repr(i32)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
@ -261,7 +232,7 @@ pub struct EGLBuffer {
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
impl EGLBuffer { impl EGLBuffer {
/// Amount of planes of these `EGLImages` /// Amount of planes of this EGLBuffer
pub fn num_planes(&self) -> usize { pub fn num_planes(&self) -> usize {
self.format.num_planes() self.format.num_planes()
} }

View File

@ -172,7 +172,6 @@ impl EGLNativeDisplay for WinitWindow {
/// The returned [`NativeWindowType`](ffi::NativeWindowType) must be valid for EGL /// The returned [`NativeWindowType`](ffi::NativeWindowType) must be valid for EGL
/// and there is no way to test that. /// and there is no way to test that.
pub unsafe trait EGLNativeSurface: Send + Sync { pub unsafe trait EGLNativeSurface: Send + Sync {
/// Error type thrown by the surface creation in case of failure.
/// Create an EGLSurface from the internal native type. /// Create an EGLSurface from the internal native type.
/// ///
/// Must be able to deal with re-creation of existing resources, /// Must be able to deal with re-creation of existing resources,

View File

@ -46,10 +46,10 @@ impl EGLSurface {
/// Create a new `EGLSurface`. /// Create a new `EGLSurface`.
/// ///
/// Requires: /// Requires:
/// - A EGLDisplay supported by the corresponding plattform matching the surface type /// - A EGLDisplay supported by the corresponding platform matching the surface type
/// - A pixel format /// - A pixel format
/// - An (optional) preference for double_buffering
/// - A valid `EGLConfig` (see `EGLContext::config_id()`) /// - A valid `EGLConfig` (see `EGLContext::config_id()`)
/// - A native type backing the surface matching the used platform
/// - An (optional) Logger /// - An (optional) Logger
pub fn new<N, L>( pub fn new<N, L>(
display: &EGLDisplay, display: &EGLDisplay,

View File

@ -407,7 +407,7 @@ impl Gles2Renderer {
/// # Implementation details /// # Implementation details
/// ///
/// - Texture handles created by the resulting renderer are valid for every rendered created with an /// - Texture handles created by the resulting renderer are valid for every rendered created with an
/// `EGLContext` shared with the given one (see `EGLContext::new_shared`) and can be used and destroyed on /// `EGLContext` shared with the given one (see `EGLContext::new_shared`) and can be used on
/// any of these renderers. /// any of these renderers.
/// - This renderer has no default framebuffer, use `Bind::bind` before rendering. /// - This renderer has no default framebuffer, use `Bind::bind` before rendering.
/// - Binding a new target, while another one is already bound, will replace the current target. /// - Binding a new target, while another one is already bound, will replace the current target.