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