Skip to main content

vk_graph/pool/
cache.rs

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