rafx_framework/resources/
dyn_resources.rs

1use super::ResourceId;
2use crate::resources::resource_arc::ResourceWithHash;
3use crate::resources::resource_lookup::ImageResource;
4use crate::resources::ResourceArc;
5use crate::ResourceDropSink;
6use crate::{BufferResource, ImageViewResource};
7use crossbeam_channel::{Receiver, Sender};
8use rafx_api::RafxTexture;
9use rafx_api::{RafxBuffer, RafxDeviceContext, RafxResult, RafxTextureBindType};
10use std::hash::Hash;
11use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
12use std::sync::Arc;
13
14#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
15pub struct DynResourceIndex(u64);
16
17impl From<ResourceId> for DynResourceIndex {
18    fn from(resource_id: ResourceId) -> Self {
19        DynResourceIndex(resource_id.0)
20    }
21}
22
23impl Into<ResourceId> for DynResourceIndex {
24    fn into(self) -> ResourceId {
25        ResourceId(self.0)
26    }
27}
28
29//
30// A lookup of dynamic resources. They reference count using Arcs internally and send a signal when they
31// drop. This allows the resources to be collected and disposed of. This is threadsafe and the only
32// sync point is when dropping to send via a channel. (Although VMA memory allocator is probably
33// locking too) As opposed to ResourceLookup which uses mutexes to maintain a lookup map. It's
34// intended for things that get created/thrown away frequently although there is no problem with
35// keeping a resource created through this utility around for a long time.
36//
37pub struct DynResourceAllocatorInner<ResourceT>
38where
39    ResourceT: Clone,
40{
41    drop_tx: Sender<ResourceWithHash<ResourceT>>,
42    next_index: AtomicU64,
43    active_count: Arc<AtomicU32>,
44}
45
46pub struct DynResourceAllocator<ResourceT>
47where
48    ResourceT: Clone,
49{
50    inner: Arc<DynResourceAllocatorInner<ResourceT>>,
51}
52
53impl<ResourceT> DynResourceAllocator<ResourceT>
54where
55    ResourceT: Clone + std::fmt::Debug,
56{
57    fn new(
58        drop_tx: Sender<ResourceWithHash<ResourceT>>,
59        allocator_index: u32,
60        active_count: Arc<AtomicU32>,
61    ) -> Self {
62        let next_index = ((allocator_index as u64) << 32) + 1;
63
64        let inner = DynResourceAllocatorInner {
65            drop_tx,
66            next_index: AtomicU64::new(next_index),
67            active_count,
68        };
69
70        DynResourceAllocator {
71            inner: Arc::new(inner),
72        }
73    }
74
75    fn insert(
76        &self,
77        resource: ResourceT,
78    ) -> ResourceArc<ResourceT> {
79        // This index is not strictly necessary. However, we do want to be compatible with ResourceArc,
80        // and in other usecases a working index is necessary. Since we have the index anyways, we
81        // might as well produce some sort of index if only to make logging easier to follow
82        let resource_index =
83            DynResourceIndex(self.inner.next_index.fetch_add(1, Ordering::Relaxed));
84        self.inner.active_count.fetch_add(1, Ordering::Relaxed);
85
86        log::trace!(
87            "insert resource {} {:?}",
88            core::any::type_name::<ResourceT>(),
89            resource
90        );
91
92        ResourceArc::new(resource, resource_index.into(), self.inner.drop_tx.clone())
93    }
94}
95
96pub struct DynResourceAllocatorManagerInner<ResourceT>
97where
98    ResourceT: Clone,
99{
100    drop_tx: Sender<ResourceWithHash<ResourceT>>,
101    drop_rx: Receiver<ResourceWithHash<ResourceT>>,
102    next_allocator_index: AtomicU32,
103    active_count: Arc<AtomicU32>,
104}
105
106impl<ResourceT> DynResourceAllocatorManagerInner<ResourceT>
107where
108    ResourceT: Clone + std::fmt::Debug,
109{
110    fn create_allocator(&self) -> DynResourceAllocator<ResourceT> {
111        let allocator_index = self.next_allocator_index.fetch_add(1, Ordering::Relaxed);
112        DynResourceAllocator::new(
113            self.drop_tx.clone(),
114            allocator_index,
115            self.active_count.clone(),
116        )
117    }
118}
119
120pub struct DynResourceAllocatorProvider<ResourceT>
121where
122    ResourceT: Clone,
123{
124    inner: Arc<DynResourceAllocatorManagerInner<ResourceT>>,
125}
126
127impl<ResourceT> DynResourceAllocatorProvider<ResourceT>
128where
129    ResourceT: Clone + std::fmt::Debug,
130{
131    fn create_allocator(&self) -> DynResourceAllocator<ResourceT> {
132        self.inner.create_allocator()
133    }
134}
135
136pub struct DynResourceAllocatorManager<ResourceT>
137where
138    ResourceT: Clone,
139{
140    inner: Arc<DynResourceAllocatorManagerInner<ResourceT>>,
141    drop_sink: ResourceDropSink<ResourceT>,
142}
143
144impl<ResourceT> DynResourceAllocatorManager<ResourceT>
145where
146    ResourceT: Clone + std::fmt::Debug,
147{
148    fn new(max_frames_in_flight: u32) -> Self {
149        let (drop_tx, drop_rx) = crossbeam_channel::unbounded();
150        let drop_sink = ResourceDropSink::new(max_frames_in_flight);
151
152        let inner = DynResourceAllocatorManagerInner {
153            drop_tx,
154            drop_rx,
155            next_allocator_index: AtomicU32::new(1),
156            active_count: Arc::new(AtomicU32::new(0)),
157        };
158
159        DynResourceAllocatorManager {
160            inner: Arc::new(inner),
161            drop_sink,
162        }
163    }
164
165    fn create_allocator(&self) -> DynResourceAllocator<ResourceT> {
166        self.inner.create_allocator()
167    }
168
169    fn create_allocator_provider(&self) -> DynResourceAllocatorProvider<ResourceT> {
170        DynResourceAllocatorProvider {
171            inner: self.inner.clone(),
172        }
173    }
174
175    fn handle_dropped_resources(&mut self) {
176        for dropped in self.inner.drop_rx.try_iter() {
177            log::trace!(
178                "dropping {} {:?}",
179                core::any::type_name::<ResourceT>(),
180                dropped.resource
181            );
182            self.drop_sink.retire(dropped.resource);
183            self.inner.active_count.fetch_sub(1, Ordering::Relaxed);
184        }
185    }
186
187    #[profiling::function]
188    fn on_frame_complete(&mut self) -> RafxResult<()> {
189        self.handle_dropped_resources();
190        self.drop_sink.on_frame_complete()?;
191        Ok(())
192    }
193
194    fn destroy(&mut self) -> RafxResult<()> {
195        self.handle_dropped_resources();
196
197        if self.len() > 0 {
198            log::warn!(
199                "{} resource count {} > 0, resources will leak",
200                core::any::type_name::<ResourceT>(),
201                self.len()
202            );
203        }
204
205        self.drop_sink.destroy()?;
206        Ok(())
207    }
208
209    fn len(&self) -> usize {
210        self.inner.active_count.load(Ordering::Relaxed) as usize
211    }
212}
213
214// This is for providing per-frame allocation where the resource does not need to be
215pub struct DynResourceAllocatorSet {
216    pub device_context: RafxDeviceContext,
217    pub images: DynResourceAllocator<ImageResource>,
218    pub image_views: DynResourceAllocator<ImageViewResource>,
219    pub buffers: DynResourceAllocator<BufferResource>,
220}
221
222impl DynResourceAllocatorSet {
223    pub fn insert_texture(
224        &self,
225        image: RafxTexture,
226    ) -> ResourceArc<ImageResource> {
227        let image_resource = ImageResource {
228            image_key: None,
229            image,
230        };
231        self.images.insert(image_resource)
232    }
233
234    pub fn insert_image_view(
235        &self,
236        image: &ResourceArc<ImageResource>,
237        texture_bind_type: Option<RafxTextureBindType>,
238    ) -> RafxResult<ResourceArc<ImageViewResource>> {
239        Ok(self.insert_image_view_raw(image.clone(), texture_bind_type))
240    }
241
242    pub fn insert_image_view_raw(
243        &self,
244        image: ResourceArc<ImageResource>,
245        texture_bind_type: Option<RafxTextureBindType>,
246    ) -> ResourceArc<ImageViewResource> {
247        let image_view_resource = ImageViewResource {
248            image,
249            texture_bind_type,
250            image_view_key: None,
251        };
252
253        self.image_views.insert(image_view_resource)
254    }
255
256    pub fn insert_buffer(
257        &self,
258        buffer: RafxBuffer,
259    ) -> ResourceArc<BufferResource> {
260        let buffer_resource = BufferResource {
261            buffer_key: None,
262            buffer: Arc::new(buffer),
263        };
264
265        self.buffers.insert(buffer_resource)
266    }
267}
268
269#[derive(Debug)]
270pub struct ResourceMetrics {
271    pub image_count: usize,
272    pub image_view_count: usize,
273    pub buffer_count: usize,
274}
275
276pub struct DynResourceAllocatorSetProvider {
277    pub device_context: RafxDeviceContext,
278    pub images: DynResourceAllocatorProvider<ImageResource>,
279    pub image_views: DynResourceAllocatorProvider<ImageViewResource>,
280    pub buffers: DynResourceAllocatorProvider<BufferResource>,
281}
282
283impl DynResourceAllocatorSetProvider {
284    pub fn get_allocator(&self) -> DynResourceAllocatorSet {
285        DynResourceAllocatorSet {
286            device_context: self.device_context.clone(),
287            images: self.images.create_allocator(),
288            image_views: self.image_views.create_allocator(),
289            buffers: self.buffers.create_allocator(),
290        }
291    }
292}
293
294pub struct DynResourceAllocatorSetManager {
295    pub device_context: RafxDeviceContext,
296    pub images: DynResourceAllocatorManager<ImageResource>,
297    pub image_views: DynResourceAllocatorManager<ImageViewResource>,
298    pub buffers: DynResourceAllocatorManager<BufferResource>,
299}
300
301impl DynResourceAllocatorSetManager {
302    pub fn new(
303        device_context: &RafxDeviceContext,
304        max_frames_in_flight: u32,
305    ) -> Self {
306        DynResourceAllocatorSetManager {
307            device_context: device_context.clone(),
308            images: DynResourceAllocatorManager::new(max_frames_in_flight),
309            image_views: DynResourceAllocatorManager::new(max_frames_in_flight),
310            buffers: DynResourceAllocatorManager::new(max_frames_in_flight),
311        }
312    }
313
314    pub fn create_allocator_provider(&self) -> DynResourceAllocatorSetProvider {
315        DynResourceAllocatorSetProvider {
316            device_context: self.device_context.clone(),
317            images: self.images.create_allocator_provider(),
318            image_views: self.image_views.create_allocator_provider(),
319            buffers: self.buffers.create_allocator_provider(),
320        }
321    }
322
323    pub fn get_allocator(&self) -> DynResourceAllocatorSet {
324        DynResourceAllocatorSet {
325            device_context: self.device_context.clone(),
326            images: self.images.create_allocator(),
327            image_views: self.image_views.create_allocator(),
328            buffers: self.buffers.create_allocator(),
329        }
330    }
331
332    #[profiling::function]
333    pub fn on_frame_complete(&mut self) -> RafxResult<()> {
334        self.buffers.on_frame_complete()?;
335        self.images.on_frame_complete()?;
336        self.image_views.on_frame_complete()?;
337        Ok(())
338    }
339
340    pub fn destroy(&mut self) -> RafxResult<()> {
341        //WARNING: These need to be in order of dependencies to avoid frame-delays on destroying
342        // resources.
343        self.image_views.destroy()?;
344        self.images.destroy()?;
345        self.buffers.destroy()?;
346        Ok(())
347    }
348
349    pub fn metrics(&self) -> ResourceMetrics {
350        ResourceMetrics {
351            image_count: self.images.len(),
352            image_view_count: self.image_views.len(),
353            buffer_count: self.buffers.len(),
354        }
355    }
356}