wgpu_native_texture_interop/sync.rs
1use crate::{ImportedTexture, InteropError, NativeFrame};
2
3/// Describes how the producer signals that a frame is ready and how the
4/// consumer signals that it has finished reading.
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6#[non_exhaustive]
7pub enum SyncMechanism {
8 /// No synchronization is needed (e.g. single-threaded, or the producer
9 /// and consumer share a submission queue).
10 None,
11 /// The producer calls `glFlush()` before handing the frame over. This is
12 /// the most common mechanism for GL→wgpu interop and the default used by
13 /// [`ImplicitOnlySynchronizer`].
14 ImplicitGlFlush,
15 /// An explicit Vulkan/Metal external semaphore is signalled by the
16 /// producer. Not yet handled by any built-in synchronizer.
17 ExplicitExternalSemaphore,
18 /// An explicit CPU-side fence is used. Not yet handled by any built-in
19 /// synchronizer.
20 ExplicitFence,
21}
22
23/// Hook points called by [`WgpuTextureImporter`](crate::WgpuTextureImporter)
24/// around each frame import.
25///
26/// Implement this trait to add custom fence/semaphore logic. Two built-in
27/// implementations are provided: [`NoopSynchronizer`] and
28/// [`ImplicitOnlySynchronizer`].
29pub trait InteropSynchronizer {
30 /// Called after the frame is acquired from the producer, before import.
31 /// Use this to wait on any producer-side signal.
32 fn producer_complete(
33 &self,
34 frame: &NativeFrame,
35 mechanism: SyncMechanism,
36 ) -> Result<(), InteropError>;
37 /// Called after the texture has been imported and is ready for the
38 /// consumer. Use this to signal any consumer-side fence or semaphore.
39 fn consumer_ready(
40 &self,
41 texture: &ImportedTexture,
42 mechanism: SyncMechanism,
43 ) -> Result<(), InteropError>;
44}
45
46/// A synchronizer that does nothing. Suitable when the caller manages all
47/// synchronization externally (e.g. via a shared queue or explicit barriers).
48#[derive(Default)]
49pub struct NoopSynchronizer;
50
51impl InteropSynchronizer for NoopSynchronizer {
52 fn producer_complete(
53 &self,
54 _frame: &NativeFrame,
55 _mechanism: SyncMechanism,
56 ) -> Result<(), InteropError> {
57 Ok(())
58 }
59
60 fn consumer_ready(
61 &self,
62 _texture: &ImportedTexture,
63 _mechanism: SyncMechanism,
64 ) -> Result<(), InteropError> {
65 Ok(())
66 }
67}
68
69/// A synchronizer that accepts [`SyncMechanism::None`] and
70/// [`SyncMechanism::ImplicitGlFlush`] and rejects any explicit semaphore or
71/// fence mechanism with [`InteropError::UnsupportedSynchronization`].
72///
73/// This is the default synchronizer used by
74/// [`WgpuTextureImporter::new`](crate::WgpuTextureImporter::new). It is
75/// correct for the GL→Vulkan/Metal paths implemented in this crate, which rely
76/// on `glFlush()` having been called by the producer before the frame is
77/// handed over.
78#[derive(Default)]
79pub struct ImplicitOnlySynchronizer;
80
81impl InteropSynchronizer for ImplicitOnlySynchronizer {
82 fn producer_complete(
83 &self,
84 _frame: &NativeFrame,
85 mechanism: SyncMechanism,
86 ) -> Result<(), InteropError> {
87 Self::validate(mechanism)
88 }
89
90 fn consumer_ready(
91 &self,
92 _texture: &ImportedTexture,
93 mechanism: SyncMechanism,
94 ) -> Result<(), InteropError> {
95 Self::validate(mechanism)
96 }
97}
98
99impl ImplicitOnlySynchronizer {
100 pub(crate) fn validate(mechanism: SyncMechanism) -> Result<(), InteropError> {
101 match mechanism {
102 SyncMechanism::None | SyncMechanism::ImplicitGlFlush => Ok(()),
103 other => Err(InteropError::UnsupportedSynchronization(other)),
104 }
105 }
106}