blob: 44dda8bdfabe9b9fd10ff03a6b539ce98af03958 [file] [log] [blame]
// 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!(),
}
}
}