use crate::core::Instance;
use ash::khr::surface;
use ash::vk;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum SurfaceError {
#[error("Surface creation failed: {0}")]
CreationFailed(vk::Result),
#[error("Surface query failed: {0}")]
QueryFailed(vk::Result),
#[error("No suitable surface formats found")]
NoSurfaceFormats,
#[error("No suitable present modes found")]
NoPresentModes,
}
pub struct Surface {
surface: vk::SurfaceKHR,
surface_loader: surface::Instance,
}
impl Surface {
pub fn from_raw(instance: &Instance, surface: vk::SurfaceKHR) -> Self {
let surface_loader = surface::Instance::new(instance.entry(), instance.handle());
Self {
surface,
surface_loader,
}
}
#[inline]
pub fn handle(&self) -> vk::SurfaceKHR {
self.surface
}
#[inline]
pub fn loader(&self) -> &surface::Instance {
&self.surface_loader
}
pub fn get_capabilities(
&self,
physical_device: vk::PhysicalDevice,
) -> Result<vk::SurfaceCapabilitiesKHR, SurfaceError> {
unsafe {
self.surface_loader
.get_physical_device_surface_capabilities(physical_device, self.surface)
.map_err(SurfaceError::QueryFailed)
}
}
pub fn get_formats(
&self,
physical_device: vk::PhysicalDevice,
) -> Result<Vec<vk::SurfaceFormatKHR>, SurfaceError> {
let formats = unsafe {
self.surface_loader
.get_physical_device_surface_formats(physical_device, self.surface)
.map_err(SurfaceError::QueryFailed)?
};
if formats.is_empty() {
return Err(SurfaceError::NoSurfaceFormats);
}
Ok(formats)
}
pub fn get_present_modes(
&self,
physical_device: vk::PhysicalDevice,
) -> Result<Vec<vk::PresentModeKHR>, SurfaceError> {
let present_modes = unsafe {
self.surface_loader
.get_physical_device_surface_present_modes(physical_device, self.surface)
.map_err(SurfaceError::QueryFailed)?
};
if present_modes.is_empty() {
return Err(SurfaceError::NoPresentModes);
}
Ok(present_modes)
}
pub fn get_physical_device_surface_support(
&self,
physical_device: vk::PhysicalDevice,
queue_family_index: u32,
) -> Result<bool, SurfaceError> {
unsafe {
self.surface_loader
.get_physical_device_surface_support(
physical_device,
queue_family_index,
self.surface,
)
.map_err(SurfaceError::QueryFailed)
}
}
pub fn destroy(&mut self) {
unsafe {
self.surface_loader.destroy_surface(self.surface, None);
}
self.surface = vk::SurfaceKHR::null();
}
}
impl Drop for Surface {
fn drop(&mut self) {
if self.surface != vk::SurfaceKHR::null() {
eprintln!("WARNING: Surface dropped without manual destroy() - potential leak");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::InstanceCreateInfo;
#[test]
fn test_surface_from_raw() {
let instance = Instance::new(InstanceCreateInfo {
enable_validation: false,
..Default::default()
})
.unwrap();
let raw_surface = vk::SurfaceKHR::null();
let surface = Surface::from_raw(&instance, raw_surface);
assert_eq!(surface.handle(), vk::SurfaceKHR::null());
}
}