Skip to content

Commit

Permalink
feat: support wp_linux_drm_syncobj_timeline
Browse files Browse the repository at this point in the history
Also known as explicit sync.
  • Loading branch information
colinmarc committed Dec 11, 2024
1 parent 4bb63d3 commit 0d2baba
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 10 deletions.
8 changes: 8 additions & 0 deletions mm-server/src/compositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use wayland_protocols::{
wp::{
fractional_scale::v1::server::wp_fractional_scale_manager_v1,
linux_dmabuf::zv1::server::zwp_linux_dmabuf_v1,
linux_drm_syncobj::v1::server::wp_linux_drm_syncobj_manager_v1,
pointer_constraints::zv1::server::zwp_pointer_constraints_v1,
presentation_time::server::{wp_presentation, wp_presentation_feedback},
relative_pointer::zv1::server::zwp_relative_pointer_manager_v1,
Expand Down Expand Up @@ -111,6 +112,7 @@ pub struct State {
buffers: SlotMap<buffers::BufferKey, buffers::Buffer>,
shm_pools: SlotMap<shm::ShmPoolKey, shm::ShmPool>,
cached_dmabuf_feedback: buffers::CachedDmabufFeedback,
imported_buffer_timelines: SlotMap<buffers::BufferTimelineKey, buffers::BufferTimeline>,
pending_presentation_feedback: Vec<(
wp_presentation_feedback::WpPresentationFeedback,
VkTimelinePoint,
Expand Down Expand Up @@ -213,6 +215,7 @@ impl Compositor {
create_global::<wl_shm::WlShm>(&dh, 1);
create_global::<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>(&dh, 5);
create_global::<wp_presentation::WpPresentation>(&dh, 1);
create_global::<wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1>(&dh, 1);

create_global::<xwayland_shell_v1::XwaylandShellV1>(&dh, 1);
create_global::<wl_drm::WlDrm>(&dh, 2);
Expand Down Expand Up @@ -252,6 +255,7 @@ impl Compositor {
buffers: SlotMap::default(),
shm_pools: SlotMap::default(),
cached_dmabuf_feedback,
imported_buffer_timelines: SlotMap::default(),
pending_presentation_feedback: Vec::new(),

surface_stack: Vec::new(),
Expand Down Expand Up @@ -825,6 +829,10 @@ impl Compositor {
let buffer = &mut self.state.buffers[content.buffer];

let sync = match &mut buffer.backing {
buffers::BufferBacking::Dmabuf { .. } if content.explicit_sync.is_some() => {
let (acquire, _) = content.explicit_sync.as_ref().unwrap();
Some(video::TextureSync::TimelineAcquire(acquire.clone()))
}
buffers::BufferBacking::Dmabuf {
fd,
interop_sema,
Expand Down
27 changes: 24 additions & 3 deletions mm-server/src/compositor/buffers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ use drm_fourcc::DrmModifier;
use hashbrown::HashSet;
pub use modifiers::*;
use tracing::trace;
use wayland_protocols::wp::linux_drm_syncobj::v1::server::wp_linux_drm_syncobj_timeline_v1;
use wayland_server::{protocol::wl_buffer, Resource as _};

use crate::{
compositor::shm::Pool,
compositor::State,
compositor::{shm::Pool, State},
vulkan::{
create_image_view, select_memory_type, VkContext, VkHostBuffer, VkImage, VkTimelinePoint,
VkTimelineSemaphore,
},
};

Expand All @@ -38,6 +39,10 @@ pub struct Buffer {
/// buffer.
pub release_wait: Option<VkTimelinePoint>,

/// If set, we should signal this timeline point when we're done with
/// the buffer (instead of using the normal wl_buffer.release signal).
pub release_signal: Option<VkTimelinePoint>,

/// Next time we release this buffer, we should destroy it as well.
pub needs_destruction: bool,
}
Expand Down Expand Up @@ -101,6 +106,13 @@ pub struct PlaneMetadata {
pub offset: u32,
}

slotmap::new_key_type! { pub struct BufferTimelineKey; }

pub struct BufferTimeline {
pub _wp_syncobj_timeline: wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
pub sema: VkTimelineSemaphore,
}

impl State {
pub fn release_buffers(&mut self) -> anyhow::Result<()> {
let mut used_buffers = HashSet::new();
Expand Down Expand Up @@ -128,7 +140,14 @@ impl State {
"releasing buffer"
);

buffer.wl_buffer.release();
if let Some(tp) = &buffer.release_signal.take() {
unsafe {
tp.signal()?;
}
} else {
buffer.wl_buffer.release();
}

buffer.needs_release = false;
buffer.release_wait = None;
if buffer.needs_destruction {
Expand Down Expand Up @@ -194,6 +213,7 @@ pub fn import_shm_buffer(
},
needs_release: false,
release_wait: None,
release_signal: None,
needs_destruction: false,
})
}
Expand Down Expand Up @@ -347,6 +367,7 @@ pub fn import_dmabuf_buffer(
},
needs_release: false,
release_wait: None,
release_signal: None,
needs_destruction: false,
})
}
Expand Down
1 change: 1 addition & 0 deletions mm-server/src/compositor/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod wl_seat;
mod wl_shm;
mod wp_fractional_scale;
mod wp_linux_dmabuf;
mod wp_linux_drm_syncobj;
mod wp_pointer_constraints;
mod wp_presentation;
mod wp_relative_pointer;
Expand Down
181 changes: 181 additions & 0 deletions mm-server/src/compositor/dispatch/wp_linux_drm_syncobj.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Copyright 2024 Colin Marc <[email protected]>
//
// SPDX-License-Identifier: BUSL-1.1

use tracing::error;
use wayland_protocols::wp::linux_drm_syncobj::v1::server::{
wp_linux_drm_syncobj_manager_v1, wp_linux_drm_syncobj_surface_v1,
wp_linux_drm_syncobj_timeline_v1,
};
use wayland_server::Resource as _;

use crate::{
compositor::{
buffers::{BufferTimeline, BufferTimelineKey},
surface::SurfaceKey,
State,
},
vulkan::VkTimelineSemaphore,
};

impl wayland_server::GlobalDispatch<wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1, ()>
for State
{
fn bind(
_state: &mut Self,
_handle: &wayland_server::DisplayHandle,
_client: &wayland_server::Client,
resource: wayland_server::New<wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1>,
_global_data: &(),
data_init: &mut wayland_server::DataInit<'_, Self>,
) {
data_init.init(resource, ());
}
}

impl wayland_server::Dispatch<wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1, ()>
for State
{
fn request(
state: &mut Self,
_client: &wayland_server::Client,
resource: &wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1,
request: wp_linux_drm_syncobj_manager_v1::Request,
_data: &(),
_dhandle: &wayland_server::DisplayHandle,
data_init: &mut wayland_server::DataInit<'_, Self>,
) {
match request {
wp_linux_drm_syncobj_manager_v1::Request::GetSurface { id, surface } => {
if let Some(surface_key) = surface.data::<SurfaceKey>() {
let wp_syncobj_surface = data_init.init(id, *surface_key);

let surface = state
.surfaces
.get_mut(*surface_key)
.expect("surface has no entry");

if surface.wp_syncobj_surface.is_some() {
resource.post_error(
wp_linux_drm_syncobj_manager_v1::Error::SurfaceExists,
"A syncobj surface already exists for that wl_surface.",
);
return;
}

surface.wp_syncobj_surface = Some(wp_syncobj_surface);
}
}
wp_linux_drm_syncobj_manager_v1::Request::ImportTimeline { id, fd } => {
let sema = match VkTimelineSemaphore::from_syncobj_fd(state.vk.clone(), fd) {
Ok(t) => t,
Err(e) => {
error!("failed to import syncobj timeline: {e:#}");
resource.post_error(
wp_linux_drm_syncobj_manager_v1::Error::InvalidTimeline,
"Failed to import timeline.",
);
return;
}
};

state
.imported_buffer_timelines
.insert_with_key(|k| BufferTimeline {
_wp_syncobj_timeline: data_init.init(id, k),
sema,
});
}
wp_linux_drm_syncobj_manager_v1::Request::Destroy => (),
_ => unreachable!(),
}
}
}

impl
wayland_server::Dispatch<
wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
SurfaceKey,
> for State
{
fn request(
state: &mut Self,
_client: &wayland_server::Client,
_resource: &wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
request: wp_linux_drm_syncobj_surface_v1::Request,
surface_key: &SurfaceKey,
_dhandle: &wayland_server::DisplayHandle,
_data_init: &mut wayland_server::DataInit<'_, Self>,
) {
match request {
wp_linux_drm_syncobj_surface_v1::Request::SetAcquirePoint {
timeline,
point_hi,
point_lo,
} => {
let timeline = timeline
.data::<BufferTimelineKey>()
.and_then(|key| state.imported_buffer_timelines.get(*key))
.expect("timeline has no entry");

let surface = state
.surfaces
.get_mut(*surface_key)
.expect("surface has no entry");

surface.pending_acquire_point =
Some(timeline.sema.new_point(super::make_u64(point_hi, point_lo)))
}
wp_linux_drm_syncobj_surface_v1::Request::SetReleasePoint {
timeline,
point_hi,
point_lo,
} => {
let timeline = timeline
.data::<BufferTimelineKey>()
.and_then(|key| state.imported_buffer_timelines.get(*key))
.expect("timeline has no entry");

let surface = state
.surfaces
.get_mut(*surface_key)
.expect("surface has no entry");

surface.pending_release_point =
Some(timeline.sema.new_point(super::make_u64(point_hi, point_lo)))
}
wp_linux_drm_syncobj_surface_v1::Request::Destroy => (),
_ => unreachable!(),
}
}

fn destroyed(
state: &mut Self,
_client: wayland_server::backend::ClientId,
_resource: &wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
surface_key: &SurfaceKey,
) {
if let Some(surface) = state.surfaces.get_mut(*surface_key) {
surface.pending_acquire_point = None;
surface.pending_release_point = None;
}
}
}

impl
wayland_server::Dispatch<
wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
BufferTimelineKey,
> for State
{
fn request(
_state: &mut Self,
_client: &wayland_server::Client,
_resource: &wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
_request: wp_linux_drm_syncobj_timeline_v1::Request,
_data: &BufferTimelineKey,
_dhandle: &wayland_server::DisplayHandle,
_data_init: &mut wayland_server::DataInit<'_, Self>,
) {
}
}
Loading

0 comments on commit 0d2baba

Please sign in to comment.