Skip to main content

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}