diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index aa3e6674cc..c9e7ed9eb2 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -801,6 +801,17 @@ impl crate::Adapter for super::Adapter { } else { None }; + let timeline_semaphore_fn = if enabled_extensions.contains(&khr::TimelineSemaphore::name()) + { + Some(super::ExtensionFn::Extension(khr::TimelineSemaphore::new( + &self.instance.entry, + &self.instance.raw, + ))) + } else if self.phd_capabilities.properties.api_version >= vk::API_VERSION_1_2 { + Some(super::ExtensionFn::Promoted) + } else { + None + }; let naga_options = { use naga::back::spv; @@ -834,6 +845,7 @@ impl crate::Adapter for super::Adapter { instance: Arc::clone(&self.instance), extension_fns: super::DeviceExtensionFunctions { draw_indirect_count: indirect_count_fn, + timeline_semaphore: timeline_semaphore_fn, }, vendor_id: self.phd_capabilities.properties.vendor_id, downlevel_flags: self.downlevel_flags, diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 295a91bd81..16c5c47bec 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -1,7 +1,11 @@ use super::conv; use arrayvec::ArrayVec; -use ash::{extensions::khr, version::DeviceV1_0, vk}; +use ash::{ + extensions::khr, + version::{DeviceV1_0, DeviceV1_2}, + vk, +}; use inplace_it::inplace_or_alloc_from_iter; use parking_lot::Mutex; @@ -1390,26 +1394,49 @@ impl crate::Device for super::Device { unsafe fn destroy_query_set(&self, set: super::QuerySet) { self.shared.raw.destroy_query_pool(set.raw, None); } + unsafe fn create_fence(&self) -> Result { - Ok(super::Fence { - last_completed: 0, - active: Vec::new(), - free: Vec::new(), + Ok(if self.shared.private_caps.timeline_semaphores { + let mut sem_type_info = + vk::SemaphoreTypeCreateInfo::builder().semaphore_type(vk::SemaphoreType::TIMELINE); + let vk_info = vk::SemaphoreCreateInfo::builder().push_next(&mut sem_type_info); + let raw = self.shared.raw.create_semaphore(&vk_info, None)?; + super::Fence::TimelineSemaphore(raw) + } else { + super::Fence::FencePool { + last_completed: 0, + active: Vec::new(), + free: Vec::new(), + } }) } unsafe fn destroy_fence(&self, fence: super::Fence) { - for (_, raw) in fence.active { - self.shared.raw.destroy_fence(raw, None); - } - for raw in fence.free { - self.shared.raw.destroy_fence(raw, None); + match fence { + super::Fence::TimelineSemaphore(raw) => { + self.shared.raw.destroy_semaphore(raw, None); + } + super::Fence::FencePool { + active, + free, + last_completed: _, + } => { + for (_, raw) in active { + self.shared.raw.destroy_fence(raw, None); + } + for raw in free { + self.shared.raw.destroy_fence(raw, None); + } + } } } unsafe fn get_fence_value( &self, fence: &super::Fence, ) -> Result { - fence.get_latest(&self.shared.raw) + fence.get_latest( + &self.shared.raw, + self.shared.extension_fns.timeline_semaphore.as_ref(), + ) } unsafe fn wait( &self, @@ -1417,21 +1444,50 @@ impl crate::Device for super::Device { wait_value: crate::FenceValue, timeout_ms: u32, ) -> Result { - if wait_value <= fence.last_completed { - Ok(true) - } else { - match fence.active.iter().find(|&&(value, _)| value >= wait_value) { - Some(&(_, raw)) => { - let timeout_us = timeout_ms as u64 * super::MILLIS_TO_NANOS; - match self.shared.raw.wait_for_fences(&[raw], true, timeout_us) { - Ok(()) => Ok(true), - Err(vk::Result::TIMEOUT) => Ok(false), - Err(other) => Err(other.into()), + let timeout_us = timeout_ms as u64 * super::MILLIS_TO_NANOS; + match *fence { + super::Fence::TimelineSemaphore(raw) => { + let semaphores = [raw]; + let values = [wait_value]; + let vk_info = vk::SemaphoreWaitInfo::builder() + .semaphores(&semaphores) + .values(&values); + let result = match self.shared.extension_fns.timeline_semaphore { + Some(super::ExtensionFn::Extension(ref ext)) => { + ext.wait_semaphores(self.shared.raw.handle(), &vk_info, timeout_us) } + Some(super::ExtensionFn::Promoted) => { + self.shared.raw.wait_semaphores(&vk_info, timeout_us) + } + None => unreachable!(), + }; + match result { + Ok(()) => Ok(true), + Err(vk::Result::TIMEOUT) => Ok(false), + Err(other) => Err(other.into()), } - None => { - log::error!("No signals reached value {}", wait_value); - Err(crate::DeviceError::Lost) + } + super::Fence::FencePool { + last_completed, + ref active, + free: _, + } => { + if wait_value <= last_completed { + Ok(true) + } else { + match active.iter().find(|&&(value, _)| value >= wait_value) { + Some(&(_, raw)) => { + match self.shared.raw.wait_for_fences(&[raw], true, timeout_us) { + Ok(()) => Ok(true), + Err(vk::Result::TIMEOUT) => Ok(false), + Err(other) => Err(other.into()), + } + } + None => { + log::error!("No signals reached value {}", wait_value); + Err(crate::DeviceError::Lost) + } + } } } } diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs index db9879705b..be34322853 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -125,7 +125,7 @@ impl super::Instance { } let surface = { - let xlib_loader = khr::XlibSurface::new(&self.entry, &self.shared.raw); + let xlib_loader = khr::XlibSurface::new(&self.shared.entry, &self.shared.raw); let info = vk::XlibSurfaceCreateInfoKHR::builder() .flags(vk::XlibSurfaceCreateFlagsKHR::empty()) .window(window) @@ -149,7 +149,7 @@ impl super::Instance { } let surface = { - let xcb_loader = khr::XcbSurface::new(&self.entry, &self.shared.raw); + let xcb_loader = khr::XcbSurface::new(&self.shared.entry, &self.shared.raw); let info = vk::XcbSurfaceCreateInfoKHR::builder() .flags(vk::XcbSurfaceCreateFlagsKHR::empty()) .window(window) @@ -173,7 +173,7 @@ impl super::Instance { } let surface = { - let w_loader = khr::WaylandSurface::new(&self.entry, &self.shared.raw); + let w_loader = khr::WaylandSurface::new(&self.shared.entry, &self.shared.raw); let info = vk::WaylandSurfaceCreateInfoKHR::builder() .flags(vk::WaylandSurfaceCreateFlagsKHR::empty()) .display(display) @@ -188,7 +188,7 @@ impl super::Instance { #[allow(dead_code)] fn create_surface_android(&self, window: *const c_void) -> super::Surface { let surface = { - let a_loader = khr::AndroidSurface::new(&self.entry, &self.shared.raw); + let a_loader = khr::AndroidSurface::new(&self.shared.entry, &self.shared.raw); let info = vk::AndroidSurfaceCreateInfoKHR::builder() .flags(vk::AndroidSurfaceCreateFlagsKHR::empty()) .window(window as *mut _); @@ -214,7 +214,7 @@ impl super::Instance { .flags(vk::Win32SurfaceCreateFlagsKHR::empty()) .hinstance(hinstance) .hwnd(hwnd); - let win32_loader = khr::Win32Surface::new(&self.entry, &self.shared.raw); + let win32_loader = khr::Win32Surface::new(&self.shared.entry, &self.shared.raw); unsafe { win32_loader .create_win32_surface(&info, None) @@ -277,7 +277,7 @@ impl super::Instance { } fn create_surface_from_vk_surface_khr(&self, surface: vk::SurfaceKHR) -> super::Surface { - let functor = khr::Surface::new(&self.entry, &self.shared.raw); + let functor = khr::Surface::new(&self.shared.entry, &self.shared.raw); super::Surface { raw: surface, functor, @@ -287,21 +287,14 @@ impl super::Instance { } } -impl Drop for super::Instance { +impl Drop for super::InstanceShared { fn drop(&mut self) { - match Arc::get_mut(&mut self.shared) { - Some(shared) => unsafe { - if let Some(du) = shared.debug_utils.take() { - du.extension - .destroy_debug_utils_messenger(du.messenger, None); - } - }, - None => { - log::error!("Unable to drop Instance: something created from it is still alive"); - } - } unsafe { - self.shared.raw.destroy_instance(None); + if let Some(du) = self.debug_utils.take() { + du.extension + .destroy_debug_utils_messenger(du.messenger, None); + } + self.raw.destroy_instance(None); } } } @@ -495,9 +488,9 @@ impl crate::Instance for super::Instance { flags: desc.flags, debug_utils, get_physical_device_properties, + entry, }), extensions, - entry, }) } diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index af321bd487..15da8f0ffc 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -36,7 +36,7 @@ use std::{borrow::Borrow, ffi::CStr, sync::Arc}; use arrayvec::ArrayVec; use ash::{ extensions::{ext, khr}, - version::DeviceV1_0, + version::{DeviceV1_0, DeviceV1_2}, vk, }; use parking_lot::Mutex; @@ -83,12 +83,12 @@ struct InstanceShared { flags: crate::InstanceFlags, debug_utils: Option, get_physical_device_properties: Option, + entry: ash::Entry, } pub struct Instance { shared: Arc, extensions: Vec<&'static CStr>, - entry: ash::Entry, } struct Swapchain { @@ -140,6 +140,7 @@ enum ExtensionFn { struct DeviceExtensionFunctions { draw_indirect_count: Option>, + timeline_semaphore: Option>, } /// Set of internal capabilities, which don't show up in the exposed @@ -355,17 +356,23 @@ pub struct QuerySet { } #[derive(Debug)] -pub struct Fence { - last_completed: crate::FenceValue, - /// The pending fence values have to be ascending. - active: Vec<(crate::FenceValue, vk::Fence)>, - free: Vec, +pub enum Fence { + TimelineSemaphore(vk::Semaphore), + FencePool { + last_completed: crate::FenceValue, + /// The pending fence values have to be ascending. + active: Vec<(crate::FenceValue, vk::Fence)>, + free: Vec, + }, } impl Fence { - fn get_latest(&self, device: &ash::Device) -> Result { - let mut max_value = self.last_completed; - for &(value, raw) in self.active.iter() { + fn check_active( + device: &ash::Device, + mut max_value: crate::FenceValue, + active: &[(crate::FenceValue, vk::Fence)], + ) -> Result { + for &(value, raw) in active.iter() { unsafe { if value > max_value && device.get_fence_status(raw)? { max_value = value; @@ -375,21 +382,52 @@ impl Fence { Ok(max_value) } + fn get_latest( + &self, + device: &ash::Device, + extension: Option<&ExtensionFn>, + ) -> Result { + match *self { + Self::TimelineSemaphore(raw) => unsafe { + Ok(match *extension.unwrap() { + ExtensionFn::Extension(ref ext) => { + ext.get_semaphore_counter_value(device.handle(), raw)? + } + ExtensionFn::Promoted => device.get_semaphore_counter_value(raw)?, + }) + }, + Self::FencePool { + last_completed, + ref active, + free: _, + } => Self::check_active(device, last_completed, active), + } + } + fn maintain(&mut self, device: &ash::Device) -> Result<(), crate::DeviceError> { - let latest = self.get_latest(device)?; - let base_free = self.free.len(); - for &(value, raw) in self.active.iter() { - if value <= latest { - self.free.push(raw); + match *self { + Self::TimelineSemaphore(_) => {} + Self::FencePool { + ref mut last_completed, + ref mut active, + ref mut free, + } => { + let latest = Self::check_active(device, *last_completed, active)?; + let base_free = free.len(); + for &(value, raw) in active.iter() { + if value <= latest { + free.push(raw); + } + } + if free.len() != base_free { + active.retain(|&(value, _)| value > latest); + unsafe { + device.reset_fences(&free[base_free..])?; + } + } + *last_completed = latest; } } - if self.free.len() != base_free { - self.active.retain(|&(value, _)| value > latest); - unsafe { - device.reset_fences(&self.free[base_free..])?; - } - } - self.last_completed = latest; Ok(()) } } @@ -400,35 +438,49 @@ impl crate::Queue for Queue { command_buffers: &[&CommandBuffer], signal_fence: Option<(&mut Fence, crate::FenceValue)>, ) -> Result<(), crate::DeviceError> { - let fence_raw = match signal_fence { - Some((fence, value)) => { - fence.maintain(&self.device.raw)?; - let raw = match fence.free.pop() { - Some(raw) => raw, - None => { - let vk_info = vk::FenceCreateInfo::builder().build(); - self.device.raw.create_fence(&vk_info, None)? - } - }; - fence.active.push((value, raw)); - raw - } - None => vk::Fence::null(), - }; - let vk_cmd_buffers = command_buffers .iter() .map(|cmd| cmd.raw) .collect::>(); - //TODO: semaphores - let vk_info = vk::SubmitInfo::builder() - .command_buffers(&vk_cmd_buffers) - .build(); + let mut vk_info = vk::SubmitInfo::builder().command_buffers(&vk_cmd_buffers); + + let mut fence_raw = vk::Fence::null(); + let mut vk_timeline_info; + let signal_values; + let signal_semaphores; + if let Some((fence, value)) = signal_fence { + fence.maintain(&self.device.raw)?; + match *fence { + Fence::TimelineSemaphore(raw) => { + signal_values = [value]; + signal_semaphores = [raw]; + vk_timeline_info = vk::TimelineSemaphoreSubmitInfo::builder() + .signal_semaphore_values(&signal_values); + vk_info = vk_info + .signal_semaphores(&signal_semaphores) + .push_next(&mut vk_timeline_info); + } + Fence::FencePool { + ref mut active, + ref mut free, + .. + } => { + fence_raw = match free.pop() { + Some(raw) => raw, + None => { + let vk_info = vk::FenceCreateInfo::builder().build(); + self.device.raw.create_fence(&vk_info, None)? + } + }; + active.push((value, fence_raw)); + } + } + } self.device .raw - .queue_submit(self.raw, &[vk_info], fence_raw)?; + .queue_submit(self.raw, &[vk_info.build()], fence_raw)?; Ok(()) }