screen_13/pool/
hash.rs

1//! Pool which leases by exactly matching the information before creating new resources.
2
3use {
4    super::{Cache, Lease, Pool, PoolInfo, lease_command_buffer},
5    crate::driver::{
6        CommandBuffer, CommandBufferInfo, DescriptorPool, DescriptorPoolInfo, DriverError,
7        RenderPass, RenderPassInfo,
8        accel_struct::{AccelerationStructure, AccelerationStructureInfo},
9        buffer::{Buffer, BufferInfo},
10        device::Device,
11        image::{Image, ImageInfo},
12    },
13    log::debug,
14    paste::paste,
15    std::{collections::HashMap, sync::Arc},
16};
17
18#[cfg(feature = "parking_lot")]
19use parking_lot::Mutex;
20
21#[cfg(not(feature = "parking_lot"))]
22use std::sync::Mutex;
23
24/// A high-performance resource allocator.
25///
26/// # Bucket Strategy
27///
28/// The information for each lease request is the key for a `HashMap` of buckets. If no bucket
29/// exists with the exact information provided a new bucket is created.
30///
31/// In practice this means that for a [`PoolInfo::image_capacity`] of `4`, requests for a 1024x1024
32/// image with certain attributes will store a maximum of `4` such images. Requests for any image
33/// having a different size or attributes will store an additional maximum of `4` images.
34///
35/// # Memory Management
36///
37/// If requests for varying resources is common [`HashPool::clear_images_by_info`] and other memory
38/// management functions are nessecery in order to avoid using all available device memory.
39#[derive(Debug)]
40pub struct HashPool {
41    acceleration_structure_cache: HashMap<AccelerationStructureInfo, Cache<AccelerationStructure>>,
42    buffer_cache: HashMap<BufferInfo, Cache<Buffer>>,
43    command_buffer_cache: HashMap<u32, Cache<CommandBuffer>>,
44    descriptor_pool_cache: HashMap<DescriptorPoolInfo, Cache<DescriptorPool>>,
45    device: Arc<Device>,
46    image_cache: HashMap<ImageInfo, Cache<Image>>,
47    info: PoolInfo,
48    render_pass_cache: HashMap<RenderPassInfo, Cache<RenderPass>>,
49}
50
51impl HashPool {
52    /// Constructs a new `HashPool`.
53    pub fn new(device: &Arc<Device>) -> Self {
54        Self::with_capacity(device, PoolInfo::default())
55    }
56
57    /// Constructs a new `HashPool` with the given capacity information.
58    pub fn with_capacity(device: &Arc<Device>, info: impl Into<PoolInfo>) -> Self {
59        let info: PoolInfo = info.into();
60        let device = Arc::clone(device);
61
62        Self {
63            acceleration_structure_cache: Default::default(),
64            buffer_cache: Default::default(),
65            command_buffer_cache: Default::default(),
66            descriptor_pool_cache: Default::default(),
67            device,
68            image_cache: Default::default(),
69            info,
70            render_pass_cache: Default::default(),
71        }
72    }
73
74    /// Clears the pool, removing all resources.
75    pub fn clear(&mut self) {
76        self.clear_accel_structs();
77        self.clear_buffers();
78        self.clear_images();
79    }
80}
81
82macro_rules! resource_mgmt_fns {
83    ($fn_plural:literal, $doc_singular:literal, $ty:ty, $field:ident) => {
84        paste! {
85            impl HashPool {
86                #[doc = "Clears the pool of " $doc_singular " resources."]
87                pub fn [<clear_ $fn_plural>](&mut self) {
88                    self.$field.clear();
89                }
90
91                #[doc = "Clears the pool of all " $doc_singular " resources matching the given
92information."]
93                pub fn [<clear_ $fn_plural _by_info>](
94                    &mut self,
95                    info: impl Into<$ty>,
96                ) {
97                    self.$field.remove(&info.into());
98                }
99
100                #[doc = "Retains only the " $doc_singular " resources specified by the predicate.\n
101\nIn other words, remove all " $doc_singular " resources for which `f(" $ty ")` returns `false`.\n
102\n"]
103                /// The elements are visited in unsorted (and unspecified) order.
104                ///
105                /// # Performance
106                ///
107                /// Provides the same performance guarantees as
108                /// [`HashMap::retain`](HashMap::retain).
109                pub fn [<retain_ $fn_plural>]<F>(&mut self, mut f: F)
110                where
111                    F: FnMut($ty) -> bool,
112                {
113                    self.$field.retain(|&info, _| f(info))
114                }
115            }
116        }
117    };
118}
119
120resource_mgmt_fns!(
121    "accel_structs",
122    "acceleration structure",
123    AccelerationStructureInfo,
124    acceleration_structure_cache
125);
126resource_mgmt_fns!("buffers", "buffer", BufferInfo, buffer_cache);
127resource_mgmt_fns!("images", "image", ImageInfo, image_cache);
128
129impl Pool<CommandBufferInfo, CommandBuffer> for HashPool {
130    #[profiling::function]
131    fn lease(&mut self, info: CommandBufferInfo) -> Result<Lease<CommandBuffer>, DriverError> {
132        let cache_ref = self
133            .command_buffer_cache
134            .entry(info.queue_family_index)
135            .or_insert_with(PoolInfo::default_cache);
136        let mut item = {
137            #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
138            let mut cache = cache_ref.lock();
139
140            #[cfg(not(feature = "parking_lot"))]
141            let mut cache = cache.unwrap();
142
143            lease_command_buffer(&mut cache)
144        }
145        .map(Ok)
146        .unwrap_or_else(|| {
147            debug!("Creating new {}", stringify!(CommandBuffer));
148
149            CommandBuffer::create(&self.device, info)
150        })?;
151
152        // Drop anything we were holding from the last submission
153        CommandBuffer::drop_fenced(&mut item);
154
155        Ok(Lease::new(Arc::downgrade(cache_ref), item))
156    }
157}
158
159impl Pool<DescriptorPoolInfo, DescriptorPool> for HashPool {
160    #[profiling::function]
161    fn lease(&mut self, info: DescriptorPoolInfo) -> Result<Lease<DescriptorPool>, DriverError> {
162        let cache_ref = self
163            .descriptor_pool_cache
164            .entry(info.clone())
165            .or_insert_with(PoolInfo::default_cache);
166        let item = {
167            #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
168            let mut cache = cache_ref.lock();
169
170            #[cfg(not(feature = "parking_lot"))]
171            let mut cache = cache.unwrap();
172
173            cache.pop()
174        }
175        .map(Ok)
176        .unwrap_or_else(|| {
177            debug!("Creating new {}", stringify!(DescriptorPool));
178
179            DescriptorPool::create(&self.device, info)
180        })?;
181
182        Ok(Lease::new(Arc::downgrade(cache_ref), item))
183    }
184}
185
186impl Pool<RenderPassInfo, RenderPass> for HashPool {
187    #[profiling::function]
188    fn lease(&mut self, info: RenderPassInfo) -> Result<Lease<RenderPass>, DriverError> {
189        let cache_ref = if let Some(cache) = self.render_pass_cache.get(&info) {
190            cache
191        } else {
192            // We tried to get the cache first in order to avoid this clone
193            self.render_pass_cache
194                .entry(info.clone())
195                .or_insert_with(PoolInfo::default_cache)
196        };
197        let item = {
198            #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
199            let mut cache = cache_ref.lock();
200
201            #[cfg(not(feature = "parking_lot"))]
202            let mut cache = cache.unwrap();
203
204            cache.pop()
205        }
206        .map(Ok)
207        .unwrap_or_else(|| {
208            debug!("Creating new {}", stringify!(RenderPass));
209
210            RenderPass::create(&self.device, info)
211        })?;
212
213        Ok(Lease::new(Arc::downgrade(cache_ref), item))
214    }
215}
216
217// Enable leasing items using their basic info
218macro_rules! lease {
219    ($info:ident => $item:ident, $capacity:ident) => {
220        paste::paste! {
221            impl Pool<$info, $item> for HashPool {
222                #[profiling::function]
223                fn lease(&mut self, info: $info) -> Result<Lease<$item>, DriverError> {
224                    let cache_ref = self.[<$item:snake _cache>].entry(info)
225                        .or_insert_with(|| {
226                            Cache::new(Mutex::new(Vec::with_capacity(self.info.$capacity)))
227                        });
228                    let item = {
229                        #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))]
230                        let mut cache = cache_ref.lock();
231
232                        #[cfg(not(feature = "parking_lot"))]
233                        let mut cache = cache.unwrap();
234
235                        cache.pop()
236                    }
237                    .map(Ok)
238                    .unwrap_or_else(|| {
239                        debug!("Creating new {}", stringify!($item));
240
241                        $item::create(&self.device, info)
242                    })?;
243
244                    Ok(Lease::new(Arc::downgrade(cache_ref), item))
245                }
246            }
247        }
248    };
249}
250
251lease!(AccelerationStructureInfo => AccelerationStructure, accel_struct_capacity);
252lease!(BufferInfo => Buffer, buffer_capacity);
253lease!(ImageInfo => Image, image_capacity);