shdrlib 0.1.5

A three-tiered Vulkan shader compilation and rendering framework built in pure Rust
Documentation
//! Queue module - Command submission queue wrapper
//!
//! **WARNING**: CORE objects do NOT enforce lifetime dependencies.
//! The queue must not outlive the device it was created from.

use crate::core::Device;
use ash::vk;
use thiserror::Error;

/// Queue operation error
#[derive(Debug, Error)]
pub enum QueueError {
    #[error("Queue submit failed: {0}")]
    SubmitFailed(vk::Result),

    #[error("Queue wait failed: {0}")]
    WaitFailed(vk::Result),

    #[error("Queue present failed: {0}")]
    PresentFailed(vk::Result),
}

/// Command submission queue wrapper
///
/// Wraps `vk::Queue` and provides command buffer submission and synchronization.
///
/// # Safety
///
/// This object does NOT enforce that the device outlives it.
/// Using the queue after dropping the device causes undefined behavior.
///
/// Note: Queue has NO Drop implementation - queues are implicitly destroyed
/// when the device is destroyed.
pub struct Queue {
    queue: vk::Queue,
    family_index: u32,
    queue_index: u32,
}

impl Queue {
    /// Get a queue from the device
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use shdrlib::core::{Queue, Device};
    ///
    /// # fn example(device: &Device, graphics_family: u32) {
    /// let queue = Queue::get(device, graphics_family, 0);
    /// # }
    /// ```
    #[inline]
    pub fn get(device: &Device, family_index: u32, queue_index: u32) -> Self {
        // SAFETY: device is valid, indices are caller's responsibility to validate
        let queue = unsafe { device.handle().get_device_queue(family_index, queue_index) };

        Self {
            queue,
            family_index,
            queue_index,
        }
    }

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

    /// Get the queue family index
    #[inline]
    pub fn family_index(&self) -> u32 {
        self.family_index
    }

    /// Get the queue index within the family
    #[inline]
    pub fn queue_index(&self) -> u32 {
        self.queue_index
    }

    /// Submit command buffers to the queue
    ///
    /// # Arguments
    ///
    /// * `device` - The device handle (needed for submission)
    /// * `command_buffers` - Command buffers to execute
    /// * `wait_semaphores` - Semaphores to wait on before execution
    /// * `wait_stages` - Pipeline stages to wait at for each semaphore
    /// * `signal_semaphores` - Semaphores to signal after execution
    /// * `fence` - Optional fence to signal when complete
    ///
    /// # Errors
    ///
    /// Returns `QueueError::SubmitFailed` if submission fails.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use shdrlib::core::Queue;
    /// use ash::vk;
    ///
    /// # fn example(
    /// #     device: &shdrlib::core::Device,
    /// #     queue: &Queue,
    /// #     command_buffer: vk::CommandBuffer,
    /// #     fence: vk::Fence
    /// # ) -> Result<(), shdrlib::core::QueueError> {
    /// queue.submit(
    ///     device,
    ///     &[command_buffer],
    ///     &[],
    ///     &[],
    ///     &[],
    ///     Some(fence),
    /// )?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn submit(
        &self,
        device: &Device,
        command_buffers: &[vk::CommandBuffer],
        wait_semaphores: &[vk::Semaphore],
        wait_stages: &[vk::PipelineStageFlags],
        signal_semaphores: &[vk::Semaphore],
        fence: Option<vk::Fence>,
    ) -> Result<(), QueueError> {
        let submit_info = vk::SubmitInfo {
            wait_semaphore_count: wait_semaphores.len() as u32,
            p_wait_semaphores: wait_semaphores.as_ptr(),
            p_wait_dst_stage_mask: wait_stages.as_ptr(),
            command_buffer_count: command_buffers.len() as u32,
            p_command_buffers: command_buffers.as_ptr(),
            signal_semaphore_count: signal_semaphores.len() as u32,
            p_signal_semaphores: signal_semaphores.as_ptr(),
            ..Default::default()
        };

        // SAFETY: device and queue are valid, submit_info is properly initialized
        unsafe {
            device
                .handle()
                .queue_submit(
                    self.queue,
                    &[submit_info],
                    fence.unwrap_or(vk::Fence::null()),
                )
                .map_err(QueueError::SubmitFailed)
        }
    }

    /// Wait for the queue to become idle
    ///
    /// This blocks until all submitted command buffers have completed execution.
    ///
    /// # Errors
    ///
    /// Returns `QueueError::WaitFailed` if wait fails.
    pub fn wait_idle(&self, device: &Device) -> Result<(), QueueError> {
        // SAFETY: device and queue are valid
        unsafe {
            device
                .handle()
                .queue_wait_idle(self.queue)
                .map_err(QueueError::WaitFailed)
        }
    }

    /// Present swapchain images to the queue
    ///
    /// # Arguments
    ///
    /// * `device` - The device handle
    /// * `wait_semaphores` - Semaphores to wait on before presenting
    /// * `swapchains` - Swapchains to present
    /// * `image_indices` - Image indices to present from each swapchain
    ///
    /// # Errors
    ///
    /// Returns `QueueError::PresentFailed` if presentation fails.
    pub fn present(
        &self,
        _device: &Device,
        _wait_semaphores: &[vk::Semaphore],
        _swapchains: &[vk::SwapchainKHR],
        _image_indices: &[u32],
    ) -> Result<bool, QueueError> {
        // NOTE: This is a placeholder until we implement the swapchain module
        // The actual implementation will use the swapchain extension loader
        Err(QueueError::PresentFailed(
            vk::Result::ERROR_FEATURE_NOT_PRESENT,
        ))
    }
}

