wayland: introduce explicit-synchronization helpers
This commit is contained in:
parent
04dc563ea0
commit
1627d51cf6
10
Cargo.toml
10
Cargo.toml
|
@ -11,9 +11,9 @@ edition = "2018"
|
||||||
members = [ "anvil" ]
|
members = [ "anvil" ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wayland-server = { version = "0.23.2", optional = true }
|
wayland-server = { version = "0.23.4", optional = true }
|
||||||
wayland-commons = { version = "0.23.3", optional = true }
|
wayland-commons = { version = "0.23.4", optional = true }
|
||||||
wayland-sys = { version = "0.23", optional = true }
|
wayland-sys = { version = "0.23.4", optional = true }
|
||||||
calloop = "0.4.2"
|
calloop = "0.4.2"
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
nix = "0.13"
|
nix = "0.13"
|
||||||
|
@ -22,7 +22,7 @@ tempfile = "3.0"
|
||||||
slog = "2.1.1"
|
slog = "2.1.1"
|
||||||
slog-stdlog = "3.0.2"
|
slog-stdlog = "3.0.2"
|
||||||
libloading = "0.5.0"
|
libloading = "0.5.0"
|
||||||
wayland-client = { version = "0.23", features = ["egl"], optional = true }
|
wayland-client = { version = "0.23.4", features = ["egl"], optional = true }
|
||||||
winit = { version = "0.18.0", optional = true }
|
winit = { version = "0.18.0", optional = true }
|
||||||
drm = { version = "^0.3.4", optional = true }
|
drm = { version = "^0.3.4", optional = true }
|
||||||
gbm = { version = "^0.5.0", optional = true, default-features = false, features = ["drm-support"] }
|
gbm = { version = "^0.5.0", optional = true, default-features = false, features = ["drm-support"] }
|
||||||
|
@ -31,7 +31,7 @@ input = { version = "0.4.1", optional = true }
|
||||||
udev = { version = "0.2.0", optional = true }
|
udev = { version = "0.2.0", optional = true }
|
||||||
dbus = { version = "0.6.1", optional = true }
|
dbus = { version = "0.6.1", optional = true }
|
||||||
systemd = { version = "0.4.0", optional = true }
|
systemd = { version = "0.4.0", optional = true }
|
||||||
wayland-protocols = { version = "0.23", features = ["unstable_protocols", "server"], optional = true }
|
wayland-protocols = { version = "0.23.4", features = ["unstable_protocols", "server"], optional = true }
|
||||||
image = { version = "0.21.0", optional = true }
|
image = { version = "0.21.0", optional = true }
|
||||||
error-chain = "0.12.0"
|
error-chain = "0.12.0"
|
||||||
lazy_static = "1.0.0"
|
lazy_static = "1.0.0"
|
||||||
|
|
|
@ -0,0 +1,315 @@
|
||||||
|
//! Explicit buffer synchronization per wayland surface
|
||||||
|
//!
|
||||||
|
//! This interface allow clients to switch from a per-buffer signaling of buffer release (via the
|
||||||
|
//! `wl_buffer.release` event) to a per-surface signaling using `dma_fence`s. This is notably used for
|
||||||
|
//! efficient synchronization of OpenGL/Vulkan clients.
|
||||||
|
//!
|
||||||
|
//! At surface commit time, in addition to a buffer the client can have attached two more properties:
|
||||||
|
//!
|
||||||
|
//! - an acquire `dma_fence` file descriptor, that the compositor is required to wait on before it will
|
||||||
|
//! try to access the contents of the associated buffer
|
||||||
|
//! - an `ExplicitBufferRelease` object, that the compositor is expect to use to signal the client when it has
|
||||||
|
//! finished using the buffer for this surface (if the same buffer is attached to multiple surfaces, the
|
||||||
|
//! release only applies for the surface associated with this release object, not the whole buffer).
|
||||||
|
//!
|
||||||
|
//! The use of these `dma_fence`s in conjunction with the graphics stack allows for efficient synchronization
|
||||||
|
//! between the clients and the compositor.
|
||||||
|
//!
|
||||||
|
//! ## Usage
|
||||||
|
//!
|
||||||
|
//! First, you need to initialize the global:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # extern crate wayland_server;
|
||||||
|
//! # #[macro_use] extern crate smithay;
|
||||||
|
//! #
|
||||||
|
//! # use smithay::wayland::compositor::roles::*;
|
||||||
|
//! # use smithay::wayland::compositor::CompositorToken;
|
||||||
|
//! use smithay::wayland::explicit_synchronization::*;
|
||||||
|
//! # define_roles!(MyRoles);
|
||||||
|
//! #
|
||||||
|
//! # fn main() {
|
||||||
|
//! # let mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap();
|
||||||
|
//! # let mut display = wayland_server::Display::new(event_loop.handle());
|
||||||
|
//! # let (compositor_token, _, _) = smithay::wayland::compositor::compositor_init::<MyRoles, _, _>(
|
||||||
|
//! # &mut display,
|
||||||
|
//! # |_, _, _| {},
|
||||||
|
//! # None
|
||||||
|
//! # );
|
||||||
|
//! init_explicit_synchronization_global(
|
||||||
|
//! &mut display,
|
||||||
|
//! compositor_token,
|
||||||
|
//! None /* You can insert a logger here */
|
||||||
|
//! );
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Then when handling a surface commit, you can retrieve the synchronization information for the surface
|
||||||
|
//! data:
|
||||||
|
//! ```no_run
|
||||||
|
//! # extern crate wayland_server;
|
||||||
|
//! # #[macro_use] extern crate smithay;
|
||||||
|
//! #
|
||||||
|
//! # use wayland_server::protocol::wl_surface::WlSurface;
|
||||||
|
//! # use smithay::wayland::compositor::CompositorToken;
|
||||||
|
//! # use smithay::wayland::explicit_synchronization::*;
|
||||||
|
//! #
|
||||||
|
//! # fn dummy_function<R: 'static>(surface: &WlSurface, compositor_token: CompositorToken<R>) {
|
||||||
|
//! compositor_token.with_surface_data(&surface, |surface_attributes| {
|
||||||
|
//! // While you retrieve the surface data from the commit ...
|
||||||
|
//! // Check the explicit synchronization data:
|
||||||
|
//! match get_explicit_synchronization_state(surface_attributes) {
|
||||||
|
//! Ok(sync_state) => {
|
||||||
|
//! /* This surface is explicitly synchronized, you need to handle
|
||||||
|
//! the contents of sync_state
|
||||||
|
//! */
|
||||||
|
//! },
|
||||||
|
//! Err(()) => {
|
||||||
|
//! /* This surface is not explicitly synchronized, nothing more to do
|
||||||
|
//! */
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! });
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
|
||||||
|
use wayland_protocols::unstable::linux_explicit_synchronization::v1::server::*;
|
||||||
|
use wayland_server::{protocol::wl_surface::WlSurface, Display, Global, NewResource};
|
||||||
|
|
||||||
|
use crate::wayland::compositor::{CompositorToken, SurfaceAttributes};
|
||||||
|
|
||||||
|
/// An object to signal end of use of a buffer
|
||||||
|
pub struct ExplicitBufferRelease {
|
||||||
|
release: zwp_linux_buffer_release_v1::ZwpLinuxBufferReleaseV1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExplicitBufferRelease {
|
||||||
|
/// Immediately release the buffer
|
||||||
|
///
|
||||||
|
/// The client can reuse it as soon as this event is sent.
|
||||||
|
pub fn immediate_release(self) {
|
||||||
|
self.release.immediate_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a release fence to the client
|
||||||
|
///
|
||||||
|
/// The client will be allowed to reuse the buffer once you signal this `dma_fence`.
|
||||||
|
pub fn send_release_fence(self, fence: RawFd) {
|
||||||
|
self.release.fenced_release(fence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An explicit synchronization state
|
||||||
|
///
|
||||||
|
/// The client is not required to fill both. `acquire` being `None` means that you don't need to wait
|
||||||
|
/// before acessing the buffer, `release` being `None` means that the client does not require additionnal
|
||||||
|
/// signaling that you are finished (you still need to send `wl_buffer.release`).
|
||||||
|
pub struct ExplicitSyncState {
|
||||||
|
/// An acquire `dma_fence` object, that you should wait on before accessing the contents of the
|
||||||
|
/// buffer associated with the surface.
|
||||||
|
pub acquire: Option<RawFd>,
|
||||||
|
/// A buffer release object, that you should use to signal the client when you are done using the
|
||||||
|
/// buffer associated with the surface.
|
||||||
|
pub release: Option<ExplicitBufferRelease>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InternalState {
|
||||||
|
sync_state: ExplicitSyncState,
|
||||||
|
sync_resource: zwp_linux_surface_synchronization_v1::ZwpLinuxSurfaceSynchronizationV1,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ESUserData {
|
||||||
|
state: Option<InternalState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ESUserData {
|
||||||
|
fn take_state(&mut self) -> Option<ExplicitSyncState> {
|
||||||
|
if let Some(ref mut state) = self.state {
|
||||||
|
Some(ExplicitSyncState {
|
||||||
|
acquire: state.sync_state.acquire.take(),
|
||||||
|
release: state.sync_state.release.take(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Possible errors you can send to an ill-behaving clients
|
||||||
|
pub enum ExplicitSyncError {
|
||||||
|
/// An invalid file descriptor was sent by the client for an acquire fence
|
||||||
|
InvalidFence,
|
||||||
|
/// The client requested synchronization for a buffer type that does not support it
|
||||||
|
UnsupportedBuffer,
|
||||||
|
/// The client requested synchronization while not having attached any buffer
|
||||||
|
NoBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve the explicit synchronization state commited by the client
|
||||||
|
///
|
||||||
|
/// This state can contain an acquire fence and a release object, for synchronization (see module-level docs).
|
||||||
|
///
|
||||||
|
/// This function will clear the pending state, preparing the surface for the next commit, as a result you
|
||||||
|
/// should always call it on surface commit to avoid getting out-of-sync with the client.
|
||||||
|
///
|
||||||
|
/// This function returns an error if the client has not setup explicit synchronization for this surface.
|
||||||
|
pub fn get_explicit_synchronization_state(attrs: &mut SurfaceAttributes) -> Result<ExplicitSyncState, ()> {
|
||||||
|
attrs
|
||||||
|
.user_data
|
||||||
|
.get_mut::<ESUserData>()
|
||||||
|
.and_then(|s| s.take_state())
|
||||||
|
.ok_or(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a synchronization error to a client
|
||||||
|
///
|
||||||
|
/// See the enum definition for possible errors. These errors are protocol errors, meaning that
|
||||||
|
/// the client associated with this `SurfaceAttributes` will be killed as a result of calling this
|
||||||
|
/// function.
|
||||||
|
pub fn send_explicit_synchronization_error(attrs: &SurfaceAttributes, error: ExplicitSyncError) {
|
||||||
|
if let Some(ref data) = attrs.user_data.get::<ESUserData>() {
|
||||||
|
if let Some(ref state) = data.state {
|
||||||
|
match error {
|
||||||
|
ExplicitSyncError::InvalidFence => state.sync_resource.as_ref().post_error(
|
||||||
|
zwp_linux_surface_synchronization_v1::Error::InvalidFence as u32,
|
||||||
|
"The fence specified by the client could not be imported.".into(),
|
||||||
|
),
|
||||||
|
ExplicitSyncError::UnsupportedBuffer => state.sync_resource.as_ref().post_error(
|
||||||
|
zwp_linux_surface_synchronization_v1::Error::UnsupportedBuffer as u32,
|
||||||
|
"The buffer does not support explicit synchronization.".into(),
|
||||||
|
),
|
||||||
|
ExplicitSyncError::NoBuffer => state.sync_resource.as_ref().post_error(
|
||||||
|
zwp_linux_surface_synchronization_v1::Error::NoBuffer as u32,
|
||||||
|
"No buffer was attached.".into(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the explicit synchronization global
|
||||||
|
///
|
||||||
|
/// See module-level documentation for its use.
|
||||||
|
pub fn init_explicit_synchronization_global<R, L>(
|
||||||
|
display: &mut Display,
|
||||||
|
compositor: CompositorToken<R>,
|
||||||
|
logger: L,
|
||||||
|
) -> Global<zwp_linux_explicit_synchronization_v1::ZwpLinuxExplicitSynchronizationV1>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
R: 'static,
|
||||||
|
{
|
||||||
|
let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "wayland_explicit_synchronization"));
|
||||||
|
|
||||||
|
display.create_global::<zwp_linux_explicit_synchronization_v1::ZwpLinuxExplicitSynchronizationV1, _>(
|
||||||
|
2,
|
||||||
|
move |new_sync, version| {
|
||||||
|
new_sync.implement_closure(
|
||||||
|
move |req, explicit_sync| match req {
|
||||||
|
zwp_linux_explicit_synchronization_v1::Request::GetSynchronization { id, surface } => {
|
||||||
|
let exists = compositor.with_surface_data(&surface, |attrs| {
|
||||||
|
attrs.user_data.insert_if_missing(|| ESUserData { state: None });
|
||||||
|
attrs
|
||||||
|
.user_data
|
||||||
|
.get::<ESUserData>()
|
||||||
|
.map(|ud| ud.state.is_some())
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
if exists {
|
||||||
|
explicit_sync.as_ref().post_error(
|
||||||
|
zwp_linux_explicit_synchronization_v1::Error::SynchronizationExists as u32,
|
||||||
|
"The surface already has a synchronization object associated.".into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let surface_sync = implement_surface_sync(id, surface.clone(), compositor);
|
||||||
|
compositor.with_surface_data(&surface, |attrs| {
|
||||||
|
let data = attrs.user_data.get_mut::<ESUserData>().unwrap();
|
||||||
|
data.state = Some(InternalState {
|
||||||
|
sync_state: ExplicitSyncState {
|
||||||
|
acquire: None,
|
||||||
|
release: None,
|
||||||
|
},
|
||||||
|
sync_resource: surface_sync,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
None::<fn(_)>,
|
||||||
|
(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn implement_surface_sync<R>(
|
||||||
|
id: NewResource<zwp_linux_surface_synchronization_v1::ZwpLinuxSurfaceSynchronizationV1>,
|
||||||
|
surface: WlSurface,
|
||||||
|
compositor: CompositorToken<R>,
|
||||||
|
) -> zwp_linux_surface_synchronization_v1::ZwpLinuxSurfaceSynchronizationV1
|
||||||
|
where
|
||||||
|
R: 'static,
|
||||||
|
{
|
||||||
|
id.implement_closure(
|
||||||
|
move |req, surface_sync| match req {
|
||||||
|
zwp_linux_surface_synchronization_v1::Request::SetAcquireFence { fd } => {
|
||||||
|
if !surface.as_ref().is_alive() {
|
||||||
|
surface_sync.as_ref().post_error(
|
||||||
|
zwp_linux_surface_synchronization_v1::Error::NoSurface as u32,
|
||||||
|
"The associated wl_surface was destroyed.".into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
compositor.with_surface_data(&surface, |attrs| {
|
||||||
|
let data = attrs.user_data.get_mut::<ESUserData>().unwrap();
|
||||||
|
if let Some(ref mut state) = data.state {
|
||||||
|
if state.sync_state.acquire.is_some() {
|
||||||
|
surface_sync.as_ref().post_error(
|
||||||
|
zwp_linux_surface_synchronization_v1::Error::DuplicateFence as u32,
|
||||||
|
"Multiple fences added for a single surface commit.".into(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
state.sync_state.acquire = Some(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
zwp_linux_surface_synchronization_v1::Request::GetRelease { release } => {
|
||||||
|
if !surface.as_ref().is_alive() {
|
||||||
|
surface_sync.as_ref().post_error(
|
||||||
|
zwp_linux_surface_synchronization_v1::Error::NoSurface as u32,
|
||||||
|
"The associated wl_surface was destroyed.".into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
compositor.with_surface_data(&surface, |attrs| {
|
||||||
|
let data = attrs.user_data.get_mut::<ESUserData>().unwrap();
|
||||||
|
if let Some(ref mut state) = data.state {
|
||||||
|
if state.sync_state.acquire.is_some() {
|
||||||
|
surface_sync.as_ref().post_error(
|
||||||
|
zwp_linux_surface_synchronization_v1::Error::DuplicateRelease as u32,
|
||||||
|
"Multiple releases added for a single surface commit.".into(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
state.sync_state.release = Some(ExplicitBufferRelease {
|
||||||
|
release: release.implement_dummy(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
zwp_linux_surface_synchronization_v1::Request::Destroy => {
|
||||||
|
// disable the ESUserData
|
||||||
|
compositor.with_surface_data(&surface, |attrs| {
|
||||||
|
if let Some(ref mut data) = attrs.user_data.get_mut::<ESUserData>() {
|
||||||
|
data.state = None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
None::<fn(_)>,
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ pub mod compositor;
|
||||||
pub mod data_device;
|
pub mod data_device;
|
||||||
#[cfg(feature = "backend_drm")]
|
#[cfg(feature = "backend_drm")]
|
||||||
pub mod dmabuf;
|
pub mod dmabuf;
|
||||||
|
pub mod explicit_synchronization;
|
||||||
pub mod output;
|
pub mod output;
|
||||||
pub mod seat;
|
pub mod seat;
|
||||||
pub mod shell;
|
pub mod shell;
|
||||||
|
|
Loading…
Reference in New Issue