Skip to content

Commit

Permalink
WIP linux-drm-syncobj-v1
Browse files Browse the repository at this point in the history
  • Loading branch information
ids1024 committed Sep 12, 2024
1 parent 31a7722 commit ae2eec6
Show file tree
Hide file tree
Showing 10 changed files with 756 additions and 18 deletions.
29 changes: 29 additions & 0 deletions anvil/src/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use std::cell::RefCell;

#[cfg(feature = "xwayland")]
use smithay::xwayland::XWaylandClientData;

#[cfg(feature = "udev")]
use smithay::wayland::drm_syncobj::DrmSyncobjCachedState;

use smithay::{
backend::renderer::utils::on_commit_buffer_handler,
desktop::{
Expand Down Expand Up @@ -111,7 +115,17 @@ impl<BackendData: Backend> CompositorHandler for AnvilState<BackendData> {

fn new_surface(&mut self, surface: &WlSurface) {
add_pre_commit_hook::<Self, _>(surface, move |state, _dh, surface| {
#[cfg(feature = "udev")]
let mut acquire_point = None;
let maybe_dmabuf = with_states(surface, |surface_data| {
#[cfg(feature = "udev")]
acquire_point.clone_from(
&surface_data
.cached_state
.get::<DrmSyncobjCachedState>()
.pending()
.acquire_point,
);
surface_data
.cached_state
.get::<SurfaceAttributes>()
Expand All @@ -124,6 +138,21 @@ impl<BackendData: Backend> CompositorHandler for AnvilState<BackendData> {
})
});
if let Some(dmabuf) = maybe_dmabuf {
#[cfg(feature = "udev")]
if let Some(acquire_point) = acquire_point {
if let Ok((blocker, source)) = acquire_point.generate_blocker() {
let client = surface.client().unwrap();
let res = state.handle.insert_source(source, move |_, _, data| {
let dh = data.display_handle.clone();
data.client_compositor_state(&client).blocker_cleared(data, &dh);
Ok(())
});
if res.is_ok() {
add_blocker(surface, blocker);
return;
}
}
}
if let Ok((blocker, source)) = dmabuf.generate_blocker(Interest::READ) {
if let Some(client) = surface.client() {
let res = state.handle.insert_source(source, move |_, _, data| {
Expand Down
27 changes: 27 additions & 0 deletions anvil/src/udev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ use smithay::{
drm_lease::{
DrmLease, DrmLeaseBuilder, DrmLeaseHandler, DrmLeaseRequest, DrmLeaseState, LeaseRejected,
},
drm_syncobj::{supports_syncobj_eventfd, DrmSyncobjHandler, DrmSyncobjState},
},
};
use smithay_drm_extras::{
Expand Down Expand Up @@ -124,6 +125,7 @@ pub struct UdevData {
pub session: LibSeatSession,
dh: DisplayHandle,
dmabuf_state: Option<(DmabufState, DmabufGlobal)>,
syncobj_state: Option<DrmSyncobjState>,
primary_gpu: DrmNode,
gpus: GpuManager<GbmGlesBackend<GlesRenderer, DrmDeviceFd>>,
backends: HashMap<DrmNode, BackendData>,
Expand Down Expand Up @@ -247,6 +249,7 @@ pub fn run_udev() {
let data = UdevData {
dh: display_handle.clone(),
dmabuf_state: None,
syncobj_state: None,
session,
primary_gpu,
gpus,
Expand Down Expand Up @@ -434,6 +437,23 @@ pub fn run_udev() {
});
});

// Expose syncobj protocol if supported by primary GPU
if let Some(primary_node) = state
.backend_data
.primary_gpu
.node_with_type(NodeType::Primary)
.and_then(|x| x.ok())
{
if let Some(backend) = state.backend_data.backends.get(&primary_node) {
let import_device = backend.drm.device_fd().clone();
if supports_syncobj_eventfd(&import_device) {
let syncobj_state =
DrmSyncobjState::new::<AnvilState<UdevData>>(&display_handle, import_device);
state.backend_data.syncobj_state = Some(syncobj_state);
}
}
}

event_loop
.handle()
.insert_source(udev_backend, move |event, _, data| match event {
Expand Down Expand Up @@ -553,6 +573,13 @@ impl DrmLeaseHandler for AnvilState<UdevData> {

delegate_drm_lease!(AnvilState<UdevData>);

impl DrmSyncobjHandler for AnvilState<UdevData> {
fn drm_syncobj_state(&mut self) -> &mut DrmSyncobjState {
self.backend_data.syncobj_state.as_mut().unwrap()
}
}
smithay::delegate_drm_syncobj!(AnvilState<UdevData>);

pub type RenderSurface = GbmBufferedSurface<GbmAllocator<DrmDeviceFd>, Option<OutputPresentationFeedback>>;

pub type GbmDrmCompositor = DrmCompositor<
Expand Down
43 changes: 41 additions & 2 deletions src/backend/drm/compositor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ use std::{
};

use drm::{
control::{connector, crtc, framebuffer, plane, Mode, PlaneType},
control::{connector, crtc, framebuffer, plane, Device as _, Mode, PlaneType},
Device, DriverCapability,
};
use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier};
Expand Down Expand Up @@ -204,6 +204,22 @@ enum ScanoutBuffer<B: Buffer> {
Cursor(GbmBuffer),
}

impl<B: Buffer> ScanoutBuffer<B> {
fn acquire_point(
&self,
signaled_fence: Option<&Arc<OwnedFd>>,
) -> Option<(SyncPoint, Option<Arc<OwnedFd>>)> {
if let Self::Wayland(buffer) = self {
// Assume `DrmSyncobjBlocker` is used, so acquire point has already
// been signaled. Instead of converting with `SyncPoint::from`.
if buffer.acquire_point().is_some() {
return Some((SyncPoint::signaled(), signaled_fence.cloned()));
}
}
None
}
}

impl<B: Buffer> ScanoutBuffer<B> {
#[inline]
fn from_underlying_storage(storage: UnderlyingStorage<'_>) -> Option<Self> {
Expand Down Expand Up @@ -1567,6 +1583,7 @@ where
supports_fencing: bool,
direct_scanout: bool,
reset_pending: bool,
signaled_fence: Option<Arc<OwnedFd>>,

framebuffer_exporter: F,

Expand Down Expand Up @@ -1633,6 +1650,24 @@ where
cursor_size: Size<u32, BufferCoords>,
gbm: Option<GbmDevice<G>>,
) -> FrameResult<Self, A, F> {
let signaled_fence = match surface.create_syncobj(true) {
Ok(signaled_syncobj) => match surface.syncobj_to_fd(signaled_syncobj, true) {
Ok(signaled_fence) => {
let _ = surface.destroy_syncobj(signaled_syncobj);
Some(Arc::new(signaled_fence))
}
Err(err) => {
tracing::warn!(?err, "failed to export signaled syncobj");
let _ = surface.destroy_syncobj(signaled_syncobj);
None
}
},
Err(err) => {
tracing::warn!(?err, "failed to create signaled syncobj");
None
}
};

let span = info_span!(
parent: None,
"drm_compositor",
Expand Down Expand Up @@ -1735,6 +1770,7 @@ where
primary_is_opaque: is_opaque,
direct_scanout: true,
reset_pending: true,
signaled_fence,
current_frame,
pending_frame: None,
queued_frame: None,
Expand Down Expand Up @@ -4095,7 +4131,10 @@ where
buffer: element_config.buffer.clone(),
damage_clips,
plane_claim,
sync: None,
sync: element_config
.buffer
.buffer
.acquire_point(self.signaled_fence.as_ref()),
};

let is_compatible = previous_state
Expand Down
4 changes: 2 additions & 2 deletions src/backend/drm/device/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use tracing::{error, info, warn};

use crate::utils::{DevPath, DeviceFd};

#[derive(Debug)]
#[derive(Debug, PartialEq)]
struct InternalDrmDeviceFd {
fd: DeviceFd,
privileged: bool,
Expand All @@ -33,7 +33,7 @@ impl BasicDevice for InternalDrmDeviceFd {}
impl ControlDevice for InternalDrmDeviceFd {}

/// Ref-counted file descriptor of an open drm device
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct DrmDeviceFd(Arc<InternalDrmDeviceFd>);

impl AsFd for DrmDeviceFd {
Expand Down
5 changes: 5 additions & 0 deletions src/backend/renderer/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ impl SyncPoint {
}
}

/// Returns `true` if `SyncPoint` contains a [`Fence`]
pub fn contains_fence(&self) -> bool {
self.fence.is_some()
}

/// Get a reference to the underlying [`Fence`] if any
///
/// Returns `None` if the sync point does not contain a fence
Expand Down
54 changes: 40 additions & 14 deletions src/backend/renderer/utils/wayland.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(feature = "backend_drm")]
use crate::wayland::drm_syncobj::{DrmSyncPoint, DrmSyncobjCachedState};
use crate::{
backend::renderer::{buffer_dimensions, buffer_has_alpha, element::RenderElement, ImportAll, Renderer},
utils::{Buffer as BufferCoord, Coordinate, Logical, Physical, Point, Rectangle, Scale, Size, Transform},
Expand Down Expand Up @@ -56,12 +58,24 @@ unsafe impl Send for RendererSurfaceState {}
unsafe impl Sync for RendererSurfaceState {}

#[derive(Debug)]
struct InnerBuffer(WlBuffer);
struct InnerBuffer {
buffer: WlBuffer,
#[cfg(feature = "backend_drm")]
acquire_point: Option<DrmSyncPoint>,
#[cfg(feature = "backend_drm")]
release_point: Option<DrmSyncPoint>,
}

impl Drop for InnerBuffer {
#[inline]
fn drop(&mut self) {
self.0.release();
self.buffer.release();
#[cfg(feature = "backend_drm")]
if let Some(release_point) = &self.release_point {
if let Err(err) = release_point.signal() {
tracing::error!("Failed to signal syncobj release point: {}", err);
}
}
}
}

Expand All @@ -71,12 +85,11 @@ pub struct Buffer {
inner: Arc<InnerBuffer>,
}

impl From<WlBuffer> for Buffer {
#[inline]
fn from(buffer: WlBuffer) -> Self {
Buffer {
inner: Arc::new(InnerBuffer(buffer)),
}
impl Buffer {
#[cfg(feature = "backend_drm")]
#[allow(dead_code)]
pub(crate) fn acquire_point(&self) -> Option<&DrmSyncPoint> {
self.inner.acquire_point.as_ref()
}
}

Expand All @@ -85,29 +98,34 @@ impl std::ops::Deref for Buffer {

#[inline]
fn deref(&self) -> &Self::Target {
&self.inner.0
&self.inner.buffer
}
}

impl PartialEq<WlBuffer> for Buffer {
#[inline]
fn eq(&self, other: &WlBuffer) -> bool {
self.inner.0 == *other
self.inner.buffer == *other
}
}

impl PartialEq<WlBuffer> for &Buffer {
#[inline]
fn eq(&self, other: &WlBuffer) -> bool {
self.inner.0 == *other
self.inner.buffer == *other
}
}

impl RendererSurfaceState {
#[profiling::function]
pub(crate) fn update_buffer(&mut self, states: &SurfaceData) {
let mut attrs_state = states.cached_state.get::<SurfaceAttributes>();
let attrs = attrs_state.current();
#[cfg(feature = "backend_drm")]
let mut guard = states.cached_state.get::<DrmSyncobjCachedState>();
#[cfg(feature = "backend_drm")]
let syncobj_state = guard.current();

let mut guard = states.cached_state.get::<SurfaceAttributes>();
let attrs = guard.current();
self.buffer_delta = attrs.buffer_delta.take();

if let Some(delta) = self.buffer_delta {
Expand All @@ -129,7 +147,15 @@ impl RendererSurfaceState {
self.buffer_transform = attrs.buffer_transform.into();

if !self.buffer.as_ref().map_or(false, |b| b == buffer) {
self.buffer = Some(Buffer::from(buffer));
self.buffer = Some(Buffer {
inner: Arc::new(InnerBuffer {
buffer,
#[cfg(feature = "backend_drm")]
acquire_point: syncobj_state.acquire_point.take(),
#[cfg(feature = "backend_drm")]
release_point: syncobj_state.release_point.take(),
}),
});
}

self.textures.clear();
Expand Down
18 changes: 18 additions & 0 deletions src/wayland/compositor/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use crate::{
},
wayland::compositor::SUBSURFACE_ROLE,
};
#[cfg(feature = "backend_drm")]
use crate::wayland::drm_syncobj::DrmSyncobjCachedState;

use super::{
cache::MultiCache,
Expand Down Expand Up @@ -150,9 +152,25 @@ impl PrivateSurfaceData {
if let Some(BufferAssignment::NewBuffer(buffer)) = guard.pending().buffer.take() {
buffer.release();
};
drop(guard);
#[cfg(feature = "backend_drm")]
let mut guard = my_data.public_data.cached_state.get::<DrmSyncobjCachedState>();
#[cfg(feature = "backend_drm")]
if let Some(release_point) = &guard.pending().release_point {
if let Err(err) = release_point.signal() {
tracing::error!("Failed to signal syncobj release point: {}", err);
}
}
#[cfg(feature = "backend_drm")]
if let Some(release_point) = &guard.current().release_point {
if let Err(err) = release_point.signal() {
tracing::error!("Failed to signal syncobj release point: {}", err);
}
}

let hooks = my_data.destruction_hooks.clone();
// don't hold the mutex while the hooks are invoked
#[cfg(feature = "backend_drm")]
drop(guard);
drop(my_data);
for hook in hooks {
Expand Down
Loading

0 comments on commit ae2eec6

Please sign in to comment.