use std::cmp;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::Weak;
use fnv::FnvHasher;
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 device::Device;
use device::DeviceOwned;
use OomError;
use VulkanObject;
thread_local!(static THREAD_ID: Box<u8> = Box::new(0));
#[inline]
fn curr_thread_id() -> usize { THREAD_ID.with(|data| &**data as *const u8 as usize) }
pub struct StandardCommandPool {
device: Arc<Device>,
queue_family: u32,
per_thread: Mutex<HashMap<usize, Weak<Mutex<StandardCommandPoolPerThread>>,
BuildHasherDefault<FnvHasher>>>,
}
unsafe impl Send for StandardCommandPool {}
unsafe impl Sync for StandardCommandPool {}
struct StandardCommandPoolPerThread {
pool: UnsafeCommandPool,
available_primary_command_buffers: Vec<UnsafeCommandPoolAlloc>,
available_secondary_command_buffers: Vec<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 = Box<Iterator<Item = 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();
let curr_thread_id = curr_thread_id();
let per_thread = hashmap.get(&curr_thread_id).and_then(|p| p.upgrade());
let per_thread = match per_thread {
Some(pt) => pt,
None => {
let new_pool = try!(UnsafeCommandPool::new(self.device.clone(), self.queue_family(),
false, true));
let pt = Arc::new(Mutex::new(StandardCommandPoolPerThread {
pool: new_pool,
available_primary_command_buffers: Vec::new(),
available_secondary_command_buffers: Vec::new(),
}));
hashmap.insert(curr_thread_id, Arc::downgrade(&pt));
pt
},
};
let mut pt_lock = per_thread.lock().unwrap();
let (num_from_existing, from_existing) = {
let mut existing = if secondary { &mut pt_lock.available_secondary_command_buffers }
else { &mut pt_lock.available_primary_command_buffers };
let num_from_existing = cmp::min(count as usize, existing.len());
let from_existing = existing.drain(0 .. num_from_existing).collect::<Vec<_>>().into_iter();
(num_from_existing, from_existing)
};
let num_new = count as usize - num_from_existing;
debug_assert!(num_new <= count as usize); let newly_allocated = try!(pt_lock.pool.alloc_command_buffers(secondary, num_new));
let device = self.device.clone();
let queue_family_id = self.queue_family;
let per_thread = per_thread.clone();
let final_iter = from_existing.chain(newly_allocated).map(move |cmd| {
StandardCommandPoolBuilder {
cmd: Some(cmd),
pool: per_thread.clone(),
secondary: secondary,
device: device.clone(),
queue_family_id: queue_family_id,
dummy_avoid_send_sync: PhantomData,
}
}).collect::<Vec<_>>();
Ok(Box::new(final_iter.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 {
cmd: Option<UnsafeCommandPoolAlloc>,
pool: Arc<Mutex<StandardCommandPoolPerThread>>,
secondary: bool,
device: Arc<Device>,
queue_family_id: u32,
dummy_avoid_send_sync: PhantomData<*const u8>,
}
unsafe impl CommandPoolBuilderAlloc for StandardCommandPoolBuilder {
type Alloc = StandardCommandPoolAlloc;
#[inline]
fn inner(&self) -> &UnsafeCommandPoolAlloc {
self.cmd.as_ref().unwrap()
}
#[inline]
fn into_alloc(mut self) -> Self::Alloc {
StandardCommandPoolAlloc {
cmd: Some(self.cmd.take().unwrap()),
pool: self.pool.clone(),
secondary: self.secondary,
device: self.device.clone(),
queue_family_id: self.queue_family_id,
}
}
#[inline]
fn queue_family(&self) -> QueueFamily {
self.device.physical_device().queue_family_by_id(self.queue_family_id).unwrap()
}
}
unsafe impl DeviceOwned for StandardCommandPoolBuilder {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl Drop for StandardCommandPoolBuilder {
fn drop(&mut self) {
if let Some(cmd) = self.cmd.take() {
let mut pool = self.pool.lock().unwrap();
if self.secondary {
pool.available_secondary_command_buffers.push(cmd);
} else {
pool.available_primary_command_buffers.push(cmd);
}
}
}
}
pub struct StandardCommandPoolAlloc {
cmd: Option<UnsafeCommandPoolAlloc>,
pool: Arc<Mutex<StandardCommandPoolPerThread>>,
secondary: bool,
device: Arc<Device>,
queue_family_id: u32,
}
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 {
self.device.physical_device().queue_family_by_id(self.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 mut pool = self.pool.lock().unwrap();
if self.secondary {
pool.available_secondary_command_buffers.push(self.cmd.take().unwrap());
} else {
pool.available_primary_command_buffers.push(self.cmd.take().unwrap());
}
}
}