use smallvec::SmallVec;
use std::error;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::vec::IntoIter as VecIntoIter;
use instance::QueueFamily;
use Error;
use OomError;
use VulkanObject;
use check_errors;
use device::Device;
use device::DeviceOwned;
use vk;
#[derive(Debug)]
pub struct UnsafeCommandPool {
pool: vk::CommandPool,
device: Arc<Device>,
queue_family_index: u32,
dummy_avoid_sync: PhantomData<*const u8>,
}
unsafe impl Send for UnsafeCommandPool {
}
impl UnsafeCommandPool {
pub fn new(device: Arc<Device>, queue_family: QueueFamily, transient: bool, reset_cb: bool)
-> Result<UnsafeCommandPool, OomError> {
assert_eq!(device.physical_device().internal_object(),
queue_family.physical_device().internal_object(),
"Device doesn't match physical device when creating a command pool");
let vk = device.pointers();
let flags = {
let flag1 = if transient {
vk::COMMAND_POOL_CREATE_TRANSIENT_BIT
} else {
0
};
let flag2 = if reset_cb {
vk::COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
} else {
0
};
flag1 | flag2
};
let pool = unsafe {
let infos = vk::CommandPoolCreateInfo {
sType: vk::STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
pNext: ptr::null(),
flags: flags,
queueFamilyIndex: queue_family.id(),
};
let mut output = mem::uninitialized();
check_errors(vk.CreateCommandPool(device.internal_object(),
&infos,
ptr::null(),
&mut output))?;
output
};
Ok(UnsafeCommandPool {
pool: pool,
device: device.clone(),
queue_family_index: queue_family.id(),
dummy_avoid_sync: PhantomData,
})
}
pub unsafe fn reset(&self, release_resources: bool) -> Result<(), OomError> {
let flags = if release_resources {
vk::COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT
} else {
0
};
let vk = self.device.pointers();
check_errors(vk.ResetCommandPool(self.device.internal_object(), self.pool, flags))?;
Ok(())
}
pub fn trim(&self) -> Result<(), CommandPoolTrimError> {
unsafe {
if !self.device.loaded_extensions().khr_maintenance1 {
return Err(CommandPoolTrimError::Maintenance1ExtensionNotEnabled);
}
let vk = self.device.pointers();
vk.TrimCommandPoolKHR(self.device.internal_object(),
self.pool,
0 );
Ok(())
}
}
pub fn alloc_command_buffers(&self, secondary: bool, count: usize)
-> Result<UnsafeCommandPoolAllocIter, OomError> {
if count == 0 {
return Ok(UnsafeCommandPoolAllocIter { list: vec![].into_iter() });
}
let infos = vk::CommandBufferAllocateInfo {
sType: vk::STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
pNext: ptr::null(),
commandPool: self.pool,
level: if secondary {
vk::COMMAND_BUFFER_LEVEL_SECONDARY
} else {
vk::COMMAND_BUFFER_LEVEL_PRIMARY
},
commandBufferCount: count as u32,
};
unsafe {
let vk = self.device.pointers();
let mut out = Vec::with_capacity(count);
check_errors(vk.AllocateCommandBuffers(self.device.internal_object(),
&infos,
out.as_mut_ptr()))?;
out.set_len(count);
Ok(UnsafeCommandPoolAllocIter { list: out.into_iter() })
}
}
pub unsafe fn free_command_buffers<I>(&self, command_buffers: I)
where I: Iterator<Item = UnsafeCommandPoolAlloc>
{
let command_buffers: SmallVec<[_; 4]> = command_buffers.map(|cb| cb.0).collect();
let vk = self.device.pointers();
vk.FreeCommandBuffers(self.device.internal_object(),
self.pool,
command_buffers.len() as u32,
command_buffers.as_ptr())
}
#[inline]
pub fn queue_family(&self) -> QueueFamily {
self.device
.physical_device()
.queue_family_by_id(self.queue_family_index)
.unwrap()
}
}
unsafe impl DeviceOwned for UnsafeCommandPool {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
unsafe impl VulkanObject for UnsafeCommandPool {
type Object = vk::CommandPool;
#[inline]
fn internal_object(&self) -> vk::CommandPool {
self.pool
}
}
impl Drop for UnsafeCommandPool {
#[inline]
fn drop(&mut self) {
unsafe {
let vk = self.device.pointers();
vk.DestroyCommandPool(self.device.internal_object(), self.pool, ptr::null());
}
}
}
pub struct UnsafeCommandPoolAlloc(vk::CommandBuffer);
unsafe impl VulkanObject for UnsafeCommandPoolAlloc {
type Object = vk::CommandBuffer;
#[inline]
fn internal_object(&self) -> vk::CommandBuffer {
self.0
}
}
#[derive(Debug)]
pub struct UnsafeCommandPoolAllocIter {
list: VecIntoIter<vk::CommandBuffer>,
}
impl Iterator for UnsafeCommandPoolAllocIter {
type Item = UnsafeCommandPoolAlloc;
#[inline]
fn next(&mut self) -> Option<UnsafeCommandPoolAlloc> {
self.list
.next()
.map(|cb| UnsafeCommandPoolAlloc(cb))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.list.size_hint()
}
}
impl ExactSizeIterator for UnsafeCommandPoolAllocIter {
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CommandPoolTrimError {
Maintenance1ExtensionNotEnabled,
}
impl error::Error for CommandPoolTrimError {
#[inline]
fn description(&self) -> &str {
match *self {
CommandPoolTrimError::Maintenance1ExtensionNotEnabled =>
"the `KHR_maintenance1` extension was not enabled",
}
}
}
impl fmt::Display for CommandPoolTrimError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
impl From<Error> for CommandPoolTrimError {
#[inline]
fn from(err: Error) -> CommandPoolTrimError {
panic!("unexpected error: {:?}", err)
}
}
#[cfg(test)]
mod tests {
use command_buffer::pool::CommandPoolTrimError;
use command_buffer::pool::UnsafeCommandPool;
#[test]
fn basic_create() {
let (device, queue) = gfx_dev_and_queue!();
let _ = UnsafeCommandPool::new(device, queue.family(), false, false).unwrap();
}
#[test]
fn queue_family_getter() {
let (device, queue) = gfx_dev_and_queue!();
let pool = UnsafeCommandPool::new(device, queue.family(), false, false).unwrap();
assert_eq!(pool.queue_family().id(), queue.family().id());
}
#[test]
fn panic_if_not_match_family() {
let (device, _) = gfx_dev_and_queue!();
let (_, queue) = gfx_dev_and_queue!();
assert_should_panic!("Device doesn't match physical device when creating a command pool", {
let _ = UnsafeCommandPool::new(device, queue.family(), false, false);
});
}
#[test]
fn check_maintenance_when_trim() {
let (device, queue) = gfx_dev_and_queue!();
let pool = UnsafeCommandPool::new(device, queue.family(), false, false).unwrap();
match pool.trim() {
Err(CommandPoolTrimError::Maintenance1ExtensionNotEnabled) => (),
_ => panic!(),
}
}
#[test]
fn basic_alloc() {
let (device, queue) = gfx_dev_and_queue!();
let pool = UnsafeCommandPool::new(device, queue.family(), false, false).unwrap();
let iter = pool.alloc_command_buffers(false, 12).unwrap();
assert_eq!(iter.count(), 12);
}
}