use crate::core::{Device, Instance};
use ash::khr::swapchain;
use ash::vk;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum SwapchainError {
#[error("Swapchain creation failed: {0}")]
CreationFailed(vk::Result),
#[error("Acquire image failed: {0}")]
AcquireImageFailed(vk::Result),
#[error("Present failed: {0}")]
PresentFailed(vk::Result),
#[error("Swapchain out of date")]
OutOfDate,
#[error("Swapchain suboptimal")]
Suboptimal,
}
pub struct Swapchain {
swapchain: vk::SwapchainKHR,
swapchain_loader: swapchain::Device,
images: Vec<vk::Image>,
format: vk::Format,
extent: vk::Extent2D,
}
impl Swapchain {
pub fn new(
instance: &Instance,
device: &Device,
create_info: &vk::SwapchainCreateInfoKHR,
) -> Result<Self, SwapchainError> {
let swapchain_loader = swapchain::Device::new(instance.handle(), device.handle());
let swapchain_khr = unsafe {
swapchain_loader
.create_swapchain(create_info, None)
.map_err(SwapchainError::CreationFailed)?
};
let images = unsafe {
swapchain_loader
.get_swapchain_images(swapchain_khr)
.map_err(SwapchainError::CreationFailed)?
};
Ok(Self {
swapchain: swapchain_khr,
swapchain_loader,
images,
format: create_info.image_format,
extent: create_info.image_extent,
})
}
#[inline]
pub fn handle(&self) -> vk::SwapchainKHR {
self.swapchain
}
#[inline]
pub fn loader(&self) -> &swapchain::Device {
&self.swapchain_loader
}
#[inline]
pub fn images(&self) -> &[vk::Image] {
&self.images
}
#[inline]
pub fn format(&self) -> vk::Format {
self.format
}
#[inline]
pub fn extent(&self) -> vk::Extent2D {
self.extent
}
pub fn acquire_next_image(
&self,
timeout: u64,
semaphore: vk::Semaphore,
fence: vk::Fence,
) -> Result<(u32, bool), SwapchainError> {
unsafe {
self.swapchain_loader
.acquire_next_image(self.swapchain, timeout, semaphore, fence)
.map_err(SwapchainError::AcquireImageFailed)
}
}
pub fn queue_present(
&self,
queue: vk::Queue,
present_info: &vk::PresentInfoKHR,
) -> Result<bool, SwapchainError> {
unsafe {
self.swapchain_loader
.queue_present(queue, present_info)
.map_err(SwapchainError::PresentFailed)
}
}
pub fn destroy(&mut self) {
unsafe {
self.swapchain_loader
.destroy_swapchain(self.swapchain, None);
}
self.swapchain = vk::SwapchainKHR::null();
}
}
impl Drop for Swapchain {
fn drop(&mut self) {
if self.swapchain != vk::SwapchainKHR::null() {
eprintln!("WARNING: Swapchain dropped without manual destroy() - potential leak");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_swapchain_error_types() {
let err = SwapchainError::OutOfDate;
assert_eq!(err.to_string(), "Swapchain out of date");
let err = SwapchainError::Suboptimal;
assert_eq!(err.to_string(), "Swapchain suboptimal");
}
}