Skip to main content

vk_graph/pool/
cache.rs

1//! Pool wrapper which enables memory-efficient resource caching.
2
3use {
4    super::{
5        BufferHostMappingCompatibility, Lease, Pool, compatible_buffer_info, compatible_image_info,
6    },
7    crate::driver::{
8        DriverError,
9        accel_struct::{AccelerationStructure, AccelerationStructureInfo},
10        buffer::{Buffer, BufferInfo},
11        image::{Image, ImageInfo},
12    },
13    log::debug,
14    std::{
15        collections::HashMap,
16        hash::Hash,
17        ops::{Deref, DerefMut},
18        sync::{Arc, Weak},
19    },
20};
21
22#[derive(Default)]
23struct AliasSet {
24    accel_structs: Vec<(
25        AccelerationStructureInfo,
26        Weak<Lease<AccelerationStructure>>,
27    )>,
28    buffers: Vec<(BufferInfo, Weak<Lease<Buffer>>)>,
29    images: Vec<(ImageInfo, Weak<Lease<Image>>)>,
30}
31
32/// A memory-efficient resource cache for any [`Pool`] type.
33///
34/// Use [`Cache::tag`] to create a tag-scoped view that caches resources independently from other
35/// tags. Untagged access still behaves like the default cache wrapper.
36///
37/// # Examples
38///
39/// ```no_run
40/// # use vk_graph::driver::device::{Device, DeviceInfo};
41/// # use vk_graph::driver::image::ImageInfo;
42/// # use vk_graph::pool::cache::Cache;
43/// # use vk_graph::pool::hash::HashPool;
44/// # fn main() {
45/// # let device = Device::create(DeviceInfo::default()).unwrap();
46/// # let mut cache = Cache::new(HashPool::new(&device));
47/// let mut shadow = cache.tag("shadow");
48/// let image = shadow
49///     .resource(ImageInfo::image_2d(
50///         32,
51///         32,
52///         ash::vk::Format::R8G8B8A8_UNORM,
53///         ash::vk::ImageUsageFlags::SAMPLED,
54///     ))
55///     .unwrap();
56/// # let _ = image;
57/// # }
58/// ```
59pub struct Cache<T, Tag = ()> {
60    aliases: HashMap<Tag, AliasSet>,
61    pool: T,
62}
63
64/// A tag-scoped cache view.
65pub struct TaggedCache<'a, T, Tag> {
66    cache: &'a mut Cache<T, Tag>,
67    tag: Tag,
68}
69
70impl<T, Tag> Cache<T, Tag>
71where
72    Tag: Eq + Hash,
73{
74    /// Creates a new cache wrapper over the given pool.
75    pub fn new(pool: T) -> Self {
76        Self {
77            aliases: Default::default(),
78            pool,
79        }
80    }
81
82    /// Returns a tag-scoped cache view.
83    pub fn tag(&mut self, tag: Tag) -> TaggedCache<'_, T, Tag> {
84        TaggedCache { cache: self, tag }
85    }
86
87    fn alias_set(&mut self, tag: Tag) -> &mut AliasSet {
88        self.aliases.entry(tag).or_default()
89    }
90
91    fn resource_accel_struct_tagged(
92        &mut self,
93        tag: Tag,
94        info: AccelerationStructureInfo,
95    ) -> Result<Arc<Lease<AccelerationStructure>>, DriverError>
96    where
97        Tag: Clone,
98        T: Pool<AccelerationStructureInfo, AccelerationStructure>,
99    {
100        let mut result = None;
101
102        {
103            let state = self.alias_set(tag.clone());
104            state
105                .accel_structs
106                .retain(|(_, item)| item.strong_count() > 0);
107
108            profiling::scope!("check aliases");
109
110            for (item_info, item) in &state.accel_structs {
111                if item_info.acceleration_structure_type == info.acceleration_structure_type
112                    && item_info.size >= info.size
113                    && let Some(item) = item.upgrade()
114                {
115                    result = Some(item);
116                    break;
117                }
118            }
119        }
120
121        if let Some(item) = result {
122            return Ok(item);
123        }
124
125        debug!("Leasing new {}", stringify!(AccelerationStructure));
126
127        let item = Arc::new(self.pool.resource(info)?);
128        self.alias_set(tag)
129            .accel_structs
130            .push((info, Arc::downgrade(&item)));
131
132        Ok(item)
133    }
134
135    fn resource_buffer_tagged(
136        &mut self,
137        tag: Tag,
138        info: BufferInfo,
139    ) -> Result<Arc<Lease<Buffer>>, DriverError>
140    where
141        Tag: Clone,
142        T: Pool<BufferInfo, Buffer>,
143    {
144        let mut result = None;
145
146        {
147            let state = self.alias_set(tag.clone());
148            state.buffers.retain(|(_, item)| item.strong_count() > 0);
149
150            profiling::scope!("check aliases");
151
152            for (item_info, item) in &state.buffers {
153                if compatible_buffer_info(item_info, &info, BufferHostMappingCompatibility::Exact)
154                    && let Some(item) = item.upgrade()
155                {
156                    result = Some(item);
157                    break;
158                }
159            }
160        }
161
162        if let Some(item) = result {
163            return Ok(item);
164        }
165
166        debug!("Leasing new {}", stringify!(Buffer));
167
168        let item = Arc::new(self.pool.resource(info)?);
169        self.alias_set(tag)
170            .buffers
171            .push((info, Arc::downgrade(&item)));
172
173        Ok(item)
174    }
175
176    fn resource_image_tagged(
177        &mut self,
178        tag: Tag,
179        info: ImageInfo,
180    ) -> Result<Arc<Lease<Image>>, DriverError>
181    where
182        Tag: Clone,
183        T: Pool<ImageInfo, Image>,
184    {
185        let mut result = None;
186
187        {
188            let state = self.alias_set(tag.clone());
189            state.images.retain(|(_, item)| item.strong_count() > 0);
190
191            profiling::scope!("check aliases");
192
193            for (item_info, item) in &state.images {
194                if compatible_image_info(item_info, &info)
195                    && let Some(item) = item.upgrade()
196                {
197                    result = Some(item);
198                    break;
199                }
200            }
201        }
202
203        if let Some(item) = result {
204            return Ok(item);
205        }
206
207        debug!("Leasing new {}", stringify!(Image));
208
209        let item = Arc::new(self.pool.resource(info)?);
210        self.alias_set(tag)
211            .images
212            .push((info, Arc::downgrade(&item)));
213
214        Ok(item)
215    }
216}
217
218impl<T> Cache<T, ()>
219where
220    T: Pool<AccelerationStructureInfo, AccelerationStructure>
221        + Pool<BufferInfo, Buffer>
222        + Pool<ImageInfo, Image>,
223{
224    /// Alias an acceleration structure using the default tag.
225    pub fn accel_struct(
226        &mut self,
227        info: AccelerationStructureInfo,
228    ) -> Result<Arc<Lease<AccelerationStructure>>, DriverError> {
229        self.resource_accel_struct_tagged((), info)
230    }
231
232    /// Alias a buffer using the default tag.
233    pub fn buffer(&mut self, info: BufferInfo) -> Result<Arc<Lease<Buffer>>, DriverError> {
234        self.resource_buffer_tagged((), info)
235    }
236
237    /// Alias an image using the default tag.
238    pub fn image(&mut self, info: ImageInfo) -> Result<Arc<Lease<Image>>, DriverError> {
239        self.resource_image_tagged((), info)
240    }
241}
242
243impl<'a, T, Tag> TaggedCache<'a, T, Tag>
244where
245    Tag: Eq + Hash + Clone,
246{
247    /// Alias a resource using this cache tag.
248    pub fn resource<I>(&mut self, info: I) -> Result<Arc<Lease<I::Item>>, DriverError>
249    where
250        I: TaggedCacheResource<Tag>,
251        T: Pool<I, I::Item>,
252    {
253        I::resource(self.cache, self.tag.clone(), info)
254    }
255}
256
257#[allow(private_bounds)]
258#[doc(hidden)]
259pub trait TaggedCacheResource<Tag>: cache_private::TaggedCacheResourceSealed + Sized {
260    type Item;
261
262    fn resource<T>(
263        cache: &mut Cache<T, Tag>,
264        tag: Tag,
265        info: Self,
266    ) -> Result<Arc<Lease<Self::Item>>, DriverError>
267    where
268        Tag: Eq + Hash + Clone,
269        T: Pool<Self, Self::Item>;
270}
271
272mod cache_private {
273    pub trait TaggedCacheResourceSealed {}
274}
275
276impl cache_private::TaggedCacheResourceSealed for AccelerationStructureInfo {}
277
278impl cache_private::TaggedCacheResourceSealed for BufferInfo {}
279
280impl cache_private::TaggedCacheResourceSealed for ImageInfo {}
281
282impl<Tag> TaggedCacheResource<Tag> for AccelerationStructureInfo
283where
284    Tag: Eq + Hash + Clone,
285{
286    type Item = AccelerationStructure;
287
288    fn resource<T>(
289        cache: &mut Cache<T, Tag>,
290        tag: Tag,
291        info: Self,
292    ) -> Result<Arc<Lease<Self::Item>>, DriverError>
293    where
294        T: Pool<Self, Self::Item>,
295    {
296        cache.resource_accel_struct_tagged(tag, info)
297    }
298}
299
300impl<Tag> TaggedCacheResource<Tag> for BufferInfo
301where
302    Tag: Eq + Hash + Clone,
303{
304    type Item = Buffer;
305
306    fn resource<T>(
307        cache: &mut Cache<T, Tag>,
308        tag: Tag,
309        info: Self,
310    ) -> Result<Arc<Lease<Self::Item>>, DriverError>
311    where
312        T: Pool<Self, Self::Item>,
313    {
314        cache.resource_buffer_tagged(tag, info)
315    }
316}
317
318impl<Tag> TaggedCacheResource<Tag> for ImageInfo
319where
320    Tag: Eq + Hash + Clone,
321{
322    type Item = Image;
323
324    fn resource<T>(
325        cache: &mut Cache<T, Tag>,
326        tag: Tag,
327        info: Self,
328    ) -> Result<Arc<Lease<Self::Item>>, DriverError>
329    where
330        T: Pool<Self, Self::Item>,
331    {
332        cache.resource_image_tagged(tag, info)
333    }
334}
335
336impl<T, Tag> Deref for Cache<T, Tag> {
337    type Target = T;
338
339    fn deref(&self) -> &Self::Target {
340        &self.pool
341    }
342}
343
344impl<T, Tag> DerefMut for Cache<T, Tag> {
345    fn deref_mut(&mut self) -> &mut Self::Target {
346        &mut self.pool
347    }
348}