use crossbeam::sync::MsQueue;
use fnv::FnvHashMap;
use std::collections::hash_map::Entry;
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::Weak;
use std::thread;
use std::vec::IntoIter as VecIntoIter;
use command_buffer::pool::CommandPool;
use command_buffer::pool::CommandPoolAlloc;
use command_buffer::pool::CommandPoolBuilderAlloc;
use command_buffer::pool::UnsafeCommandPool;
use command_buffer::pool::UnsafeCommandPoolAlloc;
use instance::QueueFamily;
use OomError;
use VulkanObject;
use device::Device;
use device::DeviceOwned;
pub struct StandardCommandPool {
device: Arc<Device>,
queue_family: u32,
per_thread: Mutex<FnvHashMap<thread::ThreadId, Weak<StandardCommandPoolPerThread>>>,
}
unsafe impl Send for StandardCommandPool {
}
unsafe impl Sync for StandardCommandPool {
}
struct StandardCommandPoolPerThread {
pool: Mutex<UnsafeCommandPool>,
available_primary_command_buffers: MsQueue<UnsafeCommandPoolAlloc>,
available_secondary_command_buffers: MsQueue<UnsafeCommandPoolAlloc>,
}
impl StandardCommandPool {
pub fn new(device: Arc<Device>, queue_family: QueueFamily) -> StandardCommandPool {
assert_eq!(device.physical_device().internal_object(),
queue_family.physical_device().internal_object());
StandardCommandPool {
device: device,
queue_family: queue_family.id(),
per_thread: Mutex::new(Default::default()),
}
}
}
unsafe impl CommandPool for Arc<StandardCommandPool> {
type Iter = VecIntoIter<StandardCommandPoolBuilder>;
type Builder = StandardCommandPoolBuilder;
type Alloc = StandardCommandPoolAlloc;
fn alloc(&self, secondary: bool, count: u32) -> Result<Self::Iter, OomError> {
let mut hashmap = self.per_thread.lock().unwrap();
hashmap.retain(|_, w| w.upgrade().is_some());
let per_thread = match hashmap.entry(thread::current().id()) {
Entry::Occupied(mut entry) => {
entry.get().upgrade().unwrap()
},
Entry::Vacant(entry) => {
let new_pool =
UnsafeCommandPool::new(self.device.clone(), self.queue_family(), false, true)?;
let pt = Arc::new(StandardCommandPoolPerThread {
pool: Mutex::new(new_pool),
available_primary_command_buffers: MsQueue::new(),
available_secondary_command_buffers: MsQueue::new(),
});
entry.insert(Arc::downgrade(&pt));
pt
},
};
let mut output = Vec::with_capacity(count as usize);
{
let existing = if secondary {
&per_thread.available_secondary_command_buffers
} else {
&per_thread.available_primary_command_buffers
};
for _ in 0 .. count as usize {
if let Some(cmd) = existing.try_pop() {
output.push(StandardCommandPoolBuilder {
inner: StandardCommandPoolAlloc {
cmd: Some(cmd),
pool: per_thread.clone(),
pool_parent: self.clone(),
secondary: secondary,
device: self.device.clone(),
},
dummy_avoid_send_sync: PhantomData,
});
} else {
break;
}
}
};
if output.len() < count as usize {
let pool_lock = per_thread.pool.lock().unwrap();
let num_new = count as usize - output.len();
for cmd in pool_lock.alloc_command_buffers(secondary, num_new)? {
output.push(StandardCommandPoolBuilder {
inner: StandardCommandPoolAlloc {
cmd: Some(cmd),
pool: per_thread.clone(),
pool_parent: self.clone(),
secondary: secondary,
device: self.device.clone(),
},
dummy_avoid_send_sync: PhantomData,
});
}
}
Ok(output.into_iter())
}
#[inline]
fn queue_family(&self) -> QueueFamily {
self.device
.physical_device()
.queue_family_by_id(self.queue_family)
.unwrap()
}
}
unsafe impl DeviceOwned for StandardCommandPool {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
pub struct StandardCommandPoolBuilder {
inner: StandardCommandPoolAlloc,
dummy_avoid_send_sync: PhantomData<*const u8>,
}
unsafe impl CommandPoolBuilderAlloc for StandardCommandPoolBuilder {
type Alloc = StandardCommandPoolAlloc;
#[inline]
fn inner(&self) -> &UnsafeCommandPoolAlloc {
self.inner.inner()
}
#[inline]
fn into_alloc(self) -> Self::Alloc {
self.inner
}
#[inline]
fn queue_family(&self) -> QueueFamily {
self.inner.queue_family()
}
}
unsafe impl DeviceOwned for StandardCommandPoolBuilder {
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
pub struct StandardCommandPoolAlloc {
cmd: Option<UnsafeCommandPoolAlloc>,
pool: Arc<StandardCommandPoolPerThread>,
pool_parent: Arc<StandardCommandPool>,
secondary: bool,
device: Arc<Device>,
}
unsafe impl Send for StandardCommandPoolAlloc {
}
unsafe impl Sync for StandardCommandPoolAlloc {
}
unsafe impl CommandPoolAlloc for StandardCommandPoolAlloc {
#[inline]
fn inner(&self) -> &UnsafeCommandPoolAlloc {
self.cmd.as_ref().unwrap()
}
#[inline]
fn queue_family(&self) -> QueueFamily {
let queue_family_id = self.pool.pool.lock().unwrap().queue_family().id();
self.device
.physical_device()
.queue_family_by_id(queue_family_id)
.unwrap()
}
}
unsafe impl DeviceOwned for StandardCommandPoolAlloc {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl Drop for StandardCommandPoolAlloc {
fn drop(&mut self) {
let cmd = self.cmd.take().unwrap();
if self.secondary {
self.pool.available_secondary_command_buffers.push(cmd);
} else {
self.pool.available_primary_command_buffers.push(cmd);
}
}
}
#[cfg(test)]
mod tests {
use command_buffer::pool::CommandPool;
use command_buffer::pool::CommandPoolBuilderAlloc;
use command_buffer::pool::StandardCommandPool;
use device::Device;
use std::sync::Arc;
use VulkanObject;
#[test]
fn reuse_command_buffers() {
let (device, _) = gfx_dev_and_queue!();
let queue_family = device.physical_device().queue_families().next().unwrap();
let pool = Device::standard_command_pool(&device, queue_family);
let cb = pool.alloc(false, 1).unwrap().next().unwrap();
let raw = cb.inner().internal_object();
drop(cb);
let cb2 = pool.alloc(false, 1).unwrap().next().unwrap();
assert_eq!(raw, cb2.inner().internal_object());
}
#[test]
fn pool_kept_alive_by_allocs() {
let (device, queue) = gfx_dev_and_queue!();
let pool = Arc::new(StandardCommandPool::new(device, queue.family()));
let pool_weak = Arc::downgrade(&pool);
let cb = pool.alloc(false, 1).unwrap().next().unwrap();
drop(pool);
assert!(pool_weak.upgrade().is_some());
drop(cb);
assert!(pool_weak.upgrade().is_none());
}
}