rafx_framework/graph/
graph_resource_cache.rs

1use crate::graph::graph_buffer::PhysicalBufferId;
2use crate::graph::graph_image::{PhysicalImageId, PhysicalImageViewId};
3use crate::graph::{
4    RenderGraphBufferSpecification, RenderGraphImageSpecification, RenderGraphPlan,
5    SwapchainSurfaceInfo,
6};
7use crate::{BufferResource, ImageResource, ImageViewResource, ResourceArc, ResourceLookupSet};
8use fnv::FnvHashMap;
9use rafx_api::{RafxBufferDef, RafxDeviceContext, RafxMemoryUsage, RafxResult, RafxTextureDef};
10use std::sync::{Arc, Mutex};
11
12#[derive(Clone, Hash, PartialEq, Eq, Debug)]
13struct RenderGraphCachedBufferKey {
14    specification: RenderGraphBufferSpecification,
15}
16
17struct RenderGraphCachedBuffer {
18    keep_until_frame: u64,
19    buffer: ResourceArc<BufferResource>,
20}
21
22#[derive(Clone, Hash, PartialEq, Eq, Debug)]
23struct RenderGraphCachedImageKey {
24    specification: RenderGraphImageSpecification,
25    swapchain_surface_info: SwapchainSurfaceInfo,
26}
27
28struct RenderGraphCachedImage {
29    keep_until_frame: u64,
30    image: ResourceArc<ImageResource>,
31}
32
33pub struct RenderGraphCacheInner {
34    buffers: FnvHashMap<RenderGraphCachedBufferKey, Vec<RenderGraphCachedBuffer>>,
35    images: FnvHashMap<RenderGraphCachedImageKey, Vec<RenderGraphCachedImage>>,
36    current_frame_index: u64,
37    frames_to_persist: u64,
38}
39
40impl RenderGraphCacheInner {
41    pub fn new(
42        max_frames_in_flight: u32,
43        reuse_resources: bool,
44    ) -> Self {
45        let frames_to_persist = if reuse_resources {
46            (max_frames_in_flight + 1) as u64
47        } else {
48            0
49        };
50
51        RenderGraphCacheInner {
52            buffers: Default::default(),
53            images: Default::default(),
54            current_frame_index: 0,
55            frames_to_persist,
56        }
57    }
58
59    pub fn on_frame_complete(&mut self) {
60        //println!("-- FRAME COMPLETE -- drop framebuffer if keep_until <= {}", self.current_frame_index);
61        let current_frame_index = self.current_frame_index;
62
63        for value in self.buffers.values_mut() {
64            value.retain(|x| x.keep_until_frame > current_frame_index);
65        }
66
67        self.buffers.retain(|_k, v| !v.is_empty());
68
69        for value in self.images.values_mut() {
70            value.retain(|x| x.keep_until_frame > current_frame_index);
71        }
72
73        self.images.retain(|_k, v| !v.is_empty());
74
75        self.current_frame_index += 1;
76    }
77
78    pub fn clear(&mut self) {
79        self.buffers.clear();
80        self.images.clear();
81    }
82
83    pub(super) fn allocate_buffers(
84        &mut self,
85        device_context: &RafxDeviceContext,
86        graph: &RenderGraphPlan,
87        resources: &ResourceLookupSet,
88    ) -> RafxResult<FnvHashMap<PhysicalBufferId, ResourceArc<BufferResource>>> {
89        log::trace!("Allocate buffers for rendergraph");
90        let mut buffer_resources: FnvHashMap<PhysicalBufferId, ResourceArc<BufferResource>> =
91            Default::default();
92
93        // Keeps track of what index in the cache we will use next. This starts at 0 for each key
94        // and increments every time we use an image. If the next image is >= length of buffers, we
95        // allocate one and push it into that key's list of cached buffers
96        let mut next_buffer_to_use = FnvHashMap::<RenderGraphCachedBufferKey, usize>::default();
97
98        // Using a buffer will bump the keep_until_frame for that buffer
99        let keep_until_frame = self.current_frame_index + self.frames_to_persist;
100
101        for (&physical_id, buffer) in &graph.external_buffers {
102            buffer_resources.insert(physical_id, buffer.resource.clone());
103        }
104
105        // Iterate all intermediate buffers, assigning an existing buffer from a previous frame or
106        // allocating a new one
107        for (&id, specification) in &graph.intermediate_buffers {
108            let key = RenderGraphCachedBufferKey {
109                specification: specification.clone(),
110            };
111
112            let next_buffer_index = next_buffer_to_use.entry(key.clone()).or_insert(0);
113            let matching_cached_buffers = self
114                .buffers
115                .entry(key.clone())
116                .or_insert_with(Default::default);
117
118            if let Some(cached_buffer) = matching_cached_buffers.get_mut(*next_buffer_index) {
119                log::trace!(
120                    "  Buffer {:?} - REUSE {:?}  (key: {:?}, index: {})",
121                    id,
122                    cached_buffer.buffer,
123                    key,
124                    next_buffer_index
125                );
126
127                // Reuse a buffer from a previous frame, bump keep_until_frame
128                cached_buffer.keep_until_frame = keep_until_frame;
129                *next_buffer_index += 1;
130
131                if device_context.device_info().debug_names_enabled {
132                    cached_buffer
133                        .buffer
134                        .get_raw()
135                        .buffer
136                        .set_debug_name(&format!("RenderGraph Buffer {:?}", id));
137                }
138
139                buffer_resources.insert(id, cached_buffer.buffer.clone());
140            } else {
141                // No unused buffer available, create one
142                let buffer = device_context.create_buffer(&RafxBufferDef {
143                    size: key.specification.size,
144                    //alignment: key.specification.alignment,
145                    memory_usage: RafxMemoryUsage::GpuOnly,
146                    resource_type: key.specification.resource_type,
147                    //initial_state: key.specification.initial_state,
148                    ..Default::default()
149                })?;
150                if device_context.device_info().debug_names_enabled {
151                    buffer.set_debug_name(&format!("RenderGraph Buffer {:?}", id));
152                }
153                let buffer = resources.insert_buffer(buffer);
154
155                log::trace!(
156                    "  Buffer {:?} - CREATE {:?}  (key: {:?}, index: {})",
157                    id,
158                    buffer.get_raw().buffer,
159                    key,
160                    next_buffer_index
161                );
162
163                // Add the buffer to the cache
164                debug_assert_eq!(matching_cached_buffers.len(), *next_buffer_index);
165                matching_cached_buffers.push(RenderGraphCachedBuffer {
166                    keep_until_frame,
167                    buffer: buffer.clone(),
168                });
169                *next_buffer_index += 1;
170
171                // Associate the physical id with this buffer
172                buffer_resources.insert(id, buffer);
173            }
174        }
175
176        Ok(buffer_resources)
177    }
178
179    pub(super) fn allocate_images(
180        &mut self,
181        device_context: &RafxDeviceContext,
182        graph: &RenderGraphPlan,
183        resources: &ResourceLookupSet,
184        swapchain_surface_info: &SwapchainSurfaceInfo,
185    ) -> RafxResult<FnvHashMap<PhysicalImageId, ResourceArc<ImageResource>>> {
186        log::trace!("Allocate images for rendergraph");
187        let mut image_resources: FnvHashMap<PhysicalImageId, ResourceArc<ImageResource>> =
188            Default::default();
189
190        // Keeps track of what index in the cache we will use next. This starts at 0 for each key
191        // and increments every time we use an image. If the next image is >= length of images, we
192        // allocate one and push it into that key's list of cached images
193        let mut next_image_to_use = FnvHashMap::<RenderGraphCachedImageKey, usize>::default();
194
195        // Using an image will bump the keep_until_frame for that image
196        let keep_until_frame = self.current_frame_index + self.frames_to_persist;
197
198        for (id, image) in &graph.external_images {
199            let physical_id = graph.image_views[id.0].physical_image;
200            image_resources.insert(physical_id, image.resource.get_raw().image);
201        }
202
203        // Iterate all intermediate images, assigning an existing image from a previous frame or
204        // allocating a new one
205        for (&id, specification) in &graph.intermediate_images {
206            let key = RenderGraphCachedImageKey {
207                specification: specification.clone(),
208                swapchain_surface_info: swapchain_surface_info.clone(),
209            };
210
211            let next_image_index = next_image_to_use.entry(key.clone()).or_insert(0);
212            let matching_cached_images = self
213                .images
214                .entry(key.clone())
215                .or_insert_with(Default::default);
216
217            if let Some(cached_image) = matching_cached_images.get_mut(*next_image_index) {
218                log::trace!(
219                    "  Image {:?} - REUSE {:?}  (key: {:?}, index: {})",
220                    id,
221                    cached_image.image.get_raw().image,
222                    key,
223                    next_image_index
224                );
225
226                // Reuse an image from a previous frame, bump keep_until_frame
227                cached_image.keep_until_frame = keep_until_frame;
228                *next_image_index += 1;
229
230                if device_context.device_info().debug_names_enabled {
231                    cached_image
232                        .image
233                        .get_raw()
234                        .image
235                        .set_debug_name(&format!("RenderGraph Image {:?}", id));
236                }
237                image_resources.insert(id, cached_image.image.clone());
238            } else {
239                // No unused image available, create one
240                let extents = key.specification.extents;
241
242                let image = device_context.create_texture(&RafxTextureDef {
243                    extents,
244                    array_length: specification.layer_count,
245                    mip_count: specification.mip_count,
246                    format: specification.format,
247                    sample_count: specification.samples,
248                    resource_type: specification.resource_type,
249                    dimensions: Default::default(),
250                })?;
251                if device_context.device_info().debug_names_enabled {
252                    image.set_debug_name(&format!("RenderGraph Image {:?}", id));
253                }
254                let image = resources.insert_image(image);
255
256                log::trace!(
257                    "  Image {:?} - CREATE {:?}  (key: {:?}, index: {})",
258                    id,
259                    image.get_raw().image,
260                    key,
261                    next_image_index
262                );
263
264                // Add the image to the cache
265                debug_assert_eq!(matching_cached_images.len(), *next_image_index);
266                matching_cached_images.push(RenderGraphCachedImage {
267                    keep_until_frame,
268                    image: image.clone(),
269                });
270                *next_image_index += 1;
271
272                // Associate the physical id with this image
273                image_resources.insert(id, image);
274            }
275        }
276
277        Ok(image_resources)
278    }
279
280    pub(super) fn allocate_image_views(
281        &mut self,
282        graph: &RenderGraphPlan,
283        resources: &ResourceLookupSet,
284        image_resources: &FnvHashMap<PhysicalImageId, ResourceArc<ImageResource>>,
285    ) -> RafxResult<FnvHashMap<PhysicalImageViewId, ResourceArc<ImageViewResource>>> {
286        let mut image_view_resources: FnvHashMap<
287            PhysicalImageViewId,
288            ResourceArc<ImageViewResource>,
289        > = Default::default();
290
291        // For output images, the physical id just needs to be associated with the image provided by
292        // the user
293        for (id, image) in &graph.external_images {
294            image_view_resources.insert(*id, image.resource.clone());
295        }
296
297        for (id, view) in graph.image_views.iter().enumerate() {
298            let id = PhysicalImageViewId(id);
299
300            // Skip output images (handled above). They already have ImageViewResources
301            if image_view_resources.contains_key(&id) {
302                continue;
303            }
304
305            log::trace!("get_or_create_image_view for {:?}", view.physical_image);
306            let image_resource = &image_resources[&view.physical_image];
307
308            let old = image_view_resources.insert(
309                id,
310                resources.get_or_create_image_view(
311                    image_resource,
312                    view.view_options.texture_bind_type,
313                )?,
314            );
315            assert!(old.is_none());
316        }
317
318        Ok(image_view_resources)
319    }
320}
321
322#[derive(Clone)]
323pub struct RenderGraphCache {
324    pub(super) inner: Arc<Mutex<RenderGraphCacheInner>>,
325}
326
327impl RenderGraphCache {
328    pub fn new(
329        max_frames_in_flight: u32,
330        reuse_resources: bool,
331    ) -> Self {
332        let inner = RenderGraphCacheInner::new(max_frames_in_flight, reuse_resources);
333        RenderGraphCache {
334            inner: Arc::new(Mutex::new(inner)),
335        }
336    }
337
338    #[profiling::function]
339    pub fn on_frame_complete(&self) {
340        self.inner.lock().unwrap().on_frame_complete();
341    }
342
343    pub fn clear(&self) {
344        self.inner.lock().unwrap().clear();
345    }
346}