diff options
Diffstat (limited to 'src/swapchain/surface.rs')
-rw-r--r-- | src/swapchain/surface.rs | 856 |
1 files changed, 856 insertions, 0 deletions
diff --git a/src/swapchain/surface.rs b/src/swapchain/surface.rs new file mode 100644 index 0000000..9bf5fd8 --- /dev/null +++ b/src/swapchain/surface.rs @@ -0,0 +1,856 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use crate::check_errors; +use crate::device::physical::PhysicalDevice; +use crate::device::physical::QueueFamily; +use crate::format::Format; +use crate::image::ImageUsage; +use crate::instance::Instance; +use crate::swapchain::capabilities::SupportedSurfaceTransforms; +use crate::swapchain::display::DisplayMode; +use crate::swapchain::display::DisplayPlane; +use crate::swapchain::Capabilities; +use crate::swapchain::SurfaceSwapchainLock; +use crate::Error; +use crate::OomError; +use crate::VulkanObject; +use std::convert::TryFrom; +use std::error; +use std::fmt; +use std::mem::MaybeUninit; +use std::os::raw::c_ulong; +use std::ptr; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; + +/// Represents a surface on the screen. +/// +/// Creating a `Surface` is platform-specific. +pub struct Surface<W> { + window: W, + instance: Arc<Instance>, + surface: ash::vk::SurfaceKHR, + + // If true, a swapchain has been associated to this surface, and that any new swapchain + // creation should be forbidden. + has_swapchain: AtomicBool, +} + +impl<W> Surface<W> { + /// Creates a `Surface` given the raw handler. + /// + /// Be careful when using it + /// + pub unsafe fn from_raw_surface( + instance: Arc<Instance>, + surface: ash::vk::SurfaceKHR, + win: W, + ) -> Surface<W> { + Surface { + window: win, + instance, + surface, + has_swapchain: AtomicBool::new(false), + } + } + + /// Creates a `Surface` that covers a display mode. + /// + /// # Panic + /// + /// - Panics if `display_mode` and `plane` don't belong to the same physical device. + /// - Panics if `plane` doesn't support the display of `display_mode`. + /// + pub fn from_display_mode( + display_mode: &DisplayMode, + plane: &DisplayPlane, + ) -> Result<Arc<Surface<()>>, SurfaceCreationError> { + if !display_mode + .display() + .physical_device() + .instance() + .enabled_extensions() + .khr_display + { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_KHR_display", + }); + } + + assert_eq!( + display_mode.display().physical_device().internal_object(), + plane.physical_device().internal_object() + ); + assert!(plane.supports(display_mode.display())); + + let instance = display_mode.display().physical_device().instance(); + let fns = instance.fns(); + + let surface = unsafe { + let infos = ash::vk::DisplaySurfaceCreateInfoKHR { + flags: ash::vk::DisplaySurfaceCreateFlagsKHR::empty(), + display_mode: display_mode.internal_object(), + plane_index: plane.index(), + plane_stack_index: 0, // FIXME: plane.properties.currentStackIndex, + transform: ash::vk::SurfaceTransformFlagsKHR::IDENTITY, // TODO: let user choose + global_alpha: 0.0, // TODO: let user choose + alpha_mode: ash::vk::DisplayPlaneAlphaFlagsKHR::OPAQUE, // TODO: let user choose + image_extent: ash::vk::Extent2D { + // TODO: let user choose + width: display_mode.visible_region()[0], + height: display_mode.visible_region()[1], + }, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.khr_display.create_display_plane_surface_khr( + instance.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + window: (), + instance: instance.clone(), + surface, + has_swapchain: AtomicBool::new(false), + })) + } + + /// Creates a `Surface` from a Win32 window. + /// + /// The surface's min, max and current extent will always match the window's dimensions. + /// + /// # Safety + /// + /// The caller must ensure that the `hinstance` and the `hwnd` are both correct and stay + /// alive for the entire lifetime of the surface. The `win` parameter can be used to ensure this. + + pub unsafe fn from_hwnd<T, U>( + instance: Arc<Instance>, + hinstance: *const T, + hwnd: *const U, + win: W, + ) -> Result<Arc<Surface<W>>, SurfaceCreationError> { + let fns = instance.fns(); + + if !instance.enabled_extensions().khr_win32_surface { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_KHR_win32_surface", + }); + } + + let surface = { + let infos = ash::vk::Win32SurfaceCreateInfoKHR { + flags: ash::vk::Win32SurfaceCreateFlagsKHR::empty(), + hinstance: hinstance as *mut _, + hwnd: hwnd as *mut _, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.khr_win32_surface.create_win32_surface_khr( + instance.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + window: win, + instance: instance.clone(), + surface, + has_swapchain: AtomicBool::new(false), + })) + } + + /// Creates a `Surface` from an XCB window. + /// + /// The surface's min, max and current extent will always match the window's dimensions. + /// + /// # Safety + /// + /// The caller must ensure that the `connection` and the `window` are both correct and stay + /// alive for the entire lifetime of the surface. The `win` parameter can be used to ensure this. + pub unsafe fn from_xcb<C>( + instance: Arc<Instance>, + connection: *const C, + window: u32, + win: W, + ) -> Result<Arc<Surface<W>>, SurfaceCreationError> { + let fns = instance.fns(); + + if !instance.enabled_extensions().khr_xcb_surface { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_KHR_xcb_surface", + }); + } + + let surface = { + let infos = ash::vk::XcbSurfaceCreateInfoKHR { + flags: ash::vk::XcbSurfaceCreateFlagsKHR::empty(), + connection: connection as *mut _, + window, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.khr_xcb_surface.create_xcb_surface_khr( + instance.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + window: win, + instance: instance.clone(), + surface, + has_swapchain: AtomicBool::new(false), + })) + } + + /// Creates a `Surface` from an Xlib window. + /// + /// The surface's min, max and current extent will always match the window's dimensions. + /// + /// # Safety + /// + /// The caller must ensure that the `display` and the `window` are both correct and stay + /// alive for the entire lifetime of the surface. The `win` parameter can be used to ensure this. + pub unsafe fn from_xlib<D>( + instance: Arc<Instance>, + display: *const D, + window: c_ulong, + win: W, + ) -> Result<Arc<Surface<W>>, SurfaceCreationError> { + let fns = instance.fns(); + + if !instance.enabled_extensions().khr_xlib_surface { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_KHR_xlib_surface", + }); + } + + let surface = { + let infos = ash::vk::XlibSurfaceCreateInfoKHR { + flags: ash::vk::XlibSurfaceCreateFlagsKHR::empty(), + dpy: display as *mut _, + window, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.khr_xlib_surface.create_xlib_surface_khr( + instance.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + window: win, + instance: instance.clone(), + surface, + has_swapchain: AtomicBool::new(false), + })) + } + + /// Creates a `Surface` from a Wayland window. + /// + /// The window's dimensions will be set to the size of the swapchain. + /// + /// # Safety + /// + /// The caller must ensure that the `display` and the `surface` are both correct and stay + /// alive for the entire lifetime of the surface. The `win` parameter can be used to ensure this. + pub unsafe fn from_wayland<D, S>( + instance: Arc<Instance>, + display: *const D, + surface: *const S, + win: W, + ) -> Result<Arc<Surface<W>>, SurfaceCreationError> { + let fns = instance.fns(); + + if !instance.enabled_extensions().khr_wayland_surface { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_KHR_wayland_surface", + }); + } + + let surface = { + let infos = ash::vk::WaylandSurfaceCreateInfoKHR { + flags: ash::vk::WaylandSurfaceCreateFlagsKHR::empty(), + display: display as *mut _, + surface: surface as *mut _, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.khr_wayland_surface.create_wayland_surface_khr( + instance.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + window: win, + instance: instance.clone(), + surface, + has_swapchain: AtomicBool::new(false), + })) + } + + /// Creates a `Surface` from an Android window. + /// + /// # Safety + /// + /// The caller must ensure that the `window` is correct and stays alive for the entire + /// lifetime of the surface. The `win` parameter can be used to ensure this. + pub unsafe fn from_anativewindow<T>( + instance: Arc<Instance>, + window: *const T, + win: W, + ) -> Result<Arc<Surface<W>>, SurfaceCreationError> { + let fns = instance.fns(); + + if !instance.enabled_extensions().khr_android_surface { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_KHR_android_surface", + }); + } + + let surface = { + let infos = ash::vk::AndroidSurfaceCreateInfoKHR { + flags: ash::vk::AndroidSurfaceCreateFlagsKHR::empty(), + window: window as *mut _, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.khr_android_surface.create_android_surface_khr( + instance.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + window: win, + instance: instance.clone(), + surface, + has_swapchain: AtomicBool::new(false), + })) + } + + /// Creates a `Surface` from an iOS `UIView`. + /// + /// # Safety + /// + /// - The caller must ensure that the `view` is correct and stays alive for the entire + /// lifetime of the surface. The win parameter can be used to ensure this. + /// - The `UIView` must be backed by a `CALayer` instance of type `CAMetalLayer`. + pub unsafe fn from_ios_moltenvk<T>( + instance: Arc<Instance>, + view: *const T, + win: W, + ) -> Result<Arc<Surface<W>>, SurfaceCreationError> { + let fns = instance.fns(); + + if !instance.enabled_extensions().mvk_ios_surface { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_MVK_ios_surface", + }); + } + + let surface = { + let infos = ash::vk::IOSSurfaceCreateInfoMVK { + flags: ash::vk::IOSSurfaceCreateFlagsMVK::empty(), + p_view: view as *const _, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.mvk_ios_surface.create_ios_surface_mvk( + instance.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + window: win, + instance: instance.clone(), + surface, + has_swapchain: AtomicBool::new(false), + })) + } + + /// Creates a `Surface` from a MacOS `NSView`. + /// + /// # Safety + /// + /// - The caller must ensure that the `view` is correct and stays alive for the entire + /// lifetime of the surface. The `win` parameter can be used to ensure this. + /// - The `NSView` must be backed by a `CALayer` instance of type `CAMetalLayer`. + pub unsafe fn from_macos_moltenvk<T>( + instance: Arc<Instance>, + view: *const T, + win: W, + ) -> Result<Arc<Surface<W>>, SurfaceCreationError> { + let fns = instance.fns(); + + if !instance.enabled_extensions().mvk_macos_surface { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_MVK_macos_surface", + }); + } + + let surface = { + let infos = ash::vk::MacOSSurfaceCreateInfoMVK { + flags: ash::vk::MacOSSurfaceCreateFlagsMVK::empty(), + p_view: view as *const _, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.mvk_macos_surface.create_mac_os_surface_mvk( + instance.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + window: win, + instance: instance.clone(), + surface, + has_swapchain: AtomicBool::new(false), + })) + } + + /// Creates a `Surface` from a `code:nn::code:vi::code:Layer`. + /// + /// # Safety + /// + /// The caller must ensure that the `window` is correct and stays alive for the entire + /// lifetime of the surface. The `win` parameter can be used to ensure this. + pub unsafe fn from_vi_surface<T>( + instance: Arc<Instance>, + window: *const T, + win: W, + ) -> Result<Arc<Surface<W>>, SurfaceCreationError> { + let fns = instance.fns(); + + if !instance.enabled_extensions().nn_vi_surface { + return Err(SurfaceCreationError::MissingExtension { + name: "VK_NN_vi_surface", + }); + } + + let surface = { + let infos = ash::vk::ViSurfaceCreateInfoNN { + flags: ash::vk::ViSurfaceCreateFlagsNN::empty(), + window: window as *mut _, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.nn_vi_surface.create_vi_surface_nn( + instance.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Arc::new(Surface { + window: win, + instance: instance.clone(), + surface, + has_swapchain: AtomicBool::new(false), + })) + } + + /// Returns true if the given queue family can draw on this surface. + // FIXME: vulkano doesn't check this for the moment! + pub fn is_supported(&self, queue: QueueFamily) -> Result<bool, CapabilitiesError> { + unsafe { + let fns = self.instance.fns(); + + let mut output = MaybeUninit::uninit(); + check_errors(fns.khr_surface.get_physical_device_surface_support_khr( + queue.physical_device().internal_object(), + queue.id(), + self.surface, + output.as_mut_ptr(), + ))?; + Ok(output.assume_init() != 0) + } + } + + /// Retrieves the capabilities of a surface when used by a certain device. + /// + /// # Notes + /// + /// - Capabilities that are not supported in `vk-sys` are silently dropped + /// + /// # Panic + /// + /// - Panics if the device and the surface don't belong to the same instance. + /// + pub fn capabilities(&self, device: PhysicalDevice) -> Result<Capabilities, CapabilitiesError> { + unsafe { + assert_eq!( + &*self.instance as *const _, + &**device.instance() as *const _, + "Instance mismatch in Surface::capabilities" + ); + + let fns = self.instance.fns(); + + let caps = { + let mut out: MaybeUninit<ash::vk::SurfaceCapabilitiesKHR> = MaybeUninit::uninit(); + check_errors( + fns.khr_surface + .get_physical_device_surface_capabilities_khr( + device.internal_object(), + self.surface, + out.as_mut_ptr(), + ), + )?; + out.assume_init() + }; + + let formats = { + let mut num = 0; + check_errors(fns.khr_surface.get_physical_device_surface_formats_khr( + device.internal_object(), + self.surface, + &mut num, + ptr::null_mut(), + ))?; + + let mut formats = Vec::with_capacity(num as usize); + check_errors(fns.khr_surface.get_physical_device_surface_formats_khr( + device.internal_object(), + self.surface, + &mut num, + formats.as_mut_ptr(), + ))?; + formats.set_len(num as usize); + formats + }; + + let modes = { + let mut num = 0; + check_errors( + fns.khr_surface + .get_physical_device_surface_present_modes_khr( + device.internal_object(), + self.surface, + &mut num, + ptr::null_mut(), + ), + )?; + + let mut modes = Vec::with_capacity(num as usize); + check_errors( + fns.khr_surface + .get_physical_device_surface_present_modes_khr( + device.internal_object(), + self.surface, + &mut num, + modes.as_mut_ptr(), + ), + )?; + modes.set_len(num as usize); + debug_assert!(modes + .iter() + .find(|&&m| m == ash::vk::PresentModeKHR::FIFO) + .is_some()); + debug_assert!(modes.iter().count() > 0); + modes.into_iter().collect() + }; + + Ok(Capabilities { + min_image_count: caps.min_image_count, + max_image_count: if caps.max_image_count == 0 { + None + } else { + Some(caps.max_image_count) + }, + current_extent: if caps.current_extent.width == 0xffffffff + && caps.current_extent.height == 0xffffffff + { + None + } else { + Some([caps.current_extent.width, caps.current_extent.height]) + }, + min_image_extent: [caps.min_image_extent.width, caps.min_image_extent.height], + max_image_extent: [caps.max_image_extent.width, caps.max_image_extent.height], + max_image_array_layers: caps.max_image_array_layers, + supported_transforms: caps.supported_transforms.into(), + + current_transform: SupportedSurfaceTransforms::from(caps.current_transform) + .iter() + .next() + .unwrap(), // TODO: + supported_composite_alpha: caps.supported_composite_alpha.into(), + supported_usage_flags: { + let usage = ImageUsage::from(caps.supported_usage_flags); + debug_assert!(usage.color_attachment); // specs say that this must be true + usage + }, + supported_formats: formats + .into_iter() + .filter_map(|f| { + // TODO: Change the way capabilities not supported in vk-sys are handled + Format::try_from(f.format) + .ok() + .map(|format| (format, f.color_space.into())) + }) + .collect(), + present_modes: modes, + }) + } + } + + #[inline] + pub fn window(&self) -> &W { + &self.window + } + + /// Returns the instance this surface was created with. + #[inline] + pub fn instance(&self) -> &Arc<Instance> { + &self.instance + } +} + +unsafe impl<W> SurfaceSwapchainLock for Surface<W> { + #[inline] + fn flag(&self) -> &AtomicBool { + &self.has_swapchain + } +} + +unsafe impl<W> VulkanObject for Surface<W> { + type Object = ash::vk::SurfaceKHR; + + #[inline] + fn internal_object(&self) -> ash::vk::SurfaceKHR { + self.surface + } +} + +impl<W> fmt::Debug for Surface<W> { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "<Vulkan surface {:?}>", self.surface) + } +} + +impl<W> Drop for Surface<W> { + #[inline] + fn drop(&mut self) { + unsafe { + let fns = self.instance.fns(); + fns.khr_surface.destroy_surface_khr( + self.instance.internal_object(), + self.surface, + ptr::null(), + ); + } + } +} + +/// Error that can happen when creating a debug callback. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum SurfaceCreationError { + /// Not enough memory. + OomError(OomError), + + /// The extension required for this function was not enabled. + MissingExtension { + /// Name of the missing extension. + name: &'static str, + }, +} + +impl error::Error for SurfaceCreationError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + SurfaceCreationError::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for SurfaceCreationError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + match *self { + SurfaceCreationError::OomError(_) => "not enough memory available", + SurfaceCreationError::MissingExtension { .. } => { + "the extension required for this function was not enabled" + } + } + ) + } +} + +impl From<OomError> for SurfaceCreationError { + #[inline] + fn from(err: OomError) -> SurfaceCreationError { + SurfaceCreationError::OomError(err) + } +} + +impl From<Error> for SurfaceCreationError { + #[inline] + fn from(err: Error) -> SurfaceCreationError { + match err { + err @ Error::OutOfHostMemory => SurfaceCreationError::OomError(OomError::from(err)), + err @ Error::OutOfDeviceMemory => SurfaceCreationError::OomError(OomError::from(err)), + _ => panic!("unexpected error: {:?}", err), + } + } +} + +/// Error that can happen when retrieving a surface's capabilities. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum CapabilitiesError { + /// Not enough memory. + OomError(OomError), + + /// The surface is no longer accessible and must be recreated. + SurfaceLost, +} + +impl error::Error for CapabilitiesError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + CapabilitiesError::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for CapabilitiesError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + match *self { + CapabilitiesError::OomError(_) => "not enough memory", + CapabilitiesError::SurfaceLost => "the surface is no longer valid", + } + ) + } +} + +impl From<OomError> for CapabilitiesError { + #[inline] + fn from(err: OomError) -> CapabilitiesError { + CapabilitiesError::OomError(err) + } +} + +impl From<Error> for CapabilitiesError { + #[inline] + fn from(err: Error) -> CapabilitiesError { + match err { + err @ Error::OutOfHostMemory => CapabilitiesError::OomError(OomError::from(err)), + err @ Error::OutOfDeviceMemory => CapabilitiesError::OomError(OomError::from(err)), + Error::SurfaceLost => CapabilitiesError::SurfaceLost, + _ => panic!("unexpected error: {:?}", err), + } + } +} + +#[cfg(test)] +mod tests { + use crate::swapchain::Surface; + use crate::swapchain::SurfaceCreationError; + use std::ptr; + + #[test] + fn khr_win32_surface_ext_missing() { + let instance = instance!(); + match unsafe { Surface::from_hwnd(instance, ptr::null::<u8>(), ptr::null::<u8>(), ()) } { + Err(SurfaceCreationError::MissingExtension { .. }) => (), + _ => panic!(), + } + } + + #[test] + fn khr_xcb_surface_ext_missing() { + let instance = instance!(); + match unsafe { Surface::from_xcb(instance, ptr::null::<u8>(), 0, ()) } { + Err(SurfaceCreationError::MissingExtension { .. }) => (), + _ => panic!(), + } + } + + #[test] + fn khr_xlib_surface_ext_missing() { + let instance = instance!(); + match unsafe { Surface::from_xlib(instance, ptr::null::<u8>(), 0, ()) } { + Err(SurfaceCreationError::MissingExtension { .. }) => (), + _ => panic!(), + } + } + + #[test] + fn khr_wayland_surface_ext_missing() { + let instance = instance!(); + match unsafe { Surface::from_wayland(instance, ptr::null::<u8>(), ptr::null::<u8>(), ()) } { + Err(SurfaceCreationError::MissingExtension { .. }) => (), + _ => panic!(), + } + } + + #[test] + fn khr_android_surface_ext_missing() { + let instance = instance!(); + match unsafe { Surface::from_anativewindow(instance, ptr::null::<u8>(), ()) } { + Err(SurfaceCreationError::MissingExtension { .. }) => (), + _ => panic!(), + } + } +} |