shdrlib 0.1.5

A three-tiered Vulkan shader compilation and rendering framework built in pure Rust
Documentation
//! EX-tier resource wrappers with automatic cleanup
//!
//! Unlike CORE tier resources which require manual `.destroy()` calls,
//! EX-tier wrappers automatically clean up GPU resources when dropped.
//!
//! While EX tier is "explicit", it doesn't mean you should have to remember
//! to clean up every resource manually. These wrappers provide memory safety
//! while maintaining the explicit configuration philosophy.

use crate::core::{
    Buffer as CoreBuffer, DescriptorPool as CoreDescriptorPool,
    DescriptorSetLayout as CoreDescriptorSetLayout, Device, Image as CoreImage,
};
use ash::vk;
use std::sync::Arc;

/// Buffer wrapper with automatic cleanup
///
/// Wraps a CORE `Buffer` and automatically destroys it when dropped.
/// Provides the same explicit control as CORE but with memory safety.
///
/// # Example
///
/// ```rust,no_run
/// use shdrlib::ex::*;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut runtime = RuntimeManager::new(RuntimeConfig::default())?;
/// let device = runtime.device();
///
/// // Create a buffer - cleanup is automatic!
/// let buffer = helpers::create_vertex_buffer(&device, &[1.0f32, 2.0, 3.0])?;
///
/// // Use the buffer...
/// // No cleanup needed - Drop handles it!
/// # Ok(())
/// # }
/// ```
pub struct ExBuffer {
    device: Arc<Device>,
    buffer: CoreBuffer,
}

impl ExBuffer {
    /// Create a new ExBuffer (internal use)
    pub(crate) fn new(device: Arc<Device>, buffer: CoreBuffer) -> Self {
        Self { device, buffer }
    }

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

    /// Get the buffer size in bytes
    #[inline]
    pub fn size(&self) -> vk::DeviceSize {
        self.buffer.size()
    }

    /// Get the buffer usage flags
    #[inline]
    pub fn usage(&self) -> vk::BufferUsageFlags {
        self.buffer.usage()
    }

    /// Get a reference to the underlying CORE buffer (for advanced usage)
    #[inline]
    pub fn core_buffer(&self) -> &CoreBuffer {
        &self.buffer
    }

    /// Consume this wrapper and return the underlying CORE buffer
    /// 
    /// This is useful when you need to transfer ownership to another system
    /// that will handle cleanup. The wrapper's Drop will not run.
    /// 
    /// # Safety
    /// 
    /// The caller assumes responsibility for destroying the returned buffer.
    #[inline]
    pub fn into_core(self) -> CoreBuffer {
        // Use ManuallyDrop to prevent Drop from running
        let buffer = std::mem::ManuallyDrop::new(self);
        // SAFETY: We're transferring ownership, preventing double-free
        unsafe { std::ptr::read(&buffer.buffer) }
    }
}

impl Drop for ExBuffer {
    fn drop(&mut self) {
        self.buffer.destroy(&self.device);
    }
}

/// Image wrapper with automatic cleanup
///
/// Wraps a CORE `Image` and automatically destroys it when dropped.
/// Provides the same explicit control as CORE but with memory safety.
///
/// # Example
///
/// ```rust,no_run
/// use shdrlib::ex::*;
/// use ash::vk;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut runtime = RuntimeManager::new(RuntimeConfig::default())?;
/// let device = runtime.device();
///
/// // Create a texture - cleanup is automatic!
/// let texture = helpers::create_texture(&device, 512, 512, vk::Format::R8G8B8A8_UNORM)?;
///
/// // Use the texture...
/// // No cleanup needed - Drop handles it!
/// # Ok(())
/// # }
/// ```
pub struct ExImage {
    device: Arc<Device>,
    image: CoreImage,
}

impl ExImage {
    /// Create a new ExImage (internal use)
    pub(crate) fn new(device: Arc<Device>, image: CoreImage) -> Self {
        Self { device, image }
    }

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

    /// Get the image view handle
    #[inline]
    pub fn view(&self) -> vk::ImageView {
        self.image.view()
    }

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

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

    /// Get the image usage flags
    #[inline]
    pub fn usage(&self) -> vk::ImageUsageFlags {
        self.image.usage()
    }

    /// Get a reference to the underlying CORE image (for advanced usage)
    #[inline]
    pub fn core_image(&self) -> &CoreImage {
        &self.image
    }

    /// Consume this wrapper and return the underlying CORE image
    /// 
    /// This is useful when you need to transfer ownership to another system
    /// that will handle cleanup. The wrapper's Drop will not run.
    /// 
    /// # Safety
    /// 
    /// The caller assumes responsibility for destroying the returned image.
    #[inline]
    pub fn into_core(self) -> CoreImage {
        // Use ManuallyDrop to prevent Drop from running
        let image = std::mem::ManuallyDrop::new(self);
        // SAFETY: We're transferring ownership, preventing double-free
        unsafe { std::ptr::read(&image.image) }
    }
}

impl Drop for ExImage {
    fn drop(&mut self) {
        self.image.destroy(&self.device);
    }
}

/// DescriptorSetLayout wrapper with automatic cleanup
pub struct ExDescriptorSetLayout {
    device: Arc<Device>,
    layout: CoreDescriptorSetLayout,
}

impl ExDescriptorSetLayout {
    /// Create a new ExDescriptorSetLayout (internal use)
    #[allow(dead_code)]
    pub(crate) fn new(device: Arc<Device>, layout: CoreDescriptorSetLayout) -> Self {
        Self { device, layout }
    }

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

    /// Get a reference to the underlying CORE layout
    #[inline]
    pub fn core_layout(&self) -> &CoreDescriptorSetLayout {
        &self.layout
    }
}

impl Drop for ExDescriptorSetLayout {
    fn drop(&mut self) {
        self.layout.destroy(&self.device);
    }
}

/// DescriptorPool wrapper with automatic cleanup
pub struct ExDescriptorPool {
    device: Arc<Device>,
    pool: CoreDescriptorPool,
}

impl ExDescriptorPool {
    /// Create a new ExDescriptorPool (internal use)
    #[allow(dead_code)]
    pub(crate) fn new(device: Arc<Device>, pool: CoreDescriptorPool) -> Self {
        Self { device, pool }
    }

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

    /// Allocate descriptor sets from this pool
    #[inline]
    pub fn allocate(
        &self,
        device: &Arc<Device>,
        layouts: &[vk::DescriptorSetLayout],
    ) -> Result<Vec<crate::core::DescriptorSet>, crate::core::DescriptorPoolError> {
        self.pool.allocate(device, layouts)
    }

    /// Get a reference to the underlying CORE pool
    #[inline]
    pub fn core_pool(&self) -> &CoreDescriptorPool {
        &self.pool
    }
}

impl Drop for ExDescriptorPool {
    fn drop(&mut self) {
        self.pool.destroy(&self.device);
    }
}