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}