use crate::{
descriptor_set::{
layout::{DescriptorSetLayout, DescriptorSetLayoutCreateFlags, DescriptorType},
sys::RawDescriptorSet,
},
device::{Device, DeviceOwned, DeviceOwnedDebugWrapper},
instance::InstanceOwnedDebugWrapper,
macros::{impl_id_counter, vulkan_bitflags},
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
VulkanObject,
};
use foldhash::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)?;
Ok(unsafe { 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 create_info_fields1_vk = create_info.to_vk_fields1();
let mut create_info_extensions_vk = create_info.to_vk_extensions();
let create_info_vk =
create_info.to_vk(&create_info_fields1_vk, &mut create_info_extensions_vk);
let handle = {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
unsafe {
(fns.v1_0.create_descriptor_pool)(
device.handle(),
&create_info_vk,
ptr::null(),
output.as_mut_ptr(),
)
}
.result()
.map_err(VulkanError::from)?;
unsafe { output.assume_init() }
};
Ok(unsafe { 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(unsafe { 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: SmallVec<[_; 1]> = SmallVec::with_capacity(lower_size_bound);
let mut variable_descriptor_counts: SmallVec<[_; 1]> =
SmallVec::with_capacity(lower_size_bound);
let mut layouts: SmallVec<[_; 1]> = SmallVec::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: SmallVec<[_; 1]> = SmallVec::new();
if !layouts_vk.is_empty() {
let mut variable_desc_count_alloc_info = None;
let mut info_vk = ash::vk::DescriptorSetAllocateInfo::default()
.descriptor_pool(self.handle)
.set_layouts(&layouts_vk);
if (self.device.api_version() >= Version::V1_2
|| self.device.enabled_extensions().ext_descriptor_indexing)
&& variable_descriptor_counts.iter().any(|c| *c != 0)
{
let next = variable_desc_count_alloc_info.insert(
ash::vk::DescriptorSetVariableDescriptorCountAllocateInfo::default()
.descriptor_counts(&variable_descriptor_counts),
);
info_vk = info_vk.push_next(next);
}
output.reserve(layouts_vk.len());
let fns = self.device.fns();
unsafe {
(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,
})?;
unsafe { 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 = RawDescriptorSet>,
) -> Result<(), Validated<VulkanError>> {
self.validate_free_descriptor_sets()?;
Ok(unsafe { 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 = RawDescriptorSet>,
) -> Result<(), VulkanError> {
let sets: SmallVec<[_; 8]> = descriptor_sets.into_iter().map(|s| s.handle()).collect();
if !sets.is_empty() {
let fns = self.device.fns();
unsafe {
(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();
unsafe {
(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) {
let fns = self.device.fns();
unsafe {
(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(())
}
pub(crate) fn to_vk<'a>(
&self,
fields1_vk: &'a DescriptorPoolCreateInfoFields1Vk,
extensions_vk: &'a mut DescriptorPoolCreateInfoExtensionsVk,
) -> ash::vk::DescriptorPoolCreateInfo<'a> {
let &Self {
flags,
max_sets,
pool_sizes: _,
max_inline_uniform_block_bindings: _,
_ne: _,
} = self;
let DescriptorPoolCreateInfoFields1Vk { pool_sizes_vk } = fields1_vk;
let mut val_vk = ash::vk::DescriptorPoolCreateInfo::default()
.flags(flags.into())
.max_sets(max_sets)
.pool_sizes(pool_sizes_vk);
let DescriptorPoolCreateInfoExtensionsVk {
inline_uniform_block_vk,
} = extensions_vk;
if let Some(next) = inline_uniform_block_vk {
val_vk = val_vk.push_next(next);
}
val_vk
}
pub(crate) fn to_vk_fields1(&self) -> DescriptorPoolCreateInfoFields1Vk {
let pool_sizes_vk = self
.pool_sizes
.iter()
.map(|(&ty, &descriptor_count)| ash::vk::DescriptorPoolSize {
ty: ty.into(),
descriptor_count,
})
.collect();
DescriptorPoolCreateInfoFields1Vk { pool_sizes_vk }
}
pub(crate) fn to_vk_extensions(&self) -> DescriptorPoolCreateInfoExtensionsVk {
let inline_uniform_block_vk = (self.max_inline_uniform_block_bindings != 0).then(|| {
ash::vk::DescriptorPoolInlineUniformBlockCreateInfo::default()
.max_inline_uniform_block_bindings(self.max_inline_uniform_block_bindings)
});
DescriptorPoolCreateInfoExtensionsVk {
inline_uniform_block_vk,
}
}
}
pub(crate) struct DescriptorPoolCreateInfoExtensionsVk {
pub(crate) inline_uniform_block_vk:
Option<ash::vk::DescriptorPoolInlineUniformBlockCreateInfo<'static>>,
}
pub(crate) struct DescriptorPoolCreateInfoFields1Vk {
pub(crate) pool_sizes_vk: SmallVec<[ash::vk::DescriptorPoolSize; 8]>,
}
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();
let sets =
unsafe { 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();
let _ = unsafe {
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();
let sets = unsafe { pool.allocate_descriptor_sets([]) }.unwrap();
assert_eq!(sets.count(), 0);
}
}