use std::collections::HashMap;
use std::sync::atomic::{AtomicU32, Ordering};
use kurbo::Rect as KRect;
use crate::scene::Theme;
use super::*;
pub type Ticket = u32;
pub const CAP: usize = 384;
pub const BUDGET: usize = 6;
static NEXT: AtomicU32 = AtomicU32::new(1);
struct Asked {
key: Key,
stale: bool,
}
pub struct Plan {
held: Cache<Ticket>,
asked: HashMap<Ticket, Asked>,
budget: usize,
evicted: Vec<Ticket>,
}
#[derive(Debug)]
pub struct Work {
pub draw: Vec<(Ticket, KRect)>,
pub request: Vec<(Ticket, Key)>,
pub evict: Vec<Ticket>,
pub missing: usize,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Arrival {
Dropped,
Current,
Stale,
}
impl Plan {
pub fn new(cap: usize, budget: usize) -> Plan {
Plan {
held: Cache::new(cap),
asked: HashMap::new(),
budget,
evicted: Vec::new(),
}
}
pub fn damage(&mut self, theme: Theme, origin: (f64, f64), rects: &[KRect]) {
self.held.damage(theme, origin, rects);
for asked in self.asked.values_mut() {
if asked.key.theme == theme && asked.key.overlaps(origin, rects) {
asked.stale = true;
}
}
}
pub fn damage_all(&mut self, theme: Theme) {
self.held.damage_all(theme);
for asked in self.asked.values_mut() {
if asked.key.theme == theme {
asked.stale = true;
}
}
}
pub fn frame(
&mut self,
theme: Theme,
area: KRect,
view: &View,
needed: &[(u32, u32)],
settled: bool,
) -> Work {
let level = Level::of(view.zoom);
let missing: Vec<Key> = needed
.iter()
.map(|&(col, row)| Key {
theme,
level,
col,
row,
})
.filter(|k| self.held.get(k).is_none_or(|(_, stale)| stale))
.collect();
let mut draw: Vec<(Level, Ticket, KRect)> = self
.held
.iter()
.filter(|(k, _, _)| k.theme == theme)
.filter_map(|(k, &ticket, _)| Some((k.level, ticket, view.place(area, k)?)))
.collect();
draw.sort_by_key(|&(l, _, _)| (l == level, l.0));
let mut request = Vec::new();
if settled {
for &key in &missing {
if self.asked.len() >= self.budget {
break;
}
if self.asked.values().any(|a| a.key == key) {
continue;
}
let ticket = NEXT.fetch_add(1, Ordering::Relaxed);
self.asked.insert(ticket, Asked { key, stale: false });
request.push((ticket, key));
}
}
Work {
draw: draw.into_iter().map(|(_, t, r)| (t, r)).collect(),
request,
evict: std::mem::take(&mut self.evicted),
missing: missing.len(),
}
}
pub fn arrived(&mut self, ticket: Ticket) -> Arrival {
let Some(Asked { key, stale }) = self.asked.remove(&ticket) else {
return Arrival::Dropped;
};
self.evicted.extend(self.held.remove(&key));
let dropped = self.held.insert(key, ticket, 1);
self.evicted.extend(dropped.into_iter().map(|(_, t)| t));
if stale {
self.held.damage_all_where(|k| *k == key);
Arrival::Stale
} else {
Arrival::Current
}
}
pub fn missed(&mut self, ticket: Ticket) {
self.asked.remove(&ticket);
}
}
#[cfg(test)]
mod tests;