use crate::ResidencyMachineMetrics;
use molgfx_gpu::{
ArenaError, ArenaMetrics, CommandScratch, CommandScratchMetrics, PagedArena,
UploadBackpressure, UploadMetrics, UploadRing, UploadRingConfig,
};
use thiserror::Error;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ResidencyConfig {
pub page_size: u64,
pub page_count: u32,
pub uploads: UploadRingConfig,
pub command_capacity: usize,
pub machine_capacity: usize,
}
impl Default for ResidencyConfig {
fn default() -> Self {
const KIB: usize = 1024;
Self {
page_size: 64 * 1024,
page_count: 256,
uploads: UploadRingConfig {
capacity_bytes: 256 * KIB,
ticket_capacity: 256,
epoch_budget_bytes: 256 * KIB,
in_flight_budget_bytes: 256 * KIB,
alignment: 256,
},
command_capacity: 1024,
machine_capacity: 1024,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ResidencyMetrics {
pub arena: ArenaMetrics,
pub uploads: UploadMetrics,
pub commands: CommandScratchMetrics,
pub machine: ResidencyMachineMetrics,
pub immutable_assets: ImmutableArenaMetrics,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ImmutableArenaMetrics {
pub physical_buffers: u32,
pub physical_bytes: u64,
pub resident_bytes: u64,
pub peak_resident_bytes: u64,
pub uploaded_bytes: u64,
pub writes: u64,
pub relocations: u64,
pub allocation_stalls: u64,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ResidencyCounters {
pub allocation_events: u64,
pub upload_bytes: u64,
pub resident_bytes: u64,
pub stall_events: u64,
}
impl ResidencyMetrics {
#[must_use]
pub fn counters(self) -> ResidencyCounters {
ResidencyCounters {
allocation_events: self
.arena
.host_allocation_events
.saturating_add(self.uploads.host_allocation_events)
.saturating_add(self.commands.host_allocation_events),
upload_bytes: self.uploads.bytes_submitted,
resident_bytes: self.arena.resident_bytes,
stall_events: self
.arena
.allocation_stalls
.saturating_add(self.uploads.stall_events)
.saturating_add(self.commands.capacity_stalls)
.saturating_add(self.machine.capacity_stalls),
}
}
}
#[derive(Clone, Copy, Debug, Error, PartialEq, Eq)]
pub enum ResidencyInitError {
#[error(transparent)]
Arena(#[from] ArenaError),
#[error(transparent)]
Uploads(#[from] UploadBackpressure),
}
#[derive(Debug)]
pub struct ResidencyWorkspace<C: Copy> {
arena: PagedArena,
uploads: UploadRing,
commands: CommandScratch<C>,
}
#[derive(Debug)]
pub(crate) struct LazyUploadRing {
config: UploadRingConfig,
ring: Option<UploadRing>,
}
impl LazyUploadRing {
pub(crate) fn new(config: UploadRingConfig) -> Result<Self, UploadBackpressure> {
config.validate()?;
Ok(Self { config, ring: None })
}
pub(crate) fn begin_epoch(&mut self) {
if let Some(ring) = &mut self.ring {
ring.begin_epoch();
}
}
pub(crate) fn ensure(&mut self) -> Result<&mut UploadRing, UploadBackpressure> {
if self.ring.is_none() {
self.ring = Some(UploadRing::new(self.config)?);
}
self.ring
.as_mut()
.ok_or(UploadBackpressure::InvalidConfiguration)
}
pub(crate) const fn get_mut(&mut self) -> Option<&mut UploadRing> {
self.ring.as_mut()
}
pub(crate) fn metrics(&self) -> UploadMetrics {
self.ring
.as_ref()
.map_or_else(UploadMetrics::default, UploadRing::metrics)
}
}
impl<C: Copy> ResidencyWorkspace<C> {
pub fn new(config: ResidencyConfig) -> Result<Self, ResidencyInitError> {
Ok(Self {
arena: PagedArena::new(config.page_size, config.page_count)?,
uploads: UploadRing::new(config.uploads)?,
commands: CommandScratch::new(config.command_capacity),
})
}
pub fn begin_frame(&mut self) {
self.uploads.begin_epoch();
self.commands.clear();
}
#[must_use]
pub const fn arena(&self) -> &PagedArena {
&self.arena
}
pub const fn arena_mut(&mut self) -> &mut PagedArena {
&mut self.arena
}
#[must_use]
pub const fn uploads(&self) -> &UploadRing {
&self.uploads
}
pub const fn uploads_mut(&mut self) -> &mut UploadRing {
&mut self.uploads
}
#[must_use]
pub const fn commands(&self) -> &CommandScratch<C> {
&self.commands
}
pub const fn commands_mut(&mut self) -> &mut CommandScratch<C> {
&mut self.commands
}
#[must_use]
pub fn metrics(&self) -> ResidencyMetrics {
ResidencyMetrics {
arena: self.arena.metrics(),
uploads: self.uploads.metrics(),
commands: self.commands.metrics(),
machine: ResidencyMachineMetrics::default(),
immutable_assets: ImmutableArenaMetrics::default(),
}
}
}
#[cfg(test)]
#[path = "residency_tests.rs"]
mod tests;