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::Success; 17 use crate::VulkanObject; 18 use smallvec::SmallVec; 19 use std::error; 20 use std::fmt; 21 use std::mem::MaybeUninit; 22 use std::ptr; 23 use std::sync::atomic::AtomicBool; 24 use std::sync::atomic::Ordering; 25 use std::sync::Arc; 26 use std::time::Duration; 27 28 /// A fence is used to know when a command buffer submission has finished its execution. 29 /// 30 /// When a command buffer accesses a resource, you have to ensure that the CPU doesn't access 31 /// the same resource simultaneously (except for concurrent reads). Therefore in order to know 32 /// when the CPU can access a resource again, a fence has to be used. 33 #[derive(Debug)] 34 pub struct Fence<D = Arc<Device>> 35 where 36 D: SafeDeref<Target = Device>, 37 { 38 fence: ash::vk::Fence, 39 40 device: D, 41 42 // If true, we know that the `Fence` is signaled. If false, we don't know. 43 // This variable exists so that we don't need to call `vkGetFenceStatus` or `vkWaitForFences` 44 // multiple times. 45 signaled: AtomicBool, 46 47 // Indicates whether this fence was taken from the fence pool. 48 // If true, will be put back into fence pool on drop. 49 must_put_in_pool: bool, 50 } 51 52 impl<D> Fence<D> 53 where 54 D: SafeDeref<Target = Device>, 55 { 56 /// Takes a fence from the vulkano-provided fence pool. 57 /// If the pool is empty, a new fence will be allocated. 58 /// Upon `drop`, the fence is put back into the pool. 59 /// 60 /// For most applications, using the fence pool should be preferred, 61 /// in order to avoid creating new fences every frame. from_pool(device: D) -> Result<Fence<D>, OomError>62 pub fn from_pool(device: D) -> Result<Fence<D>, OomError> { 63 let maybe_raw_fence = device.fence_pool().lock().unwrap().pop(); 64 match maybe_raw_fence { 65 Some(raw_fence) => { 66 unsafe { 67 // Make sure the fence isn't signaled 68 let fns = device.fns(); 69 check_errors( 70 fns.v1_0 71 .reset_fences(device.internal_object(), 1, &raw_fence), 72 )?; 73 } 74 Ok(Fence { 75 fence: raw_fence, 76 device: device, 77 signaled: AtomicBool::new(false), 78 must_put_in_pool: true, 79 }) 80 } 81 None => { 82 // Pool is empty, alloc new fence 83 Fence::alloc_impl(device, false, true) 84 } 85 } 86 } 87 88 /// Builds a new fence. 89 #[inline] alloc(device: D) -> Result<Fence<D>, OomError>90 pub fn alloc(device: D) -> Result<Fence<D>, OomError> { 91 Fence::alloc_impl(device, false, false) 92 } 93 94 /// Builds a new fence in signaled state. 95 #[inline] alloc_signaled(device: D) -> Result<Fence<D>, OomError>96 pub fn alloc_signaled(device: D) -> Result<Fence<D>, OomError> { 97 Fence::alloc_impl(device, true, false) 98 } 99 alloc_impl(device: D, signaled: bool, must_put_in_pool: bool) -> Result<Fence<D>, OomError>100 fn alloc_impl(device: D, signaled: bool, must_put_in_pool: bool) -> Result<Fence<D>, OomError> { 101 let fence = unsafe { 102 let infos = ash::vk::FenceCreateInfo { 103 flags: if signaled { 104 ash::vk::FenceCreateFlags::SIGNALED 105 } else { 106 ash::vk::FenceCreateFlags::empty() 107 }, 108 ..Default::default() 109 }; 110 111 let fns = device.fns(); 112 let mut output = MaybeUninit::uninit(); 113 check_errors(fns.v1_0.create_fence( 114 device.internal_object(), 115 &infos, 116 ptr::null(), 117 output.as_mut_ptr(), 118 ))?; 119 output.assume_init() 120 }; 121 122 Ok(Fence { 123 fence: fence, 124 device: device, 125 signaled: AtomicBool::new(signaled), 126 must_put_in_pool: must_put_in_pool, 127 }) 128 } 129 130 /// Returns true if the fence is signaled. 131 #[inline] ready(&self) -> Result<bool, OomError>132 pub fn ready(&self) -> Result<bool, OomError> { 133 unsafe { 134 if self.signaled.load(Ordering::Relaxed) { 135 return Ok(true); 136 } 137 138 let fns = self.device.fns(); 139 let result = check_errors( 140 fns.v1_0 141 .get_fence_status(self.device.internal_object(), self.fence), 142 )?; 143 match result { 144 Success::Success => { 145 self.signaled.store(true, Ordering::Relaxed); 146 Ok(true) 147 } 148 Success::NotReady => Ok(false), 149 _ => unreachable!(), 150 } 151 } 152 } 153 154 /// Waits until the fence is signaled, or at least until the timeout duration has elapsed. 155 /// 156 /// Returns `Ok` if the fence is now signaled. Returns `Err` if the timeout was reached instead. 157 /// 158 /// If you pass a duration of 0, then the function will return without blocking. wait(&self, timeout: Option<Duration>) -> Result<(), FenceWaitError>159 pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FenceWaitError> { 160 unsafe { 161 if self.signaled.load(Ordering::Relaxed) { 162 return Ok(()); 163 } 164 165 let timeout_ns = if let Some(timeout) = timeout { 166 timeout 167 .as_secs() 168 .saturating_mul(1_000_000_000) 169 .saturating_add(timeout.subsec_nanos() as u64) 170 } else { 171 u64::MAX 172 }; 173 174 let fns = self.device.fns(); 175 let r = check_errors(fns.v1_0.wait_for_fences( 176 self.device.internal_object(), 177 1, 178 &self.fence, 179 ash::vk::TRUE, 180 timeout_ns, 181 ))?; 182 183 match r { 184 Success::Success => { 185 self.signaled.store(true, Ordering::Relaxed); 186 Ok(()) 187 } 188 Success::Timeout => Err(FenceWaitError::Timeout), 189 _ => unreachable!(), 190 } 191 } 192 } 193 194 /// Waits for multiple fences at once. 195 /// 196 /// # Panic 197 /// 198 /// Panics if not all fences belong to the same device. multi_wait<'a, I>(iter: I, timeout: Option<Duration>) -> Result<(), FenceWaitError> where I: IntoIterator<Item = &'a Fence<D>>, D: 'a,199 pub fn multi_wait<'a, I>(iter: I, timeout: Option<Duration>) -> Result<(), FenceWaitError> 200 where 201 I: IntoIterator<Item = &'a Fence<D>>, 202 D: 'a, 203 { 204 let mut device: Option<&Device> = None; 205 206 let fences: SmallVec<[ash::vk::Fence; 8]> = iter 207 .into_iter() 208 .filter_map(|fence| { 209 match &mut device { 210 dev @ &mut None => *dev = Some(&*fence.device), 211 &mut Some(ref dev) 212 if &**dev as *const Device == &*fence.device as *const Device => {} 213 _ => panic!( 214 "Tried to wait for multiple fences that didn't belong to the \ 215 same device" 216 ), 217 }; 218 219 if fence.signaled.load(Ordering::Relaxed) { 220 None 221 } else { 222 Some(fence.fence) 223 } 224 }) 225 .collect(); 226 227 let timeout_ns = if let Some(timeout) = timeout { 228 timeout 229 .as_secs() 230 .saturating_mul(1_000_000_000) 231 .saturating_add(timeout.subsec_nanos() as u64) 232 } else { 233 u64::MAX 234 }; 235 236 let r = if let Some(device) = device { 237 unsafe { 238 let fns = device.fns(); 239 check_errors(fns.v1_0.wait_for_fences( 240 device.internal_object(), 241 fences.len() as u32, 242 fences.as_ptr(), 243 ash::vk::TRUE, 244 timeout_ns, 245 ))? 246 } 247 } else { 248 return Ok(()); 249 }; 250 251 match r { 252 Success::Success => Ok(()), 253 Success::Timeout => Err(FenceWaitError::Timeout), 254 _ => unreachable!(), 255 } 256 } 257 258 /// Resets the fence. 259 // This function takes a `&mut self` because the Vulkan API requires that the fence be 260 // externally synchronized. 261 #[inline] reset(&mut self) -> Result<(), OomError>262 pub fn reset(&mut self) -> Result<(), OomError> { 263 unsafe { 264 let fns = self.device.fns(); 265 check_errors( 266 fns.v1_0 267 .reset_fences(self.device.internal_object(), 1, &self.fence), 268 )?; 269 self.signaled.store(false, Ordering::Relaxed); 270 Ok(()) 271 } 272 } 273 274 /// Resets multiple fences at once. 275 /// 276 /// # Panic 277 /// 278 /// - Panics if not all fences belong to the same device. 279 /// multi_reset<'a, I>(iter: I) -> Result<(), OomError> where I: IntoIterator<Item = &'a mut Fence<D>>, D: 'a,280 pub fn multi_reset<'a, I>(iter: I) -> Result<(), OomError> 281 where 282 I: IntoIterator<Item = &'a mut Fence<D>>, 283 D: 'a, 284 { 285 let mut device: Option<&Device> = None; 286 287 let fences: SmallVec<[ash::vk::Fence; 8]> = iter 288 .into_iter() 289 .map(|fence| { 290 match &mut device { 291 dev @ &mut None => *dev = Some(&*fence.device), 292 &mut Some(ref dev) 293 if &**dev as *const Device == &*fence.device as *const Device => {} 294 _ => panic!( 295 "Tried to reset multiple fences that didn't belong to the same \ 296 device" 297 ), 298 }; 299 300 fence.signaled.store(false, Ordering::Relaxed); 301 fence.fence 302 }) 303 .collect(); 304 305 if let Some(device) = device { 306 unsafe { 307 let fns = device.fns(); 308 check_errors(fns.v1_0.reset_fences( 309 device.internal_object(), 310 fences.len() as u32, 311 fences.as_ptr(), 312 ))?; 313 } 314 } 315 Ok(()) 316 } 317 } 318 319 unsafe impl DeviceOwned for Fence { 320 #[inline] device(&self) -> &Arc<Device>321 fn device(&self) -> &Arc<Device> { 322 &self.device 323 } 324 } 325 326 unsafe impl<D> VulkanObject for Fence<D> 327 where 328 D: SafeDeref<Target = Device>, 329 { 330 type Object = ash::vk::Fence; 331 332 #[inline] internal_object(&self) -> ash::vk::Fence333 fn internal_object(&self) -> ash::vk::Fence { 334 self.fence 335 } 336 } 337 338 impl<D> Drop for Fence<D> 339 where 340 D: SafeDeref<Target = Device>, 341 { 342 #[inline] drop(&mut self)343 fn drop(&mut self) { 344 unsafe { 345 if self.must_put_in_pool { 346 let raw_fence = self.fence; 347 self.device.fence_pool().lock().unwrap().push(raw_fence); 348 } else { 349 let fns = self.device.fns(); 350 fns.v1_0 351 .destroy_fence(self.device.internal_object(), self.fence, ptr::null()); 352 } 353 } 354 } 355 } 356 357 /// Error that can be returned when waiting on a fence. 358 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 359 pub enum FenceWaitError { 360 /// Not enough memory to complete the wait. 361 OomError(OomError), 362 363 /// The specified timeout wasn't long enough. 364 Timeout, 365 366 /// The device has been lost. 367 DeviceLostError, 368 } 369 370 impl error::Error for FenceWaitError { 371 #[inline] source(&self) -> Option<&(dyn error::Error + 'static)>372 fn source(&self) -> Option<&(dyn error::Error + 'static)> { 373 match *self { 374 FenceWaitError::OomError(ref err) => Some(err), 375 _ => None, 376 } 377 } 378 } 379 380 impl fmt::Display for FenceWaitError { 381 #[inline] fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>382 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 383 write!( 384 fmt, 385 "{}", 386 match *self { 387 FenceWaitError::OomError(_) => "no memory available", 388 FenceWaitError::Timeout => "the timeout has been reached", 389 FenceWaitError::DeviceLostError => "the device was lost", 390 } 391 ) 392 } 393 } 394 395 impl From<Error> for FenceWaitError { 396 #[inline] from(err: Error) -> FenceWaitError397 fn from(err: Error) -> FenceWaitError { 398 match err { 399 Error::OutOfHostMemory => FenceWaitError::OomError(From::from(err)), 400 Error::OutOfDeviceMemory => FenceWaitError::OomError(From::from(err)), 401 Error::DeviceLost => FenceWaitError::DeviceLostError, 402 _ => panic!("Unexpected error value: {}", err as i32), 403 } 404 } 405 } 406 407 #[cfg(test)] 408 mod tests { 409 use crate::sync::Fence; 410 use crate::VulkanObject; 411 use std::time::Duration; 412 413 #[test] fence_create()414 fn fence_create() { 415 let (device, _) = gfx_dev_and_queue!(); 416 417 let fence = Fence::alloc(device.clone()).unwrap(); 418 assert!(!fence.ready().unwrap()); 419 } 420 421 #[test] fence_create_signaled()422 fn fence_create_signaled() { 423 let (device, _) = gfx_dev_and_queue!(); 424 425 let fence = Fence::alloc_signaled(device.clone()).unwrap(); 426 assert!(fence.ready().unwrap()); 427 } 428 429 #[test] fence_signaled_wait()430 fn fence_signaled_wait() { 431 let (device, _) = gfx_dev_and_queue!(); 432 433 let fence = Fence::alloc_signaled(device.clone()).unwrap(); 434 fence.wait(Some(Duration::new(0, 10))).unwrap(); 435 } 436 437 #[test] fence_reset()438 fn fence_reset() { 439 let (device, _) = gfx_dev_and_queue!(); 440 441 let mut fence = Fence::alloc_signaled(device.clone()).unwrap(); 442 fence.reset().unwrap(); 443 assert!(!fence.ready().unwrap()); 444 } 445 446 #[test] multiwait_different_devices()447 fn multiwait_different_devices() { 448 let (device1, _) = gfx_dev_and_queue!(); 449 let (device2, _) = gfx_dev_and_queue!(); 450 451 assert_should_panic!( 452 "Tried to wait for multiple fences that didn't belong \ 453 to the same device", 454 { 455 let fence1 = Fence::alloc_signaled(device1.clone()).unwrap(); 456 let fence2 = Fence::alloc_signaled(device2.clone()).unwrap(); 457 458 let _ = Fence::multi_wait( 459 [&fence1, &fence2].iter().cloned(), 460 Some(Duration::new(0, 10)), 461 ); 462 } 463 ); 464 } 465 466 #[test] multireset_different_devices()467 fn multireset_different_devices() { 468 use std::iter::once; 469 470 let (device1, _) = gfx_dev_and_queue!(); 471 let (device2, _) = gfx_dev_and_queue!(); 472 473 assert_should_panic!( 474 "Tried to reset multiple fences that didn't belong \ 475 to the same device", 476 { 477 let mut fence1 = Fence::alloc_signaled(device1.clone()).unwrap(); 478 let mut fence2 = Fence::alloc_signaled(device2.clone()).unwrap(); 479 480 let _ = Fence::multi_reset(once(&mut fence1).chain(once(&mut fence2))); 481 } 482 ); 483 } 484 485 #[test] fence_pool()486 fn fence_pool() { 487 let (device, _) = gfx_dev_and_queue!(); 488 489 assert_eq!(device.fence_pool().lock().unwrap().len(), 0); 490 let fence1_internal_obj = { 491 let fence = Fence::from_pool(device.clone()).unwrap(); 492 assert_eq!(device.fence_pool().lock().unwrap().len(), 0); 493 fence.internal_object() 494 }; 495 496 assert_eq!(device.fence_pool().lock().unwrap().len(), 1); 497 let fence2 = Fence::from_pool(device.clone()).unwrap(); 498 assert_eq!(device.fence_pool().lock().unwrap().len(), 0); 499 assert_eq!(fence2.internal_object(), fence1_internal_obj); 500 } 501 } 502