use crate::{
descriptor_set::{
layout::{DescriptorSetLayout, DescriptorType},
sys::UnsafeDescriptorSet,
},
device::{Device, DeviceOwned},
OomError, Version, VulkanError, VulkanObject,
};
use ahash::HashMap;
use smallvec::SmallVec;
use std::{
cell::Cell,
error::Error,
fmt::{Display, Error as FmtError, Formatter},
marker::PhantomData,
mem::MaybeUninit,
num::NonZeroU64,
ptr,
sync::Arc,
};
#[derive(Debug)]
pub struct DescriptorPool {
handle: ash::vk::DescriptorPool,
device: Arc<Device>,
id: NonZeroU64,
max_sets: u32,
pool_sizes: HashMap<DescriptorType, u32>,
can_free_descriptor_sets: bool,
_marker: PhantomData<Cell<ash::vk::DescriptorPool>>,
}
impl DescriptorPool {
pub fn new(
device: Arc<Device>,
create_info: DescriptorPoolCreateInfo,
) -> Result<DescriptorPool, OomError> {
let DescriptorPoolCreateInfo {
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();
(fns.v1_0.create_descriptor_pool)(
device.handle(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
}
};
Ok(DescriptorPool {
handle,
device,
id: Self::next_id(),
max_sets,
pool_sizes,
can_free_descriptor_sets,
_marker: PhantomData,
})
}
#[inline]
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::DescriptorPool,
create_info: DescriptorPoolCreateInfo,
) -> DescriptorPool {
let DescriptorPoolCreateInfo {
max_sets,
pool_sizes,
can_free_descriptor_sets,
_ne: _,
} = create_info;
DescriptorPool {
handle,
device,
id: Self::next_id(),
max_sets,
pool_sizes,
can_free_descriptor_sets,
_marker: PhantomData,
}
}
#[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>(
&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.handle(), info.layout.device().handle(),);
debug_assert!(!info.layout.push_descriptor());
debug_assert!(
info.variable_descriptor_count <= info.layout.variable_descriptor_count()
);
(info.layout.handle(), info.variable_descriptor_count)
})
.unzip();
let output = if layouts.is_empty() {
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.handle(),
&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(UnsafeDescriptorSet::new))
}
pub unsafe fn free_descriptor_sets(
&self,
descriptor_sets: impl IntoIterator<Item = UnsafeDescriptorSet>,
) -> Result<(), OomError> {
let sets: SmallVec<[_; 8]> = descriptor_sets.into_iter().map(|s| s.handle()).collect();
if !sets.is_empty() {
let fns = self.device.fns();
(fns.v1_0.free_descriptor_sets)(
self.device.handle(),
self.handle,
sets.len() as u32,
sets.as_ptr(),
)
.result()
.map_err(VulkanError::from)?;
}
Ok(())
}
#[inline]
pub unsafe fn reset(&self) -> Result<(), OomError> {
let fns = self.device.fns();
(fns.v1_0.reset_descriptor_pool)(
self.device.handle(),
self.handle,
ash::vk::DescriptorPoolResetFlags::empty(),
)
.result()
.map_err(VulkanError::from)?;
Ok(())
}
}
impl Drop for DescriptorPool {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
(fns.v1_0.destroy_descriptor_pool)(self.device.handle(), self.handle, ptr::null());
}
}
}
unsafe impl VulkanObject for DescriptorPool {
type Handle = ash::vk::DescriptorPool;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for DescriptorPool {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
crate::impl_id_counter!(DescriptorPool);
#[derive(Clone, Debug)]
pub struct DescriptorPoolCreateInfo {
pub max_sets: u32,
pub pool_sizes: HashMap<DescriptorType, u32>,
pub can_free_descriptor_sets: bool,
pub _ne: crate::NonExhaustive,
}
impl Default for DescriptorPoolCreateInfo {
#[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 for DescriptorPoolAllocError {}
impl Display for DescriptorPoolAllocError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(
f,
"{}",
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::{DescriptorPool, DescriptorPoolCreateInfo};
use crate::{
descriptor_set::{
layout::{
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo,
DescriptorType,
},
pool::DescriptorSetAllocateInfo,
},
shader::ShaderStages,
};
#[test]
fn pool_create() {
let (device, _) = gfx_dev_and_queue!();
let _ = DescriptorPool::new(
device,
DescriptorPoolCreateInfo {
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 _ = DescriptorPool::new(
device,
DescriptorPoolCreateInfo {
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 _ = DescriptorPool::new(
device,
DescriptorPoolCreateInfo {
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 pool = DescriptorPool::new(
device,
DescriptorPoolCreateInfo {
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 pool = DescriptorPool::new(
device2,
DescriptorPoolCreateInfo {
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 pool = DescriptorPool::new(
device,
DescriptorPoolCreateInfo {
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);
}
}
}