Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add linux-drm-syncobj-v1 protocol #1356

Merged
merged 4 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -112,7 +116,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 @@ -125,6 +139,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) {
Drakulix marked this conversation as resolved.
Show resolved Hide resolved
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
4 changes: 2 additions & 2 deletions src/backend/allocator/dmabuf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ impl Dmabuf {
*self.0.node.lock().unwrap() = node.into();
}

/// Create an [`calloop::EventSource`] and [`crate::wayland::compositor::Blocker`] for this [`Dmabuf`].
/// Create an [`calloop::EventSource`] and [`Blocker`] for this [`Dmabuf`].
///
/// Usually used to block applying surface state on the readiness of an attached dmabuf.
#[cfg(feature = "wayland_frontend")]
Expand Down Expand Up @@ -526,7 +526,7 @@ where
}
}

/// [`crate::wayland::compositor::Blocker`] implementation for an accompaning [`DmabufSource`]
/// [`Blocker`] implementation for an accompaning [`DmabufSource`]
#[cfg(feature = "wayland_frontend")]
#[derive(Debug)]
pub struct DmabufBlocker(Arc<AtomicBool>);
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
77 changes: 64 additions & 13 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 @@ -53,12 +55,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 @@ -68,43 +82,72 @@ 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 {
/// Create a buffer with implicit sync
pub fn with_implicit(buffer: WlBuffer) -> Self {
Self {
inner: Arc::new(InnerBuffer {
buffer,
#[cfg(feature = "backend_drm")]
acquire_point: None,
#[cfg(feature = "backend_drm")]
release_point: None,
}),
}
}

/// Create a buffer with explicit acquire and release sync points
#[cfg(feature = "backend_drm")]
pub fn with_explicit(buffer: WlBuffer, acquire_point: DrmSyncPoint, release_point: DrmSyncPoint) -> Self {
Self {
inner: Arc::new(InnerBuffer {
buffer,
acquire_point: Some(acquire_point),
release_point: Some(release_point),
}),
}
}

#[cfg(feature = "backend_drm")]
#[allow(dead_code)]
pub(crate) fn acquire_point(&self) -> Option<&DrmSyncPoint> {
self.inner.acquire_point.as_ref()
}
}

impl std::ops::Deref for Buffer {
type Target = WlBuffer;

#[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();

let new_buffer = matches!(attrs.buffer, Some(BufferAssignment::NewBuffer(_)));
match attrs.buffer.take() {
Expand All @@ -121,7 +164,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
Loading
Loading