| // 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 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<Instance>, |
| id: NonZeroU64, |
| api: SurfaceApi, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| // 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<ash::vk::PhysicalDevice, Vec<PresentMode>>, |
| 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<Instance>, |
| handle: ash::vk::SurfaceKHR, |
| api: SurfaceApi, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> 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<Instance>, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<Instance>, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<Arc<Self>, 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<Arc<Self>, 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<W>( |
| instance: Arc<Instance>, |
| window: *const W, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, SurfaceCreationError> { |
| Self::validate_from_android(&instance, window)?; |
| |
| Ok(Self::from_android_unchecked(instance, window, object)?) |
| } |
| |
| fn validate_from_android<W>( |
| 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<W>( |
| instance: Arc<Instance>, |
| window: *const W, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<D, S>( |
| instance: Arc<Instance>, |
| dfb: *const D, |
| surface: *const S, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, SurfaceCreationError> { |
| Self::validate_from_directfb(&instance, dfb, surface)?; |
| |
| Ok(Self::from_directfb_unchecked( |
| instance, dfb, surface, object, |
| )?) |
| } |
| |
| fn validate_from_directfb<D, S>( |
| 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<D, S>( |
| instance: Arc<Instance>, |
| dfb: *const D, |
| surface: *const S, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<Instance>, |
| image_pipe_handle: ash::vk::zx_handle_t, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<Instance>, |
| image_pipe_handle: ash::vk::zx_handle_t, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<Instance>, |
| stream_descriptor: ash::vk::GgpStreamDescriptor, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<Instance>, |
| stream_descriptor: ash::vk::GgpStreamDescriptor, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<Instance>, |
| metal_layer: IOSMetalLayer, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<Instance>, |
| metal_layer: IOSMetalLayer, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<V>( |
| instance: Arc<Instance>, |
| view: *const V, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<V>( |
| 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<V>( |
| instance: Arc<Instance>, |
| view: *const V, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<L>( |
| instance: Arc<Instance>, |
| layer: *const L, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, SurfaceCreationError> { |
| Self::validate_from_metal(&instance, layer)?; |
| |
| Ok(Self::from_metal_unchecked(instance, layer, object)?) |
| } |
| |
| fn validate_from_metal<L>( |
| 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<L>( |
| instance: Arc<Instance>, |
| layer: *const L, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<C, W>( |
| instance: Arc<Instance>, |
| context: *const C, |
| window: *const W, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, SurfaceCreationError> { |
| Self::validate_from_qnx_screen(&instance, context, window)?; |
| |
| Ok(Self::from_qnx_screen_unchecked( |
| instance, context, window, object, |
| )?) |
| } |
| |
| fn validate_from_qnx_screen<C, W>( |
| 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<C, W>( |
| instance: Arc<Instance>, |
| context: *const C, |
| window: *const W, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<W>( |
| instance: Arc<Instance>, |
| window: *const W, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, SurfaceCreationError> { |
| Self::validate_from_vi(&instance, window)?; |
| |
| Ok(Self::from_vi_unchecked(instance, window, object)?) |
| } |
| |
| fn validate_from_vi<W>( |
| 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<W>( |
| instance: Arc<Instance>, |
| window: *const W, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<D, S>( |
| instance: Arc<Instance>, |
| display: *const D, |
| surface: *const S, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, SurfaceCreationError> { |
| Self::validate_from_wayland(&instance, display, surface)?; |
| |
| Ok(Self::from_wayland_unchecked( |
| instance, display, surface, object, |
| )?) |
| } |
| |
| fn validate_from_wayland<D, S>( |
| 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<D, S>( |
| instance: Arc<Instance>, |
| display: *const D, |
| surface: *const S, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<I, W>( |
| instance: Arc<Instance>, |
| hinstance: *const I, |
| hwnd: *const W, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, SurfaceCreationError> { |
| Self::validate_from_win32(&instance, hinstance, hwnd)?; |
| |
| Ok(Self::from_win32_unchecked( |
| instance, hinstance, hwnd, object, |
| )?) |
| } |
| |
| fn validate_from_win32<I, W>( |
| 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<I, W>( |
| instance: Arc<Instance>, |
| hinstance: *const I, |
| hwnd: *const W, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<C>( |
| instance: Arc<Instance>, |
| connection: *const C, |
| window: ash::vk::xcb_window_t, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, SurfaceCreationError> { |
| Self::validate_from_xcb(&instance, connection, window)?; |
| |
| Ok(Self::from_xcb_unchecked( |
| instance, connection, window, object, |
| )?) |
| } |
| |
| fn validate_from_xcb<C>( |
| 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<C>( |
| instance: Arc<Instance>, |
| connection: *const C, |
| window: ash::vk::xcb_window_t, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<D>( |
| instance: Arc<Instance>, |
| display: *const D, |
| window: ash::vk::Window, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, SurfaceCreationError> { |
| Self::validate_from_xlib(&instance, display, window)?; |
| |
| Ok(Self::from_xlib_unchecked( |
| instance, display, window, object, |
| )?) |
| } |
| |
| fn validate_from_xlib<D>( |
| 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<D>( |
| instance: Arc<Instance>, |
| display: *const D, |
| window: ash::vk::Window, |
| object: Option<Arc<dyn Any + Send + Sync>>, |
| ) -> Result<Arc<Self>, 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<Instance> { |
| &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<dyn Any + Send + Sync>> { |
| 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<OomError> for SurfaceCreationError { |
| fn from(err: OomError) -> SurfaceCreationError { |
| SurfaceCreationError::OomError(err) |
| } |
| } |
| |
| impl From<VulkanError> 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<Win32Monitor>, |
| 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<u32>, |
| |
| /// 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::<u8>(), ptr::null::<u8>(), 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::<u8>(), 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::<u8>(), 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::<u8>(), ptr::null::<u8>(), 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::<u8>(), None) } { |
| Err(SurfaceCreationError::RequirementNotMet { |
| requires_one_of: |
| RequiresOneOf { |
| instance_extensions, |
| .. |
| }, |
| .. |
| }) if instance_extensions.contains(&"khr_android_surface") => (), |
| _ => panic!(), |
| } |
| } |
| } |