1use rustial_math::TileId;
15use std::collections::{HashMap, VecDeque};
16
17const DEFAULT_RECENT_EVENT_CAPACITY: usize = 2048;
18const DEFAULT_RECENT_TERMINAL_RECORD_CAPACITY: usize = 512;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct TileLifecycleEvent {
23 pub frame: u64,
25 pub tile: TileId,
27 pub kind: TileLifecycleEventKind,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum TileLifecycleEventKind {
34 Selected,
36 Queued,
38 Dispatched,
40 Completed,
42 Decoded,
44 PromotedToCache,
46 UsedAsExact,
48 UsedAsFallback,
50 CancelledAsStale,
52 EvictedWhilePending,
54 EvictedAfterRenderableUse,
56 Failed,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct TileLifecycleRecord {
63 pub tile: TileId,
65 pub first_selected_frame: Option<u64>,
67 pub first_queued_frame: Option<u64>,
69 pub first_dispatched_frame: Option<u64>,
71 pub first_completed_frame: Option<u64>,
73 pub first_decoded_frame: Option<u64>,
75 pub first_promoted_frame: Option<u64>,
77 pub first_renderable_frame: Option<u64>,
79 pub first_exact_frame: Option<u64>,
81 pub first_fallback_frame: Option<u64>,
83 pub queued_frames_to_dispatch: Option<u64>,
85 pub in_flight_frames_to_complete: Option<u64>,
87 pub completion_to_visible_use_frames: Option<u64>,
89 pub last_event_frame: u64,
91 pub terminal_event: Option<TileLifecycleEventKind>,
93}
94
95impl TileLifecycleRecord {
96 fn new(tile: TileId) -> Self {
97 Self {
98 tile,
99 first_selected_frame: None,
100 first_queued_frame: None,
101 first_dispatched_frame: None,
102 first_completed_frame: None,
103 first_decoded_frame: None,
104 first_promoted_frame: None,
105 first_renderable_frame: None,
106 first_exact_frame: None,
107 first_fallback_frame: None,
108 queued_frames_to_dispatch: None,
109 in_flight_frames_to_complete: None,
110 completion_to_visible_use_frames: None,
111 last_event_frame: 0,
112 terminal_event: None,
113 }
114 }
115}
116
117#[derive(Debug, Clone, Default, PartialEq, Eq)]
119pub struct TileLifecycleDiagnostics {
120 pub active_records: Vec<TileLifecycleRecord>,
122 pub recent_terminal_records: Vec<TileLifecycleRecord>,
124 pub recent_events: Vec<TileLifecycleEvent>,
126}
127
128#[derive(Debug)]
129pub(crate) struct TileLifecycleTracker {
130 current_frame: u64,
131 active_records: HashMap<TileId, TileLifecycleRecord>,
132 recent_terminal_records: VecDeque<TileLifecycleRecord>,
133 recent_events: VecDeque<TileLifecycleEvent>,
134 recent_event_capacity: usize,
135 recent_terminal_capacity: usize,
136}
137
138impl Default for TileLifecycleTracker {
139 fn default() -> Self {
140 Self {
141 current_frame: 0,
142 active_records: HashMap::new(),
143 recent_terminal_records: VecDeque::with_capacity(
144 DEFAULT_RECENT_TERMINAL_RECORD_CAPACITY,
145 ),
146 recent_events: VecDeque::with_capacity(DEFAULT_RECENT_EVENT_CAPACITY),
147 recent_event_capacity: DEFAULT_RECENT_EVENT_CAPACITY,
148 recent_terminal_capacity: DEFAULT_RECENT_TERMINAL_RECORD_CAPACITY,
149 }
150 }
151}
152
153impl TileLifecycleTracker {
154 pub fn begin_frame(&mut self, frame: u64) {
156 self.current_frame = frame;
157 }
158
159 pub fn record_selected(&mut self, tile: TileId) {
161 let frame = self.current_frame;
162 let mut emit = false;
163 {
164 let record = self.ensure_record(tile);
165 if record.first_selected_frame.is_none() {
166 record.first_selected_frame = Some(frame);
167 emit = true;
168 }
169 record.last_event_frame = frame;
170 }
171 if emit {
172 self.push_event(tile, TileLifecycleEventKind::Selected);
173 }
174 }
175
176 pub fn record_queued(&mut self, tile: TileId) {
178 let frame = self.current_frame;
179 let mut emit = false;
180 {
181 let record = self.ensure_record(tile);
182 if record.first_queued_frame.is_none() {
183 record.first_queued_frame = Some(frame);
184 emit = true;
185 }
186 record.last_event_frame = frame;
187 }
188 if emit {
189 self.push_event(tile, TileLifecycleEventKind::Queued);
190 }
191 }
192
193 pub fn record_dispatched(&mut self, tile: TileId) {
195 let frame = self.current_frame;
196 let mut emit = false;
197 {
198 let record = self.ensure_record(tile);
199 if record.first_dispatched_frame.is_none() {
200 record.first_dispatched_frame = Some(frame);
201 if let Some(queued_frame) = record.first_queued_frame {
202 record.queued_frames_to_dispatch = Some(frame.saturating_sub(queued_frame));
203 }
204 emit = true;
205 }
206 record.last_event_frame = frame;
207 }
208 if emit {
209 self.push_event(tile, TileLifecycleEventKind::Dispatched);
210 }
211 }
212
213 pub fn record_completed(&mut self, tile: TileId) {
215 let frame = self.current_frame;
216 let mut emit = false;
217 {
218 let record = self.ensure_record(tile);
219 if record.first_completed_frame.is_none() {
220 record.first_completed_frame = Some(frame);
221 if let Some(dispatched_frame) = record.first_dispatched_frame {
222 record.in_flight_frames_to_complete =
223 Some(frame.saturating_sub(dispatched_frame));
224 }
225 emit = true;
226 }
227 record.last_event_frame = frame;
228 }
229 if emit {
230 self.push_event(tile, TileLifecycleEventKind::Completed);
231 }
232 }
233
234 pub fn record_decoded(&mut self, tile: TileId) {
236 let frame = self.current_frame;
237 let mut emit = false;
238 {
239 let record = self.ensure_record(tile);
240 if record.first_decoded_frame.is_none() {
241 record.first_decoded_frame = Some(frame);
242 emit = true;
243 }
244 record.last_event_frame = frame;
245 }
246 if emit {
247 self.push_event(tile, TileLifecycleEventKind::Decoded);
248 }
249 }
250
251 pub fn record_promoted_to_cache(&mut self, tile: TileId) {
253 let frame = self.current_frame;
254 let mut emit = false;
255 {
256 let record = self.ensure_record(tile);
257 if record.first_promoted_frame.is_none() {
258 record.first_promoted_frame = Some(frame);
259 emit = true;
260 }
261 record.last_event_frame = frame;
262 }
263 if emit {
264 self.push_event(tile, TileLifecycleEventKind::PromotedToCache);
265 }
266 }
267
268 pub fn record_used_as_exact(&mut self, tile: TileId) {
270 let frame = self.current_frame;
271 let mut emit = false;
272 {
273 let record = self.ensure_record(tile);
274 if record.first_renderable_frame.is_none() {
275 record.first_renderable_frame = Some(frame);
276 if let Some(completed_frame) = record.first_completed_frame {
277 record.completion_to_visible_use_frames =
278 Some(frame.saturating_sub(completed_frame));
279 }
280 }
281 if record.first_exact_frame.is_none() {
282 record.first_exact_frame = Some(frame);
283 emit = true;
284 }
285 record.last_event_frame = frame;
286 }
287 if emit {
288 self.push_event(tile, TileLifecycleEventKind::UsedAsExact);
289 }
290 }
291
292 pub fn record_used_as_fallback(&mut self, tile: TileId) {
294 let frame = self.current_frame;
295 let mut emit = false;
296 {
297 let record = self.ensure_record(tile);
298 if record.first_renderable_frame.is_none() {
299 record.first_renderable_frame = Some(frame);
300 if let Some(completed_frame) = record.first_completed_frame {
301 record.completion_to_visible_use_frames =
302 Some(frame.saturating_sub(completed_frame));
303 }
304 }
305 if record.first_fallback_frame.is_none() {
306 record.first_fallback_frame = Some(frame);
307 emit = true;
308 }
309 record.last_event_frame = frame;
310 }
311 if emit {
312 self.push_event(tile, TileLifecycleEventKind::UsedAsFallback);
313 }
314 }
315
316 pub fn record_failed(&mut self, tile: TileId) {
318 let frame = self.current_frame;
319 {
320 let record = self.ensure_record(tile);
321 record.last_event_frame = frame;
322 }
323 self.push_event(tile, TileLifecycleEventKind::Failed);
324 }
325
326 pub fn record_cancelled_as_stale(&mut self, tile: TileId) {
328 self.finalize(tile, TileLifecycleEventKind::CancelledAsStale);
329 }
330
331 pub fn record_evicted_while_pending(&mut self, tile: TileId) {
333 self.finalize(tile, TileLifecycleEventKind::EvictedWhilePending);
334 }
335
336 pub fn record_evicted_after_renderable_use(&mut self, tile: TileId) {
338 self.finalize(tile, TileLifecycleEventKind::EvictedAfterRenderableUse);
339 }
340
341 pub fn diagnostics(&self) -> TileLifecycleDiagnostics {
343 let mut active_records: Vec<_> = self.active_records.values().cloned().collect();
344 active_records.sort_by_key(|record| (record.tile.zoom, record.tile.y, record.tile.x));
345
346 TileLifecycleDiagnostics {
347 active_records,
348 recent_terminal_records: self.recent_terminal_records.iter().cloned().collect(),
349 recent_events: self.recent_events.iter().copied().collect(),
350 }
351 }
352
353 fn ensure_record(&mut self, tile: TileId) -> &mut TileLifecycleRecord {
354 self.active_records
355 .entry(tile)
356 .or_insert_with(|| TileLifecycleRecord::new(tile))
357 }
358
359 fn finalize(&mut self, tile: TileId, kind: TileLifecycleEventKind) {
360 let frame = self.current_frame;
361 let mut record = self
362 .active_records
363 .remove(&tile)
364 .unwrap_or_else(|| TileLifecycleRecord::new(tile));
365 record.last_event_frame = frame;
366 record.terminal_event = Some(kind);
367 self.push_event(tile, kind);
368 self.recent_terminal_records.push_back(record);
369 while self.recent_terminal_records.len() > self.recent_terminal_capacity {
370 let _ = self.recent_terminal_records.pop_front();
371 }
372 }
373
374 fn push_event(&mut self, tile: TileId, kind: TileLifecycleEventKind) {
375 self.recent_events.push_back(TileLifecycleEvent {
376 frame: self.current_frame,
377 tile,
378 kind,
379 });
380 while self.recent_events.len() > self.recent_event_capacity {
381 let _ = self.recent_events.pop_front();
382 }
383 }
384}