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