#![warn(missing_docs)]
use std::fmt::{Display, Formatter};
use std::ops::{Deref, RangeBounds, RangeFull};
use std::sync::{RwLock, RwLockReadGuard};
use once_cell::sync::Lazy;
mod cpu;
#[cfg(feature = "vulkan")]
mod vulkan;
#[cfg(all(feature = "cuda", not(apple)))]
mod cuda;
mod log;
#[non_exhaustive]
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum BackendId {
CPU,
#[cfg(feature = "vulkan")]
Vulkan,
#[cfg(all(feature = "cuda", not(apple)))]
CUDA,
}
pub const CPU_NAME: &str = "Host";
#[cfg(feature = "vulkan")]
pub const VULKAN_NAME: &str = "Vulkan";
#[cfg(all(feature = "cuda", not(apple)))]
pub const CUDA_NAME: &str = "CUDA";
static CONTEXT: Lazy<RwLock<Context>> = Lazy::new(|| {
let ctx = RwLock::new(Context::default());
ctx.write().unwrap().init();
ctx
});
struct Context {
backends: Vec<Backend>,
devices: Vec<Device>,
}
impl Context {
const fn default() -> Self {
Context {
backends: vec![],
devices: vec![],
}
}
fn init(&mut self) {
if !self.backends.is_empty() {
return;
}
unsafe {
memonitor_sys::log::set(Some(log::log));
}
let (system, cpus) = cpu::Host::init();
self.register_backend(system, cpus);
#[cfg(feature = "vulkan")]
if let Some((backend, devices)) = vulkan::Vulkan::init() {
self.register_backend(backend, devices)
}
#[cfg(all(feature = "cuda", not(apple)))]
if let Some((backend, devices)) = cuda::Cuda::init() {
self.register_backend(backend, devices)
}
}
fn register_backend<B, D>(&mut self, backend: B, mut devices: Vec<D>)
where
B: BackendHandle + 'static,
D: DeviceHandle + 'static,
{
let old_device_count = self.devices.len();
let backend_id = self.backends.len();
let mut new_device_ids = Vec::with_capacity(devices.len());
for (idx, device) in devices.drain(..).enumerate() {
let global_id = idx + old_device_count;
let device = Device {
inner: Box::new(device),
global_id,
local_id: idx,
backend_id,
};
self.devices.push(device);
new_device_ids.push(global_id);
}
let backend = Backend {
inner: Box::new(backend),
id: backend_id,
device_ids: new_device_ids,
};
self.backends.push(backend);
}
}
pub fn list_backends() -> SliceGuard<'static, Backend, RangeFull> {
let guard = CONTEXT.read().unwrap();
SliceGuard {
guard,
range: ..,
_phantom: Default::default(),
}
}
pub fn list_all_devices() -> SliceGuard<'static, Device, RangeFull> {
let guard = CONTEXT.read().unwrap();
SliceGuard {
guard,
range: ..,
_phantom: Default::default(),
}
}
pub struct SliceGuard<'s, T, R>
where
R: RangeBounds<usize>,
{
guard: RwLockReadGuard<'s, Context>,
range: R,
_phantom: std::marker::PhantomData<T>,
}
impl<'s, R> Deref for SliceGuard<'s, Backend, R>
where
R: RangeBounds<usize>,
{
type Target = [Backend];
fn deref(&self) -> &Self::Target {
&self.guard.backends[(
self.range.start_bound().cloned(),
self.range.end_bound().cloned(),
)]
}
}
impl<'s, R> Deref for SliceGuard<'s, Device, R>
where
R: RangeBounds<usize>,
{
type Target = [Device];
fn deref(&self) -> &Self::Target {
&self.guard.devices[(
self.range.start_bound().cloned(),
self.range.end_bound().cloned(),
)]
}
}
pub trait BackendHandle: Send + Sync {
fn name(&self) -> &str;
fn id(&self) -> BackendId;
}
pub struct Backend {
inner: Box<dyn BackendHandle>,
id: usize,
device_ids: Vec<usize>,
}
impl PartialEq for Backend {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Backend {}
impl Backend {
pub fn index(&self) -> usize {
self.id
}
pub fn device_indexes(&self) -> &[usize] {
&self.device_ids
}
}
impl Deref for Backend {
type Target = Box<dyn BackendHandle>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[derive(Copy, Clone)]
pub enum DeviceKind {
GPU(GPUKind),
CPU,
Other,
}
impl Display for DeviceKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
DeviceKind::GPU(GPUKind::Integrated) => write!(f, "Integrated Graphics Card"),
DeviceKind::GPU(GPUKind::Discrete) => write!(f, "Discrete Graphics Card"),
DeviceKind::GPU(GPUKind::Virtual) => write!(f, "Virtual Graphics Card"),
DeviceKind::CPU => write!(f, "CPU"),
DeviceKind::Other => write!(f, "Other"),
}
}
}
#[derive(Copy, Clone)]
pub enum GPUKind {
Integrated,
Discrete,
Virtual,
}
pub struct MemoryStats {
pub total: usize,
pub available: usize,
pub used: usize,
}
pub trait DeviceHandle: Send + Sync {
fn name(&self) -> &str;
fn kind(&self) -> DeviceKind;
fn backend(&self) -> BackendId;
fn current_memory_stats(&self) -> MemoryStats;
}
pub struct Device {
inner: Box<dyn DeviceHandle>,
global_id: usize,
local_id: usize,
backend_id: usize,
}
impl Device {
pub fn global_index(&self) -> usize {
self.global_id
}
pub fn local_id(&self) -> usize {
self.local_id
}
pub fn backend_index(&self) -> usize {
self.backend_id
}
}
impl Deref for Device {
type Target = Box<dyn DeviceHandle>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}