shdrlib 0.1.5

A three-tiered Vulkan shader compilation and rendering framework built in pure Rust
Documentation
//! Swapchain module - Image presentation
//!
//! **WARNING**: CORE objects do NOT enforce lifetime dependencies.
//! Swapchains must not outlive the device or surface.

use crate::core::{Device, Instance};
use ash::khr::swapchain;
use ash::vk;
use thiserror::Error;

/// Swapchain operation 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,
}

/// Swapchain wrapper
///
/// Manages a set of images for presentation to a surface.
///
/// # Safety
///
/// This object does NOT enforce that the device or surface outlive it.
pub struct Swapchain {
    swapchain: vk::SwapchainKHR,
    swapchain_loader: swapchain::Device,
    images: Vec<vk::Image>,
    format: vk::Format,
    extent: vk::Extent2D,
}

impl Swapchain {
    /// Create a new 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,
        })
    }

    /// Get the raw Vulkan swapchain handle
    #[inline]
    pub fn handle(&self) -> vk::SwapchainKHR {
        self.swapchain
    }

    /// Get the swapchain loader
    #[inline]
    pub fn loader(&self) -> &swapchain::Device {
        &self.swapchain_loader
    }

    /// Get swapchain images
    #[inline]
    pub fn images(&self) -> &[vk::Image] {
        &self.images
    }

    /// Get swapchain image format
    #[inline]
    pub fn format(&self) -> vk::Format {
        self.format
    }

    /// Get swapchain extent
    #[inline]
    pub fn extent(&self) -> vk::Extent2D {
        self.extent
    }

    /// Acquire next image from swapchain
    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)
        }
    }

    /// Queue an image for presentation
    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)
        }
    }

    /// Destroy the swapchain manually
    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");
    }
}