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},
17 render_pass::{RenderPass, RenderPassInfo},
18 },
19 log::debug,
20 std::{collections::HashMap, sync::Arc},
21};
22
23fn compatible_accel_struct_info(
24 item_info: &AccelerationStructureInfo,
25 requested_info: &AccelerationStructureInfo,
26) -> bool {
27 item_info.size >= requested_info.size
28 && item_info.acceleration_structure_type == requested_info.acceleration_structure_type
29}
30
31fn compatible_fifo_image_info(item_info: &ImageInfo, requested_info: &ImageInfo) -> bool {
32 item_info.array_layer_count == requested_info.array_layer_count
33 && item_info.alloc_dedicated == requested_info.alloc_dedicated
34 && item_info.depth == requested_info.depth
35 && item_info.format == requested_info.format
36 && item_info.height == requested_info.height
37 && item_info.mip_level_count == requested_info.mip_level_count
38 && item_info.sample_count == requested_info.sample_count
39 && item_info.sharing_mode == requested_info.sharing_mode
40 && item_info.tiling == requested_info.tiling
41 && item_info.image_type == requested_info.image_type
42 && item_info.width == requested_info.width
43 && item_info.flags.contains(requested_info.flags)
44 && item_info.usage.contains(requested_info.usage)
45}
46
47#[derive(Debug)]
74#[read_only::cast]
75pub struct FifoPool {
76 accel_struct_cache: Cache<AccelerationStructure>,
77 buffer_cache: Cache<Buffer>,
78 command_buffer_cache: HashMap<u32, Cache<CommandBuffer>>,
79 descriptor_pool_cache: Cache<DescriptorPool>,
80
81 #[readonly]
85 pub device: Device,
86
87 image_cache: Cache<Image>,
88
89 #[readonly]
93 pub info: PoolConfig,
94
95 render_pass_cache: HashMap<RenderPassInfo, Cache<RenderPass>>,
96}
97
98impl FifoPool {
99 pub fn new(device: &Device) -> Self {
101 Self::with_capacity(device, PoolConfig::default())
102 }
103
104 pub fn with_capacity(device: &Device, info: impl Into<PoolConfig>) -> Self {
106 let info: PoolConfig = info.into();
107 let device = device.clone();
108
109 Self {
110 accel_struct_cache: PoolConfig::explicit_cache(info.accel_struct_capacity),
111 buffer_cache: PoolConfig::explicit_cache(info.buffer_capacity),
112 command_buffer_cache: Default::default(),
113 descriptor_pool_cache: PoolConfig::default_cache(),
114 device,
115 image_cache: PoolConfig::explicit_cache(info.image_capacity),
116 info,
117 render_pass_cache: Default::default(),
118 }
119 }
120
121 pub fn clear(&mut self) {
123 self.clear_accel_structs();
124 self.clear_buffers();
125 self.clear_images();
126 }
127
128 pub fn clear_accel_structs(&mut self) {
130 self.accel_struct_cache = PoolConfig::explicit_cache(self.info.accel_struct_capacity);
131 }
132
133 pub fn clear_buffers(&mut self) {
135 self.buffer_cache = PoolConfig::explicit_cache(self.info.buffer_capacity);
136 }
137
138 pub fn clear_images(&mut self) {
140 self.image_cache = PoolConfig::explicit_cache(self.info.image_capacity);
141 }
142}
143
144impl CollectResources for FifoPool {
145 fn collect_resources(&mut self, requests: &ResourceRequests) {
146 if requests.accel_structs.is_empty() {
147 self.clear_accel_structs();
148 } else {
149 with_cache(&self.accel_struct_cache, |cache| {
150 cache.retain(|item| {
151 requests
152 .accel_structs
153 .iter()
154 .any(|info| compatible_accel_struct_info(&item.info, info))
155 });
156 });
157 }
158
159 if requests.buffers.is_empty() {
160 self.clear_buffers();
161 } else {
162 with_cache(&self.buffer_cache, |cache| {
163 cache.retain(|item| {
164 requests.buffers.iter().any(|info| {
165 compatible_buffer_info(
166 &item.info,
167 info,
168 BufferHostMappingCompatibility::Exact,
169 )
170 })
171 });
172 });
173 }
174
175 if requests.images.is_empty() {
176 self.clear_images();
177 } else {
178 with_cache(&self.image_cache, |cache| {
179 cache.retain(|item| {
180 requests
181 .images
182 .iter()
183 .any(|info| compatible_fifo_image_info(&item.info, info))
184 });
185 });
186 }
187 }
188}
189
190impl Pool<AccelerationStructureInfo, AccelerationStructure> for FifoPool {
191 #[profiling::function]
192 fn resource(
193 &mut self,
194 info: AccelerationStructureInfo,
195 ) -> Result<Lease<AccelerationStructure>, DriverError> {
196 let cache_ref = Arc::downgrade(&self.accel_struct_cache);
197
198 {
199 profiling::scope!("check cache");
200
201 if let Some(item) = with_cache(&self.accel_struct_cache, |cache| {
202 for idx in 0..cache.len() {
204 let item = unsafe { cache.get_unchecked(idx) };
205 if compatible_accel_struct_info(&item.info, &info) {
206 let item = cache.swap_remove(idx);
207
208 return Some(Lease::new(cache_ref.clone(), item));
209 }
210 }
211
212 None
213 }) {
214 return Ok(item);
215 }
216 }
217
218 debug!("Creating new {}", stringify!(AccelerationStructure));
219
220 let item = AccelerationStructure::create(&self.device, info)?;
221
222 Ok(Lease::new(cache_ref, item))
223 }
224}
225
226impl Pool<BufferInfo, Buffer> for FifoPool {
227 #[profiling::function]
228 fn resource(&mut self, info: BufferInfo) -> Result<Lease<Buffer>, DriverError> {
229 let cache_ref = Arc::downgrade(&self.buffer_cache);
230
231 {
232 profiling::scope!("check cache");
233
234 if let Some(item) = with_cache(&self.buffer_cache, |cache| {
235 for idx in 0..cache.len() {
238 let item = unsafe { cache.get_unchecked(idx) };
239 if compatible_buffer_info(
240 &item.info,
241 &info,
242 BufferHostMappingCompatibility::Exact,
243 ) {
244 let item = cache.swap_remove(idx);
245
246 return Some(Lease::new(cache_ref.clone(), item));
247 }
248 }
249
250 None
251 }) {
252 return Ok(item);
253 }
254 }
255
256 debug!("Creating new {}", stringify!(Buffer));
257
258 let item = Buffer::create(&self.device, info)?;
259
260 Ok(Lease::new(cache_ref, item))
261 }
262}
263
264impl Pool<CommandBufferInfo, CommandBuffer> for FifoPool {
265 #[profiling::function]
266 fn resource(&mut self, info: CommandBufferInfo) -> Result<Lease<CommandBuffer>, DriverError> {
267 let cache_ref = self
268 .command_buffer_cache
269 .entry(info.queue_family_index)
270 .or_insert_with(PoolConfig::default_cache);
271
272 let item = with_cache(cache_ref, Vec::pop).map(Ok).unwrap_or_else(|| {
273 debug!("Creating new {}", stringify!(CommandBuffer));
274
275 CommandBuffer::create(&self.device, info)
276 })?;
277
278 Ok(Lease::new(Arc::downgrade(cache_ref), item))
282 }
283}
284
285impl Pool<DescriptorPoolInfo, DescriptorPool> for FifoPool {
286 #[profiling::function]
287 fn resource(&mut self, info: DescriptorPoolInfo) -> Result<Lease<DescriptorPool>, DriverError> {
288 let cache_ref = Arc::downgrade(&self.descriptor_pool_cache);
289
290 {
291 profiling::scope!("check cache");
292
293 if let Some(item) = with_cache(&self.descriptor_pool_cache, |cache| {
294 for idx in 0..cache.len() {
296 let item = unsafe { cache.get_unchecked(idx) };
297 if item.info.max_sets >= info.max_sets
298 && item.info.acceleration_structure_count
299 >= info.acceleration_structure_count
300 && item.info.combined_image_sampler_count
301 >= info.combined_image_sampler_count
302 && item.info.input_attachment_count >= info.input_attachment_count
303 && item.info.sampled_image_count >= info.sampled_image_count
304 && item.info.sampler_count >= info.sampler_count
305 && item.info.storage_buffer_count >= info.storage_buffer_count
306 && item.info.storage_buffer_dynamic_count
307 >= info.storage_buffer_dynamic_count
308 && item.info.storage_image_count >= info.storage_image_count
309 && item.info.storage_texel_buffer_count >= info.storage_texel_buffer_count
310 && item.info.uniform_buffer_count >= info.uniform_buffer_count
311 && item.info.uniform_buffer_dynamic_count
312 >= info.uniform_buffer_dynamic_count
313 && item.info.uniform_texel_buffer_count >= info.uniform_texel_buffer_count
314 {
315 let item = cache.swap_remove(idx);
316
317 return Some(Lease::new(cache_ref.clone(), item));
318 }
319 }
320
321 None
322 }) {
323 return Ok(item);
324 }
325 }
326
327 debug!("Creating new {}", stringify!(DescriptorPool));
328
329 let item = DescriptorPool::create(&self.device, info)?;
330
331 Ok(Lease::new(cache_ref, item))
332 }
333}
334
335impl Pool<ImageInfo, Image> for FifoPool {
336 #[profiling::function]
337 fn resource(&mut self, info: ImageInfo) -> Result<Lease<Image>, DriverError> {
338 let cache_ref = Arc::downgrade(&self.image_cache);
339
340 {
341 profiling::scope!("check cache");
342
343 if let Some(item) = with_cache(&self.image_cache, |cache| {
344 for idx in 0..cache.len() {
347 let item = unsafe { cache.get_unchecked(idx) };
348 if compatible_fifo_image_info(&item.info, &info) {
349 let item = cache.swap_remove(idx);
350
351 return Some(Lease::new(cache_ref.clone(), item));
352 }
353 }
354
355 None
356 }) {
357 return Ok(item);
358 }
359 }
360
361 debug!("Creating new {}", stringify!(Image));
362
363 let item = Image::create(&self.device, info)?;
364
365 Ok(Lease::new(cache_ref, item))
366 }
367}
368
369impl Pool<RenderPassInfo, RenderPass> for FifoPool {
370 #[profiling::function]
371 fn resource(&mut self, info: RenderPassInfo) -> Result<Lease<RenderPass>, DriverError> {
372 let cache_ref = if let Some(cache) = self.render_pass_cache.get(&info) {
373 cache
374 } else {
375 self.render_pass_cache
377 .entry(info.clone())
378 .or_insert_with(PoolConfig::default_cache)
379 };
380 let item = with_cache(cache_ref, |cache| cache.pop())
381 .map(Ok)
382 .unwrap_or_else(|| {
383 debug!("Creating new {}", stringify!(RenderPass));
384
385 RenderPass::create(&self.device, info)
386 })?;
387
388 Ok(Lease::new(Arc::downgrade(cache_ref), item))
389 }
390}
391
392#[cfg(test)]
393mod test {
394 use {
395 super::*,
396 crate::{
397 driver::device::{Device, DeviceInfo},
398 pool::garbage_collector::GarbageCollector,
399 },
400 ash::vk,
401 };
402
403 #[test]
404 #[ignore = "requires Vulkan device"]
405 fn vulkan_garbage_collector_retains_supported_fifo_resources() -> Result<(), DriverError> {
406 let device = Device::create(DeviceInfo::default())?;
407 let mut collector = GarbageCollector::new(FifoPool::with_capacity(&device, 4));
408 let retained_info = BufferInfo::device_mem(64, vk::BufferUsageFlags::TRANSFER_SRC);
409 let removed_info = BufferInfo::device_mem(64, vk::BufferUsageFlags::STORAGE_BUFFER);
410
411 drop(collector.resource(retained_info)?);
412 drop(collector.resource(removed_info)?);
413 collector.collect_resources();
414 assert_eq!(with_cache(&collector.buffer_cache, |cache| cache.len()), 2);
415
416 drop(collector.resource(retained_info)?);
417 collector.collect_resources();
418 assert_eq!(with_cache(&collector.buffer_cache, |cache| cache.len()), 1);
419
420 collector.collect_resources();
421 assert_eq!(with_cache(&collector.buffer_cache, |cache| cache.len()), 0);
422
423 Ok(())
424 }
425}