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" ]
|
||||
|
||||
[dependencies]
|
||||
wayland-server = { version = "0.23.2", optional = true }
|
||||
wayland-commons = { version = "0.23.3", optional = true }
|
||||
wayland-sys = { version = "0.23", optional = true }
|
||||
wayland-server = { version = "0.23.4", optional = true }
|
||||
wayland-commons = { version = "0.23.4", optional = true }
|
||||
wayland-sys = { version = "0.23.4", optional = true }
|
||||
calloop = "0.4.2"
|
||||
bitflags = "1"
|
||||
nix = "0.13"
|
||||
|
@ -22,7 +22,7 @@ tempfile = "3.0"
|
|||
slog = "2.1.1"
|
||||
slog-stdlog = "3.0.2"
|
||||
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 }
|
||||
drm = { version = "^0.3.4", optional = true }
|
||||
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 }
|
||||
dbus = { version = "0.6.1", 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 }
|
||||
error-chain = "0.12.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;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
pub mod dmabuf;
|
||||
pub mod explicit_synchronization;
|
||||
pub mod output;
|
||||
pub mod seat;
|
||||
pub mod shell;
|
||||
|
|
Loading…
Reference in New Issue