1pub mod cache;
87pub mod fifo;
88pub mod hash;
89pub mod lazy;
90
91use {
92 crate::driver::{
93 DriverError,
94 accel_struct::{
95 AccelerationStructure, AccelerationStructureInfo, AccelerationStructureInfoBuilder,
96 },
97 buffer::{Buffer, BufferInfo, BufferInfoBuilder},
98 descriptor_set::{DescriptorPool, DescriptorPoolInfo},
99 image::{Image, ImageInfo, ImageInfoBuilder},
100 render_pass::{RenderPass, RenderPassInfo},
101 },
102 derive_builder::{Builder, UninitializedFieldError},
103 std::{
104 fmt::Debug,
105 mem::ManuallyDrop,
106 ops::{Deref, DerefMut},
107 sync::{Arc, Weak},
108 thread::panicking,
109 },
110};
111
112#[derive(Clone, Copy)]
113enum BufferHostMappingCompatibility {
114 Exact,
115 Superset,
116}
117
118fn compatible_buffer_info(
119 item_info: &BufferInfo,
120 requested_info: &BufferInfo,
121 host_mapping: BufferHostMappingCompatibility,
122) -> bool {
123 (item_info.alloc_dedicated & requested_info.alloc_dedicated) == requested_info.alloc_dedicated
124 && compatible_buffer_host_mapping(item_info, requested_info, host_mapping)
125 && item_info.alignment >= requested_info.alignment
126 && item_info.sharing_mode == requested_info.sharing_mode
127 && item_info.size >= requested_info.size
128 && item_info.usage.contains(requested_info.usage)
129}
130
131fn compatible_buffer_host_mapping(
132 item_info: &BufferInfo,
133 requested_info: &BufferInfo,
134 compatibility: BufferHostMappingCompatibility,
135) -> bool {
136 match compatibility {
137 BufferHostMappingCompatibility::Exact => {
138 item_info.host_readable == requested_info.host_readable
139 && item_info.host_writable == requested_info.host_writable
140 }
141 BufferHostMappingCompatibility::Superset => {
142 (item_info.host_readable & requested_info.host_readable) == requested_info.host_readable
143 && (item_info.host_writable & requested_info.host_writable)
144 == requested_info.host_writable
145 }
146 }
147}
148
149fn compatible_image_info(item_info: &ImageInfo, requested_info: &ImageInfo) -> bool {
150 item_info.array_layer_count == requested_info.array_layer_count
151 && item_info.alloc_dedicated == requested_info.alloc_dedicated
152 && item_info.depth == requested_info.depth
153 && item_info.format == requested_info.format
154 && item_info.height == requested_info.height
155 && item_info.host_readable == requested_info.host_readable
156 && item_info.host_writable == requested_info.host_writable
157 && item_info.mip_level_count == requested_info.mip_level_count
158 && item_info.sample_count == requested_info.sample_count
159 && item_info.sharing_mode == requested_info.sharing_mode
160 && item_info.tiling == requested_info.tiling
161 && item_info.image_type == requested_info.image_type
162 && item_info.width == requested_info.width
163 && item_info.flags.contains(requested_info.flags)
164 && item_info.usage.contains(requested_info.usage)
165}
166
167#[cfg(feature = "parking_lot")]
168use parking_lot::Mutex;
169
170#[cfg(not(feature = "parking_lot"))]
171use std::sync::Mutex;
172
173type Cache<T> = Arc<Mutex<Vec<T>>>;
174type CacheRef<T> = Weak<Mutex<Vec<T>>>;
175
176fn with_cache<T, R>(cache: &Cache<T>, f: impl FnOnce(&mut Vec<T>) -> R) -> R {
177 let cache = cache.lock();
178
179 #[cfg(not(feature = "parking_lot"))]
180 let cache = cache.expect("poisoned cache lock");
181
182 let mut cache = cache;
183
184 f(&mut cache)
185}
186
187#[derive(Debug)]
193pub struct Lease<T> {
194 cache_ref: CacheRef<T>,
195 item: ManuallyDrop<T>,
196}
197
198impl Lease<AccelerationStructure> {
204 pub fn with_debug_name(self, name: impl AsRef<str>) -> Self {
206 self.set_debug_name(name);
207
208 self
209 }
210}
211
212impl Lease<Buffer> {
213 pub fn with_debug_name(self, name: impl AsRef<str>) -> Self {
215 self.set_debug_name(name);
216
217 self
218 }
219}
220
221impl Lease<Image> {
222 pub fn with_debug_name(self, name: impl AsRef<str>) -> Self {
224 self.set_debug_name(name);
225
226 self
227 }
228}
229
230impl<T> Lease<T> {
231 fn new(cache_ref: CacheRef<T>, item: T) -> Self {
232 Self {
233 cache_ref,
234 item: ManuallyDrop::new(item),
235 }
236 }
237}
238
239impl<T> AsRef<T> for Lease<T> {
240 fn as_ref(&self) -> &T {
241 self
242 }
243}
244
245impl<T> Deref for Lease<T> {
246 type Target = T;
247
248 fn deref(&self) -> &Self::Target {
249 &self.item
250 }
251}
252
253impl<T> DerefMut for Lease<T> {
254 fn deref_mut(&mut self) -> &mut Self::Target {
255 &mut self.item
256 }
257}
258
259impl<T> Drop for Lease<T> {
260 #[profiling::function]
261 fn drop(&mut self) {
262 if panicking() {
263 return;
264 }
265
266 if let Some(cache) = self.cache_ref.upgrade() {
269 with_cache(&cache, |cache| {
270 if cache.len() >= cache.capacity() {
271 cache.pop();
272 }
273
274 cache.push(unsafe { ManuallyDrop::take(&mut self.item) });
275 });
276 } else {
277 unsafe {
278 ManuallyDrop::drop(&mut self.item);
279 }
280 }
281 }
282}
283
284pub trait Pool<I, T> {
286 fn resource(&mut self, info: I) -> Result<Lease<T>, DriverError>;
288}
289
290#[allow(private_bounds)]
295pub trait SubmissionPool: submission_pool_private::SubmissionPoolSealed {}
296
297impl<T> SubmissionPool for T where T: submission_pool_private::SubmissionPoolSealed {}
298
299pub(crate) mod submission_pool_private {
300 use super::*;
301
302 pub(crate) trait SubmissionPoolSealed {
303 fn descriptor_pool(
304 &mut self,
305 info: DescriptorPoolInfo,
306 ) -> Result<Lease<DescriptorPool>, DriverError>;
307
308 fn render_pass(&mut self, info: RenderPassInfo) -> Result<Lease<RenderPass>, DriverError>;
309 }
310
311 impl<T> SubmissionPoolSealed for T
312 where
313 T: Pool<DescriptorPoolInfo, DescriptorPool> + Pool<RenderPassInfo, RenderPass>,
314 {
315 fn descriptor_pool(
316 &mut self,
317 info: DescriptorPoolInfo,
318 ) -> Result<Lease<DescriptorPool>, DriverError> {
319 self.resource(info)
320 }
321
322 fn render_pass(&mut self, info: RenderPassInfo) -> Result<Lease<RenderPass>, DriverError> {
323 self.resource(info)
324 }
325 }
326}
327
328macro_rules! lease_builder {
330 ($info:ident => $item:ident) => {
331 paste::paste! {
332 impl<T> Pool<[<$info Builder>], $item> for T where T: Pool<$info, $item> {
333 fn resource(
334 &mut self,
335 builder: [<$info Builder>],
336 ) -> Result<Lease<$item>, DriverError> {
337 let info = builder.build();
338
339 self.resource(info)
340 }
341 }
342 }
343 };
344}
345
346lease_builder!(AccelerationStructureInfo => AccelerationStructure);
347lease_builder!(BufferInfo => Buffer);
348lease_builder!(ImageInfo => Image);
349
350#[derive(Builder, Clone, Copy, Debug, Eq, PartialEq)]
353#[builder(
354 build_fn(private, name = "fallible_build", error = "UninitializedFieldError"),
355 derive(Clone, Copy, Debug),
356 pattern = "owned"
357)]
358pub struct PoolConfig {
359 #[builder(
368 default = "PoolConfig::DEFAULT_RESOURCE_CAPACITY",
369 setter(strip_option)
370 )]
371 pub accel_struct_capacity: usize,
372
373 #[builder(
382 default = "PoolConfig::DEFAULT_RESOURCE_CAPACITY",
383 setter(strip_option)
384 )]
385 pub buffer_capacity: usize,
386
387 #[builder(
396 default = "PoolConfig::DEFAULT_RESOURCE_CAPACITY",
397 setter(strip_option)
398 )]
399 pub image_capacity: usize,
400}
401
402impl PoolConfig {
403 pub const DEFAULT_RESOURCE_CAPACITY: usize = 16;
405
406 pub fn builder() -> PoolConfigBuilder {
408 Default::default()
409 }
410
411 fn default_cache<T>() -> Cache<T> {
412 Cache::new(Mutex::new(Vec::with_capacity(
413 Self::DEFAULT_RESOURCE_CAPACITY,
414 )))
415 }
416
417 fn explicit_cache<T>(capacity: usize) -> Cache<T> {
418 Cache::new(Mutex::new(Vec::with_capacity(capacity)))
419 }
420
421 pub fn into_builder(self) -> PoolConfigBuilder {
423 PoolConfigBuilder {
424 accel_struct_capacity: Some(self.accel_struct_capacity),
425 buffer_capacity: Some(self.buffer_capacity),
426 image_capacity: Some(self.image_capacity),
427 }
428 }
429
430 pub const fn with_capacity(resource_capacity: usize) -> Self {
433 Self {
434 accel_struct_capacity: resource_capacity,
435 buffer_capacity: resource_capacity,
436 image_capacity: resource_capacity,
437 }
438 }
439}
440
441impl Default for PoolConfig {
442 fn default() -> Self {
443 PoolConfigBuilder::default().into()
444 }
445}
446
447impl From<PoolConfigBuilder> for PoolConfig {
448 fn from(info: PoolConfigBuilder) -> Self {
449 info.build()
450 }
451}
452
453impl From<usize> for PoolConfig {
454 fn from(value: usize) -> Self {
455 Self {
456 accel_struct_capacity: value,
457 buffer_capacity: value,
458 image_capacity: value,
459 }
460 }
461}
462
463impl PoolConfigBuilder {
465 pub fn build(self) -> PoolConfig {
467 self.fallible_build().expect("invalid pool config")
468 }
469}
470
471#[cfg(test)]
472mod test {
473 use super::*;
474 use crate::driver::ash::vk;
475
476 type Info = PoolConfig;
477 type Builder = PoolConfigBuilder;
478
479 #[test]
480 pub fn pool_info() {
481 let info = Info::default();
482 let builder = info.into_builder().build();
483
484 assert_eq!(info, builder);
485 }
486
487 #[test]
488 pub fn pool_info_builder() {
489 let info = Info {
490 accel_struct_capacity: 1,
491 buffer_capacity: 2,
492 image_capacity: 3,
493 };
494 let builder = Builder::default()
495 .accel_struct_capacity(1)
496 .buffer_capacity(2)
497 .image_capacity(3)
498 .build();
499
500 assert_eq!(info, builder);
501 }
502
503 #[test]
504 fn buffer_info_compatibility_rejects_different_sharing_mode() {
505 let exclusive = BufferInfo::device_mem(64, vk::BufferUsageFlags::STORAGE_BUFFER);
506 let concurrent = BufferInfo {
507 sharing_mode: vk::SharingMode::CONCURRENT,
508 ..exclusive
509 };
510
511 assert!(!compatible_buffer_info(
512 &exclusive,
513 &concurrent,
514 BufferHostMappingCompatibility::Exact,
515 ));
516 assert!(!compatible_buffer_info(
517 &exclusive,
518 &concurrent,
519 BufferHostMappingCompatibility::Superset,
520 ));
521 }
522
523 #[test]
524 fn image_info_compatibility_rejects_different_sharing_mode() {
525 let exclusive = ImageInfo::image_2d(
526 16,
527 16,
528 vk::Format::R8G8B8A8_UNORM,
529 vk::ImageUsageFlags::STORAGE,
530 );
531 let concurrent = ImageInfo {
532 sharing_mode: vk::SharingMode::CONCURRENT,
533 ..exclusive
534 };
535
536 assert!(!compatible_image_info(&exclusive, &concurrent));
537 }
538}