1use {
4 super::{
5 BufferHostMappingCompatibility, Cache, Lease, Pool, PoolConfig, compatible_buffer_info,
6 garbage_collector::{CollectResources, ResourceRequests},
7 with_cache,
8 },
9 crate::driver::{
10 DriverError,
11 accel_struct::{AccelerationStructure, AccelerationStructureInfo},
12 buffer::{Buffer, BufferInfo},
13 cmd_buf::{CommandBuffer, CommandBufferInfo},
14 descriptor_set::{DescriptorPool, DescriptorPoolInfo},
15 device::Device,
16 image::{Image, ImageInfo, SampleCount},
17 render_pass::{RenderPass, RenderPassInfo},
18 },
19 ash::vk,
20 log::debug,
21 std::{collections::HashMap, sync::Arc},
22};
23
24type BufferKey = (bool, vk::DeviceSize, vk::SharingMode);
25
26fn buffer_key(info: &BufferInfo) -> BufferKey {
27 (
28 info.host_readable | info.host_writable,
29 info.alignment,
30 info.sharing_mode,
31 )
32}
33
34fn compatible_accel_struct_info(
35 item_info: &AccelerationStructureInfo,
36 requested_info: &AccelerationStructureInfo,
37) -> bool {
38 item_info.acceleration_structure_type == requested_info.acceleration_structure_type
39 && item_info.size >= requested_info.size
40}
41
42fn compatible_lazy_buffer_info(item_info: &BufferInfo, requested_info: &BufferInfo) -> bool {
43 buffer_key(item_info) == buffer_key(requested_info)
44 && compatible_buffer_info(
45 item_info,
46 requested_info,
47 BufferHostMappingCompatibility::Superset,
48 )
49}
50
51#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
52struct ImageKey {
53 array_layer_count: u32,
54 depth: u32,
55 fmt: vk::Format,
56 height: u32,
57 mip_level_count: u32,
58 sample_count: SampleCount,
59 sharing_mode: vk::SharingMode,
60 tiling: vk::ImageTiling,
61 image_type: vk::ImageType,
62 width: u32,
63}
64
65impl From<ImageInfo> for ImageKey {
66 fn from(info: ImageInfo) -> Self {
67 Self {
68 array_layer_count: info.array_layer_count,
69 depth: info.depth,
70 fmt: info.format,
71 height: info.height,
72 mip_level_count: info.mip_level_count,
73 sample_count: info.sample_count,
74 sharing_mode: info.sharing_mode,
75 tiling: info.tiling,
76 image_type: info.image_type,
77 width: info.width,
78 }
79 }
80}
81
82fn compatible_lazy_image_info(item_info: &ImageInfo, requested_info: &ImageInfo) -> bool {
83 ImageKey::from(*item_info) == ImageKey::from(*requested_info)
84 && item_info.flags.contains(requested_info.flags)
85 && item_info.usage.contains(requested_info.usage)
86}
87
88#[derive(Debug)]
115#[read_only::cast]
116pub struct LazyPool {
117 accel_struct_cache: HashMap<vk::AccelerationStructureTypeKHR, Cache<AccelerationStructure>>,
118 buffer_cache: HashMap<BufferKey, Cache<Buffer>>,
119 command_buffer_cache: HashMap<u32, Cache<CommandBuffer>>,
120 descriptor_pool_cache: Cache<DescriptorPool>,
121
122 #[readonly]
126 pub device: Device,
127
128 image_cache: HashMap<ImageKey, Cache<Image>>,
129
130 #[readonly]
134 pub info: PoolConfig,
135
136 render_pass_cache: HashMap<RenderPassInfo, Cache<RenderPass>>,
137}
138
139impl LazyPool {
140 pub fn new(device: &Device) -> Self {
142 Self::with_capacity(device, PoolConfig::default())
143 }
144
145 pub fn with_capacity(device: &Device, info: impl Into<PoolConfig>) -> Self {
147 let info: PoolConfig = info.into();
148 let device = device.clone();
149
150 Self {
151 accel_struct_cache: Default::default(),
152 buffer_cache: Default::default(),
153 command_buffer_cache: Default::default(),
154 descriptor_pool_cache: PoolConfig::default_cache(),
155 device,
156 image_cache: Default::default(),
157 info,
158 render_pass_cache: Default::default(),
159 }
160 }
161
162 pub fn clear(&mut self) {
164 self.clear_accel_structs();
165 self.clear_buffers();
166 self.clear_images();
167 }
168
169 pub fn clear_accel_structs(&mut self) {
171 self.accel_struct_cache.clear();
172 }
173
174 pub fn clear_accel_structs_by_type(
176 &mut self,
177 accel_struct_ty: vk::AccelerationStructureTypeKHR,
178 ) {
179 self.accel_struct_cache.remove(&accel_struct_ty);
180 }
181
182 pub fn clear_buffers(&mut self) {
184 self.buffer_cache.clear();
185 }
186
187 pub fn clear_images(&mut self) {
189 self.image_cache.clear();
190 }
191
192 pub fn clear_images_by_info(&mut self, info: impl Into<ImageInfo>) {
194 self.image_cache.remove(&info.into().into());
195 }
196
197 pub fn retain_accel_structs<F>(&mut self, mut f: F)
209 where
210 F: FnMut(vk::AccelerationStructureTypeKHR) -> bool,
211 {
212 self.accel_struct_cache
213 .retain(|&accel_struct_ty, _| f(accel_struct_ty))
214 }
215}
216
217impl CollectResources for LazyPool {
218 fn collect_resources(&mut self, requests: &ResourceRequests) {
219 self.accel_struct_cache.retain(|accel_struct_ty, cache| {
220 let retain_bucket = requests
221 .accel_structs
222 .iter()
223 .any(|info| info.acceleration_structure_type == *accel_struct_ty);
224
225 if retain_bucket {
226 with_cache(cache, |cache| {
227 cache.retain(|item| {
228 requests
229 .accel_structs
230 .iter()
231 .any(|info| compatible_accel_struct_info(&item.info, info))
232 });
233 });
234 }
235
236 retain_bucket
237 });
238
239 self.buffer_cache.retain(|key, cache| {
240 let retain_bucket = requests.buffers.iter().any(|info| buffer_key(info) == *key);
241
242 if retain_bucket {
243 with_cache(cache, |cache| {
244 cache.retain(|item| {
245 requests
246 .buffers
247 .iter()
248 .any(|info| compatible_lazy_buffer_info(&item.info, info))
249 });
250 });
251 }
252
253 retain_bucket
254 });
255
256 self.image_cache.retain(|key, cache| {
257 let retain_bucket = requests
258 .images
259 .iter()
260 .any(|info| ImageKey::from(*info) == *key);
261
262 if retain_bucket {
263 with_cache(cache, |cache| {
264 cache.retain(|item| {
265 requests
266 .images
267 .iter()
268 .any(|info| compatible_lazy_image_info(&item.info, info))
269 });
270 });
271 }
272
273 retain_bucket
274 });
275 }
276}
277
278impl Pool<AccelerationStructureInfo, AccelerationStructure> for LazyPool {
279 #[profiling::function]
280 fn resource(
281 &mut self,
282 info: AccelerationStructureInfo,
283 ) -> Result<Lease<AccelerationStructure>, DriverError> {
284 let cache = self
285 .accel_struct_cache
286 .entry(info.acceleration_structure_type)
287 .or_insert_with(|| PoolConfig::explicit_cache(self.info.accel_struct_capacity));
288 let cache_ref = Arc::downgrade(cache);
289
290 {
291 profiling::scope!("check cache");
292
293 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
294 let mut cache = cache.lock();
295
296 #[cfg(not(feature = "parking_lot"))]
297 let mut cache = cache.expect("poisoned cache lock");
298
299 for idx in 0..cache.len() {
301 let item = unsafe { cache.get_unchecked(idx) };
302 if compatible_accel_struct_info(&item.info, &info) {
303 let item = cache.swap_remove(idx);
304
305 return Ok(Lease::new(cache_ref, item));
306 }
307 }
308 }
309
310 debug!("Creating new {}", stringify!(AccelerationStructure));
311
312 let item = AccelerationStructure::create(&self.device, info)?;
313
314 Ok(Lease::new(cache_ref, item))
315 }
316}
317
318impl Pool<BufferInfo, Buffer> for LazyPool {
319 #[profiling::function]
320 fn resource(&mut self, info: BufferInfo) -> Result<Lease<Buffer>, DriverError> {
321 let cache = self
322 .buffer_cache
323 .entry(buffer_key(&info))
324 .or_insert_with(|| PoolConfig::explicit_cache(self.info.buffer_capacity));
325 let cache_ref = Arc::downgrade(cache);
326
327 {
328 profiling::scope!("check cache");
329
330 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
331 let mut cache = cache.lock();
332
333 #[cfg(not(feature = "parking_lot"))]
334 let mut cache = cache.expect("poisoned cache lock");
335
336 for idx in 0..cache.len() {
338 let item = unsafe { cache.get_unchecked(idx) };
339 if compatible_lazy_buffer_info(&item.info, &info) {
340 let item = cache.swap_remove(idx);
341
342 return Ok(Lease::new(cache_ref, item));
343 }
344 }
345 }
346
347 debug!("Creating new {}", stringify!(Buffer));
348
349 let item = Buffer::create(&self.device, info)?;
350
351 Ok(Lease::new(cache_ref, item))
352 }
353}
354
355impl Pool<CommandBufferInfo, CommandBuffer> for LazyPool {
356 #[profiling::function]
357 fn resource(&mut self, info: CommandBufferInfo) -> Result<Lease<CommandBuffer>, DriverError> {
358 let cache_ref = self
359 .command_buffer_cache
360 .entry(info.queue_family_index)
361 .or_insert_with(PoolConfig::default_cache);
362 let item = {
363 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
364 let mut cache = cache_ref.lock();
365
366 #[cfg(not(feature = "parking_lot"))]
367 let mut cache = cache.expect("poisoned cache lock");
368
369 cache.pop()
370 }
371 .map(Ok)
372 .unwrap_or_else(|| {
373 debug!("Creating new {}", stringify!(CommandBuffer));
374
375 CommandBuffer::create(&self.device, info)
376 })?;
377
378 Ok(Lease::new(Arc::downgrade(cache_ref), item))
382 }
383}
384
385impl Pool<DescriptorPoolInfo, DescriptorPool> for LazyPool {
386 #[profiling::function]
387 fn resource(&mut self, info: DescriptorPoolInfo) -> Result<Lease<DescriptorPool>, DriverError> {
388 let cache_ref = Arc::downgrade(&self.descriptor_pool_cache);
389
390 {
391 profiling::scope!("check cache");
392
393 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
394 let mut cache = self.descriptor_pool_cache.lock();
395
396 #[cfg(not(feature = "parking_lot"))]
397 let mut cache = cache.expect("poisoned cache lock");
398
399 for idx in 0..cache.len() {
401 let item = unsafe { cache.get_unchecked(idx) };
402 if item.info.max_sets >= info.max_sets
403 && item.info.acceleration_structure_count >= info.acceleration_structure_count
404 && item.info.combined_image_sampler_count >= info.combined_image_sampler_count
405 && item.info.input_attachment_count >= info.input_attachment_count
406 && item.info.sampled_image_count >= info.sampled_image_count
407 && item.info.sampler_count >= info.sampled_image_count
408 && item.info.storage_buffer_count >= info.storage_buffer_count
409 && item.info.storage_buffer_dynamic_count >= info.storage_buffer_dynamic_count
410 && item.info.storage_image_count >= info.storage_image_count
411 && item.info.storage_texel_buffer_count >= info.storage_texel_buffer_count
412 && item.info.uniform_buffer_count >= info.uniform_buffer_count
413 && item.info.uniform_buffer_dynamic_count >= info.uniform_buffer_dynamic_count
414 && item.info.uniform_texel_buffer_count >= info.uniform_texel_buffer_count
415 {
416 let item = cache.swap_remove(idx);
417
418 return Ok(Lease::new(cache_ref, item));
419 }
420 }
421 }
422
423 debug!("Creating new {}", stringify!(DescriptorPool));
424
425 let item = DescriptorPool::create(&self.device, info)?;
426
427 Ok(Lease::new(cache_ref, item))
428 }
429}
430
431impl Pool<ImageInfo, Image> for LazyPool {
432 #[profiling::function]
433 fn resource(&mut self, info: ImageInfo) -> Result<Lease<Image>, DriverError> {
434 let cache = self
435 .image_cache
436 .entry(info.into())
437 .or_insert_with(|| PoolConfig::explicit_cache(self.info.image_capacity));
438 let cache_ref = Arc::downgrade(cache);
439
440 {
441 profiling::scope!("check cache");
442
443 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
444 let mut cache = cache.lock();
445
446 #[cfg(not(feature = "parking_lot"))]
447 let mut cache = cache.expect("poisoned cache lock");
448
449 for idx in 0..cache.len() {
451 let item = unsafe { cache.get_unchecked(idx) };
452 if compatible_lazy_image_info(&item.info, &info) {
453 let item = cache.swap_remove(idx);
454
455 return Ok(Lease::new(cache_ref, item));
456 }
457 }
458 }
459
460 debug!("Creating new {}", stringify!(Image));
461
462 let item = Image::create(&self.device, info)?;
463
464 Ok(Lease::new(cache_ref, item))
465 }
466}
467
468impl Pool<RenderPassInfo, RenderPass> for LazyPool {
469 #[profiling::function]
470 fn resource(&mut self, info: RenderPassInfo) -> Result<Lease<RenderPass>, DriverError> {
471 let cache_ref = if let Some(cache) = self.render_pass_cache.get(&info) {
472 cache
473 } else {
474 self.render_pass_cache
476 .entry(info.clone())
477 .or_insert_with(PoolConfig::default_cache)
478 };
479 let item = {
480 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
481 let mut cache = cache_ref.lock();
482
483 #[cfg(not(feature = "parking_lot"))]
484 let mut cache = cache.expect("poisoned cache lock");
485
486 cache.pop()
487 }
488 .map(Ok)
489 .unwrap_or_else(|| {
490 debug!("Creating new {}", stringify!(RenderPass));
491
492 RenderPass::create(&self.device, info)
493 })?;
494
495 Ok(Lease::new(Arc::downgrade(cache_ref), item))
496 }
497}
498
499#[cfg(test)]
500mod test {
501 use {
502 super::*,
503 crate::{
504 driver::device::{Device, DeviceInfo},
505 pool::garbage_collector::GarbageCollector,
506 },
507 };
508
509 #[test]
510 #[ignore = "requires Vulkan device"]
511 fn vulkan_garbage_collector_retains_supported_lazy_resources() -> Result<(), DriverError> {
512 let device = Device::create(DeviceInfo::default())?;
513 let mut collector = GarbageCollector::new(LazyPool::with_capacity(&device, 4));
514 let retained_info = BufferInfo::device_mem(64, vk::BufferUsageFlags::TRANSFER_SRC);
515 let removed_info = BufferInfo::device_mem(64, vk::BufferUsageFlags::STORAGE_BUFFER);
516 let key = buffer_key(&retained_info);
517
518 drop(collector.resource(retained_info)?);
519 drop(collector.resource(removed_info)?);
520 collector.collect_resources();
521 assert_eq!(
522 with_cache(&collector.buffer_cache[&key], |cache| cache.len()),
523 2
524 );
525
526 drop(collector.resource(retained_info)?);
527 collector.collect_resources();
528 assert_eq!(
529 with_cache(&collector.buffer_cache[&key], |cache| cache.len()),
530 1
531 );
532
533 collector.collect_resources();
534 assert!(collector.buffer_cache.is_empty());
535
536 Ok(())
537 }
538}