DRM sync objects (syncobjs) are kernel-managed binding points for GPU fences. A fence is a single-use signal that transitions from unsignalled to signalled when a GPU task — such as a command buffer submission — completes. Syncobjs hold these fences and expose them to userspace through file descriptors that can be polled, shared between processes, or imported into other APIs. In practice, syncobjs underpin Vulkan fence objects: when you submit work to a Vulkan queue with an associated fence, the GPU driver attaches a DRM fence to a syncobj. Your application can then export that fence as aDocumentation 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.
sync_file fd and use it to synchronise with display page flips, other GPU engines, or CPU-side waits.
drm-rs exposes the full syncobj API through the drm::control::Device trait. Before using it, verify the driver supports syncobjs:
Binary vs timeline syncobjs
drm-rs supports two syncobj flavours:- Binary syncobjs have two states: unsignalled and signalled. Once signalled, they stay signalled until explicitly reset. They are the simplest form and are used with Vulkan binary fences and semaphores.
- Timeline syncobjs associate a monotonically increasing 64-bit sequence counter with their fence slots. You can wait for, signal, or query arbitrary points on the timeline. They map to Vulkan timeline semaphores and enable fine-grained producer/consumer synchronisation without allocating a new syncobj per event.
DriverCapability::TimelineSyncObj support before using the timeline API.
Binary syncobj workflow
let cap = card
.get_driver_capability(drm::DriverCapability::SyncObj)
.expect("Could not query SyncObj capability");
assert!(cap != 0, "Driver does not support sync objects");
create_syncobj takes a boolean that controls whether the object starts in the signalled state. Pass false for an object that will be signalled later by a GPU command submission:use drm::control::syncobj;
let syncobj: syncobj::Handle = card
.create_syncobj(false) // start unsignalled
.expect("Failed to create syncobj");
In a real application the GPU driver signals the syncobj when submitted commands complete. For testing without an actual GPU submission you can signal it manually:
Both
syncobj_signal and syncobj_reset accept a slice of handles so you can operate on multiple objects in a single ioctl.A syncobj can be exported either as a plain handle (for sharing between processes via
SCM_RIGHTS) or as a sync file fd. The sync file form is what you use for polling and for passing to other kernel APIs:use std::os::unix::io::OwnedFd;
let sync_fd: OwnedFd = card
.syncobj_to_fd(syncobj, true) // true = export as sync_file
.expect("Failed to export syncobj as sync_file");
When
export_sync_file is false, the exported fd is a raw syncobj handle fd suitable for inter-process sharing via fd_to_syncobj.syncobj_wait blocks the calling thread until one or all of the specified syncobjs become signalled, or until the timeout elapses:card.syncobj_wait(
&[syncobj],
5_000_000_000i64, // timeout in nanoseconds (5 seconds)
true, // wait_all: wait for ALL handles in the slice
false, // wait_for_submit: also wait for a fence to be submitted
)
.expect("Syncobj wait failed or timed out");
Set
wait_for_submit to true if you want to wait even before a fence has been attached to the syncobj — useful when you know a GPU submission is in flight but may not have reached the kernel yet.syncobj_reset moves the syncobj back to the unsignalled state so it can be reused for the next GPU submission:Polling a sync_file fd without blocking
The sync file fd exported bysyncobj_to_fd is a standard Linux file descriptor that becomes readable when the underlying fence is signalled. This makes it directly compatible with epoll, poll, and Rust async runtimes:
Importing a foreign sync_file fd
If you receive a sync_file fd from another process (via socket ancillary data) or from another kernel API, import it back into a local syncobj handle withfd_to_syncobj:
import_sync_file is false, the fd is treated as a raw syncobj handle fd rather than a sync_file.
Timeline syncobj operations
Timeline syncobjs extend the binary model with a 64-bit point counter. All operations take a slice of(handle, point) pairs:
syncobj_timeline_signal
Signal a specific point on the timeline. All waiters blocked on this point or any earlier point are unblocked:
syncobj_timeline_wait
Wait until one or all specified timeline points are signalled:
syncobj_timeline_query
Query the current signalled point of one or more timeline syncobjs without blocking:
syncobj_timeline_transfer
Copy the fence at a specific point from one timeline syncobj to a specific point on another. Useful for chaining GPU work across different queues or devices:
syncobj_eventfd
Register a Linux eventfd to be notified when a specific timeline point is signalled. The eventfd receives a write of 1 when the point is reached, making it suitable for epoll-based event loops:
Syncobjs are the mechanism Vulkan drivers use to implement fence and semaphore objects. When you call
vkQueueSubmit with a VkFence, the Vulkan driver internally passes a syncobj handle to the command submission ioctl. The GPU sets the fence in that syncobj on completion. You can export the resulting fence via syncobj_to_fd to create a cross-API synchronisation chain — for example, to ensure a page flip waits for Vulkan rendering to complete.