use crate::{
check_errors,
descriptor_set::{
layout::{DescriptorSetLayout, DescriptorType},
sys::UnsafeDescriptorSet,
},
device::{Device, DeviceOwned},
OomError, Version, VulkanObject,
};
use smallvec::SmallVec;
use std::{
collections::HashMap,
error, fmt,
hash::{Hash, Hasher},
mem::MaybeUninit,
ptr,
sync::Arc,
};
#[derive(Debug)]
pub struct UnsafeDescriptorPool {
handle: ash::vk::DescriptorPool,
device: Arc<Device>,
max_sets: u32,
pool_sizes: HashMap<DescriptorType, u32>,
can_free_descriptor_sets: bool,
}
impl UnsafeDescriptorPool {
pub fn new(
device: Arc<Device>,
create_info: UnsafeDescriptorPoolCreateInfo,
) -> Result<UnsafeDescriptorPool, OomError> {
let UnsafeDescriptorPoolCreateInfo {
max_sets,
pool_sizes,
can_free_descriptor_sets,
_ne: _,
} = create_info;
assert!(max_sets != 0);
assert!(!pool_sizes.is_empty());
let handle = {
let pool_sizes: SmallVec<[_; 8]> = pool_sizes
.iter()
.map(|(&ty, &descriptor_count)| {
assert!(descriptor_count != 0);
ash::vk::DescriptorPoolSize {
ty: ty.into(),
descriptor_count,
}
})
.collect();
let mut flags = ash::vk::DescriptorPoolCreateFlags::empty();
if can_free_descriptor_sets {
flags |= ash::vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET;
}
let create_info = ash::vk::DescriptorPoolCreateInfo {
flags,
max_sets,
pool_size_count: pool_sizes.len() as u32,
p_pool_sizes: pool_sizes.as_ptr(),
..Default::default()
};
unsafe {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
check_errors(fns.v1_0.create_descriptor_pool(
device.internal_object(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
))?;
output.assume_init()
}
};
Ok(UnsafeDescriptorPool {
handle,
device,
max_sets,
pool_sizes,
can_free_descriptor_sets,
})
}
#[inline]
pub fn max_sets(&self) -> u32 {
self.max_sets
}
#[inline]
pub fn pool_sizes(&self) -> &HashMap<DescriptorType, u32> {
&self.pool_sizes
}
#[inline]
pub fn can_free_descriptor_sets(&self) -> bool {
self.can_free_descriptor_sets
}
pub unsafe fn allocate_descriptor_sets<'a>(
&mut self,
allocate_info: impl IntoIterator<Item = DescriptorSetAllocateInfo<'a>>,
) -> Result<impl ExactSizeIterator<Item = UnsafeDescriptorSet>, DescriptorPoolAllocError> {
let (layouts, variable_descriptor_counts): (SmallVec<[_; 1]>, SmallVec<[_; 1]>) =
allocate_info
.into_iter()
.map(|info| {
assert_eq!(
self.device.internal_object(),
info.layout.device().internal_object(),
);
debug_assert!(!info.layout.push_descriptor());
debug_assert!(
info.variable_descriptor_count <= info.layout.variable_descriptor_count()
);
(
info.layout.internal_object(),
info.variable_descriptor_count,
)
})
.unzip();
let output = if layouts.len() == 0 {
vec![]
} else {
let variable_desc_count_alloc_info = if (self.device.api_version() >= Version::V1_2
|| self.device.enabled_extensions().ext_descriptor_indexing)
&& variable_descriptor_counts.iter().any(|c| *c != 0)
{
Some(ash::vk::DescriptorSetVariableDescriptorCountAllocateInfo {
descriptor_set_count: layouts.len() as u32,
p_descriptor_counts: variable_descriptor_counts.as_ptr(),
..Default::default()
})
} else {
None
};
let infos = ash::vk::DescriptorSetAllocateInfo {
descriptor_pool: self.handle,
descriptor_set_count: layouts.len() as u32,
p_set_layouts: layouts.as_ptr(),
p_next: if let Some(next) = variable_desc_count_alloc_info.as_ref() {
next as *const _ as *const _
} else {
ptr::null()
},
..Default::default()
};
let mut output = Vec::with_capacity(layouts.len());
let fns = self.device.fns();
let ret = fns.v1_0.allocate_descriptor_sets(
self.device.internal_object(),
&infos,
output.as_mut_ptr(),
);
match ret {
ash::vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
return Err(DescriptorPoolAllocError::OutOfHostMemory);
}
ash::vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => {
return Err(DescriptorPoolAllocError::OutOfDeviceMemory);
}
ash::vk::Result::ERROR_OUT_OF_POOL_MEMORY_KHR => {
return Err(DescriptorPoolAllocError::OutOfPoolMemory);
}
c if c.as_raw() < 0 => {
return Err(DescriptorPoolAllocError::FragmentedPool);
}
_ => (),
};
output.set_len(layouts.len());
output
};
Ok(output
.into_iter()
.map(|handle| UnsafeDescriptorSet::new(handle)))
}
pub unsafe fn free_descriptor_sets<I>(&mut self, descriptor_sets: I) -> Result<(), OomError>
where
I: IntoIterator<Item = UnsafeDescriptorSet>,
{
let sets: SmallVec<[_; 8]> = descriptor_sets
.into_iter()
.map(|s| s.internal_object())
.collect();
if !sets.is_empty() {
let fns = self.device.fns();
check_errors(fns.v1_0.free_descriptor_sets(
self.device.internal_object(),
self.handle,
sets.len() as u32,
sets.as_ptr(),
))?;
}
Ok(())
}
pub unsafe fn reset(&mut self) -> Result<(), OomError> {
let fns = self.device.fns();
check_errors(fns.v1_0.reset_descriptor_pool(
self.device.internal_object(),
self.handle,
ash::vk::DescriptorPoolResetFlags::empty(),
))?;
Ok(())
}
}
impl Drop for UnsafeDescriptorPool {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
fns.v1_0.destroy_descriptor_pool(
self.device.internal_object(),
self.handle,
ptr::null(),
);
}
}
}
unsafe impl VulkanObject for UnsafeDescriptorPool {
type Object = ash::vk::DescriptorPool;
#[inline]
fn internal_object(&self) -> Self::Object {
self.handle
}
}
unsafe impl DeviceOwned for UnsafeDescriptorPool {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl PartialEq for UnsafeDescriptorPool {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.device() == other.device()
}
}
impl Eq for UnsafeDescriptorPool {}
impl Hash for UnsafeDescriptorPool {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.device().hash(state);
}
}
#[derive(Clone, Debug)]
pub struct UnsafeDescriptorPoolCreateInfo {
pub max_sets: u32,
pub pool_sizes: HashMap<DescriptorType, u32>,
pub can_free_descriptor_sets: bool,
pub _ne: crate::NonExhaustive,
}
impl Default for UnsafeDescriptorPoolCreateInfo {
#[inline]
fn default() -> Self {
Self {
max_sets: 0,
pool_sizes: HashMap::default(),
can_free_descriptor_sets: false,
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug)]
pub struct DescriptorSetAllocateInfo<'a> {
pub layout: &'a DescriptorSetLayout,
pub variable_descriptor_count: u32,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DescriptorPoolAllocError {
OutOfHostMemory,
OutOfDeviceMemory,
FragmentedPool,
OutOfPoolMemory,
}
impl error::Error for DescriptorPoolAllocError {}
impl fmt::Display for DescriptorPoolAllocError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
DescriptorPoolAllocError::OutOfHostMemory => "no memory available on the host",
DescriptorPoolAllocError::OutOfDeviceMemory => {
"no memory available on the graphical device"
}
DescriptorPoolAllocError::FragmentedPool => {
"allocation has failed because the pool is too fragmented"
}
DescriptorPoolAllocError::OutOfPoolMemory => {
"there is no more space available in the descriptor pool"
}
}
)
}
}
#[cfg(test)]
mod tests {
use super::{UnsafeDescriptorPool, UnsafeDescriptorPoolCreateInfo};
use crate::{
descriptor_set::{
layout::{
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo,
DescriptorType,
},
pool::DescriptorSetAllocateInfo,
},
shader::ShaderStages,
};
#[test]
fn pool_create() {
let (device, _) = gfx_dev_and_queue!();
let _ = UnsafeDescriptorPool::new(
device,
UnsafeDescriptorPoolCreateInfo {
max_sets: 10,
pool_sizes: [(DescriptorType::UniformBuffer, 1)].into_iter().collect(),
..Default::default()
},
)
.unwrap();
}
#[test]
fn zero_max_set() {
let (device, _) = gfx_dev_and_queue!();
assert_should_panic!({
let _ = UnsafeDescriptorPool::new(
device,
UnsafeDescriptorPoolCreateInfo {
max_sets: 0,
pool_sizes: [(DescriptorType::UniformBuffer, 1)].into_iter().collect(),
..Default::default()
},
);
});
}
#[test]
fn zero_descriptors() {
let (device, _) = gfx_dev_and_queue!();
assert_should_panic!({
let _ = UnsafeDescriptorPool::new(
device,
UnsafeDescriptorPoolCreateInfo {
max_sets: 10,
..Default::default()
},
);
});
}
#[test]
fn basic_alloc() {
let (device, _) = gfx_dev_and_queue!();
let set_layout = DescriptorSetLayout::new(
device.clone(),
DescriptorSetLayoutCreateInfo {
bindings: [(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::all_graphics(),
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
},
)]
.into(),
..Default::default()
},
)
.unwrap();
let mut pool = UnsafeDescriptorPool::new(
device,
UnsafeDescriptorPoolCreateInfo {
max_sets: 10,
pool_sizes: [(DescriptorType::UniformBuffer, 10)].into_iter().collect(),
..Default::default()
},
)
.unwrap();
unsafe {
let sets = pool
.allocate_descriptor_sets([DescriptorSetAllocateInfo {
layout: set_layout.as_ref(),
variable_descriptor_count: 0,
}])
.unwrap();
assert_eq!(sets.count(), 1);
}
}
#[test]
fn alloc_diff_device() {
let (device1, _) = gfx_dev_and_queue!();
let (device2, _) = gfx_dev_and_queue!();
let set_layout = DescriptorSetLayout::new(
device1,
DescriptorSetLayoutCreateInfo {
bindings: [(
0,
DescriptorSetLayoutBinding {
stages: ShaderStages::all_graphics(),
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
},
)]
.into(),
..Default::default()
},
)
.unwrap();
assert_should_panic!({
let mut pool = UnsafeDescriptorPool::new(
device2,
UnsafeDescriptorPoolCreateInfo {
max_sets: 10,
pool_sizes: [(DescriptorType::UniformBuffer, 10)].into_iter().collect(),
..Default::default()
},
)
.unwrap();
unsafe {
let _ = pool.allocate_descriptor_sets([DescriptorSetAllocateInfo {
layout: set_layout.as_ref(),
variable_descriptor_count: 0,
}]);
}
});
}
#[test]
fn alloc_zero() {
let (device, _) = gfx_dev_and_queue!();
let mut pool = UnsafeDescriptorPool::new(
device,
UnsafeDescriptorPoolCreateInfo {
max_sets: 1,
pool_sizes: [(DescriptorType::UniformBuffer, 1)].into_iter().collect(),
..Default::default()
},
)
.unwrap();
unsafe {
let sets = pool.allocate_descriptor_sets([]).unwrap();
assert_eq!(sets.count(), 0);
}
}
}