Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Smithay/drm-rs/llms.txt

Use this file to discover all available pages before exploring further.

DRM sync objects are kernel-managed fence binding points. A sync object starts empty (unsignalled). A GPU command submission IOCTL — or an explicit signal call — attaches a fence to it. Once the fence fires (the GPU work completes), the sync object becomes signalled and any waiters are unblocked. Sync objects serve as the bridge between GPU execution and display: an atomic commit can accept a sync object that fires only when the previous frame’s GPU rendering finishes, preventing tearing and removing the need for CPU-side blocking. drm-rs exposes both the binary sync object model (a single fence slot, suitable for Vulkan VkFence semantics) and the timeline sync object model (an ordered sequence of sequence points, suitable for Vulkan VkSemaphore with timeline semantics).
Check DriverCapability::SyncObj before using binary sync objects and DriverCapability::TimelineSyncObj before using timeline operations. Calling sync object ioctls on a driver that does not support them returns an error.

syncobj::Handle

An opaque, copy-cheap handle to a sync object resource. Internally a NonZeroU32 wrapped with repr(transparent). Also implements bytemuck::NoUninit, allowing slices of handles to be cast directly to &[u32] for batch operations.
let so: syncobj::Handle = card.create_syncobj(false).unwrap();

Binary Sync Object Operations

create_syncobj

fn create_syncobj(&self, signalled: bool) -> io::Result<syncobj::Handle>
Creates a new sync object. If signalled is true, the object starts in the signalled state (useful for priming the first frame of a sequence where no prior GPU work exists).

destroy_syncobj

fn destroy_syncobj(&self, handle: syncobj::Handle) -> io::Result<()>
Destroys a sync object and frees its kernel resources. All exported file descriptors become independent of the destroyed handle.

syncobj_to_fd

fn syncobj_to_fd(
    &self,
    handle: syncobj::Handle,
    export_sync_file: bool,
) -> io::Result<OwnedFd>
Exports a sync object as a file descriptor.
  • export_sync_file: false — exports as an opaque inter-process sync object fd, suitable for importing into another process with fd_to_syncobj.
  • export_sync_file: true — exports as a pollable sync_file fd. This fd becomes readable (EPOLLIN) when the underlying fence signals. Compatible with epoll, poll, and tokio::io::unix::AsyncFd.

fd_to_syncobj

fn fd_to_syncobj(
    &self,
    fd: BorrowedFd<'_>,
    import_sync_file: bool,
) -> io::Result<syncobj::Handle>
Imports a file descriptor back into a process-local sync object handle.
  • import_sync_file: false — imports an opaque sync object fd exported by syncobj_to_fd(false).
  • import_sync_file: true — imports a sync_file fd (e.g. from a GPU command submission) into a new sync object.

syncobj_wait

fn syncobj_wait(
    &self,
    handles: &[syncobj::Handle],
    timeout_nsec: i64,
    wait_all: bool,
    wait_for_submit: bool,
) -> io::Result<u32>
Waits for one or more sync objects to become signalled.
ParameterDescription
handlesSlice of sync object handles to wait on
timeout_nsecTimeout in nanoseconds. Use i64::MAX to wait indefinitely. A value of 0 polls without blocking.
wait_alltrue to wait until all handles signal; false to return when any handle signals
wait_for_submittrue to wait even if no fence has been attached yet (the sync object is empty); false to return immediately with an error if a handle has no attached fence
Returns the index of the first signalled handle in handles, or an error if the timeout expires.

syncobj_reset

fn syncobj_reset(&self, handles: &[syncobj::Handle]) -> io::Result<()>
Removes the currently attached fence from each listed sync object, returning them to the empty (unsignalled) state. Call this before reusing a sync object for a new submission.

syncobj_signal

fn syncobj_signal(&self, handles: &[syncobj::Handle]) -> io::Result<()>
Explicitly signals one or more sync objects without attaching a GPU fence. Useful for priming the initial state of a ring of sync objects, or for CPU-side signalling in testing.

Timeline Sync Object Operations

Timeline sync objects assign a monotonically increasing u64 sequence point to each fence. This maps directly to Vulkan timeline semaphore semantics and allows multiple in-flight frames to share a single sync object.
Requires DriverCapability::TimelineSyncObj. Check the capability before calling any of these methods.

syncobj_timeline_wait

fn syncobj_timeline_wait(
    &self,
    handles: &[syncobj::Handle],
    points: &[u64],
    timeout_nsec: i64,
    wait_all: bool,
    wait_for_submit: bool,
    wait_available: bool,
) -> io::Result<u32>
Waits for specific sequence points on one or more timeline sync objects. handles and points are parallel slices — handles[i] must reach at least points[i] for that entry to be considered signalled. wait_available: true waits until the timeline point has been submitted (a fence attached at that point) without waiting for it to signal. Useful for chaining submission on the CPU side. Returns the index of the first satisfied entry.

syncobj_timeline_query

fn syncobj_timeline_query(
    &self,
    handles: &[syncobj::Handle],
    points: &mut [u64],
    last_submitted: bool,
) -> io::Result<()>
Queries the current state of timeline sync objects. On return, points[i] holds the highest signalled sequence point for handles[i]. If last_submitted is true, reports the highest submitted (not yet necessarily signalled) point instead.

syncobj_timeline_transfer

fn syncobj_timeline_transfer(
    &self,
    src_handle: syncobj::Handle,
    dst_handle: syncobj::Handle,
    src_point: u64,
    dst_point: u64,
) -> io::Result<()>
Copies the fence at src_point of src_handle into dst_point of dst_handle. This lets you alias a single GPU fence across multiple timeline points or sync objects without re-exporting.

syncobj_timeline_signal

fn syncobj_timeline_signal(
    &self,
    handles: &[syncobj::Handle],
    points: &[u64],
) -> io::Result<()>
Explicitly signals specific timeline points on the listed sync objects without a GPU fence. Parallel slices: handles[i] is signalled at points[i].

syncobj_eventfd

fn syncobj_eventfd(
    &self,
    handle: syncobj::Handle,
    point: u64,
    eventfd: BorrowedFd<'_>,
    wait_available: bool,
) -> io::Result<()>
Registers an eventfd to be incremented when a timeline sync object reaches the given point. wait_available: true fires the eventfd when the point is submitted rather than when it signals. Useful for integrating sync object events into an epoll-based or async event loop.

Usage Example

use drm::control::Device as ControlDevice;

// Create an unsignalled sync object
let syncobj = card.create_syncobj(false).unwrap();

// Explicitly signal it (in real usage a GPU submission would do this)
card.syncobj_signal(&[syncobj]).unwrap();

// Export as a sync_file fd compatible with epoll / poll
let sync_fd = card.syncobj_to_fd(syncobj, true).unwrap();

// Synchronous wait with a 1-second timeout
let first_signalled = card
    .syncobj_wait(&[syncobj], 1_000_000_000, true, false)
    .unwrap();
println!("Handle at index {} signalled first", first_signalled);

// Reset before reuse (clear the attached fence)
card.syncobj_reset(&[syncobj]).unwrap();

// Clean up
card.destroy_syncobj(syncobj).unwrap();
Sync file fds exported with syncobj_to_fd(handle, true) are poll()-able. They become readable (POLLIN / EPOLLIN) when the attached fence signals. This makes them compatible with:
  • epoll_wait in a traditional event loop
  • poll / ppoll
  • tokio::io::unix::AsyncFd for Rust async/await integration

Build docs developers (and LLMs) love