1use crate::ResidencyMachineMetrics;
8use molgfx_gpu::{
9 ArenaError, ArenaMetrics, CommandScratch, CommandScratchMetrics, PagedArena,
10 UploadBackpressure, UploadMetrics, UploadRing, UploadRingConfig,
11};
12use thiserror::Error;
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub struct ResidencyConfig {
17 pub page_size: u64,
19 pub page_count: u32,
21 pub uploads: UploadRingConfig,
23 pub command_capacity: usize,
25 pub machine_capacity: usize,
27}
28
29impl Default for ResidencyConfig {
30 fn default() -> Self {
31 const KIB: usize = 1024;
32 Self {
33 page_size: 64 * 1024,
34 page_count: 256,
35 uploads: UploadRingConfig {
36 capacity_bytes: 256 * KIB,
37 ticket_capacity: 256,
38 epoch_budget_bytes: 256 * KIB,
39 in_flight_budget_bytes: 256 * KIB,
40 alignment: 256,
41 },
42 command_capacity: 1024,
43 machine_capacity: 1024,
44 }
45 }
46}
47
48#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
50pub struct ResidencyMetrics {
51 pub arena: ArenaMetrics,
53 pub uploads: UploadMetrics,
55 pub commands: CommandScratchMetrics,
57 pub machine: ResidencyMachineMetrics,
59 pub immutable_assets: ImmutableArenaMetrics,
61}
62
63#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
65pub struct ImmutableArenaMetrics {
66 pub physical_buffers: u32,
68 pub physical_bytes: u64,
70 pub resident_bytes: u64,
72 pub peak_resident_bytes: u64,
74 pub uploaded_bytes: u64,
76 pub writes: u64,
78 pub relocations: u64,
80 pub allocation_stalls: u64,
82}
83
84#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
89pub struct ResidencyCounters {
90 pub allocation_events: u64,
92 pub upload_bytes: u64,
94 pub resident_bytes: u64,
96 pub stall_events: u64,
98}
99
100impl ResidencyMetrics {
101 #[must_use]
103 pub fn counters(self) -> ResidencyCounters {
104 ResidencyCounters {
105 allocation_events: self
106 .arena
107 .host_allocation_events
108 .saturating_add(self.uploads.host_allocation_events)
109 .saturating_add(self.commands.host_allocation_events),
110 upload_bytes: self.uploads.bytes_submitted,
111 resident_bytes: self.arena.resident_bytes,
112 stall_events: self
113 .arena
114 .allocation_stalls
115 .saturating_add(self.uploads.stall_events)
116 .saturating_add(self.commands.capacity_stalls)
117 .saturating_add(self.machine.capacity_stalls),
118 }
119 }
120}
121
122#[derive(Clone, Copy, Debug, Error, PartialEq, Eq)]
124pub enum ResidencyInitError {
125 #[error(transparent)]
127 Arena(#[from] ArenaError),
128 #[error(transparent)]
130 Uploads(#[from] UploadBackpressure),
131}
132
133#[derive(Debug)]
135pub struct ResidencyWorkspace<C: Copy> {
136 arena: PagedArena,
137 uploads: UploadRing,
138 commands: CommandScratch<C>,
139}
140
141#[derive(Debug)]
143pub(crate) struct LazyUploadRing {
144 config: UploadRingConfig,
145 ring: Option<UploadRing>,
146}
147
148impl LazyUploadRing {
149 pub(crate) fn new(config: UploadRingConfig) -> Result<Self, UploadBackpressure> {
150 config.validate()?;
151 Ok(Self { config, ring: None })
152 }
153
154 pub(crate) fn begin_epoch(&mut self) {
155 if let Some(ring) = &mut self.ring {
156 ring.begin_epoch();
157 }
158 }
159
160 pub(crate) fn ensure(&mut self) -> Result<&mut UploadRing, UploadBackpressure> {
161 if self.ring.is_none() {
162 self.ring = Some(UploadRing::new(self.config)?);
163 }
164 self.ring
165 .as_mut()
166 .ok_or(UploadBackpressure::InvalidConfiguration)
167 }
168
169 pub(crate) const fn get_mut(&mut self) -> Option<&mut UploadRing> {
170 self.ring.as_mut()
171 }
172
173 pub(crate) fn metrics(&self) -> UploadMetrics {
174 self.ring
175 .as_ref()
176 .map_or_else(UploadMetrics::default, UploadRing::metrics)
177 }
178}
179
180impl<C: Copy> ResidencyWorkspace<C> {
181 pub fn new(config: ResidencyConfig) -> Result<Self, ResidencyInitError> {
187 Ok(Self {
188 arena: PagedArena::new(config.page_size, config.page_count)?,
189 uploads: UploadRing::new(config.uploads)?,
190 commands: CommandScratch::new(config.command_capacity),
191 })
192 }
193
194 pub fn begin_frame(&mut self) {
196 self.uploads.begin_epoch();
197 self.commands.clear();
198 }
199
200 #[must_use]
202 pub const fn arena(&self) -> &PagedArena {
203 &self.arena
204 }
205
206 pub const fn arena_mut(&mut self) -> &mut PagedArena {
208 &mut self.arena
209 }
210
211 #[must_use]
213 pub const fn uploads(&self) -> &UploadRing {
214 &self.uploads
215 }
216
217 pub const fn uploads_mut(&mut self) -> &mut UploadRing {
219 &mut self.uploads
220 }
221
222 #[must_use]
224 pub const fn commands(&self) -> &CommandScratch<C> {
225 &self.commands
226 }
227
228 pub const fn commands_mut(&mut self) -> &mut CommandScratch<C> {
230 &mut self.commands
231 }
232
233 #[must_use]
235 pub fn metrics(&self) -> ResidencyMetrics {
236 ResidencyMetrics {
237 arena: self.arena.metrics(),
238 uploads: self.uploads.metrics(),
239 commands: self.commands.metrics(),
240 machine: ResidencyMachineMetrics::default(),
241 immutable_assets: ImmutableArenaMetrics::default(),
242 }
243 }
244}
245
246#[cfg(test)]
247#[path = "residency_tests.rs"]
248mod tests;