// 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 super::{FullScreenExclusive, Win32Monitor}; use crate::{ cache::OnceCache, format::Format, image::ImageUsage, instance::Instance, macros::{impl_id_counter, vulkan_bitflags_enum, vulkan_enum}, swapchain::{ display::{DisplayMode, DisplayPlane}, SurfaceSwapchainLock, }, OomError, RequiresOneOf, VulkanError, VulkanObject, }; #[cfg(target_os = "ios")] use objc::{class, msg_send, runtime::Object, sel, sel_impl}; use std::{ any::Any, error::Error, fmt::{Debug, Display, Error as FmtError, Formatter}, mem::MaybeUninit, num::NonZeroU64, ptr, sync::{atomic::AtomicBool, Arc}, }; /// Represents a surface on the screen. /// /// Creating a `Surface` is platform-specific. pub struct Surface { handle: ash::vk::SurfaceKHR, instance: Arc, id: NonZeroU64, api: SurfaceApi, object: Option>, // If true, a swapchain has been associated to this surface, and that any new swapchain // creation should be forbidden. has_swapchain: AtomicBool, #[cfg(target_os = "ios")] metal_layer: IOSMetalLayer, // Data queried by the user at runtime, cached for faster lookups. // This is stored here rather than on `PhysicalDevice` to ensure that it's freed when the // `Surface` is destroyed. pub(crate) surface_formats: OnceCache<(ash::vk::PhysicalDevice, SurfaceInfo), Vec<(Format, ColorSpace)>>, pub(crate) surface_present_modes: OnceCache>, pub(crate) surface_support: OnceCache<(ash::vk::PhysicalDevice, u32), bool>, } impl Surface { /// Creates a `Surface` from a raw handle. /// /// # Safety /// /// - `handle` must be a valid Vulkan object handle created from `instance`. /// - `handle` must have been created from `api`. /// - The window object that `handle` was created from must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_handle( instance: Arc, handle: ash::vk::SurfaceKHR, api: SurfaceApi, object: Option>, ) -> Self { Surface { handle, instance, id: Self::next_id(), api, object, has_swapchain: AtomicBool::new(false), #[cfg(target_os = "ios")] metal_layer: IOSMetalLayer::new(std::ptr::null_mut(), std::ptr::null_mut()), surface_formats: OnceCache::new(), surface_present_modes: OnceCache::new(), surface_support: OnceCache::new(), } } /// Creates a `Surface` with no backing window or display. /// /// Presenting to a headless surface does nothing, so this is mostly useless in itself. However, /// it may be useful for testing, and it is available for future extensions to layer on top of. pub fn headless( instance: Arc, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_headless(&instance)?; unsafe { Ok(Self::headless_unchecked(instance, object)?) } } fn validate_headless(instance: &Instance) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().ext_headless_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::headless`", requires_one_of: RequiresOneOf { instance_extensions: &["ext_headless_surface"], ..Default::default() }, }); } Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn headless_unchecked( instance: Arc, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::HeadlessSurfaceCreateInfoEXT { flags: ash::vk::HeadlessSurfaceCreateFlagsEXT::empty(), ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.ext_headless_surface.create_headless_surface_ext)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::Headless, object, ))) } /// Creates a `Surface` from a `DisplayPlane`. /// /// # Panics /// /// - 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_plane( display_mode: &DisplayMode, plane: &DisplayPlane, ) -> Result, SurfaceCreationError> { Self::validate_from_display_plane(display_mode, plane)?; unsafe { Ok(Self::from_display_plane_unchecked(display_mode, plane)?) } } fn validate_from_display_plane( display_mode: &DisplayMode, plane: &DisplayPlane, ) -> Result<(), SurfaceCreationError> { if !display_mode .display() .physical_device() .instance() .enabled_extensions() .khr_display { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_display_plane`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_display"], ..Default::default() }, }); } assert_eq!( display_mode.display().physical_device(), plane.physical_device() ); assert!(plane.supports(display_mode.display())); Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_display_plane_unchecked( display_mode: &DisplayMode, plane: &DisplayPlane, ) -> Result, VulkanError> { let instance = display_mode.display().physical_device().instance(); let create_info = ash::vk::DisplaySurfaceCreateInfoKHR { flags: ash::vk::DisplaySurfaceCreateFlagsKHR::empty(), display_mode: display_mode.handle(), 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 handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.khr_display.create_display_plane_surface_khr)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance.clone(), handle, SurfaceApi::DisplayPlane, None, ))) } /// Creates a `Surface` from an Android window. /// /// # Safety /// /// - `window` must be a valid Android `ANativeWindow` handle. /// - The object referred to by `window` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_android( instance: Arc, window: *const W, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_android(&instance, window)?; Ok(Self::from_android_unchecked(instance, window, object)?) } fn validate_from_android( instance: &Instance, _window: *const W, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().khr_android_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_android`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_android_surface"], ..Default::default() }, }); } // VUID-VkAndroidSurfaceCreateInfoKHR-window-01248 // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_android_unchecked( instance: Arc, window: *const W, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::AndroidSurfaceCreateInfoKHR { flags: ash::vk::AndroidSurfaceCreateFlagsKHR::empty(), window: window as *mut _, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.khr_android_surface.create_android_surface_khr)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::Android, object, ))) } /// Creates a `Surface` from a DirectFB surface. /// /// # Safety /// /// - `dfb` must be a valid DirectFB `IDirectFB` handle. /// - `surface` must be a valid DirectFB `IDirectFBSurface` handle. /// - The object referred to by `dfb` and `surface` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_directfb( instance: Arc, dfb: *const D, surface: *const S, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_directfb(&instance, dfb, surface)?; Ok(Self::from_directfb_unchecked( instance, dfb, surface, object, )?) } fn validate_from_directfb( instance: &Instance, _dfb: *const D, _surface: *const S, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().ext_directfb_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_directfb`", requires_one_of: RequiresOneOf { instance_extensions: &["ext_directfb_surface"], ..Default::default() }, }); } // VUID-VkDirectFBSurfaceCreateInfoEXT-dfb-04117 // Can't validate, therefore unsafe // VUID-VkDirectFBSurfaceCreateInfoEXT-surface-04118 // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_directfb_unchecked( instance: Arc, dfb: *const D, surface: *const S, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::DirectFBSurfaceCreateInfoEXT { flags: ash::vk::DirectFBSurfaceCreateFlagsEXT::empty(), dfb: dfb as *mut _, surface: surface as *mut _, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.ext_directfb_surface.create_direct_fb_surface_ext)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::DirectFB, object, ))) } /// Creates a `Surface` from an Fuchsia ImagePipe. /// /// # Safety /// /// - `image_pipe_handle` must be a valid Fuchsia `zx_handle_t` handle. /// - The object referred to by `image_pipe_handle` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_fuchsia_image_pipe( instance: Arc, image_pipe_handle: ash::vk::zx_handle_t, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_fuchsia_image_pipe(&instance, image_pipe_handle)?; Ok(Self::from_fuchsia_image_pipe_unchecked( instance, image_pipe_handle, object, )?) } fn validate_from_fuchsia_image_pipe( instance: &Instance, _image_pipe_handle: ash::vk::zx_handle_t, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().fuchsia_imagepipe_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_fuchsia_image_pipe`", requires_one_of: RequiresOneOf { instance_extensions: &["fuchsia_imagepipe_surface"], ..Default::default() }, }); } // VUID-VkImagePipeSurfaceCreateInfoFUCHSIA-imagePipeHandle-04863 // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_fuchsia_image_pipe_unchecked( instance: Arc, image_pipe_handle: ash::vk::zx_handle_t, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::ImagePipeSurfaceCreateInfoFUCHSIA { flags: ash::vk::ImagePipeSurfaceCreateFlagsFUCHSIA::empty(), image_pipe_handle, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.fuchsia_imagepipe_surface .create_image_pipe_surface_fuchsia)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::FuchsiaImagePipe, object, ))) } /// Creates a `Surface` from a Google Games Platform stream descriptor. /// /// # Safety /// /// - `stream_descriptor` must be a valid Google Games Platform `GgpStreamDescriptor` handle. /// - The object referred to by `stream_descriptor` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_ggp_stream_descriptor( instance: Arc, stream_descriptor: ash::vk::GgpStreamDescriptor, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_ggp_stream_descriptor(&instance, stream_descriptor)?; Ok(Self::from_ggp_stream_descriptor_unchecked( instance, stream_descriptor, object, )?) } fn validate_from_ggp_stream_descriptor( instance: &Instance, _stream_descriptor: ash::vk::GgpStreamDescriptor, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().ggp_stream_descriptor_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_ggp_stream_descriptor`", requires_one_of: RequiresOneOf { instance_extensions: &["ggp_stream_descriptor_surface"], ..Default::default() }, }); } // VUID-VkStreamDescriptorSurfaceCreateInfoGGP-streamDescriptor-02681 // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_ggp_stream_descriptor_unchecked( instance: Arc, stream_descriptor: ash::vk::GgpStreamDescriptor, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::StreamDescriptorSurfaceCreateInfoGGP { flags: ash::vk::StreamDescriptorSurfaceCreateFlagsGGP::empty(), stream_descriptor, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.ggp_stream_descriptor_surface .create_stream_descriptor_surface_ggp)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::GgpStreamDescriptor, object, ))) } /// Creates a `Surface` from an iOS `UIView`. /// /// # Safety /// /// - `metal_layer` must be a valid `IOSMetalLayer` handle. /// - The object referred to by `metal_layer` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. /// - The `UIView` must be backed by a `CALayer` instance of type `CAMetalLayer`. #[cfg(target_os = "ios")] pub unsafe fn from_ios( instance: Arc, metal_layer: IOSMetalLayer, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_ios(&instance, &metal_layer)?; Ok(Self::from_ios_unchecked(instance, metal_layer, object)?) } #[cfg(target_os = "ios")] fn validate_from_ios( instance: &Instance, _metal_layer: &IOSMetalLayer, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().mvk_ios_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_ios`", requires_one_of: RequiresOneOf { instance_extensions: &["mvk_ios_surface"], ..Default::default() }, }); } // VUID-VkIOSSurfaceCreateInfoMVK-pView-04143 // Can't validate, therefore unsafe // VUID-VkIOSSurfaceCreateInfoMVK-pView-01316 // Can't validate, therefore unsafe Ok(()) } #[cfg(target_os = "ios")] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_ios_unchecked( instance: Arc, metal_layer: IOSMetalLayer, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::IOSSurfaceCreateInfoMVK { flags: ash::vk::IOSSurfaceCreateFlagsMVK::empty(), p_view: metal_layer.render_layer.0 as *const _, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.mvk_ios_surface.create_ios_surface_mvk)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::Ios, object, ))) } /// Creates a `Surface` from a MacOS `NSView`. /// /// # Safety /// /// - `view` must be a valid `CAMetalLayer` or `NSView` handle. /// - The object referred to by `view` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. /// - The `NSView` must be backed by a `CALayer` instance of type `CAMetalLayer`. #[cfg(target_os = "macos")] pub unsafe fn from_mac_os( instance: Arc, view: *const V, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_mac_os(&instance, view)?; Ok(Self::from_mac_os_unchecked(instance, view, object)?) } #[cfg(target_os = "macos")] fn validate_from_mac_os( instance: &Instance, _view: *const V, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().mvk_macos_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_mac_os`", requires_one_of: RequiresOneOf { instance_extensions: &["mvk_macos_surface"], ..Default::default() }, }); } // VUID-VkMacOSSurfaceCreateInfoMVK-pView-04144 // Can't validate, therefore unsafe // VUID-VkMacOSSurfaceCreateInfoMVK-pView-01317 // Can't validate, therefore unsafe Ok(()) } #[cfg(target_os = "macos")] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_mac_os_unchecked( instance: Arc, view: *const V, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::MacOSSurfaceCreateInfoMVK { flags: ash::vk::MacOSSurfaceCreateFlagsMVK::empty(), p_view: view as *const _, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.mvk_macos_surface.create_mac_os_surface_mvk)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::MacOs, object, ))) } /// Creates a `Surface` from a Metal `CAMetalLayer`. /// /// # Safety /// /// - `layer` must be a valid Metal `CAMetalLayer` handle. /// - The object referred to by `layer` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_metal( instance: Arc, layer: *const L, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_metal(&instance, layer)?; Ok(Self::from_metal_unchecked(instance, layer, object)?) } fn validate_from_metal( instance: &Instance, _layer: *const L, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().ext_metal_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_metal`", requires_one_of: RequiresOneOf { instance_extensions: &["ext_metal_surface"], ..Default::default() }, }); } Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_metal_unchecked( instance: Arc, layer: *const L, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::MetalSurfaceCreateInfoEXT { flags: ash::vk::MetalSurfaceCreateFlagsEXT::empty(), p_layer: layer as *const _, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.ext_metal_surface.create_metal_surface_ext)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::Metal, object, ))) } /// Creates a `Surface` from a QNX Screen window. /// /// # Safety /// /// - `context` must be a valid QNX Screen `_screen_context` handle. /// - `window` must be a valid QNX Screen `_screen_window` handle. /// - The object referred to by `window` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_qnx_screen( instance: Arc, context: *const C, window: *const W, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_qnx_screen(&instance, context, window)?; Ok(Self::from_qnx_screen_unchecked( instance, context, window, object, )?) } fn validate_from_qnx_screen( instance: &Instance, _context: *const C, _window: *const W, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().qnx_screen_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_qnx_screen`", requires_one_of: RequiresOneOf { instance_extensions: &["qnx_screen_surface"], ..Default::default() }, }); } // VUID-VkScreenSurfaceCreateInfoQNX-context-04741 // Can't validate, therefore unsafe // VUID-VkScreenSurfaceCreateInfoQNX-window-04742 // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_qnx_screen_unchecked( instance: Arc, context: *const C, window: *const W, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::ScreenSurfaceCreateInfoQNX { flags: ash::vk::ScreenSurfaceCreateFlagsQNX::empty(), context: context as *mut _, window: window as *mut _, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.qnx_screen_surface.create_screen_surface_qnx)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::Qnx, object, ))) } /// Creates a `Surface` from a `code:nn::code:vi::code:Layer`. /// /// # Safety /// /// - `window` must be a valid `nn::vi::NativeWindowHandle` handle. /// - The object referred to by `window` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_vi( instance: Arc, window: *const W, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_vi(&instance, window)?; Ok(Self::from_vi_unchecked(instance, window, object)?) } fn validate_from_vi( instance: &Instance, _window: *const W, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().nn_vi_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_vi`", requires_one_of: RequiresOneOf { instance_extensions: &["nn_vi_surface"], ..Default::default() }, }); } // VUID-VkViSurfaceCreateInfoNN-window-01318 // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_vi_unchecked( instance: Arc, window: *const W, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::ViSurfaceCreateInfoNN { flags: ash::vk::ViSurfaceCreateFlagsNN::empty(), window: window as *mut _, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.nn_vi_surface.create_vi_surface_nn)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::Vi, object, ))) } /// Creates a `Surface` from a Wayland window. /// /// The window's dimensions will be set to the size of the swapchain. /// /// # Safety /// /// - `display` must be a valid Wayland `wl_display` handle. /// - `surface` must be a valid Wayland `wl_surface` handle. /// - The objects referred to by `display` and `surface` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_wayland( instance: Arc, display: *const D, surface: *const S, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_wayland(&instance, display, surface)?; Ok(Self::from_wayland_unchecked( instance, display, surface, object, )?) } fn validate_from_wayland( instance: &Instance, _display: *const D, _surface: *const S, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().khr_wayland_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_wayland`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_wayland_surface"], ..Default::default() }, }); } // VUID-VkWaylandSurfaceCreateInfoKHR-display-01304 // Can't validate, therefore unsafe // VUID-VkWaylandSurfaceCreateInfoKHR-surface-01305 // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_wayland_unchecked( instance: Arc, display: *const D, surface: *const S, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::WaylandSurfaceCreateInfoKHR { flags: ash::vk::WaylandSurfaceCreateFlagsKHR::empty(), display: display as *mut _, surface: surface as *mut _, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.khr_wayland_surface.create_wayland_surface_khr)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::Wayland, object, ))) } /// Creates a `Surface` from a Win32 window. /// /// The surface's min, max and current extent will always match the window's dimensions. /// /// # Safety /// /// - `hinstance` must be a valid Win32 `HINSTANCE` handle. /// - `hwnd` must be a valid Win32 `HWND` handle. /// - The objects referred to by `hwnd` and `hinstance` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_win32( instance: Arc, hinstance: *const I, hwnd: *const W, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_win32(&instance, hinstance, hwnd)?; Ok(Self::from_win32_unchecked( instance, hinstance, hwnd, object, )?) } fn validate_from_win32( instance: &Instance, _hinstance: *const I, _hwnd: *const W, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().khr_win32_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_win32`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_win32_surface"], ..Default::default() }, }); } // VUID-VkWin32SurfaceCreateInfoKHR-hinstance-01307 // Can't validate, therefore unsafe // VUID-VkWin32SurfaceCreateInfoKHR-hwnd-01308 // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_win32_unchecked( instance: Arc, hinstance: *const I, hwnd: *const W, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::Win32SurfaceCreateInfoKHR { flags: ash::vk::Win32SurfaceCreateFlagsKHR::empty(), hinstance: hinstance as *mut _, hwnd: hwnd as *mut _, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.khr_win32_surface.create_win32_surface_khr)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::Win32, object, ))) } /// Creates a `Surface` from an XCB window. /// /// The surface's min, max and current extent will always match the window's dimensions. /// /// # Safety /// /// - `connection` must be a valid X11 `xcb_connection_t` handle. /// - `window` must be a valid X11 `xcb_window_t` handle. /// - The objects referred to by `connection` and `window` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_xcb( instance: Arc, connection: *const C, window: ash::vk::xcb_window_t, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_xcb(&instance, connection, window)?; Ok(Self::from_xcb_unchecked( instance, connection, window, object, )?) } fn validate_from_xcb( instance: &Instance, _connection: *const C, _window: ash::vk::xcb_window_t, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().khr_xcb_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_xcb`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_xcb_surface"], ..Default::default() }, }); } // VUID-VkXcbSurfaceCreateInfoKHR-connection-01310 // Can't validate, therefore unsafe // VUID-VkXcbSurfaceCreateInfoKHR-window-01311 // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_xcb_unchecked( instance: Arc, connection: *const C, window: ash::vk::xcb_window_t, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::XcbSurfaceCreateInfoKHR { flags: ash::vk::XcbSurfaceCreateFlagsKHR::empty(), connection: connection as *mut _, window, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.khr_xcb_surface.create_xcb_surface_khr)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::Xcb, object, ))) } /// Creates a `Surface` from an Xlib window. /// /// The surface's min, max and current extent will always match the window's dimensions. /// /// # Safety /// /// - `display` must be a valid Xlib `Display` handle. /// - `window` must be a valid Xlib `Window` handle. /// - The objects referred to by `display` and `window` must outlive the created `Surface`. /// The `object` parameter can be used to ensure this. pub unsafe fn from_xlib( instance: Arc, display: *const D, window: ash::vk::Window, object: Option>, ) -> Result, SurfaceCreationError> { Self::validate_from_xlib(&instance, display, window)?; Ok(Self::from_xlib_unchecked( instance, display, window, object, )?) } fn validate_from_xlib( instance: &Instance, _display: *const D, _window: ash::vk::Window, ) -> Result<(), SurfaceCreationError> { if !instance.enabled_extensions().khr_xlib_surface { return Err(SurfaceCreationError::RequirementNotMet { required_for: "`Surface::from_xlib`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_xlib_surface"], ..Default::default() }, }); } // VUID-VkXlibSurfaceCreateInfoKHR-dpy-01313 // Can't validate, therefore unsafe // VUID-VkXlibSurfaceCreateInfoKHR-window-01314 // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn from_xlib_unchecked( instance: Arc, display: *const D, window: ash::vk::Window, object: Option>, ) -> Result, VulkanError> { let create_info = ash::vk::XlibSurfaceCreateInfoKHR { flags: ash::vk::XlibSurfaceCreateFlagsKHR::empty(), dpy: display as *mut _, window, ..Default::default() }; let handle = { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.khr_xlib_surface.create_xlib_surface_khr)( instance.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Self::from_handle( instance, handle, SurfaceApi::Xlib, object, ))) } /// Returns the instance this surface was created with. #[inline] pub fn instance(&self) -> &Arc { &self.instance } /// Returns the windowing API that was used to construct the surface. #[inline] pub fn api(&self) -> SurfaceApi { self.api } /// Returns a reference to the `object` parameter that was passed when creating the /// surface. #[inline] pub fn object(&self) -> Option<&Arc> { self.object.as_ref() } /// Resizes the sublayer bounds on iOS. /// It may not be necessary if original window size matches device's, but often it does not. /// Thus this should be called after a resize has occurred abd swapchain has been recreated. /// /// On iOS, we've created CAMetalLayer as a sublayer. However, when the view changes size, /// its sublayers are not automatically resized, and we must resize /// it here. #[cfg(target_os = "ios")] #[inline] pub unsafe fn update_ios_sublayer_on_resize(&self) { use core_graphics_types::geometry::CGRect; let class = class!(CAMetalLayer); let main_layer: *mut Object = self.metal_layer.main_layer.0; let bounds: CGRect = msg_send![main_layer, bounds]; let render_layer: *mut Object = self.metal_layer.render_layer.0; let () = msg_send![render_layer, setFrame: bounds]; } } impl Drop for Surface { #[inline] fn drop(&mut self) { unsafe { let fns = self.instance.fns(); (fns.khr_surface.destroy_surface_khr)(self.instance.handle(), self.handle, ptr::null()); } } } unsafe impl VulkanObject for Surface { type Handle = ash::vk::SurfaceKHR; #[inline] fn handle(&self) -> Self::Handle { self.handle } } impl_id_counter!(Surface); impl Debug for Surface { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { let Self { handle, instance, api, object: _, has_swapchain, .. } = self; f.debug_struct("Surface") .field("handle", handle) .field("instance", instance) .field("api", api) .field("window", &()) .field("has_swapchain", &has_swapchain) .finish() } } unsafe impl SurfaceSwapchainLock for Surface { #[inline] fn flag(&self) -> &AtomicBool { &self.has_swapchain } } /// Error that can happen when creating a debug callback. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum SurfaceCreationError { /// Not enough memory. OomError(OomError), RequirementNotMet { required_for: &'static str, requires_one_of: RequiresOneOf, }, } impl Error for SurfaceCreationError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { SurfaceCreationError::OomError(err) => Some(err), _ => None, } } } impl Display for SurfaceCreationError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { match self { Self::OomError(_) => write!(f, "not enough memory available"), Self::RequirementNotMet { required_for, requires_one_of, } => write!( f, "a requirement was not met for: {}; requires one of: {}", required_for, requires_one_of, ), } } } impl From for SurfaceCreationError { fn from(err: OomError) -> SurfaceCreationError { SurfaceCreationError::OomError(err) } } impl From for SurfaceCreationError { fn from(err: VulkanError) -> SurfaceCreationError { match err { err @ VulkanError::OutOfHostMemory => { SurfaceCreationError::OomError(OomError::from(err)) } err @ VulkanError::OutOfDeviceMemory => { SurfaceCreationError::OomError(OomError::from(err)) } _ => panic!("unexpected error: {:?}", err), } } } /// The windowing API that was used to construct a surface. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum SurfaceApi { Headless, DisplayPlane, // Alphabetical order Android, DirectFB, FuchsiaImagePipe, GgpStreamDescriptor, Ios, MacOs, Metal, Qnx, Vi, Wayland, Win32, Xcb, Xlib, } vulkan_enum! { #[non_exhaustive] /// The mode of action when a swapchain image is presented. /// /// Swapchain images can be in one of three possible states: /// - Exactly one image is currently displayed on the screen. /// - Zero or more are acquired by the application, or available to be acquired. /// - Some may be held inside the presentation engine waiting to be displayed. The present mode /// concerns the behaviour of this category, and by extension, which images are left over for /// acquiring. /// /// The present mode affects what is commonly known as "vertical sync" or "vsync" for short. /// The `Immediate` mode is equivalent to disabling vertical sync, while the others enable /// vertical sync in various forms. An important aspect of the present modes is their potential /// *latency*: the time between when an image is presented, and when it actually appears on /// the display. /// /// Only `Fifo` is guaranteed to be supported on every device. For the others, you must call /// [`surface_present_modes`] to see if they are supported. /// /// [`surface_present_modes`]: crate::device::physical::PhysicalDevice::surface_present_modes PresentMode = PresentModeKHR(i32); /// The presentation engine holds only the currently displayed image. When presenting an image, /// the currently displayed image is immediately replaced with the presented image. The old /// image will be available for future acquire operations. /// /// This mode has the lowest latency of all present modes, but if the display is not in a /// vertical blanking period when the image is replaced, a tear will be visible. Immediate = IMMEDIATE, /// The presentation engine holds the currently displayed image, and optionally another in a /// waiting slot. The presentation engine waits until the next vertical blanking period, then /// removes any image from the waiting slot and displays it. Tearing will never be visible. /// When presenting an image, it is stored in the waiting slot. Any previous entry /// in the slot is discarded, and will be available for future acquire operations. /// /// Latency is relatively low with this mode, and will never be longer than the time between /// vertical blanking periods. However, if a previous image in the waiting slot is discarded, /// the work that went into producing that image was wasted. /// /// With two swapchain images, this mode behaves essentially identical to `Fifo`: once both /// images are held in the presentation engine, no images can be acquired until one is finished /// displaying. But with three or more swapchain images, any images beyond those two are always /// available to acquire. Mailbox = MAILBOX, /// The presentation engine holds the currently displayed image, and a queue of waiting images. /// When presenting an image, it is added to the tail of the queue, after previously presented /// images. The presentation engine waits until the next vertical blanking period, then removes /// an image from the head of the queue and displays it. Tearing will never be visible. Images /// become available for future acquire operations only after they have been displayed. /// /// This mode is guaranteed to be always supported. It is possible for all swapchain images to /// end up being held by the presentation engine, either being displayed or in the queue. When /// that happens, no images can be acquired until one is finished displaying. This can be used /// to limit the presentation rate to the display frame rate. Latency is bounded only by the /// number of images in the swapchain. /// /// This is the equivalent of OpenGL's `SwapInterval` with a value of 1. Fifo = FIFO, /// Similar to `Fifo`, but with the ability for images to "skip the queue" if presentation is /// lagging behind the display frame rate. If the queue is empty and a vertical blanking period /// has already passed since the previous image was displayed, then the currently displayed /// image is immediately replaced with the presented image, as in `Immediate`. /// /// This mode has high latency if images are presented faster than the display frame rate, /// as they will accumulate in the queue. But the latency is low if images are presented slower /// than the display frame rate. However, slower presentation can result in visible tearing. /// /// This is the equivalent of OpenGL's `SwapInterval` with a value of -1. FifoRelaxed = FIFO_RELAXED, /* TODO: enable // TODO: document SharedDemandRefresh = SHARED_DEMAND_REFRESH_KHR { device_extensions: [khr_shared_presentable_image], },*/ /* TODO: enable // TODO: document SharedContinuousRefresh = SHARED_CONTINUOUS_REFRESH_KHR { device_extensions: [khr_shared_presentable_image], },*/ } vulkan_bitflags_enum! { #[non_exhaustive] /// A set of [`SurfaceTransform`] values. SurfaceTransforms, /// The presentation transform to apply when presenting a swapchain image to a surface. SurfaceTransform, = SurfaceTransformFlagsKHR(u32); /// Don't transform the image. IDENTITY, Identity = IDENTITY, /// Rotate 90 degrees. ROTATE_90, Rotate90 = ROTATE_90, /// Rotate 180 degrees. ROTATE_180, Rotate180 = ROTATE_180, /// Rotate 270 degrees. ROTATE_270, Rotate270 = ROTATE_270, /// Mirror the image horizontally. HORIZONTAL_MIRROR, HorizontalMirror = HORIZONTAL_MIRROR, /// Mirror the image horizontally and rotate 90 degrees. HORIZONTAL_MIRROR_ROTATE_90, HorizontalMirrorRotate90 = HORIZONTAL_MIRROR_ROTATE_90, /// Mirror the image horizontally and rotate 180 degrees. HORIZONTAL_MIRROR_ROTATE_180, HorizontalMirrorRotate180 = HORIZONTAL_MIRROR_ROTATE_180, /// Mirror the image horizontally and rotate 270 degrees. HORIZONTAL_MIRROR_ROTATE_270, HorizontalMirrorRotate270 = HORIZONTAL_MIRROR_ROTATE_270, /// Let the operating system or driver implementation choose. INHERIT, Inherit = INHERIT, } impl Default for SurfaceTransform { #[inline] fn default() -> SurfaceTransform { SurfaceTransform::Identity } } vulkan_bitflags_enum! { #[non_exhaustive] /// A set of [`CompositeAlpha`] values. CompositeAlphas, /// How the alpha values of the pixels of the window are treated. CompositeAlpha, = CompositeAlphaFlagsKHR(u32); /// The alpha channel of the image is ignored. All the pixels are considered as if they have a /// value of 1.0. OPAQUE, Opaque = OPAQUE, /// The alpha channel of the image is respected. The color channels are expected to have /// already been multiplied by the alpha value. PRE_MULTIPLIED, PreMultiplied = PRE_MULTIPLIED, /// The alpha channel of the image is respected. The color channels will be multiplied by the /// alpha value by the compositor before being added to what is behind. POST_MULTIPLIED, PostMultiplied = POST_MULTIPLIED, /// Let the operating system or driver implementation choose. INHERIT, Inherit = INHERIT, } vulkan_enum! { #[non_exhaustive] /// How the presentation engine should interpret the data. /// /// # A quick lesson about color spaces /// /// ## What is a color space? /// /// Each pixel of a monitor is made of three components: one red, one green, and one blue. In /// the past, computers would simply send to the monitor the intensity of each of the three /// components. /// /// This proved to be problematic, because depending on the brand of the monitor the colors /// would not exactly be the same. For example on some monitors, a value of `[1.0, 0.0, 0.0]` /// would be a /// bit more orange than on others. /// /// In order to standardize this, there exist what are called *color spaces*: sRGB, AdobeRGB, /// DCI-P3, scRGB, etc. When you manipulate RGB values in a specific color space, these values /// have a precise absolute meaning in terms of color, that is the same across all systems and /// monitors. /// /// > **Note**: Color spaces are orthogonal to concept of RGB. *RGB* only indicates what is the /// > representation of the data, but not how it is interpreted. You can think of this a bit /// > like text encoding. An *RGB* value is a like a byte, in other words it is the medium by /// > which values are communicated, and a *color space* is like a text encoding (eg. UTF-8), /// > in other words it is the way the value should be interpreted. /// /// The most commonly used color space today is sRGB. Most monitors today use this color space, /// and most images files are encoded in this color space. /// /// ## Pixel formats and linear vs non-linear /// /// In Vulkan all images have a specific format in which the data is stored. The data of an /// image consists of pixels in RGB but contains no information about the color space (or lack /// thereof) of these pixels. You are free to store them in whatever color space you want. /// /// But one big practical problem with color spaces is that they are sometimes not linear, and /// in particular the popular sRGB color space is not linear. In a non-linear color space, a /// value of `[0.6, 0.6, 0.6]` for example is **not** twice as bright as a value of `[0.3, 0.3, /// 0.3]`. This is problematic, because operations such as taking the average of two colors or /// calculating the lighting of a texture with a dot product are mathematically incorrect and /// will produce incorrect colors. /// /// > **Note**: If the texture format has an alpha component, it is not affected by the color /// > space and always behaves linearly. /// /// In order to solve this Vulkan also provides image formats with the `Srgb` suffix, which are /// expected to contain RGB data in the sRGB color space. When you sample an image with such a /// format from a shader, the implementation will automatically turn the pixel values into a /// linear color space that is suitable for linear operations (such as additions or /// multiplications). When you write to a framebuffer attachment with such a format, the /// implementation will automatically perform the opposite conversion. These conversions are /// most of the time performed by the hardware and incur no additional cost. /// /// ## Color space of the swapchain /// /// The color space that you specify when you create a swapchain is how the implementation will /// interpret the raw data inside of the image. /// /// > **Note**: The implementation can choose to send the data in the swapchain image directly /// > to the monitor, but it can also choose to write it in an intermediary buffer that is then /// > read by the operating system or windowing system. Therefore the color space that the /// > implementation supports is not necessarily the same as the one supported by the monitor. /// /// It is *your* job to ensure that the data in the swapchain image is in the color space /// that is specified here, otherwise colors will be incorrect. The implementation will never /// perform any additional automatic conversion after the colors have been written to the /// swapchain image. /// /// # How do I handle this correctly? /// /// The easiest way to handle color spaces in a cross-platform program is: /// /// - Always request the `SrgbNonLinear` color space when creating the swapchain. /// - Make sure that all your image files use the sRGB color space, and load them in images /// whose format has the `Srgb` suffix. Only use non-sRGB image formats for intermediary /// computations or to store non-color data. /// - Swapchain images should have a format with the `Srgb` suffix. /// /// > **Note**: Lots of developers are confused by color spaces. You can sometimes find articles /// > talking about gamma correction and suggestion to put your colors to the power 2.2 for /// > example. These are all hacks and you should use the sRGB pixel formats instead. /// /// If you follow these three rules, then everything should render the same way on all /// platforms. /// /// Additionally you can try detect whether the implementation supports any additional color /// space and perform a manual conversion to that color space from inside your shader. ColorSpace = ColorSpaceKHR(i32); // TODO: document SrgbNonLinear = SRGB_NONLINEAR, // TODO: document DisplayP3NonLinear = DISPLAY_P3_NONLINEAR_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document ExtendedSrgbLinear = EXTENDED_SRGB_LINEAR_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document ExtendedSrgbNonLinear = EXTENDED_SRGB_NONLINEAR_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document DisplayP3Linear = DISPLAY_P3_LINEAR_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document DciP3NonLinear = DCI_P3_NONLINEAR_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document Bt709Linear = BT709_LINEAR_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document Bt709NonLinear = BT709_NONLINEAR_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document Bt2020Linear = BT2020_LINEAR_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document Hdr10St2084 = HDR10_ST2084_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document DolbyVision = DOLBYVISION_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document Hdr10Hlg = HDR10_HLG_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document AdobeRgbLinear = ADOBERGB_LINEAR_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document AdobeRgbNonLinear = ADOBERGB_NONLINEAR_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document PassThrough = PASS_THROUGH_EXT { instance_extensions: [ext_swapchain_colorspace], }, // TODO: document DisplayNative = DISPLAY_NATIVE_AMD { device_extensions: [amd_display_native_hdr], }, } /// Parameters for [`PhysicalDevice::surface_capabilities`] and [`PhysicalDevice::surface_formats`]. /// /// [`PhysicalDevice::surface_capabilities`]: crate::device::physical::PhysicalDevice::surface_capabilities /// [`PhysicalDevice::surface_formats`]: crate::device::physical::PhysicalDevice::surface_formats #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct SurfaceInfo { pub full_screen_exclusive: FullScreenExclusive, pub win32_monitor: Option, pub _ne: crate::NonExhaustive, } impl Default for SurfaceInfo { #[inline] fn default() -> Self { Self { full_screen_exclusive: FullScreenExclusive::Default, win32_monitor: None, _ne: crate::NonExhaustive(()), } } } #[cfg(target_os = "ios")] struct LayerHandle(*mut Object); #[cfg(target_os = "ios")] unsafe impl Send for LayerHandle {} #[cfg(target_os = "ios")] unsafe impl Sync for LayerHandle {} /// Represents the metal layer for IOS #[cfg(target_os = "ios")] pub struct IOSMetalLayer { main_layer: LayerHandle, render_layer: LayerHandle, } #[cfg(target_os = "ios")] impl IOSMetalLayer { #[inline] pub fn new(main_layer: *mut Object, render_layer: *mut Object) -> Self { Self { main_layer: LayerHandle(main_layer), render_layer: LayerHandle(render_layer), } } } #[cfg(target_os = "ios")] unsafe impl Send for IOSMetalLayer {} #[cfg(target_os = "ios")] unsafe impl Sync for IOSMetalLayer {} /// The capabilities of a surface when used by a physical device. /// /// You have to match these capabilities when you create a swapchain. #[derive(Clone, Debug)] #[non_exhaustive] pub struct SurfaceCapabilities { /// Minimum number of images that must be present in the swapchain. pub min_image_count: u32, /// Maximum number of images that must be present in the swapchain, or `None` if there is no /// maximum value. Note that "no maximum" doesn't mean that you can set a very high value, as /// you may still get out of memory errors. pub max_image_count: Option, /// The current dimensions of the surface. `None` means that the surface's dimensions will /// depend on the dimensions of the swapchain that you are going to create. pub current_extent: Option<[u32; 2]>, /// Minimum width and height of a swapchain that uses this surface. pub min_image_extent: [u32; 2], /// Maximum width and height of a swapchain that uses this surface. pub max_image_extent: [u32; 2], /// Maximum number of image layers if you create an image array. The minimum is 1. pub max_image_array_layers: u32, /// List of transforms supported for the swapchain. pub supported_transforms: SurfaceTransforms, /// Current transform used by the surface. pub current_transform: SurfaceTransform, /// List of composite alpha modes supports for the swapchain. pub supported_composite_alpha: CompositeAlphas, /// List of image usages that are supported for images of the swapchain. Only /// the `color_attachment` usage is guaranteed to be supported. pub supported_usage_flags: ImageUsage, /// Whether creating a protected swapchain is supported. pub supports_protected: bool, /// Whether full-screen exclusivity is supported. pub full_screen_exclusive_supported: bool, } #[cfg(test)] mod tests { use crate::{ swapchain::{Surface, SurfaceCreationError}, RequiresOneOf, }; use std::ptr; #[test] fn khr_win32_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_win32(instance, ptr::null::(), ptr::null::(), None) } { Err(SurfaceCreationError::RequirementNotMet { requires_one_of: RequiresOneOf { instance_extensions, .. }, .. }) if instance_extensions.contains(&"khr_win32_surface") => (), _ => panic!(), } } #[test] fn khr_xcb_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_xcb(instance, ptr::null::(), 0, None) } { Err(SurfaceCreationError::RequirementNotMet { requires_one_of: RequiresOneOf { instance_extensions, .. }, .. }) if instance_extensions.contains(&"khr_xcb_surface") => (), _ => panic!(), } } #[test] fn khr_xlib_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_xlib(instance, ptr::null::(), 0, None) } { Err(SurfaceCreationError::RequirementNotMet { requires_one_of: RequiresOneOf { instance_extensions, .. }, .. }) if instance_extensions.contains(&"khr_xlib_surface") => (), _ => panic!(), } } #[test] fn khr_wayland_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_wayland(instance, ptr::null::(), ptr::null::(), None) } { Err(SurfaceCreationError::RequirementNotMet { requires_one_of: RequiresOneOf { instance_extensions, .. }, .. }) if instance_extensions.contains(&"khr_wayland_surface") => (), _ => panic!(), } } #[test] fn khr_android_surface_ext_missing() { let instance = instance!(); match unsafe { Surface::from_android(instance, ptr::null::(), None) } { Err(SurfaceCreationError::RequirementNotMet { requires_one_of: RequiresOneOf { instance_extensions, .. }, .. }) if instance_extensions.contains(&"khr_android_surface") => (), _ => panic!(), } } }