use {
super::{DriverError, device::Device, instance::Instance},
ash::vk,
ash_window::create_surface,
log::warn,
raw_window_handle::{HasDisplayHandle, HasWindowHandle},
std::{
fmt::{Debug, Formatter},
thread::panicking,
},
};
#[read_only::cast]
pub struct Surface {
pub device: Device,
pub handle: vk::SurfaceKHR,
}
impl Surface {
pub fn capabilities(&self) -> Result<vk::SurfaceCapabilitiesKHR, DriverError> {
let surface_ext = Device::expect_surface_ext(&self.device);
unsafe {
surface_ext.get_physical_device_surface_capabilities(
self.device.physical_device.handle,
self.handle,
)
}
.map_err(|err| {
warn!("unable to get surface capabilities: {err}");
DriverError::Unsupported
})
}
#[profiling::function]
pub fn create(
device: &Device,
display: impl HasDisplayHandle,
window: impl HasWindowHandle,
) -> Result<Self, DriverError> {
let device = device.clone();
let display_handle = display.display_handle().map_err(|err| {
warn!("unable to get display handle: {err}");
DriverError::Unsupported
})?;
let window_handle = window.window_handle().map_err(|err| {
warn!("unable to get window handle: {err}");
DriverError::Unsupported
})?;
let handle = unsafe {
create_surface(
Instance::entry(&device.physical_device.instance),
&device.physical_device.instance,
display_handle.as_raw(),
window_handle.as_raw(),
None,
)
}
.map_err(|err| {
warn!("unable to create surface: {err}");
DriverError::Unsupported
})?;
Ok(Self { device, handle })
}
#[profiling::function]
pub fn formats(&self) -> Result<Vec<vk::SurfaceFormatKHR>, DriverError> {
let surface_ext = Device::expect_surface_ext(&self.device);
unsafe {
surface_ext.get_physical_device_surface_formats(
self.device.physical_device.handle,
self.handle,
)
}
.map_err(|err| {
warn!("unable to get surface formats: {err}");
DriverError::Unsupported
})
}
#[profiling::function]
pub fn linear(formats: &[vk::SurfaceFormatKHR]) -> Option<vk::SurfaceFormatKHR> {
formats
.iter()
.find(|&&vk::SurfaceFormatKHR { format, .. }| {
matches!(
format,
vk::Format::R8G8B8A8_UNORM | vk::Format::B8G8R8A8_UNORM
)
})
.copied()
}
pub fn linear_or_default(formats: &[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR {
Self::linear(formats).unwrap_or_else(|| formats.first().copied().unwrap_or_default())
}
pub fn physical_device_support(&self, queue_family_index: u32) -> Result<bool, DriverError> {
let surface_ext = Device::expect_surface_ext(&self.device);
unsafe {
surface_ext.get_physical_device_surface_support(
self.device.physical_device.handle,
queue_family_index,
self.handle,
)
}
.map_err(|err| {
warn!("unable to get physical device support: {err}");
match err {
vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
DriverError::OutOfMemory
}
vk::Result::ERROR_SURFACE_LOST_KHR => DriverError::InvalidData,
_ => DriverError::Unsupported,
}
})
}
pub fn present_modes(&self) -> Result<Vec<vk::PresentModeKHR>, DriverError> {
let surface_ext = Device::expect_surface_ext(&self.device);
unsafe {
surface_ext.get_physical_device_surface_present_modes(
self.device.physical_device.handle,
self.handle,
)
}
.map_err(|err| {
warn!("unable to get present modes: {err}");
DriverError::Unsupported
})
}
#[profiling::function]
pub fn srgb(formats: &[vk::SurfaceFormatKHR]) -> Option<vk::SurfaceFormatKHR> {
formats
.iter()
.find(
|&&vk::SurfaceFormatKHR {
color_space,
format,
}| {
matches!(color_space, vk::ColorSpaceKHR::SRGB_NONLINEAR)
&& matches!(
format,
vk::Format::R8G8B8A8_SRGB | vk::Format::B8G8R8A8_SRGB
)
},
)
.copied()
}
pub fn srgb_or_default(formats: &[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR {
Self::srgb(formats).unwrap_or_else(|| formats.first().copied().unwrap_or_default())
}
}
impl Debug for Surface {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("Surface")
}
}
impl Drop for Surface {
#[profiling::function]
fn drop(&mut self) {
if panicking() {
return;
}
let surface_ext = Device::expect_surface_ext(&self.device);
unsafe {
surface_ext.destroy_surface(self.handle, None);
}
}
}
impl Eq for Surface {}
impl PartialEq for Surface {
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle
}
}
#[cfg(test)]
mod test {
use super::Surface;
use ash::vk;
#[test]
fn linear_prefers_known_unorm_formats() {
let formats = [
vk::SurfaceFormatKHR {
format: vk::Format::R8G8B8A8_SRGB,
..Default::default()
},
vk::SurfaceFormatKHR {
format: vk::Format::R8G8B8A8_UNORM,
..Default::default()
},
];
assert_eq!(
Surface::linear(&formats).unwrap().format,
vk::Format::R8G8B8A8_UNORM
);
}
#[test]
fn linear_or_default_falls_back_to_first_format() {
let formats = [vk::SurfaceFormatKHR {
format: vk::Format::R16G16B16A16_SFLOAT,
..Default::default()
}];
assert_eq!(
Surface::linear_or_default(&formats).format,
vk::Format::R16G16B16A16_SFLOAT
);
}
#[test]
fn srgb_prefers_known_srgb_formats() {
let formats = [
vk::SurfaceFormatKHR {
color_space: vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT,
format: vk::Format::B8G8R8A8_SRGB,
},
vk::SurfaceFormatKHR {
color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
format: vk::Format::R8G8B8A8_SRGB,
},
];
assert_eq!(
Surface::srgb(&formats).unwrap().format,
vk::Format::R8G8B8A8_SRGB
);
}
#[test]
fn srgb_or_default_falls_back_to_first_format() {
let formats = [vk::SurfaceFormatKHR {
color_space: vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT,
format: vk::Format::R16G16B16A16_SFLOAT,
}];
assert_eq!(
Surface::srgb_or_default(&formats).format,
vk::Format::R16G16B16A16_SFLOAT
);
}
}