1use {
4 super::{
5 BufferHostMappingCompatibility, Cache, Lease, Pool, PoolConfig, compatible_buffer_info,
6 },
7 crate::driver::{
8 DriverError,
9 accel_struct::{AccelerationStructure, AccelerationStructureInfo},
10 buffer::{Buffer, BufferInfo},
11 cmd_buf::{CommandBuffer, CommandBufferInfo},
12 descriptor_set::{DescriptorPool, DescriptorPoolInfo},
13 device::Device,
14 image::{Image, ImageInfo, SampleCount},
15 render_pass::{RenderPass, RenderPassInfo},
16 },
17 ash::vk,
18 log::debug,
19 std::{collections::HashMap, sync::Arc},
20};
21
22#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
23struct ImageKey {
24 array_layer_count: u32,
25 depth: u32,
26 fmt: vk::Format,
27 height: u32,
28 mip_level_count: u32,
29 sample_count: SampleCount,
30 sharing_mode: vk::SharingMode,
31 tiling: vk::ImageTiling,
32 image_type: vk::ImageType,
33 width: u32,
34}
35
36impl From<ImageInfo> for ImageKey {
37 fn from(info: ImageInfo) -> Self {
38 Self {
39 array_layer_count: info.array_layer_count,
40 depth: info.depth,
41 fmt: info.format,
42 height: info.height,
43 mip_level_count: info.mip_level_count,
44 sample_count: info.sample_count,
45 sharing_mode: info.sharing_mode,
46 tiling: info.tiling,
47 image_type: info.image_type,
48 width: info.width,
49 }
50 }
51}
52
53#[derive(Debug)]
80#[read_only::cast]
81pub struct LazyPool {
82 accel_struct_cache: HashMap<vk::AccelerationStructureTypeKHR, Cache<AccelerationStructure>>,
83 buffer_cache: HashMap<(bool, vk::DeviceSize, vk::SharingMode), Cache<Buffer>>,
84 command_buffer_cache: HashMap<u32, Cache<CommandBuffer>>,
85 descriptor_pool_cache: Cache<DescriptorPool>,
86
87 #[readonly]
91 pub device: Device,
92
93 image_cache: HashMap<ImageKey, Cache<Image>>,
94
95 #[readonly]
99 pub info: PoolConfig,
100
101 render_pass_cache: HashMap<RenderPassInfo, Cache<RenderPass>>,
102}
103
104impl LazyPool {
105 pub fn new(device: &Device) -> Self {
107 Self::with_capacity(device, PoolConfig::default())
108 }
109
110 pub fn with_capacity(device: &Device, info: impl Into<PoolConfig>) -> Self {
112 let info: PoolConfig = info.into();
113 let device = device.clone();
114
115 Self {
116 accel_struct_cache: Default::default(),
117 buffer_cache: Default::default(),
118 command_buffer_cache: Default::default(),
119 descriptor_pool_cache: PoolConfig::default_cache(),
120 device,
121 image_cache: Default::default(),
122 info,
123 render_pass_cache: Default::default(),
124 }
125 }
126
127 pub fn clear(&mut self) {
129 self.clear_accel_structs();
130 self.clear_buffers();
131 self.clear_images();
132 }
133
134 pub fn clear_accel_structs(&mut self) {
136 self.accel_struct_cache.clear();
137 }
138
139 pub fn clear_accel_structs_by_type(
141 &mut self,
142 accel_struct_ty: vk::AccelerationStructureTypeKHR,
143 ) {
144 self.accel_struct_cache.remove(&accel_struct_ty);
145 }
146
147 pub fn clear_buffers(&mut self) {
149 self.buffer_cache.clear();
150 }
151
152 pub fn clear_images(&mut self) {
154 self.image_cache.clear();
155 }
156
157 pub fn clear_images_by_info(&mut self, info: impl Into<ImageInfo>) {
159 self.image_cache.remove(&info.into().into());
160 }
161
162 pub fn retain_accel_structs<F>(&mut self, mut f: F)
174 where
175 F: FnMut(vk::AccelerationStructureTypeKHR) -> bool,
176 {
177 self.accel_struct_cache
178 .retain(|&accel_struct_ty, _| f(accel_struct_ty))
179 }
180}
181
182impl Pool<AccelerationStructureInfo, AccelerationStructure> for LazyPool {
183 #[profiling::function]
184 fn resource(
185 &mut self,
186 info: AccelerationStructureInfo,
187 ) -> Result<Lease<AccelerationStructure>, DriverError> {
188 let cache = self
189 .accel_struct_cache
190 .entry(info.acceleration_structure_type)
191 .or_insert_with(|| PoolConfig::explicit_cache(self.info.accel_struct_capacity));
192 let cache_ref = Arc::downgrade(cache);
193
194 {
195 profiling::scope!("check cache");
196
197 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
198 let mut cache = cache.lock();
199
200 #[cfg(not(feature = "parking_lot"))]
201 let mut cache = cache.expect("poisoned cache lock");
202
203 for idx in 0..cache.len() {
205 let item = unsafe { cache.get_unchecked(idx) };
206 if item.info.size >= info.size {
207 let item = cache.swap_remove(idx);
208
209 return Ok(Lease::new(cache_ref, item));
210 }
211 }
212 }
213
214 debug!("Creating new {}", stringify!(AccelerationStructure));
215
216 let item = AccelerationStructure::create(&self.device, info)?;
217
218 Ok(Lease::new(cache_ref, item))
219 }
220}
221
222impl Pool<BufferInfo, Buffer> for LazyPool {
223 #[profiling::function]
224 fn resource(&mut self, info: BufferInfo) -> Result<Lease<Buffer>, DriverError> {
225 let cache = self
226 .buffer_cache
227 .entry((
228 info.host_readable | info.host_writable,
229 info.alignment,
230 info.sharing_mode,
231 ))
232 .or_insert_with(|| PoolConfig::explicit_cache(self.info.buffer_capacity));
233 let cache_ref = Arc::downgrade(cache);
234
235 {
236 profiling::scope!("check cache");
237
238 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
239 let mut cache = cache.lock();
240
241 #[cfg(not(feature = "parking_lot"))]
242 let mut cache = cache.expect("poisoned cache lock");
243
244 for idx in 0..cache.len() {
246 let item = unsafe { cache.get_unchecked(idx) };
247 if compatible_buffer_info(
248 &item.info,
249 &info,
250 BufferHostMappingCompatibility::Superset,
251 ) {
252 let item = cache.swap_remove(idx);
253
254 return Ok(Lease::new(cache_ref, item));
255 }
256 }
257 }
258
259 debug!("Creating new {}", stringify!(Buffer));
260
261 let item = Buffer::create(&self.device, info)?;
262
263 Ok(Lease::new(cache_ref, item))
264 }
265}
266
267impl Pool<CommandBufferInfo, CommandBuffer> for LazyPool {
268 #[profiling::function]
269 fn resource(&mut self, info: CommandBufferInfo) -> Result<Lease<CommandBuffer>, DriverError> {
270 let cache_ref = self
271 .command_buffer_cache
272 .entry(info.queue_family_index)
273 .or_insert_with(PoolConfig::default_cache);
274 let item = {
275 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
276 let mut cache = cache_ref.lock();
277
278 #[cfg(not(feature = "parking_lot"))]
279 let mut cache = cache.expect("poisoned cache lock");
280
281 cache.pop()
282 }
283 .map(Ok)
284 .unwrap_or_else(|| {
285 debug!("Creating new {}", stringify!(CommandBuffer));
286
287 CommandBuffer::create(&self.device, info)
288 })?;
289
290 Ok(Lease::new(Arc::downgrade(cache_ref), item))
294 }
295}
296
297impl Pool<DescriptorPoolInfo, DescriptorPool> for LazyPool {
298 #[profiling::function]
299 fn resource(&mut self, info: DescriptorPoolInfo) -> Result<Lease<DescriptorPool>, DriverError> {
300 let cache_ref = Arc::downgrade(&self.descriptor_pool_cache);
301
302 {
303 profiling::scope!("check cache");
304
305 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
306 let mut cache = self.descriptor_pool_cache.lock();
307
308 #[cfg(not(feature = "parking_lot"))]
309 let mut cache = cache.expect("poisoned cache lock");
310
311 for idx in 0..cache.len() {
313 let item = unsafe { cache.get_unchecked(idx) };
314 if item.info.max_sets >= info.max_sets
315 && item.info.acceleration_structure_count >= info.acceleration_structure_count
316 && item.info.combined_image_sampler_count >= info.combined_image_sampler_count
317 && item.info.input_attachment_count >= info.input_attachment_count
318 && item.info.sampled_image_count >= info.sampled_image_count
319 && item.info.sampler_count >= info.sampled_image_count
320 && item.info.storage_buffer_count >= info.storage_buffer_count
321 && item.info.storage_buffer_dynamic_count >= info.storage_buffer_dynamic_count
322 && item.info.storage_image_count >= info.storage_image_count
323 && item.info.storage_texel_buffer_count >= info.storage_texel_buffer_count
324 && item.info.uniform_buffer_count >= info.uniform_buffer_count
325 && item.info.uniform_buffer_dynamic_count >= info.uniform_buffer_dynamic_count
326 && item.info.uniform_texel_buffer_count >= info.uniform_texel_buffer_count
327 {
328 let item = cache.swap_remove(idx);
329
330 return Ok(Lease::new(cache_ref, item));
331 }
332 }
333 }
334
335 debug!("Creating new {}", stringify!(DescriptorPool));
336
337 let item = DescriptorPool::create(&self.device, info)?;
338
339 Ok(Lease::new(cache_ref, item))
340 }
341}
342
343impl Pool<ImageInfo, Image> for LazyPool {
344 #[profiling::function]
345 fn resource(&mut self, info: ImageInfo) -> Result<Lease<Image>, DriverError> {
346 let cache = self
347 .image_cache
348 .entry(info.into())
349 .or_insert_with(|| PoolConfig::explicit_cache(self.info.image_capacity));
350 let cache_ref = Arc::downgrade(cache);
351
352 {
353 profiling::scope!("check cache");
354
355 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
356 let mut cache = cache.lock();
357
358 #[cfg(not(feature = "parking_lot"))]
359 let mut cache = cache.expect("poisoned cache lock");
360
361 for idx in 0..cache.len() {
363 let item = unsafe { cache.get_unchecked(idx) };
364 if item.info.flags.contains(info.flags) && item.info.usage.contains(info.usage) {
365 let item = cache.swap_remove(idx);
366
367 return Ok(Lease::new(cache_ref, item));
368 }
369 }
370 }
371
372 debug!("Creating new {}", stringify!(Image));
373
374 let item = Image::create(&self.device, info)?;
375
376 Ok(Lease::new(cache_ref, item))
377 }
378}
379
380impl Pool<RenderPassInfo, RenderPass> for LazyPool {
381 #[profiling::function]
382 fn resource(&mut self, info: RenderPassInfo) -> Result<Lease<RenderPass>, DriverError> {
383 let cache_ref = if let Some(cache) = self.render_pass_cache.get(&info) {
384 cache
385 } else {
386 self.render_pass_cache
388 .entry(info.clone())
389 .or_insert_with(PoolConfig::default_cache)
390 };
391 let item = {
392 #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
393 let mut cache = cache_ref.lock();
394
395 #[cfg(not(feature = "parking_lot"))]
396 let mut cache = cache.expect("poisoned cache lock");
397
398 cache.pop()
399 }
400 .map(Ok)
401 .unwrap_or_else(|| {
402 debug!("Creating new {}", stringify!(RenderPass));
403
404 RenderPass::create(&self.device, info)
405 })?;
406
407 Ok(Lease::new(Arc::downgrade(cache_ref), item))
408 }
409}