use std::sync::{Arc, Mutex, MutexGuard};
use anyhow::Result;
use ash::vk;
use crate::{
Allocator, CmdBuffer, DescriptorCache, Device, Error, Fence, IncompleteCmdBuffer, PipelineCache,
};
use crate::command_buffer::command_pool::CommandPool;
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum QueueType {
#[default]
Graphics = vk::QueueFlags::GRAPHICS.as_raw(),
Compute = vk::QueueFlags::COMPUTE.as_raw(),
Transfer = vk::QueueFlags::TRANSFER.as_raw(),
}
#[derive(Default, Debug, Copy, Clone)]
pub struct QueueInfo {
pub queue_type: QueueType,
pub dedicated: bool,
pub can_present: bool,
pub family_index: u32,
pub flags: vk::QueueFlags,
}
#[derive(Debug)]
pub(crate) struct DeviceQueue {
pub handle: vk::Queue,
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Queue {
#[derivative(Debug = "ignore")]
device: Device,
queue: Arc<Mutex<DeviceQueue>>,
pool: CommandPool,
info: QueueInfo,
family_properties: vk::QueueFamilyProperties,
}
impl Queue {
pub(crate) fn new(
device: Device,
queue: Arc<Mutex<DeviceQueue>>,
info: QueueInfo,
family_properties: vk::QueueFamilyProperties,
) -> Result<Self> {
let pool = CommandPool::new(
device.clone(),
info.family_index,
vk::CommandPoolCreateFlags::TRANSIENT,
)?;
Ok(Queue {
device,
queue,
pool,
info,
family_properties,
})
}
fn acquire_device_queue(&self) -> Result<MutexGuard<DeviceQueue>> {
Ok(self.queue.lock().map_err(|_| Error::PoisonError)?)
}
#[deprecated(since = "0.9.0", note = "Prefer using Queue::submit2() instead to make full use of the synchronization2 feature.")]
pub fn submit(&self, submits: &[vk::SubmitInfo], fence: Option<&Fence>) -> Result<()> {
let fence = match fence {
None => vk::Fence::null(),
Some(fence) => unsafe { fence.handle() },
};
let queue = self.acquire_device_queue()?;
unsafe { Ok(self.device.queue_submit(queue.handle, submits, fence)?) }
}
pub fn submit2(&self, submits: &[vk::SubmitInfo2], fence: Option<&Fence>) -> Result<()> {
let fence = match fence {
None => vk::Fence::null(),
Some(fence) => unsafe { fence.handle() },
};
let queue = self.acquire_device_queue()?;
unsafe { Ok(self.device.queue_submit2(queue.handle, submits, fence)?) }
}
pub unsafe fn handle(&self) -> vk::Queue {
let queue = self.acquire_device_queue().unwrap();
queue.handle
}
pub(crate) fn allocate_command_buffer<'q, A: Allocator, CmdBuf: IncompleteCmdBuffer<'q, A>>(
device: Device,
queue_lock: MutexGuard<'q, Queue>,
pipelines: PipelineCache<A>,
descriptors: DescriptorCache,
) -> Result<CmdBuf> {
let info = vk::CommandBufferAllocateInfo {
s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO,
p_next: std::ptr::null(),
command_pool: unsafe { queue_lock.pool.handle() },
level: vk::CommandBufferLevel::PRIMARY,
command_buffer_count: 1,
};
let handle = unsafe { device.allocate_command_buffers(&info)? }
.into_iter()
.next()
.ok_or_else(|| Error::Uncategorized("Command buffer allocation failed."))?;
CmdBuf::new(
device,
queue_lock,
handle,
vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT,
pipelines,
descriptors,
)
}
pub(crate) unsafe fn free_command_buffer<CmdBuf: CmdBuffer<A>, A: Allocator>(
&self,
cmd: vk::CommandBuffer,
) -> Result<()> {
self.device
.free_command_buffers(self.pool.handle(), std::slice::from_ref(&cmd));
Ok(())
}
pub fn info(&self) -> &QueueInfo {
&self.info
}
pub fn family_properties(&self) -> &vk::QueueFamilyProperties {
&self.family_properties
}
}