use serde::{Deserialize, Serialize};
use super::capability::{AttrValue, Capability};
pub const MEM_UNIFIED: &str = "mem.unified";
pub const MEM_SYSTEM: &str = "mem.system";
pub const MEM_VRAM: &str = "mem.vram";
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct MemoryDemand {
#[serde(default)]
pub host_bytes: u64,
#[serde(default)]
pub accel_bytes: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryPool {
Unified,
System,
Vram,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MemoryShortfall {
pub pool: MemoryPool,
pub need: u64,
pub free: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MemoryLedger {
unified: bool,
host_free: u64,
vram_free: u64,
}
fn bytes(cap: &Capability, attr: &str) -> Option<u64> {
cap.attrs
.get(attr)
.and_then(AttrValue::as_f64)
.map(|f| if f > 0.0 { f as u64 } else { 0 })
}
fn free_of(cap: &Capability) -> u64 {
bytes(cap, "free")
.or_else(|| bytes(cap, "total"))
.unwrap_or(0)
}
impl MemoryLedger {
pub fn from_capabilities(caps: &[Capability]) -> Self {
let find = |id: &str| caps.iter().find(|c| c.id.as_str() == id);
let system = find(MEM_SYSTEM).map(free_of).unwrap_or(0);
match find(MEM_UNIFIED) {
Some(u) => Self {
unified: true,
host_free: bytes(u, "free")
.or_else(|| bytes(u, "total"))
.unwrap_or(system),
vram_free: 0,
},
None => Self {
unified: false,
host_free: system,
vram_free: caps
.iter()
.filter(|c| c.id.as_str() == MEM_VRAM)
.map(free_of)
.fold(0u64, u64::saturating_add),
},
}
}
pub fn is_unified(&self) -> bool {
self.unified
}
pub fn host_free(&self) -> u64 {
self.host_free
}
pub fn vram_free(&self) -> u64 {
self.vram_free
}
pub fn check(&self, d: MemoryDemand) -> Result<(), MemoryShortfall> {
if self.unified {
let need = d.host_bytes.saturating_add(d.accel_bytes);
return if need <= self.host_free {
Ok(())
} else {
Err(MemoryShortfall {
pool: MemoryPool::Unified,
need,
free: self.host_free,
})
};
}
if d.host_bytes > self.host_free {
return Err(MemoryShortfall {
pool: MemoryPool::System,
need: d.host_bytes,
free: self.host_free,
});
}
if d.accel_bytes > self.vram_free {
return Err(MemoryShortfall {
pool: MemoryPool::Vram,
need: d.accel_bytes,
free: self.vram_free,
});
}
Ok(())
}
pub fn reserve(&mut self, d: MemoryDemand) -> Result<(), MemoryShortfall> {
self.check(d)?;
if self.unified {
self.host_free -= d.host_bytes.saturating_add(d.accel_bytes);
} else {
self.host_free -= d.host_bytes;
self.vram_free -= d.accel_bytes;
}
Ok(())
}
}