shdrlib 0.1.5

A three-tiered Vulkan shader compilation and rendering framework built in pure Rust
Documentation
//! Surface module - Window surface wrapper
//!
//! **WARNING**: CORE objects do NOT enforce lifetime dependencies.
//! Surfaces must not outlive the instance.

use crate::core::Instance;
use ash::khr::surface;
use ash::vk;
use thiserror::Error;

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

/// Surface wrapper
///
/// Represents a platform-specific window surface for rendering.
///
/// # Safety
///
/// This object does NOT enforce that the instance outlives it.
/// Using a surface after dropping the instance causes undefined behavior.
pub struct Surface {
    surface: vk::SurfaceKHR,
    surface_loader: surface::Instance,
}

impl Surface {
    /// Create a surface from a raw Vulkan surface handle
    pub fn from_raw(instance: &Instance, surface: vk::SurfaceKHR) -> Self {
        let surface_loader = surface::Instance::new(instance.entry(), instance.handle());
        Self {
            surface,
            surface_loader,
        }
    }

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

    /// Get the surface loader
    #[inline]
    pub fn loader(&self) -> &surface::Instance {
        &self.surface_loader
    }

    /// Query surface capabilities for a physical device
    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)
        }
    }

    /// Query supported surface formats for a physical device
    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)
    }

    /// Query supported present modes for a physical device
    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)
    }

    /// Check if a queue family supports presentation to this surface
    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)
        }
    }

    /// Destroy the surface manually
    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());
    }
}