// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // 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 { window: W, instance: Arc, 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 Surface { /// Creates a `Surface` given the raw handler. /// /// Be careful when using it /// pub unsafe fn from_raw_surface( instance: Arc, surface: ash::vk::SurfaceKHR, win: W, ) -> Surface { 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>, 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( instance: Arc, hinstance: *const T, hwnd: *const U, win: W, ) -> Result>, 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( instance: Arc, connection: *const C, window: u32, win: W, ) -> Result>, 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( instance: Arc, display: *const D, window: c_ulong, win: W, ) -> Result>, 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( instance: Arc, display: *const D, surface: *const S, win: W, ) -> Result>, 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( instance: Arc, window: *const T, win: W, ) -> Result>, 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( instance: Arc, view: *const T, win: W, ) -> Result>, 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( instance: Arc, view: *const T, win: W, ) -> Result>, 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( instance: Arc, window: *const T, win: W, ) -> Result>, 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 { 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 { 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 = 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 { &self.instance } } unsafe impl SurfaceSwapchainLock for Surface { #[inline] fn flag(&self) -> &AtomicBool { &self.has_swapchain } } unsafe impl VulkanObject for Surface { type Object = ash::vk::SurfaceKHR; #[inline] fn internal_object(&self) -> ash::vk::SurfaceKHR { self.surface } } impl fmt::Debug for Surface { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(fmt, "", self.surface) } } impl Drop for Surface { #[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 for SurfaceCreationError { #[inline] fn from(err: OomError) -> SurfaceCreationError { SurfaceCreationError::OomError(err) } } impl From 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 for CapabilitiesError { #[inline] fn from(err: OomError) -> CapabilitiesError { CapabilitiesError::OomError(err) } } impl From 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::(), ptr::null::(), ()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!(), } } #[test] fn khr_xcb_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_xcb(instance, ptr::null::(), 0, ()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!(), } } #[test] fn khr_xlib_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_xlib(instance, ptr::null::(), 0, ()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!(), } } #[test] fn khr_wayland_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_wayland(instance, ptr::null::(), ptr::null::(), ()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!(), } } #[test] fn khr_android_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_anativewindow(instance, ptr::null::(), ()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!(), } } }