2025-03-26 20:02:58 -04:00
use std ::sync ::Arc ;
2025-03-28 16:33:40 -04:00
use ash ::{ vk , Device as AshDevice } ;
2025-03-26 20:02:58 -04:00
use parking_lot ::Mutex ;
use crate ::device ::Device ;
use crate ::error ::Result ;
use crate ::sync ::Fence ;
/// Represents a Vulkan device queue.
///
/// Holds a reference to the `Device` and the raw `vk::Queue` handle.
/// Provides methods for submitting command buffers.
pub struct Queue {
device : Arc < Device > ,
queue : vk ::Queue ,
family_index : u32 ,
// Each queue submission must be externally synchronized or locked internally.
// Using a Mutex here provides a simple internal locking per queue.
submit_lock : Mutex < ( ) > ,
}
impl Queue {
/// Creates a new Queue wrapper. Called internally by `Device`.
pub ( crate ) fn new ( device : Arc < Device > , queue : vk ::Queue , family_index : u32 ) -> Self {
Self {
device ,
queue ,
family_index ,
submit_lock : Mutex ::new ( ( ) ) ,
}
}
/// Gets the raw `vk::Queue` handle.
pub fn handle ( & self ) -> vk ::Queue {
self . queue
}
/// Gets the queue family index this queue belongs to.
pub fn family_index ( & self ) -> u32 {
self . family_index
}
/// Gets a reference to the logical device this queue belongs to.
pub fn device ( & self ) -> & Arc < Device > {
& self . device
}
2025-03-28 16:33:40 -04:00
/// Submits command buffers to the queue using the provided device handle.
2025-03-26 20:02:58 -04:00
///
/// This method acquires an internal lock for the duration of the submission call
/// to prevent concurrent `vkQueueSubmit` calls on the same queue from this wrapper.
///
/// # Arguments
2025-03-28 16:33:40 -04:00
/// * `submit_device_raw` - The `ash::Device` handle corresponding to the device that owns the resources in `submits` and the `signal_fence`.
2025-03-26 20:02:58 -04:00
/// * `submits` - A slice of `vk::SubmitInfo` describing the work to submit.
2025-03-28 16:33:40 -04:00
/// * `signal_fence` - An optional `Fence` to signal when the submission completes. The fence must have been created with the same logical device as `submit_device_raw`.
2025-03-26 20:02:58 -04:00
///
/// # Safety
2025-03-28 16:33:40 -04:00
/// - `submit_device_raw` must be the correct, valid `ash::Device` handle associated with the resources being submitted.
/// - The command buffers and synchronization primitives within `submits` must be valid and owned by the same logical device as `submit_device_raw`.
/// - The `signal_fence`, if provided, must be valid, unsignaled, and owned by the same logical device as `submit_device_raw`.
2025-03-26 20:02:58 -04:00
pub unsafe fn submit (
& self ,
2025-03-28 16:33:40 -04:00
submit_device_raw : & AshDevice , // <<< Accept the ash::Device to use
2025-03-26 20:02:58 -04:00
submits : & [ vk ::SubmitInfo ] ,
signal_fence : Option < & Fence > ,
) -> Result < ( ) > {
2025-03-28 16:33:40 -04:00
debug_assert! (
self . device . raw ( ) . handle ( ) = = submit_device_raw . handle ( ) ,
" Queue::submit called with an ash::Device from a different logical VkDevice than the queue belongs to! "
) ;
// Optional: Check fence device consistency
if let Some ( fence ) = signal_fence {
debug_assert! (
fence . device ( ) . raw ( ) . handle ( ) = = submit_device_raw . handle ( ) ,
" Fence passed to Queue::submit belongs to a different logical device than submit_device_raw! "
) ;
}
2025-03-26 20:02:58 -04:00
let fence_handle = signal_fence . map_or ( vk ::Fence ::null ( ) , | f | f . handle ( ) ) ;
2025-03-28 16:33:40 -04:00
// Keep the lock for thread-safety on the VkQueue object itself
2025-03-26 20:02:58 -04:00
let _lock = self . submit_lock . lock ( ) ;
tracing ::trace! (
" Submitting {} batch(es) to queue family {} " ,
submits . len ( ) ,
self . family_index
) ;
2025-03-28 16:33:40 -04:00
// Use the EXPLICITLY PASSED submit_device_raw for the Vulkan call
submit_device_raw . queue_submit ( self . queue , submits , fence_handle ) ? ;
2025-03-26 20:02:58 -04:00
tracing ::trace! ( " Submission successful. " ) ;
Ok ( ( ) )
}
/// Waits until this queue becomes idle.
///
/// This is a heavy operation and blocks the current thread.
pub fn wait_idle ( & self ) -> Result < ( ) > {
tracing ::debug! ( " Waiting for queue idle (family {})... " , self . family_index ) ;
// Lock the mutex while waiting to prevent submissions during the wait?
// Or allow submissions and let Vulkan handle it? Let Vulkan handle it.
unsafe { self . device . raw ( ) . queue_wait_idle ( self . queue ) ? } ;
tracing ::debug! ( " Queue idle (family {}). " , self . family_index ) ;
Ok ( ( ) )
}
// Note: vkQueuePresentKHR is intentionally omitted here.
// Presentation is tightly coupled with Swapchain. It's safer to
// have a method like `Swapchain::present(&self, queue: &Queue, ...)`
// which internally calls `queue.device().raw().queue_present_khr(...)`
// using the swapchain's loader.
// If direct access is needed, it can be done with `queue.device().raw()`.
}
// Queues don't own the vk::Queue handle (the Device does), so no Drop impl.