Skip to main content

glium/
semaphore.rs

1/*!
2Contains everything related to external API semaphores.
3*/
4// TODO: Add Windows support via EXT_external_objects_win32
5
6use std::rc::Rc;
7
8use crate::{
9    buffer::{Buffer, Content},
10    texture::TextureAny,
11    Context, ContextExt, GlObject,
12};
13
14use crate::{backend::Facade, context::CommandContext, gl};
15
16/// Describes an error encountered during semaphore creation
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum SemaphoreCreationError {
19    /// Driver does not support EXT_semaphore
20    SemaphoreObjectNotSupported,
21    /// Driver does not support EXT_semaphore_fd
22    SemaphoreObjectFdNotSupported,
23    /// OpenGL returned a null pointer when creating semaphore
24    NullResult,
25}
26
27impl std::fmt::Display for SemaphoreCreationError {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        use self::SemaphoreCreationError::*;
30
31        let desc = match *self {
32            SemaphoreObjectNotSupported => "Driver does not support EXT_semaphore",
33            SemaphoreObjectFdNotSupported => "Driver does not support EXT_semaphore_fd",
34            NullResult => "OpenGL returned a null pointer when creating semaphore",
35        };
36        f.write_str(desc)
37    }
38}
39
40impl std::error::Error for SemaphoreCreationError {}
41
42/// Describes a Vulkan image layout that a texture can be in. See <https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkImageLayout.html>
43#[derive(Debug, Clone, Copy)]
44pub enum TextureLayout {
45    /// Corresponds to VK_IMAGE_LAYOUT_UNDEFINED
46    None,
47    /// Corresponds to VK_IMAGE_LAYOUT_GENERAL
48    General,
49    /// Corresponds to VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
50    ColorAttachment,
51    /// Corresponds to VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT
52    DepthStencilAttachment,
53    /// Corresponds to VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
54    DepthStencilReadOnly,
55    /// Corresponds to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
56    ShaderReadOnly,
57    /// Corresponds to VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
58    TransferSrc,
59    /// Corresponds to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
60    TransferDst,
61    /// Corresponds to VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR
62    DepthReadOnlyStencilAttachment,
63    /// Corresponds to VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR
64    DepthAttachmentStencilReadOnly,
65}
66
67impl Into<crate::gl::types::GLenum> for TextureLayout {
68    fn into(self) -> crate::gl::types::GLenum {
69        match self {
70            TextureLayout::None => gl::NONE,
71            TextureLayout::General => gl::LAYOUT_GENERAL_EXT,
72            TextureLayout::ColorAttachment => gl::LAYOUT_COLOR_ATTACHMENT_EXT,
73            TextureLayout::DepthStencilAttachment => gl::LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT,
74            TextureLayout::DepthStencilReadOnly => gl::LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT,
75            TextureLayout::ShaderReadOnly => gl::LAYOUT_SHADER_READ_ONLY_EXT,
76            TextureLayout::TransferSrc => gl::LAYOUT_TRANSFER_SRC_EXT,
77            TextureLayout::TransferDst => gl::LAYOUT_TRANSFER_DST_EXT,
78            TextureLayout::DepthReadOnlyStencilAttachment => gl::LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT,
79            TextureLayout::DepthAttachmentStencilReadOnly => {
80                gl::LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT
81            }
82        }
83    }
84}
85
86/// Similar to a GL sync object, this describes a semaphore which can be used for OpenGL-Vulkan command queue synchronization.
87pub struct Semaphore {
88    context: Rc<Context>,
89    id: gl::types::GLuint,    
90}
91
92impl Semaphore {
93    /// Creates a semaphore imported from an opaque file descriptor.
94    #[cfg(target_os = "linux")]
95    pub unsafe fn new_from_fd<F: Facade + ?Sized>(
96        facade: &F,
97        fd: std::fs::File,
98    ) -> Result<Self, SemaphoreCreationError> {
99        use std::os::unix::io::AsRawFd;
100
101        let ctxt = facade.get_context().make_current();
102        let sem = Self::new(facade, &ctxt)?;
103
104        if ctxt.extensions.gl_ext_semaphore_fd {
105            ctxt.gl
106                .ImportSemaphoreFdEXT(sem.id, gl::HANDLE_TYPE_OPAQUE_FD_EXT, fd.as_raw_fd());
107
108            if ctxt.gl.IsSemaphoreEXT(sem.id) == gl::FALSE {
109                Err(SemaphoreCreationError::NullResult)
110            } else {
111                std::mem::forget(fd);
112
113                Ok(sem)
114            }
115        } else {
116            Err(SemaphoreCreationError::SemaphoreObjectFdNotSupported)
117        }
118    }
119
120    fn new<F: Facade + ?Sized>(
121        facade: &F,
122        ctxt: &CommandContext<'_>,
123    ) -> Result<Self, SemaphoreCreationError> {
124        if ctxt.extensions.gl_ext_semaphore {
125            let id = unsafe {
126                let mut id: gl::types::GLuint = 0;
127                ctxt.gl.GenSemaphoresEXT(1, &mut id as *mut u32);
128                id
129            };
130
131            Ok(Self {
132                context: facade.get_context().clone(),
133                id,
134            })
135        } else {
136            Err(SemaphoreCreationError::SemaphoreObjectNotSupported)
137        }
138    }
139    /// Same as `wait` but without `buffers` parameter
140    pub fn wait_textures(&self, textures: Option<&[(&TextureAny, TextureLayout)]>) {
141        // Don't care about type parameter
142        self.wait::<u32>(textures, None)
143    }
144
145    /// The semaphore blocks the GPU's command queue until the semaphore is signalled. This does not block the CPU.
146    /// After this completes, the semaphore is returned to the unsignalled state.
147    /// Once the operation is complete, the memory corresponding to the passed `textures` and `buffers` is made available to OpenGL.
148    /// The layouts given with each texture must match the image layout used in Vulkan directly before the semaphore is signalled by it.
149    ///
150    /// # Caution
151    ///
152    /// - You must make sure that a command to signal the semaphore you are waiting on has been flushed to the command queue (Possibly
153    ///   by the external API). Failure to do so may result in segfaults depending on the driver used.
154    pub fn wait<T: ?Sized>(
155        &self,
156        textures: Option<&[(&TextureAny, TextureLayout)]>,
157        buffers: Option<&[&Buffer<T>]>,
158    ) where
159        T: Content,
160    {
161        let ctxt = self.context.get_context().make_current();
162
163        let (buffer_ids, buffer_num, _) = if let Some(buffs) = buffers {
164            let ids = buffs.iter().map(|b| b.get_id()).collect::<Vec<_>>();
165            (ids.as_ptr(), buffs.len(), Some(ids))
166        } else {
167            (std::ptr::null(), 0, None)
168        };
169
170        let (texture_ids, texture_layouts, textures_num, _, _) = if let Some(textures) = textures {
171            let ids = textures.iter().map(|t| t.0.get_id()).collect::<Vec<_>>();
172            let layouts = textures
173                .iter()
174                .map(|t| t.1.into())
175                .collect::<Vec<gl::types::GLenum>>();
176            (
177                ids.as_ptr(),
178                layouts.as_ptr(),
179                textures.len(),
180                Some(ids),
181                Some(layouts),
182            )
183        } else {
184            (std::ptr::null(), std::ptr::null(), 0, None, None)
185        };
186
187        unsafe {
188            ctxt.gl.WaitSemaphoreEXT(
189                self.id,
190                buffer_num as u32,
191                buffer_ids,
192                textures_num as u32,
193                texture_ids,
194                texture_layouts,
195            )
196        }
197    }
198
199    /// Same as `signal`, but without buffers parameter
200    pub fn signal_textures(&self, textures: Option<&[(&TextureAny, TextureLayout)]>) {
201        // Don't care about type parameter.
202        self.signal::<u32>(textures, None)
203    }
204
205    /// Sends signal through semaphore.
206    /// The memory of the `textures` and `buffers` is made available to the external API.
207    /// Before signalling the semaphore, the `textures` are transitioned into the layout specified.
208    pub fn signal<T: ?Sized>(
209        &self,
210        textures: Option<&[(&TextureAny, TextureLayout)]>,
211        buffers: Option<&[&Buffer<T>]>,
212    ) where
213        T: Content,
214    {
215        let ctxt = self.context.get_context().make_current();
216
217        let (buffer_ids, buffer_num, _) = if let Some(buffs) = buffers {
218            let ids = buffs.iter().map(|b| b.get_id()).collect::<Vec<_>>();
219            (ids.as_ptr(), buffs.len(), Some(ids))
220        } else {
221            (std::ptr::null(), 0, None)
222        };
223
224        let (texture_ids, texture_layouts, textures_num, _, _) = if let Some(textures) = textures {
225            let ids = textures.iter().map(|t| t.0.get_id()).collect::<Vec<_>>();
226            let layouts = textures
227                .iter()
228                .map(|t| t.1.into())
229                .collect::<Vec<gl::types::GLenum>>();
230            (
231                ids.as_ptr(),
232                layouts.as_ptr(),
233                textures.len(),
234                Some(ids),
235                Some(layouts),
236            )
237        } else {
238            (std::ptr::null(), std::ptr::null(), 0, None, None)
239        };
240
241        unsafe {
242            ctxt.gl.SignalSemaphoreEXT(
243                self.id,
244                buffer_num as u32,
245                buffer_ids,
246                textures_num as u32,
247                texture_ids,
248                texture_layouts,
249            );
250            ctxt.gl.Flush(); // Must flush after signalling semaphore
251        }
252    }
253}
254
255impl GlObject for Semaphore {
256    type Id = gl::types::GLuint;
257
258    #[inline]
259    fn get_id(&self) -> gl::types::GLuint {
260        self.id
261    }
262}
263
264impl Drop for Semaphore {
265    fn drop(&mut self) {
266        let ctxt = self.context.get_context().make_current();
267        unsafe { ctxt.gl.DeleteSemaphoresEXT(1, &mut self.id as *mut u32) };
268    }
269}