diff options
Diffstat (limited to 'third_party/rust/gfx-backend-vulkan/src/window.rs')
-rw-r--r-- | third_party/rust/gfx-backend-vulkan/src/window.rs | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/third_party/rust/gfx-backend-vulkan/src/window.rs b/third_party/rust/gfx-backend-vulkan/src/window.rs new file mode 100644 index 0000000000..5add2e8e2f --- /dev/null +++ b/third_party/rust/gfx-backend-vulkan/src/window.rs @@ -0,0 +1,584 @@ +use std::{ + borrow::Borrow, + fmt, hash, + os::raw::c_void, + sync::{Arc, Mutex}, + time::Instant, +}; + +use ash::{extensions::khr, version::DeviceV1_0 as _, vk}; +use hal::{format::Format, window as w}; +use smallvec::SmallVec; + +use crate::{conv, info, native}; +use crate::{ + Backend, Device, Instance, PhysicalDevice, QueueFamily, RawDevice, RawInstance, VK_ENTRY, +}; + +#[derive(Debug, Default)] +pub struct FramebufferCache { + // We expect exactly one framebuffer per frame, but can support more. + pub framebuffers: SmallVec<[vk::Framebuffer; 1]>, +} + +#[derive(Debug, Default)] +pub struct FramebufferCachePtr(pub Arc<Mutex<FramebufferCache>>); + +impl hash::Hash for FramebufferCachePtr { + fn hash<H: hash::Hasher>(&self, hasher: &mut H) { + (self.0.as_ref() as *const Mutex<FramebufferCache>).hash(hasher) + } +} +impl PartialEq for FramebufferCachePtr { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} +impl Eq for FramebufferCachePtr {} + +#[derive(Debug)] +struct SurfaceFrame { + image: vk::Image, + view: vk::ImageView, + framebuffers: FramebufferCachePtr, +} + +#[derive(Debug)] +pub struct SurfaceSwapchain { + pub(crate) swapchain: Swapchain, + device: Arc<RawDevice>, + fence: native::Fence, + pub(crate) semaphore: native::Semaphore, + frames: Vec<SurfaceFrame>, +} + +impl SurfaceSwapchain { + unsafe fn release_resources(self, device: &ash::Device) -> Swapchain { + let _ = device.device_wait_idle(); + device.destroy_fence(self.fence.0, None); + device.destroy_semaphore(self.semaphore.0, None); + for frame in self.frames { + device.destroy_image_view(frame.view, None); + for framebuffer in frame.framebuffers.0.lock().unwrap().framebuffers.drain(..) { + device.destroy_framebuffer(framebuffer, None); + } + } + self.swapchain + } +} + +pub struct Surface { + // Vk (EXT) specs [29.2.7 Platform-Independent Information] + // For vkDestroySurfaceKHR: Host access to surface must be externally synchronized + pub(crate) raw: Arc<RawSurface>, + pub(crate) swapchain: Option<SurfaceSwapchain>, +} + +impl fmt::Debug for Surface { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Surface") + } +} + +pub struct RawSurface { + pub(crate) handle: vk::SurfaceKHR, + pub(crate) functor: khr::Surface, + pub(crate) instance: Arc<RawInstance>, +} + +impl Instance { + #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] + pub fn create_surface_from_xlib(&self, dpy: *mut vk::Display, window: vk::Window) -> Surface { + let entry = VK_ENTRY + .as_ref() + .expect("Unable to load Vulkan entry points"); + + if !self.extensions.contains(&khr::XlibSurface::name()) { + panic!("Vulkan driver does not support VK_KHR_XLIB_SURFACE"); + } + + let surface = { + let xlib_loader = khr::XlibSurface::new(entry, &self.raw.inner); + let info = vk::XlibSurfaceCreateInfoKHR::builder() + .flags(vk::XlibSurfaceCreateFlagsKHR::empty()) + .window(window) + .dpy(dpy); + + unsafe { xlib_loader.create_xlib_surface(&info, None) } + .expect("XlibSurface::create_xlib_surface() failed") + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] + pub fn create_surface_from_xcb( + &self, + connection: *mut vk::xcb_connection_t, + window: vk::xcb_window_t, + ) -> Surface { + let entry = VK_ENTRY + .as_ref() + .expect("Unable to load Vulkan entry points"); + + if !self.extensions.contains(&khr::XcbSurface::name()) { + panic!("Vulkan driver does not support VK_KHR_XCB_SURFACE"); + } + + let surface = { + let xcb_loader = khr::XcbSurface::new(entry, &self.raw.inner); + let info = vk::XcbSurfaceCreateInfoKHR::builder() + .flags(vk::XcbSurfaceCreateFlagsKHR::empty()) + .window(window) + .connection(connection); + + unsafe { xcb_loader.create_xcb_surface(&info, None) } + .expect("XcbSurface::create_xcb_surface() failed") + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + #[cfg(all(unix, not(target_os = "android")))] + pub fn create_surface_from_wayland( + &self, + display: *mut c_void, + surface: *mut c_void, + ) -> Surface { + let entry = VK_ENTRY + .as_ref() + .expect("Unable to load Vulkan entry points"); + + if !self.extensions.contains(&khr::WaylandSurface::name()) { + panic!("Vulkan driver does not support VK_KHR_WAYLAND_SURFACE"); + } + + let surface = { + let w_loader = khr::WaylandSurface::new(entry, &self.raw.inner); + let info = vk::WaylandSurfaceCreateInfoKHR::builder() + .flags(vk::WaylandSurfaceCreateFlagsKHR::empty()) + .display(display) + .surface(surface); + + unsafe { w_loader.create_wayland_surface(&info, None) }.expect("WaylandSurface failed") + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + #[cfg(target_os = "android")] + pub fn create_surface_android(&self, window: *const c_void) -> Surface { + let entry = VK_ENTRY + .as_ref() + .expect("Unable to load Vulkan entry points"); + + let surface = { + let loader = khr::AndroidSurface::new(entry, &self.raw.inner); + let info = vk::AndroidSurfaceCreateInfoKHR::builder() + .flags(vk::AndroidSurfaceCreateFlagsKHR::empty()) + .window(window as *mut _); + + unsafe { loader.create_android_surface(&info, None) }.expect("AndroidSurface failed") + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + #[cfg(windows)] + pub fn create_surface_from_hwnd(&self, hinstance: *mut c_void, hwnd: *mut c_void) -> Surface { + let entry = VK_ENTRY + .as_ref() + .expect("Unable to load Vulkan entry points"); + + if !self.extensions.contains(&khr::Win32Surface::name()) { + panic!("Vulkan driver does not support VK_KHR_WIN32_SURFACE"); + } + + let surface = { + let info = vk::Win32SurfaceCreateInfoKHR::builder() + .flags(vk::Win32SurfaceCreateFlagsKHR::empty()) + .hinstance(hinstance) + .hwnd(hwnd); + let win32_loader = khr::Win32Surface::new(entry, &self.raw.inner); + unsafe { + win32_loader + .create_win32_surface(&info, None) + .expect("Unable to create Win32 surface") + } + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + #[cfg(target_os = "macos")] + pub fn create_surface_from_ns_view(&self, view: *mut c_void) -> Surface { + use ash::extensions::mvk; + use core_graphics_types::{base::CGFloat, geometry::CGRect}; + use objc::runtime::{Object, BOOL, YES}; + + // TODO: this logic is duplicated from gfx-backend-metal, refactor? + unsafe { + let view = view as *mut Object; + let existing: *mut Object = msg_send![view, layer]; + let class = class!(CAMetalLayer); + + let use_current = if existing.is_null() { + false + } else { + let result: BOOL = msg_send![existing, isKindOfClass: class]; + result == YES + }; + + if !use_current { + let layer: *mut Object = msg_send![class, new]; + let () = msg_send![view, setLayer: layer]; + let bounds: CGRect = msg_send![view, bounds]; + let () = msg_send![layer, setBounds: bounds]; + + let window: *mut Object = msg_send![view, window]; + if !window.is_null() { + let scale_factor: CGFloat = msg_send![window, backingScaleFactor]; + let () = msg_send![layer, setContentsScale: scale_factor]; + } + } + } + + let entry = VK_ENTRY + .as_ref() + .expect("Unable to load Vulkan entry points"); + + if !self.extensions.contains(&mvk::MacOSSurface::name()) { + panic!("Vulkan driver does not support VK_MVK_MACOS_SURFACE"); + } + + let surface = { + let mac_os_loader = mvk::MacOSSurface::new(entry, &self.raw.inner); + let mut info = vk::MacOSSurfaceCreateInfoMVK::builder() + .flags(vk::MacOSSurfaceCreateFlagsMVK::empty()); + if let Some(view) = unsafe { view.as_ref() } { + info = info.view(view); + } + + unsafe { + mac_os_loader + .create_mac_os_surface_mvk(&info, None) + .expect("Unable to create macOS surface") + } + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + pub fn create_surface_from_vk_surface_khr(&self, surface: vk::SurfaceKHR) -> Surface { + let entry = VK_ENTRY + .as_ref() + .expect("Unable to load Vulkan entry points"); + + let functor = khr::Surface::new(entry, &self.raw.inner); + + let raw = Arc::new(RawSurface { + handle: surface, + functor, + instance: self.raw.clone(), + }); + + Surface { + raw, + swapchain: None, + } + } +} + +impl w::Surface<Backend> for Surface { + fn supports_queue_family(&self, queue_family: &QueueFamily) -> bool { + match unsafe { + self.raw.functor.get_physical_device_surface_support( + queue_family.device, + queue_family.index, + self.raw.handle, + ) + } { + Ok(ok) => ok, + Err(e) => { + error!("get_physical_device_surface_support error {:?}", e); + false + } + } + } + + fn capabilities(&self, physical_device: &PhysicalDevice) -> w::SurfaceCapabilities { + // Capabilities + let caps = unsafe { + self.raw + .functor + .get_physical_device_surface_capabilities(physical_device.handle, self.raw.handle) + } + .expect("Unable to query surface capabilities"); + + // If image count is 0, the support number of images is unlimited. + let max_images = if caps.max_image_count == 0 { + !0 + } else { + caps.max_image_count + }; + + // `0xFFFFFFFF` indicates that the extent depends on the created swapchain. + let current_extent = if caps.current_extent.width != !0 && caps.current_extent.height != !0 + { + Some(w::Extent2D { + width: caps.current_extent.width, + height: caps.current_extent.height, + }) + } else { + None + }; + + let min_extent = w::Extent2D { + width: caps.min_image_extent.width, + height: caps.min_image_extent.height, + }; + + let max_extent = w::Extent2D { + width: caps.max_image_extent.width, + height: caps.max_image_extent.height, + }; + + let raw_present_modes = unsafe { + self.raw + .functor + .get_physical_device_surface_present_modes(physical_device.handle, self.raw.handle) + } + .expect("Unable to query present modes"); + + w::SurfaceCapabilities { + present_modes: raw_present_modes + .into_iter() + .fold(w::PresentMode::empty(), |u, m| { + u | conv::map_vk_present_mode(m) + }), + composite_alpha_modes: conv::map_vk_composite_alpha(caps.supported_composite_alpha), + image_count: caps.min_image_count..=max_images, + current_extent, + extents: min_extent..=max_extent, + max_image_layers: caps.max_image_array_layers as _, + usage: conv::map_vk_image_usage(caps.supported_usage_flags), + } + } + + fn supported_formats(&self, physical_device: &PhysicalDevice) -> Option<Vec<Format>> { + // Swapchain formats + let raw_formats = unsafe { + self.raw + .functor + .get_physical_device_surface_formats(physical_device.handle, self.raw.handle) + } + .expect("Unable to query surface formats"); + + match raw_formats[0].format { + // If pSurfaceFormats includes just one entry, whose value for format is + // VK_FORMAT_UNDEFINED, surface has no preferred format. In this case, the application + // can use any valid VkFormat value. + vk::Format::UNDEFINED => None, + _ => Some( + raw_formats + .into_iter() + .filter_map(|sf| conv::map_vk_format(sf.format)) + .collect(), + ), + } + } +} + +#[derive(Debug)] +pub struct SurfaceImage { + pub(crate) index: w::SwapImageIndex, + image: native::Image, + view: native::ImageView, +} + +impl Borrow<native::Image> for SurfaceImage { + fn borrow(&self) -> &native::Image { + &self.image + } +} + +impl Borrow<native::ImageView> for SurfaceImage { + fn borrow(&self) -> &native::ImageView { + &self.view + } +} + +impl w::PresentationSurface<Backend> for Surface { + type SwapchainImage = SurfaceImage; + + unsafe fn configure_swapchain( + &mut self, + device: &Device, + config: w::SwapchainConfig, + ) -> Result<(), w::CreationError> { + use hal::device::Device as _; + + let format = config.format; + let old = self + .swapchain + .take() + .map(|ssc| ssc.release_resources(&device.shared.raw)); + + let (swapchain, images) = device.create_swapchain(self, config, old)?; + + self.swapchain = Some(SurfaceSwapchain { + swapchain, + device: Arc::clone(&device.shared), + fence: device.create_fence(false).unwrap(), + semaphore: device.create_semaphore().unwrap(), + frames: images + .iter() + .map(|image| { + let view = device + .create_image_view( + image, + hal::image::ViewKind::D2, + format, + hal::format::Swizzle::NO, + hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + ..Default::default() + }, + ) + .unwrap(); + SurfaceFrame { + image: view.image, + view: view.view, + framebuffers: Default::default(), + } + }) + .collect(), + }); + + Ok(()) + } + + unsafe fn unconfigure_swapchain(&mut self, device: &Device) { + if let Some(ssc) = self.swapchain.take() { + let swapchain = ssc.release_resources(&device.shared.raw); + swapchain.functor.destroy_swapchain(swapchain.raw, None); + } + } + + unsafe fn acquire_image( + &mut self, + mut timeout_ns: u64, + ) -> Result<(Self::SwapchainImage, Option<w::Suboptimal>), w::AcquireError> { + let ssc = self.swapchain.as_mut().unwrap(); + let moment = Instant::now(); + let (index, suboptimal) = + ssc.swapchain + .acquire_image(timeout_ns, None, Some(&ssc.fence))?; + timeout_ns = timeout_ns.saturating_sub(moment.elapsed().as_nanos() as u64); + let fences = &[ssc.fence.0]; + + match ssc.device.raw.wait_for_fences(fences, true, timeout_ns) { + Ok(()) => { + ssc.device.raw.reset_fences(fences).unwrap(); + let frame = &ssc.frames[index as usize]; + // We have just waited for the frame to be fully available on CPU. + // All the associated framebuffers are expected to be destroyed by now. + for framebuffer in frame.framebuffers.0.lock().unwrap().framebuffers.drain(..) { + ssc.device.raw.destroy_framebuffer(framebuffer, None); + } + let image = Self::SwapchainImage { + index, + image: native::Image { + raw: frame.image, + ty: vk::ImageType::TYPE_2D, + flags: vk::ImageCreateFlags::empty(), + extent: ssc.swapchain.extent, + }, + view: native::ImageView { + image: frame.image, + view: frame.view, + range: hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + ..Default::default() + }, + owner: native::ImageViewOwner::Surface(FramebufferCachePtr(Arc::clone( + &frame.framebuffers.0, + ))), + }, + }; + Ok((image, suboptimal)) + } + Err(vk::Result::NOT_READY) => Err(w::AcquireError::NotReady), + Err(vk::Result::TIMEOUT) => Err(w::AcquireError::Timeout), + Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => Err(w::AcquireError::OutOfDate), + Err(vk::Result::ERROR_SURFACE_LOST_KHR) => { + Err(w::AcquireError::SurfaceLost(hal::device::SurfaceLost)) + } + Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => { + Err(w::AcquireError::OutOfMemory(hal::device::OutOfMemory::Host)) + } + Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(w::AcquireError::OutOfMemory( + hal::device::OutOfMemory::Device, + )), + Err(vk::Result::ERROR_DEVICE_LOST) => { + Err(w::AcquireError::DeviceLost(hal::device::DeviceLost)) + } + _ => unreachable!(), + } + } +} + +pub struct Swapchain { + pub(crate) raw: vk::SwapchainKHR, + pub(crate) functor: khr::Swapchain, + pub(crate) vendor_id: u32, + pub(crate) extent: vk::Extent3D, +} + +impl fmt::Debug for Swapchain { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Swapchain") + } +} + +impl Swapchain { + unsafe fn acquire_image( + &mut self, + timeout_ns: u64, + semaphore: Option<&native::Semaphore>, + fence: Option<&native::Fence>, + ) -> Result<(w::SwapImageIndex, Option<w::Suboptimal>), w::AcquireError> { + let semaphore = semaphore.map_or(vk::Semaphore::null(), |s| s.0); + let fence = fence.map_or(vk::Fence::null(), |f| f.0); + + // will block if no image is available + let index = self + .functor + .acquire_next_image(self.raw, timeout_ns, semaphore, fence); + + match index { + // special case for Intel Vulkan returning bizzare values (ugh) + Ok((i, _)) if self.vendor_id == info::intel::VENDOR && i > 0x100 => { + Err(w::AcquireError::OutOfDate) + } + Ok((i, true)) => Ok((i, Some(w::Suboptimal))), + Ok((i, false)) => Ok((i, None)), + Err(vk::Result::NOT_READY) => Err(w::AcquireError::NotReady), + Err(vk::Result::TIMEOUT) => Err(w::AcquireError::Timeout), + Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => Err(w::AcquireError::OutOfDate), + Err(vk::Result::ERROR_SURFACE_LOST_KHR) => { + Err(w::AcquireError::SurfaceLost(hal::device::SurfaceLost)) + } + Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => { + Err(w::AcquireError::OutOfMemory(hal::device::OutOfMemory::Host)) + } + Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(w::AcquireError::OutOfMemory( + hal::device::OutOfMemory::Device, + )), + Err(vk::Result::ERROR_DEVICE_LOST) => { + Err(w::AcquireError::DeviceLost(hal::device::DeviceLost)) + } + _ => panic!("Failed to acquire image."), + } + } +} |