use crate::{
descriptor_set::{
layout::{DescriptorSetLayout, DescriptorSetLayoutCreateFlags, DescriptorType},
sys::UnsafeDescriptorSet,
},
device::{Device, DeviceOwned, DeviceOwnedDebugWrapper},
instance::InstanceOwnedDebugWrapper,
macros::{impl_id_counter, vulkan_bitflags},
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
VulkanObject,
};
use ahash::HashMap;
use smallvec::SmallVec;
use std::{cell::Cell, marker::PhantomData, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc};
#[derive(Debug)]
pub struct DescriptorPool {
handle: ash::vk::DescriptorPool,
device: InstanceOwnedDebugWrapper<Arc<Device>>,
id: NonZeroU64,
flags: DescriptorPoolCreateFlags,
max_sets: u32,
pool_sizes: HashMap<DescriptorType, u32>,
max_inline_uniform_block_bindings: u32,
_marker: PhantomData<Cell<ash::vk::DescriptorPool>>,
}
impl DescriptorPool {
#[inline]
pub fn new(
device: Arc<Device>,
create_info: DescriptorPoolCreateInfo,
) -> Result<DescriptorPool, Validated<VulkanError>> {
Self::validate_new(&device, &create_info)?;
unsafe { Ok(Self::new_unchecked(device, create_info)?) }
}
fn validate_new(
device: &Device,
create_info: &DescriptorPoolCreateInfo,
) -> Result<(), Box<ValidationError>> {
create_info
.validate(device)
.map_err(|err| err.add_context("create_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked(
device: Arc<Device>,
create_info: DescriptorPoolCreateInfo,
) -> Result<DescriptorPool, VulkanError> {
let &DescriptorPoolCreateInfo {
flags,
max_sets,
ref pool_sizes,
max_inline_uniform_block_bindings,
_ne: _,
} = &create_info;
let pool_sizes_vk: SmallVec<[_; 8]> = pool_sizes
.iter()
.map(|(&ty, &descriptor_count)| ash::vk::DescriptorPoolSize {
ty: ty.into(),
descriptor_count,
})
.collect();
let mut create_info_vk = ash::vk::DescriptorPoolCreateInfo {
flags: flags.into(),
max_sets,
pool_size_count: pool_sizes_vk.len() as u32,
p_pool_sizes: pool_sizes_vk.as_ptr(),
..Default::default()
};
let mut inline_uniform_block_create_info_vk = None;
if max_inline_uniform_block_bindings != 0 {
let next = inline_uniform_block_create_info_vk.insert(
ash::vk::DescriptorPoolInlineUniformBlockCreateInfo {
max_inline_uniform_block_bindings,
..Default::default()
},
);
next.p_next = create_info_vk.p_next;
create_info_vk.p_next = next as *const _ as *const _;
}
let handle = unsafe {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
(fns.v1_0.create_descriptor_pool)(
device.handle(),
&create_info_vk,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
unsafe { Ok(Self::from_handle(device, handle, create_info)) }
}
#[inline]
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::DescriptorPool,
create_info: DescriptorPoolCreateInfo,
) -> DescriptorPool {
let DescriptorPoolCreateInfo {
flags,
max_sets,
pool_sizes,
max_inline_uniform_block_bindings,
_ne: _,
} = create_info;
DescriptorPool {
handle,
device: InstanceOwnedDebugWrapper(device),
id: Self::next_id(),
flags,
max_sets,
pool_sizes,
max_inline_uniform_block_bindings,
_marker: PhantomData,
}
}
#[inline]
pub fn flags(&self) -> DescriptorPoolCreateFlags {
self.flags
}
#[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 max_inline_uniform_block_bindings(&self) -> u32 {
self.max_inline_uniform_block_bindings
}
#[inline]
pub unsafe fn allocate_descriptor_sets(
&self,
allocate_infos: impl IntoIterator<Item = DescriptorSetAllocateInfo>,
) -> Result<impl ExactSizeIterator<Item = DescriptorPoolAlloc>, Validated<VulkanError>> {
let allocate_infos: SmallVec<[_; 1]> = allocate_infos.into_iter().collect();
self.validate_allocate_descriptor_sets(&allocate_infos)?;
Ok(self.allocate_descriptor_sets_unchecked(allocate_infos)?)
}
fn validate_allocate_descriptor_sets(
&self,
allocate_infos: &[DescriptorSetAllocateInfo],
) -> Result<(), Box<ValidationError>> {
for (index, info) in allocate_infos.iter().enumerate() {
info.validate(self.device())
.map_err(|err| err.add_context(format!("allocate_infos[{}]", index)))?;
let &DescriptorSetAllocateInfo {
ref layout,
variable_descriptor_count: _,
_ne: _,
} = info;
if layout
.flags()
.intersects(DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL)
&& !self
.flags
.intersects(DescriptorPoolCreateFlags::UPDATE_AFTER_BIND)
{
return Err(Box::new(ValidationError {
problem: format!(
"`allocate_infos[{}].layout.flags()` contains \
`DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL`, but \
`self.flags` does not contain \
`DescriptorPoolCreateFlags::UPDATE_AFTER_BIND`",
index
)
.into(),
vuids: &["VUID-VkDescriptorSetAllocateInfo-pSetLayouts-03044"],
..Default::default()
}));
}
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn allocate_descriptor_sets_unchecked(
&self,
allocate_infos: impl IntoIterator<Item = DescriptorSetAllocateInfo>,
) -> Result<impl ExactSizeIterator<Item = DescriptorPoolAlloc>, VulkanError> {
let allocate_infos = allocate_infos.into_iter();
let (lower_size_bound, _) = allocate_infos.size_hint();
let mut layouts_vk = Vec::with_capacity(lower_size_bound);
let mut variable_descriptor_counts = Vec::with_capacity(lower_size_bound);
let mut layouts = Vec::with_capacity(lower_size_bound);
for info in allocate_infos {
let DescriptorSetAllocateInfo {
layout,
variable_descriptor_count,
_ne: _,
} = info;
layouts_vk.push(layout.handle());
variable_descriptor_counts.push(variable_descriptor_count);
layouts.push(layout);
}
let mut output = vec![];
if !layouts_vk.is_empty() {
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_vk.len() as u32,
p_descriptor_counts: variable_descriptor_counts.as_ptr(),
..Default::default()
})
} else {
None
};
let info_vk = ash::vk::DescriptorSetAllocateInfo {
descriptor_pool: self.handle,
descriptor_set_count: layouts_vk.len() as u32,
p_set_layouts: layouts_vk.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()
};
output.reserve(layouts_vk.len());
let fns = self.device.fns();
(fns.v1_0.allocate_descriptor_sets)(
self.device.handle(),
&info_vk,
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)
.map_err(|err| match err {
VulkanError::OutOfHostMemory
| VulkanError::OutOfDeviceMemory
| VulkanError::OutOfPoolMemory => err,
_ => VulkanError::FragmentedPool,
})?;
output.set_len(layouts_vk.len());
}
Ok(output
.into_iter()
.zip(layouts)
.zip(variable_descriptor_counts)
.map(
move |((handle, layout), variable_descriptor_count)| DescriptorPoolAlloc {
handle,
id: DescriptorPoolAlloc::next_id(),
layout: DeviceOwnedDebugWrapper(layout),
variable_descriptor_count,
},
))
}
#[inline]
pub unsafe fn free_descriptor_sets(
&self,
descriptor_sets: impl IntoIterator<Item = UnsafeDescriptorSet>,
) -> Result<(), Validated<VulkanError>> {
self.validate_free_descriptor_sets()?;
Ok(self.free_descriptor_sets_unchecked(descriptor_sets)?)
}
fn validate_free_descriptor_sets(&self) -> Result<(), Box<ValidationError>> {
if !self
.flags
.intersects(DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET)
{
return Err(Box::new(ValidationError {
context: "self.flags()".into(),
problem: "does not contain `DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET`".into(),
vuids: &["VUID-vkFreeDescriptorSets-descriptorPool-00312"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn free_descriptor_sets_unchecked(
&self,
descriptor_sets: impl IntoIterator<Item = UnsafeDescriptorSet>,
) -> Result<(), VulkanError> {
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<(), VulkanError> {
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
}
}
impl_id_counter!(DescriptorPool);
#[derive(Clone, Debug)]
pub struct DescriptorPoolCreateInfo {
pub flags: DescriptorPoolCreateFlags,
pub max_sets: u32,
pub pool_sizes: HashMap<DescriptorType, u32>,
pub max_inline_uniform_block_bindings: u32,
pub _ne: crate::NonExhaustive,
}
impl Default for DescriptorPoolCreateInfo {
#[inline]
fn default() -> Self {
Self {
flags: DescriptorPoolCreateFlags::empty(),
max_sets: 0,
pool_sizes: HashMap::default(),
max_inline_uniform_block_bindings: 0,
_ne: crate::NonExhaustive(()),
}
}
}
impl DescriptorPoolCreateInfo {
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
flags,
max_sets,
ref pool_sizes,
max_inline_uniform_block_bindings,
_ne: _,
} = self;
flags.validate_device(device).map_err(|err| {
err.add_context("flags")
.set_vuids(&["VUID-VkDescriptorPoolCreateInfo-flags-parameter"])
})?;
if max_sets == 0 {
return Err(Box::new(ValidationError {
context: "max_sets".into(),
problem: "is zero".into(),
vuids: &["VUID-VkDescriptorPoolCreateInfo-maxSets-00301"],
..Default::default()
}));
}
if pool_sizes.is_empty() {
return Err(Box::new(ValidationError {
context: "pool_sizes".into(),
problem: "is empty".into(),
..Default::default()
}));
}
for (&descriptor_type, &pool_size) in pool_sizes.iter() {
flags.validate_device(device).map_err(|err| {
err.add_context("pool_sizes")
.set_vuids(&["VUID-VkDescriptorPoolSize-type-parameter"])
})?;
if pool_size == 0 {
return Err(Box::new(ValidationError {
context: format!("pool_sizes[DescriptorType::{:?}]", descriptor_type).into(),
problem: "is zero".into(),
vuids: &["VUID-VkDescriptorPoolSize-descriptorCount-00302"],
..Default::default()
}));
}
if descriptor_type == DescriptorType::InlineUniformBlock {
return Err(Box::new(ValidationError {
context: "pool_sizes[DescriptorType::InlineUniformBlock]".into(),
problem: "is not a multiple of 4".into(),
vuids: &["VUID-VkDescriptorPoolSize-type-02218"],
..Default::default()
}));
}
}
if max_inline_uniform_block_bindings != 0
&& !(device.api_version() >= Version::V1_3
|| device.enabled_extensions().ext_inline_uniform_block)
{
return Err(Box::new(ValidationError {
context: "max_inline_uniform_block_bindings".into(),
problem: "is not zero".into(),
requires_one_of: RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]),
RequiresAllOf(&[Requires::DeviceExtension("ext_inline_uniform_block")]),
]),
..Default::default()
}));
}
Ok(())
}
}
vulkan_bitflags! {
#[non_exhaustive]
DescriptorPoolCreateFlags = DescriptorPoolCreateFlags(u32);
FREE_DESCRIPTOR_SET = FREE_DESCRIPTOR_SET,
UPDATE_AFTER_BIND = UPDATE_AFTER_BIND
RequiresOneOf([
RequiresAllOf([APIVersion(V1_2)]),
RequiresAllOf([DeviceExtension(ext_descriptor_indexing)]),
]),
}
#[derive(Clone, Debug)]
pub struct DescriptorSetAllocateInfo {
pub layout: Arc<DescriptorSetLayout>,
pub variable_descriptor_count: u32,
pub _ne: crate::NonExhaustive,
}
impl DescriptorSetAllocateInfo {
#[inline]
pub fn new(layout: Arc<DescriptorSetLayout>) -> Self {
Self {
layout,
variable_descriptor_count: 0,
_ne: crate::NonExhaustive(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref layout,
variable_descriptor_count,
_ne,
} = self;
assert_eq!(device, layout.device().as_ref());
if layout
.flags()
.intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR)
{
return Err(Box::new(ValidationError {
context: "layout.flags()".into(),
problem: "contains `DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR`".into(),
vuids: &["VUID-VkDescriptorSetAllocateInfo-pSetLayouts-00308"],
..Default::default()
}));
}
if variable_descriptor_count > layout.variable_descriptor_count() {
return Err(Box::new(ValidationError {
problem: "`variable_descriptor_count` is greater than
`layout.variable_descriptor_count()`"
.into(),
..Default::default()
}));
}
Ok(())
}
}
#[derive(Debug)]
pub struct DescriptorPoolAlloc {
handle: ash::vk::DescriptorSet,
id: NonZeroU64,
layout: DeviceOwnedDebugWrapper<Arc<DescriptorSetLayout>>,
variable_descriptor_count: u32,
}
impl DescriptorPoolAlloc {
#[inline]
pub fn layout(&self) -> &Arc<DescriptorSetLayout> {
&self.layout
}
#[inline]
pub fn variable_descriptor_count(&self) -> u32 {
self.variable_descriptor_count
}
}
unsafe impl VulkanObject for DescriptorPoolAlloc {
type Handle = ash::vk::DescriptorSet;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for DescriptorPoolAlloc {
#[inline]
fn device(&self) -> &Arc<Device> {
self.layout.device()
}
}
impl_id_counter!(DescriptorPoolAlloc);
#[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!();
DescriptorPool::new(
device,
DescriptorPoolCreateInfo {
max_sets: 0,
pool_sizes: [(DescriptorType::UniformBuffer, 1)].into_iter().collect(),
..Default::default()
},
)
.unwrap_err();
}
#[test]
fn zero_descriptors() {
let (device, _) = gfx_dev_and_queue!();
DescriptorPool::new(
device,
DescriptorPoolCreateInfo {
max_sets: 10,
..Default::default()
},
)
.unwrap_err();
}
#[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::new(set_layout)])
.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::new(set_layout)]);
}
});
}
#[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);
}
}
}