#[non_exhaustive]pub enum SyncPoint {
Show 13 variants
Vulkan {
semaphore: u64,
kind: VulkanSemaphoreKind,
value: u64,
device: *mut c_void,
waiter: Arc<dyn SyncWaiter>,
},
D3D12 {
fence: *mut c_void,
value: u64,
waiter: Arc<dyn SyncWaiter>,
},
D3D11 {
keyed_mutex: *mut c_void,
key: u64,
waiter: Arc<dyn SyncWaiter>,
},
Cuda {
event: *mut c_void,
value: Option<u64>,
waiter: Arc<dyn SyncWaiter>,
},
CudaEvent {
event: *mut c_void,
context: *mut c_void,
device: i32,
waiter: Arc<dyn SyncWaiter>,
},
OpenCl {
event: *mut c_void,
value: Option<u64>,
waiter: Arc<dyn SyncWaiter>,
},
Metal {
event: *mut c_void,
value: u64,
waiter: Arc<dyn SyncWaiter>,
},
OpenGL {
semaphore: u32,
value: Option<u64>,
context: *mut c_void,
waiter: Arc<dyn SyncWaiter>,
},
OpenGLSync {
glsync: *mut c_void,
backend: GlBackend,
waiter: Arc<dyn SyncWaiter>,
},
Wgpu {
submission_index: SubmissionIndex,
waiter: Arc<dyn SyncWaiter>,
},
DeferredWgpu {
slot: Arc<DeferredWgpuSlot>,
device: Device,
waiter: Arc<dyn SyncWaiter>,
},
Cpu,
Noop,
}Expand description
GPU sync primitive. Produced by every submit-side path; consumed by anything that needs to serialise on a prior GPU submission.
Cross-backend bridges are deliberately absent from this leaf crate — those belong in an interop layer, where the target-device context is available.
§Lifetime
The per-variant waiter: Arc<dyn SyncWaiter> field anchors the
lifetime of any raw primitive the variant exposes (*mut c_void
fence pointers, u64 semaphore handles, GLsync opaques, etc.).
Cloning a SyncPoint is Arc::clone on the waiter — cheap, no
driver round-trip. The raw fields are guaranteed to remain valid
for as long as any clone is alive.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Vulkan
Vulkan semaphore. device is the VkDevice the semaphore was
created on, exposed for bridges that need to re-import it. The
waiter owns whatever anchors the VkSemaphore’s lifetime.
kind distinguishes timeline vs binary semaphores —
see VulkanSemaphoreKind. value is the timeline payload
coordinate when kind == Timeline; for kind == Binary the
field is meaningless (set to 0 by convention) and consumers
route through a binary wait (with wgpu-hal,
vulkan::Queue::add_wait_semaphore(_, None, _)).
D3D12
D3D12 fence sync. fence is an ID3D12Fence* (COM pointer);
value is the monotonic signal value the consumer waits on
(fence->GetCompletedValue() >= value). The waiter holds the
COM reference that keeps the fence alive.
value is monotonically signaled by the producer. Bridges that
reuse a single shared fence across calls serialise the
(mint + Signal) pair under a value-lock so concurrent bridges
always sequence in monotonic order.
A consumer waiting on value=N is guaranteed to pass once a
later value=M >= N has been signaled, even if the producer’s
counter has since advanced past M.
D3D11
D3D11 keyed-mutex sync. keyed_mutex is an IDXGIKeyedMutex*;
key is the integer key the producer released to. The
waiter holds the COM reference that keeps the mutex alive.
Cuda
CUDA external semaphore. event is a CUexternalSemaphore;
value is Some(v) for timeline waits — including the products
of Vulkan→CUDA / D3D12→CUDA bridges, which carry the producer’s
timeline value so the consumer can enqueue
cuWaitExternalSemaphoresAsync on whichever CUDA stream their
work runs on. None is reserved for setup-metadata SyncPoints
where the consumer mints the actual signal value at emission time
and doesn’t need a resolvable wait at construction.
CudaEvent
Intra-CUDA event. event is a CUevent recorded on the
producing stream via cuEventRecord. Consumers wait via
cuStreamWaitEvent (chained CUDA streams — cross-context
supported per NVIDIA Driver API) or cuEventSynchronize
(blocking CPU wait).
Distinct from Self::Cuda — that variant carries a
CUexternalSemaphore waited via cuWaitExternalSemaphoresAsync.
CUevent and CUexternalSemaphore are different opaque types
in the CUDA Driver API; passing a CUevent into the external-
semaphore wait path returns CUDA_ERROR_INVALID_HANDLE.
Cross-API consumers cannot wait on a CUevent directly; they
need a CUexternalSemaphore (the Self::Cuda shape); an
interop layer performs that conversion when the chain spans a
backend boundary.
context is the event’s owning CUcontext — bound at
cuEventCreate time per the NVIDIA Driver API (“creates an event
for the current context”). The owning context must still exist
at destroy time or cuEventDestroy_v2 returns
CUDA_ERROR_INVALID_CONTEXT. There is no cuEventGetCtx API —
the metadata must travel with the event.
device is the CUDA ordinal the owning context was created on
(derivable from context via cuCtxGetDevice but cheap to
carry and matches the device-keyed bridge shape used by
Self::Cuda).
waiter implements CudaEventWaiter — a supertrait of
SyncWaiter that adds the CUDA-specific
wait_on_foreign_stream extension method and the push-owning-
context-before-destroy Drop discipline. The leaf crate stores
the upcasted Arc<dyn SyncWaiter> so the generic Self::waiter
dispatch path is uniform with every other variant; cross-API
bridges that need the CUDA extension methods downcast through
SyncWaiter::as_any.
OpenCl
OpenCL sync. event is either a cl_event (binary,
signaled-on-completion; value is None) or a
cl_semaphore_khr aliasing a timeline (value is the
caller-signaled timeline value). The waiter keeps the
producing context alive.
Metal
Metal shared event. event is an MTLSharedEvent*; value is
the signal value the consumer waits on. The waiter holds the
reference that keeps the event alive.
OpenGL
OpenGL semaphore (GLuint produced by glGenSemaphoresEXT,
imported from a cross-API OS handle via
glImportSemaphoreFdEXT / glImportSemaphoreWin32HandleEXT).
value is Some(_) for timeline-equivalent semaphores
(D3D12_FENCE_EXT handle types); None for plain binary
(OPAQUE_FD / OPAQUE_WIN32) — the GL driver latches the D3D12
fence value at wait/signal time via
glSemaphoreParameterui64vEXT(sem, GL_D3D12_FENCE_VALUE_EXT, &v).
context is the caller’s EGLContext / HGLRC / CGLContextObj
this semaphore was created against. Waits must be dispatched on
a thread where the same context is current.
OpenGLSync
GL fence sync. glsync is a GLsync opaque pointer produced
by glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0).
Distinct from Self::OpenGL — that one carries an imported
GL semaphore (GLuint); this one is the raw GL-native fence.
backend records the GlBackend flavour of the GL context that
produced this GLsync. Cross-API consumers (an OpenGL→X bridge)
route through it to pick the matching loader (wglGetProcAddress
vs eglGetProcAddress) when resolving glClientWaitSync for the
CPU-fallback path.
Without it the consumer would hard-code Desktop, which silently
resolves the wrong proc-address on EGL / ANGLE / Android hosts and
stalls forever on a never-signalled handle.
Wgpu
wgpu only.wgpu::Queue::submit return value. Wait dispatches through the
waiter, which holds a wgpu::Device clone and calls
device.poll(PollType::Wait { submission_index: Some(..), .. }).
DeferredWgpu
wgpu only.wgpu::Queue::submit index not yet known at SyncPoint mint
time. Used where the caller owns the encoder and submits after the
producer has returned; slot.set(idx) is called once after
queue.submit(...), and until then the waiter falls back to
device.poll(PollType::Wait { submission_index: None, .. })
(full device drain). After commit, waits resolve precisely on
the recorded SubmissionIndex.
Cpu
CPU-only work — no GPU sync needed. wait returns Ok(())
immediately.
Noop
No-op sync — used for pipelines where the consumer side handles
ordering through a separate channel (e.g. SurfaceControl
transactions). Behaves identically to Cpu for waits.
Implementations§
Source§impl SyncPoint
impl SyncPoint
Sourcepub async fn wait(&self) -> Result<(), Error>
pub async fn wait(&self) -> Result<(), Error>
Async wait. Default 10-second timeout.
Async is the default. Use wait_blocking
when the caller is on a synchronous code path (sync-loop
renderers, test harnesses, JNI dispatcher Drop paths).
Sourcepub async fn wait_with_timeout_async(
&self,
timeout: Duration,
) -> Result<(), Error>
pub async fn wait_with_timeout_async( &self, timeout: Duration, ) -> Result<(), Error>
Async wait with explicit timeout.
Sourcepub fn wait_blocking(&self) -> Result<(), Error>
pub fn wait_blocking(&self) -> Result<(), Error>
Synchronous wait with the default 10-second timeout.
Sourcepub fn wait_with_timeout(&self, timeout: Duration) -> Result<(), Error>
pub fn wait_with_timeout(&self, timeout: Duration) -> Result<(), Error>
Synchronous wait with an explicit timeout.
Timeout granularity is backend-dependent — see
SyncWaiter::wait for the per-backend rounding contract.
In particular, Metal and Win32 are millisecond-granular and
round sub-ms positive timeouts up to 1 ms; Vulkan / D3D12 /
CUDA / OpenCL are nanosecond-granular. For sub-ms polls use
Self::is_signaled in a loop with your own Instant budget.
Sourcepub fn chain(self, then: SyncPoint) -> SyncPoint
pub fn chain(self, then: SyncPoint) -> SyncPoint
Sequence two sync points. Returns a SyncPoint whose wait
completes only after both self and then have completed.
If either side is Cpu / Noop, the other is returned unchanged.
Otherwise the result is an opaque carrier: a small adapter
waiter drives both inner waits in sequence (one heap allocation),
and it is stored in the SyncPoint::Cuda variant with a null
event and no value. Only use the result through
wait, wait_blocking,
is_signaled, backend
(the first sync point’s backend) and waiter;
its raw fields carry nothing.
Source§impl SyncPoint
impl SyncPoint
Sourcepub fn from_raw_d3d12_fence(fence: *mut c_void, value: u64) -> SyncPoint
pub fn from_raw_d3d12_fence(fence: *mut c_void, value: u64) -> SyncPoint
Reconstruct a SyncPoint::D3D12 transport carrier from a raw
ID3D12Fence* pointer + signal value that crossed an API / process boundary
(e.g. a #[repr(C)] fence descriptor over a C ABI).
fence is an ID3D12Fence* COM pointer the caller guarantees is live for
the carrier’s lifetime; value is the monotonic value the consumer waits for
(fence->GetCompletedValue() >= value). The returned SyncPoint is a
carrier (RawFenceCarrierWaiter) — its raw (fence, value) fields feed
a device-side import (e.g. an interop layer’s acquire-sync path); a CPU
wait() on it before import returns Error::NotSupported.
Sourcepub fn from_raw_vulkan_semaphore(
semaphore: u64,
kind: VulkanSemaphoreKind,
value: u64,
device: *mut c_void,
) -> SyncPoint
pub fn from_raw_vulkan_semaphore( semaphore: u64, kind: VulkanSemaphoreKind, value: u64, device: *mut c_void, ) -> SyncPoint
Reconstruct a SyncPoint::Vulkan transport carrier from a raw
VkSemaphore + VkDevice that crossed an API / process boundary.
semaphore is the raw VkSemaphore (a 64-bit non-dispatchable handle);
kind distinguishes timeline vs binary (see VulkanSemaphoreKind);
value is the timeline coordinate (ignored for Binary); device is the
VkDevice* the semaphore lives on (needed by an importer that re-exports it
for a cross-device wait). The returned SyncPoint is a carrier — its raw
fields feed a device-side import; a CPU wait() before import returns
Error::NotSupported.
Sourcepub fn from_raw_metal_event(event: *mut c_void, value: u64) -> SyncPoint
pub fn from_raw_metal_event(event: *mut c_void, value: u64) -> SyncPoint
Reconstruct a SyncPoint::Metal transport carrier from a raw
MTLSharedEvent* + signal value that crossed an API / process boundary.
event is an MTLSharedEvent* the caller guarantees is live for the
carrier’s lifetime; value is the value the consumer waits for. The returned
SyncPoint is a carrier — its raw (event, value) fields feed a
device-side import; a CPU wait() before import returns
Error::NotSupported.
Source§impl SyncPoint
impl SyncPoint
Sourcepub fn is_signaled(&self) -> Result<bool, Error>
pub fn is_signaled(&self) -> Result<bool, Error>
Non-blocking probe.
Ok(true)— sync point reached (or trivialCpu/Noop).Ok(false)— work still in flight.Err(_)— driver-level failure (device lost / TDR / wrong-submission-index). SeeSyncWaiter::is_signaledfor the rationale on surfacing errors instead of folding them intofalse.
Sourcepub fn backend(&self) -> BackendKind
pub fn backend(&self) -> BackendKind
Backend identity. For the trivial variants (Cpu, Noop) this
returns BackendKind::Cpu; otherwise it dispatches to the
per-variant waiter.
Sourcepub fn waiter(&self) -> Option<&Arc<dyn SyncWaiter>>
pub fn waiter(&self) -> Option<&Arc<dyn SyncWaiter>>
Borrow the per-variant waiter, if any. Cpu and Noop return
None.