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