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