// No Drop implementation - queues are implicitly destroyed with the device

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::{Device, DeviceCreateInfo, Instance, InstanceCreateInfo, QueueCreateInfo};

    #[test]
    fn test_queue_get() {
        let instance = Instance::new(InstanceCreateInfo {
            enable_validation: false,
            ..Default::default()
        })
        .unwrap();
        let physical_devices = instance.enumerate_physical_devices().unwrap();
        let physical_device = physical_devices[0];

        let graphics_family = unsafe {
            instance
                .get_physical_device_queue_family_properties(physical_device)
                .iter()
                .enumerate()
                .find(|(_, qf)| qf.queue_flags.contains(vk::QueueFlags::GRAPHICS))
                .map(|(i, _)| i as u32)
                .unwrap()
        };

        let device = Device::new(
            &instance,
            physical_device,
            DeviceCreateInfo {
                queue_create_infos: vec![QueueCreateInfo {
                    queue_family_index: graphics_family,
                    queue_count: 1,
                    queue_priorities: vec![1.0],
                }],
                ..Default::default()
            },
        )
        .unwrap();

        let queue = Queue::get(&device, graphics_family, 0);
        assert_eq!(queue.family_index(), graphics_family);
        assert_eq!(queue.queue_index(), 0);
    }

    #[test]
    fn test_queue_wait_idle() {
        let instance = Instance::new(InstanceCreateInfo {
            enable_validation: false,
            ..Default::default()
        })
        .unwrap();
        let physical_devices = instance.enumerate_physical_devices().unwrap();
        let physical_device = physical_devices[0];

        let graphics_family = unsafe {
            instance
                .get_physical_device_queue_family_properties(physical_device)
                .iter()
                .enumerate()
                .find(|(_, qf)| qf.queue_flags.contains(vk::QueueFlags::GRAPHICS))
                .map(|(i, _)| i as u32)
                .unwrap()
        };

        let device = Device::new(
            &instance,
            physical_device,
            DeviceCreateInfo {
                queue_create_infos: vec![QueueCreateInfo {
                    queue_family_index: graphics_family,
                    queue_count: 1,
                    queue_priorities: vec![1.0],
                }],
                ..Default::default()
            },
        )
        .unwrap();

        let queue = Queue::get(&device, graphics_family, 0);
        assert!(queue.wait_idle(&device).is_ok());
    }
}