• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 use crate::check_errors;
11 use crate::device::Device;
12 use crate::device::DeviceOwned;
13 use crate::Error;
14 use crate::OomError;
15 use crate::SafeDeref;
16 use crate::VulkanObject;
17 use std::fmt;
18 #[cfg(target_os = "linux")]
19 use std::fs::File;
20 use std::mem::MaybeUninit;
21 #[cfg(target_os = "linux")]
22 use std::os::unix::io::FromRawFd;
23 use std::ptr;
24 use std::sync::Arc;
25 
26 use crate::sync::semaphore::ExternalSemaphoreHandleType;
27 
28 /// Used to provide synchronization between command buffers during their execution.
29 ///
30 /// It is similar to a fence, except that it is purely on the GPU side. The CPU can't query a
31 /// semaphore's status or wait for it to be signaled.
32 #[derive(Debug)]
33 pub struct Semaphore<D = Arc<Device>>
34 where
35     D: SafeDeref<Target = Device>,
36 {
37     semaphore: ash::vk::Semaphore,
38     device: D,
39     must_put_in_pool: bool,
40 }
41 
42 // TODO: Add support for VkExportSemaphoreWin32HandleInfoKHR
43 // TODO: Add suport for importable semaphores
44 pub struct SemaphoreBuilder<D = Arc<Device>>
45 where
46     D: SafeDeref<Target = Device>,
47 {
48     device: D,
49     export_info: Option<ash::vk::ExportSemaphoreCreateInfo>,
50     create: ash::vk::SemaphoreCreateInfo,
51     must_put_in_pool: bool,
52 }
53 
54 impl<D> SemaphoreBuilder<D>
55 where
56     D: SafeDeref<Target = Device>,
57 {
new(device: D) -> Self58     pub fn new(device: D) -> Self {
59         let create = ash::vk::SemaphoreCreateInfo::default();
60 
61         Self {
62             device,
63             export_info: None,
64             create,
65             must_put_in_pool: false,
66         }
67     }
68     /// Configures the semaphore to be added to the semaphore pool once it is destroyed.
in_pool(mut self) -> Self69     pub(crate) fn in_pool(mut self) -> Self {
70         self.must_put_in_pool = true;
71         self
72     }
73 
74     /// Sets an optional field for exportable allocations in the `SemaphoreBuilder`.
75     ///
76     /// # Panic
77     ///
78     /// - Panics if the export info has already been set.
export_info(mut self, handle_types: ExternalSemaphoreHandleType) -> Self79     pub fn export_info(mut self, handle_types: ExternalSemaphoreHandleType) -> Self {
80         assert!(self.export_info.is_none());
81         let export_info = ash::vk::ExportSemaphoreCreateInfo {
82             handle_types: handle_types.into(),
83             ..Default::default()
84         };
85 
86         self.export_info = Some(export_info);
87         self.create.p_next = unsafe { std::mem::transmute(&export_info) };
88 
89         self
90     }
91 
build(self) -> Result<Semaphore<D>, SemaphoreError>92     pub fn build(self) -> Result<Semaphore<D>, SemaphoreError> {
93         if self.export_info.is_some()
94             && !self
95                 .device
96                 .instance()
97                 .enabled_extensions()
98                 .khr_external_semaphore_capabilities
99         {
100             Err(SemaphoreError::MissingExtension(
101                 "khr_external_semaphore_capabilities",
102             ))
103         } else {
104             let semaphore = unsafe {
105                 let fns = self.device.fns();
106                 let mut output = MaybeUninit::uninit();
107                 check_errors(fns.v1_0.create_semaphore(
108                     self.device.internal_object(),
109                     &self.create,
110                     ptr::null(),
111                     output.as_mut_ptr(),
112                 ))?;
113                 output.assume_init()
114             };
115 
116             Ok(Semaphore {
117                 device: self.device,
118                 semaphore,
119                 must_put_in_pool: self.must_put_in_pool,
120             })
121         }
122     }
123 }
124 
125 impl<D> Semaphore<D>
126 where
127     D: SafeDeref<Target = Device>,
128 {
129     /// Takes a semaphore from the vulkano-provided semaphore pool.
130     /// If the pool is empty, a new semaphore will be allocated.
131     /// Upon `drop`, the semaphore is put back into the pool.
132     ///
133     /// For most applications, using the pool should be preferred,
134     /// in order to avoid creating new semaphores every frame.
from_pool(device: D) -> Result<Semaphore<D>, SemaphoreError>135     pub fn from_pool(device: D) -> Result<Semaphore<D>, SemaphoreError> {
136         let maybe_raw_sem = device.semaphore_pool().lock().unwrap().pop();
137         match maybe_raw_sem {
138             Some(raw_sem) => Ok(Semaphore {
139                 device,
140                 semaphore: raw_sem,
141                 must_put_in_pool: true,
142             }),
143             None => {
144                 // Pool is empty, alloc new semaphore
145                 SemaphoreBuilder::new(device).in_pool().build()
146             }
147         }
148     }
149 
150     /// Builds a new semaphore.
151     #[inline]
alloc(device: D) -> Result<Semaphore<D>, SemaphoreError>152     pub fn alloc(device: D) -> Result<Semaphore<D>, SemaphoreError> {
153         SemaphoreBuilder::new(device).build()
154     }
155 
156     /// Same as `alloc`, but allows exportable opaque file descriptor on Linux
157     #[inline]
158     #[cfg(target_os = "linux")]
alloc_with_exportable_fd(device: D) -> Result<Semaphore<D>, SemaphoreError>159     pub fn alloc_with_exportable_fd(device: D) -> Result<Semaphore<D>, SemaphoreError> {
160         SemaphoreBuilder::new(device)
161             .export_info(ExternalSemaphoreHandleType::posix())
162             .build()
163     }
164 
165     #[cfg(target_os = "linux")]
export_opaque_fd(&self) -> Result<File, SemaphoreError>166     pub fn export_opaque_fd(&self) -> Result<File, SemaphoreError> {
167         let fns = self.device.fns();
168 
169         assert!(self.device.enabled_extensions().khr_external_semaphore);
170         assert!(self.device.enabled_extensions().khr_external_semaphore_fd);
171 
172         let fd = unsafe {
173             let info = ash::vk::SemaphoreGetFdInfoKHR {
174                 semaphore: self.semaphore,
175                 handle_type: ash::vk::ExternalSemaphoreHandleTypeFlagsKHR::OPAQUE_FD,
176                 ..Default::default()
177             };
178 
179             let mut output = MaybeUninit::uninit();
180             check_errors(fns.khr_external_semaphore_fd.get_semaphore_fd_khr(
181                 self.device.internal_object(),
182                 &info,
183                 output.as_mut_ptr(),
184             ))?;
185             output.assume_init()
186         };
187         let file = unsafe { File::from_raw_fd(fd) };
188         Ok(file)
189     }
190 }
191 
192 unsafe impl DeviceOwned for Semaphore {
193     #[inline]
device(&self) -> &Arc<Device>194     fn device(&self) -> &Arc<Device> {
195         &self.device
196     }
197 }
198 
199 unsafe impl<D> VulkanObject for Semaphore<D>
200 where
201     D: SafeDeref<Target = Device>,
202 {
203     type Object = ash::vk::Semaphore;
204 
205     #[inline]
internal_object(&self) -> ash::vk::Semaphore206     fn internal_object(&self) -> ash::vk::Semaphore {
207         self.semaphore
208     }
209 }
210 
211 impl<D> Drop for Semaphore<D>
212 where
213     D: SafeDeref<Target = Device>,
214 {
215     #[inline]
drop(&mut self)216     fn drop(&mut self) {
217         unsafe {
218             if self.must_put_in_pool {
219                 let raw_sem = self.semaphore;
220                 self.device.semaphore_pool().lock().unwrap().push(raw_sem);
221             } else {
222                 let fns = self.device.fns();
223                 fns.v1_0.destroy_semaphore(
224                     self.device.internal_object(),
225                     self.semaphore,
226                     ptr::null(),
227                 );
228             }
229         }
230     }
231 }
232 
233 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
234 pub enum SemaphoreError {
235     /// Not enough memory available.
236     OomError(OomError),
237     /// An extensions is missing.
238     MissingExtension(&'static str),
239 }
240 
241 impl fmt::Display for SemaphoreError {
fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result242     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243         match *self {
244             SemaphoreError::OomError(_) => write!(fmt, "not enough memory available"),
245             SemaphoreError::MissingExtension(s) => {
246                 write!(fmt, "Missing the following extension: {}", s)
247             }
248         }
249     }
250 }
251 
252 impl From<Error> for SemaphoreError {
253     #[inline]
from(err: Error) -> SemaphoreError254     fn from(err: Error) -> SemaphoreError {
255         match err {
256             e @ Error::OutOfHostMemory | e @ Error::OutOfDeviceMemory => {
257                 SemaphoreError::OomError(e.into())
258             }
259             _ => panic!("unexpected error: {:?}", err),
260         }
261     }
262 }
263 
264 impl std::error::Error for SemaphoreError {
265     #[inline]
source(&self) -> Option<&(dyn std::error::Error + 'static)>266     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
267         match *self {
268             SemaphoreError::OomError(ref err) => Some(err),
269             _ => None,
270         }
271     }
272 }
273 
274 impl From<OomError> for SemaphoreError {
275     #[inline]
from(err: OomError) -> SemaphoreError276     fn from(err: OomError) -> SemaphoreError {
277         SemaphoreError::OomError(err)
278     }
279 }
280 
281 #[cfg(test)]
282 mod tests {
283     use crate::device::physical::PhysicalDevice;
284     use crate::device::{Device, DeviceExtensions};
285     use crate::instance::{Instance, InstanceExtensions};
286     use crate::VulkanObject;
287     use crate::{sync::Semaphore, Version};
288 
289     #[test]
semaphore_create()290     fn semaphore_create() {
291         let (device, _) = gfx_dev_and_queue!();
292         let _ = Semaphore::alloc(device.clone());
293     }
294 
295     #[test]
semaphore_pool()296     fn semaphore_pool() {
297         let (device, _) = gfx_dev_and_queue!();
298 
299         assert_eq!(device.semaphore_pool().lock().unwrap().len(), 0);
300         let sem1_internal_obj = {
301             let sem = Semaphore::from_pool(device.clone()).unwrap();
302             assert_eq!(device.semaphore_pool().lock().unwrap().len(), 0);
303             sem.internal_object()
304         };
305 
306         assert_eq!(device.semaphore_pool().lock().unwrap().len(), 1);
307         let sem2 = Semaphore::from_pool(device.clone()).unwrap();
308         assert_eq!(device.semaphore_pool().lock().unwrap().len(), 0);
309         assert_eq!(sem2.internal_object(), sem1_internal_obj);
310     }
311 
312     #[test]
313     #[cfg(target_os = "linux")]
semaphore_export()314     fn semaphore_export() {
315         let supported_ext = InstanceExtensions::supported_by_core().unwrap();
316         if supported_ext.khr_get_display_properties2
317             && supported_ext.khr_external_semaphore_capabilities
318         {
319             let instance = Instance::new(
320                 None,
321                 Version::V1_1,
322                 &InstanceExtensions {
323                     khr_get_physical_device_properties2: true,
324                     khr_external_semaphore_capabilities: true,
325                     ..InstanceExtensions::none()
326                 },
327                 None,
328             )
329             .unwrap();
330 
331             let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
332 
333             let queue_family = physical.queue_families().next().unwrap();
334 
335             let device_ext = DeviceExtensions {
336                 khr_external_semaphore: true,
337                 khr_external_semaphore_fd: true,
338                 ..DeviceExtensions::none()
339             };
340             let (device, _) = Device::new(
341                 physical,
342                 physical.supported_features(),
343                 &device_ext,
344                 [(queue_family, 0.5)].iter().cloned(),
345             )
346             .unwrap();
347 
348             let supported_ext = physical.supported_extensions();
349             if supported_ext.khr_external_semaphore && supported_ext.khr_external_semaphore_fd {
350                 let sem = Semaphore::alloc_with_exportable_fd(device.clone()).unwrap();
351                 let fd = sem.export_opaque_fd().unwrap();
352             }
353         }
354     }
355 }
356