rafx_framework/resources/
cleanup.rs

1use rafx_api::RafxResult;
2use std::collections::VecDeque;
3use std::num::Wrapping;
4
5struct DropSinkResourceInFlight<T> {
6    // prefixed with _ to silence "field not used" warning. The purpose of the var is to hold the
7    // resource for a while then drop this entire structure
8    _resource: T,
9    live_until_frame: Wrapping<u32>,
10}
11
12/// This handles waiting for N frames to pass before dropping the resource.
13pub struct ResourceDropSink<T> {
14    // We are assuming that all resources can survive for the same amount of time so the data in
15    // this VecDeque will naturally be orderered such that things that need to be destroyed sooner
16    // are at the front
17    resources_in_flight: VecDeque<DropSinkResourceInFlight<T>>,
18
19    // All resources pushed into the sink will be destroyed after N frames
20    max_in_flight_frames: Wrapping<u32>,
21
22    // Incremented when on_frame_complete is called
23    frame_index: Wrapping<u32>,
24}
25
26impl<T> ResourceDropSink<T> {
27    /// Create a drop sink that will destroy resources after N frames. Keep in mind that if for
28    /// example you want to push a single resource per frame, up to N+1 resources will exist
29    /// in the sink. If max_in_flight_frames is 2, then you would have a resource that has
30    /// likely not been submitted to the GPU yet, plus a resource per the N frames that have
31    /// been submitted
32    pub fn new(max_in_flight_frames: u32) -> Self {
33        ResourceDropSink {
34            resources_in_flight: Default::default(),
35            max_in_flight_frames: Wrapping(max_in_flight_frames),
36            frame_index: Wrapping(0),
37        }
38    }
39
40    /// Schedule the resource to drop after we complete N frames
41    pub fn retire(
42        &mut self,
43        resource: T,
44    ) {
45        self.resources_in_flight
46            .push_back(DropSinkResourceInFlight::<T> {
47                _resource: resource,
48                live_until_frame: self.frame_index + self.max_in_flight_frames,
49            });
50    }
51
52    /// Call when we are ready to drop another set of resources, most likely when a frame is
53    /// presented or a new frame begins
54    pub fn on_frame_complete(&mut self) -> RafxResult<()> {
55        self.frame_index += Wrapping(1);
56
57        // Determine how many resources we should drain
58        let mut resources_to_drop = 0;
59        for resource_in_flight in &self.resources_in_flight {
60            // If frame_index matches or exceeds live_until_frame, then the result will be a very
61            // high value due to wrapping a negative value to u32::MAX
62            if resource_in_flight.live_until_frame - self.frame_index > Wrapping(std::u32::MAX / 2)
63            {
64                resources_to_drop += 1;
65            } else {
66                break;
67            }
68        }
69
70        // Reset them and add them to the list of pools ready to be allocated
71        let resources_to_drop: Vec<_> = self
72            .resources_in_flight
73            .drain(0..resources_to_drop)
74            .collect();
75
76        std::mem::drop(resources_to_drop);
77
78        Ok(())
79    }
80
81    /// Immediately destroy everything. We assume the device is idle and nothing is in flight.
82    /// Calling this function when the device is not idle could result in a deadlock
83    pub fn destroy(&mut self) -> RafxResult<()> {
84        for resource in self.resources_in_flight.drain(..) {
85            std::mem::drop(resource);
86        }
87
88        Ok(())
89    }
90}
91
92// We assume destroy was called
93impl<T> Drop for ResourceDropSink<T> {
94    fn drop(&mut self) {
95        assert!(self.resources_in_flight.is_empty())
96    }
97}