bitflags::bitflags! { /// Flags to augment descriptor set allocation. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pubstruct DescriptorSetLayoutCreateFlags: u32 { /// Specified that descriptor set must be allocated from\ /// pool with `DescriptorPoolCreateFlags::UPDATE_AFTER_BIND`. /// /// This flag must be specified when and only when layout was created with matching backend-specific flag, /// that allows layout to have UpdateAfterBind bindings. const UPDATE_AFTER_BIND = 0x2;
}
}
/// Descriptor set from allocator. #[derive(Debug)] pubstruct DescriptorSet<S> {
raw: S,
pool_id: u64,
size: DescriptorTotalCount,
update_after_bind: bool,
}
impl<S> DescriptorSet<S> { /// Returns reference to raw descriptor set. pubfn raw(&self) -> &S {
&self.raw
}
/// Returns mutable reference to raw descriptor set. /// /// # Safety /// /// Object must not be replaced. pubunsafefn raw_mut(&mutself) -> &mut S {
&mutself.raw
}
}
/// AllocationError that may occur during descriptor sets allocation. #[derive(Debug)] pubenum AllocationError { /// Backend reported that device memory has been exhausted.\ /// Deallocating device memory or other resources may increase chance /// that another allocation would succeed.
OutOfDeviceMemory,
/// Backend reported that host memory has been exhausted.\ /// Deallocating host memory may increase chance that another allocation would succeed.
OutOfHostMemory,
/// The total number of descriptors across all pools created\ /// with flag `CREATE_UPDATE_AFTER_BIND_BIT` set exceeds `max_update_after_bind_descriptors_in_all_pools` /// Or fragmentation of the underlying hardware resources occurs.
Fragmentation,
}
fn new_pool_size(&self, minimal_set_count: u32) -> (DescriptorTotalCount, u32) { letmut max_sets = MIN_SETS // at least MIN_SETS
.max(minimal_set_count) // at least enough for allocation
.max(self.total.min(MAX_SETS)) // at least as much as was allocated so far capped to MAX_SETS
.checked_next_power_of_two() // rounded up to nearest 2^N
.unwrap_or(i32::MAX as u32);
while count > 0 { let (pool_size, max_sets) = self.new_pool_size(count); #[cfg(feature = "tracing")]
tracing::trace!( "Create new pool with {} sets and {:?} descriptors",
max_sets,
pool_size,
);
let pool_id = self.pools.len() as u64 + self.offset;
let allocate = max_sets.min(count); let result = device.alloc_descriptor_sets(
&mut raw,
(0..allocate).map(|_| layout),
&mut Allocation {
pool_id,
size: self.size,
update_after_bind: self.update_after_bind,
sets: allocated_sets,
},
);
match result {
Ok(()) => {}
Err(err) => {
device.destroy_descriptor_pool(raw); match err {
DeviceAllocationError::OutOfDeviceMemory => { return Err(AllocationError::OutOfDeviceMemory)
}
DeviceAllocationError::OutOfHostMemory => { return Err(AllocationError::OutOfHostMemory)
}
DeviceAllocationError::FragmentedPool => { // Should not happen, but better this than panicing. #[cfg(feature = "trace")]
trace::error!("Unexpectedly failed to allocated descriptor sets due to pool fragmentation");
}
DeviceAllocationError::OutOfPoolMemory => {}
}
panic!("Failed to allocate descriptor sets from fresh pool");
}
}
/// Descriptor allocator. /// Can be used to allocate descriptor sets for any layout. #[derive(Debug)] pubstruct DescriptorAllocator<P, S> {
buckets: HashMap<(DescriptorTotalCount, bool), DescriptorBucket<P>>,
sets_cache: Vec<DescriptorSet<S>>,
raw_sets_cache: Vec<S>,
max_update_after_bind_descriptors_in_all_pools: u32,
current_update_after_bind_descriptors_in_all_pools: u32,
total: u32,
}
impl<P, S> Drop for DescriptorAllocator<P, S> { fn drop(&mutself) { ifself.buckets.drain().any(|(_, bucket)| bucket.total != 0) { #[cfg(feature = "tracing")]
tracing::error!( "`DescriptorAllocator` is dropped while some descriptor sets were not deallocated"
);
}
}
}
/// Allocate descriptor set with specified layout. /// /// # Safety /// /// * Same `device` instance must be passed to all method calls of /// one `DescriptorAllocator` instance. /// * `flags` must match flags that were used to create the layout. /// * `layout_descriptor_count` must match descriptor numbers in the layout. pubunsafefn allocate<L, D>(
&mutself,
device: &D,
layout: &L,
flags: DescriptorSetLayoutCreateFlags,
layout_descriptor_count: &DescriptorTotalCount,
count: u32,
) -> Result<Vec<DescriptorSet<S>>, AllocationError> where
S: Debug,
L: Debug,
D: DescriptorDevice<L, P, S>,
{ if count == 0 { return Ok(Vec::new());
}
let descriptor_count = count * layout_descriptor_count.total();
let update_after_bind = flags.contains(DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND);
// Free sets allocated so far. letmut last = None;
for set inself.sets_cache.drain(..) { if Some(set.pool_id) != last { iflet Some(last_id) = last { // Free contiguous range of sets from one pool in one go.
bucket.free(device, self.raw_sets_cache.drain(..), last_id);
}
}
last = Some(set.pool_id); self.raw_sets_cache.push(set.raw);
}
iflet Some(last_id) = last {
bucket.free(device, self.raw_sets_cache.drain(..), last_id);
}
Err(err)
}
}
}
/// Free descriptor sets. /// /// # Safety /// /// * Same `device` instance must be passed to all method calls of /// one `DescriptorAllocator` instance. /// * None of descriptor sets can be referenced in any pending command buffers. /// * All command buffers where at least one of descriptor sets referenced /// move to invalid state. pubunsafefn free<L, D, I>(&mutself, device: &D, sets: I) where
D: DescriptorDevice<L, P, S>,
I: IntoIterator<Item = DescriptorSet<S>>,
{
debug_assert!(self.raw_sets_cache.is_empty());
for set in sets { if last_key != (set.size, set.update_after_bind) || last_pool_id != Some(set.pool_id) { iflet Some(pool_id) = last_pool_id { let bucket = self
.buckets
.get_mut(&last_key)
.expect("Set must be allocated from this allocator");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.