1#![warn(missing_docs)]
37
38use std::fmt::{Display, Formatter};
39use std::ops::{Deref, RangeBounds, RangeFull};
40use std::sync::{RwLock, RwLockReadGuard};
41
42use once_cell::sync::Lazy;
43
44mod cpu;
45
46#[cfg(feature = "vulkan")]
47mod vulkan;
48
49#[cfg(all(feature = "cuda", not(apple)))]
50mod cuda;
51mod log;
52
53#[non_exhaustive]
55#[derive(Debug, Eq, PartialEq, Copy, Clone)]
56pub enum BackendId {
57 CPU,
59
60 #[cfg(feature = "vulkan")]
62 Vulkan,
63
64 #[cfg(all(feature = "cuda", not(apple)))]
66 CUDA,
67}
68
69pub const CPU_NAME: &str = "Host";
71
72#[cfg(feature = "vulkan")]
74pub const VULKAN_NAME: &str = "Vulkan";
75
76#[cfg(all(feature = "cuda", not(apple)))]
78pub const CUDA_NAME: &str = "CUDA";
79
80static CONTEXT: Lazy<RwLock<Context>> = Lazy::new(|| {
81 let ctx = RwLock::new(Context::default());
82 ctx.write().unwrap().init();
83 ctx
84});
85
86struct Context {
87 backends: Vec<Backend>,
88 devices: Vec<Device>,
89}
90
91impl Context {
92 const fn default() -> Self {
93 Context {
94 backends: vec![],
95 devices: vec![],
96 }
97 }
98
99 fn init(&mut self) {
100 if !self.backends.is_empty() {
101 return;
102 }
103
104 unsafe {
105 memonitor_sys::log::set(Some(log::log));
106 }
107
108 let (system, cpus) = cpu::Host::init();
109 self.register_backend(system, cpus);
110
111 #[cfg(feature = "vulkan")]
112 if let Some((backend, devices)) = vulkan::Vulkan::init() {
113 self.register_backend(backend, devices)
114 }
115
116 #[cfg(all(feature = "cuda", not(apple)))]
117 if let Some((backend, devices)) = cuda::Cuda::init() {
118 self.register_backend(backend, devices)
119 }
120 }
121
122 fn register_backend<B, D>(&mut self, backend: B, mut devices: Vec<D>)
123 where
124 B: BackendHandle + 'static,
125 D: DeviceHandle + 'static,
126 {
127 let old_device_count = self.devices.len();
128 let backend_id = self.backends.len();
129 let mut new_device_ids = Vec::with_capacity(devices.len());
130
131 for (idx, device) in devices.drain(..).enumerate() {
132 let global_id = idx + old_device_count;
133 let device = Device {
134 inner: Box::new(device),
135 global_id,
136 local_id: idx,
137 backend_id,
138 };
139 self.devices.push(device);
140 new_device_ids.push(global_id);
141 }
142
143 let backend = Backend {
144 inner: Box::new(backend),
145 id: backend_id,
146 device_ids: new_device_ids,
147 };
148 self.backends.push(backend);
149 }
150}
151
152pub fn list_backends() -> SliceGuard<'static, Backend, RangeFull> {
159 let guard = CONTEXT.read().unwrap();
160 SliceGuard {
161 guard,
162 range: ..,
163 _phantom: Default::default(),
164 }
165}
166
167pub fn list_all_devices() -> SliceGuard<'static, Device, RangeFull> {
172 let guard = CONTEXT.read().unwrap();
173 SliceGuard {
174 guard,
175 range: ..,
176 _phantom: Default::default(),
177 }
178}
179
180pub struct SliceGuard<'s, T, R>
182where
183 R: RangeBounds<usize>,
184{
185 guard: RwLockReadGuard<'s, Context>,
186 range: R,
187 _phantom: std::marker::PhantomData<T>,
188}
189
190impl<'s, R> Deref for SliceGuard<'s, Backend, R>
191where
192 R: RangeBounds<usize>,
193{
194 type Target = [Backend];
195
196 fn deref(&self) -> &Self::Target {
197 &self.guard.backends[(
198 self.range.start_bound().cloned(),
199 self.range.end_bound().cloned(),
200 )]
201 }
202}
203
204impl<'s, R> Deref for SliceGuard<'s, Device, R>
205where
206 R: RangeBounds<usize>,
207{
208 type Target = [Device];
209
210 fn deref(&self) -> &Self::Target {
211 &self.guard.devices[(
212 self.range.start_bound().cloned(),
213 self.range.end_bound().cloned(),
214 )]
215 }
216}
217
218pub trait BackendHandle: Send + Sync {
220 fn name(&self) -> &str;
222
223 fn id(&self) -> BackendId;
225}
226
227pub struct Backend {
229 inner: Box<dyn BackendHandle>,
230 id: usize,
231 device_ids: Vec<usize>,
232}
233
234impl PartialEq for Backend {
235 fn eq(&self, other: &Self) -> bool {
236 self.id == other.id
237 }
238}
239
240impl Eq for Backend {}
241
242impl Backend {
243 pub fn index(&self) -> usize {
247 self.id
248 }
249
250 pub fn device_indexes(&self) -> &[usize] {
254 &self.device_ids
255 }
256}
257
258impl Deref for Backend {
259 type Target = Box<dyn BackendHandle>;
260
261 fn deref(&self) -> &Self::Target {
262 &self.inner
263 }
264}
265
266#[derive(Copy, Clone)]
268pub enum DeviceKind {
269 GPU(GPUKind),
271 CPU,
273 Other,
275}
276
277impl Display for DeviceKind {
278 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
279 match self {
280 DeviceKind::GPU(GPUKind::Integrated) => write!(f, "Integrated Graphics Card"),
281 DeviceKind::GPU(GPUKind::Discrete) => write!(f, "Discrete Graphics Card"),
282 DeviceKind::GPU(GPUKind::Virtual) => write!(f, "Virtual Graphics Card"),
283 DeviceKind::CPU => write!(f, "CPU"),
284 DeviceKind::Other => write!(f, "Other"),
285 }
286 }
287}
288
289#[derive(Copy, Clone)]
291pub enum GPUKind {
292 Integrated,
294 Discrete,
296 Virtual,
298}
299
300pub struct MemoryStats {
302 pub total: usize,
304 pub available: usize,
306 pub used: usize,
308}
309
310pub trait DeviceHandle: Send + Sync {
312 fn name(&self) -> &str;
314
315 fn kind(&self) -> DeviceKind;
317
318 fn backend(&self) -> BackendId;
320
321 fn current_memory_stats(&self) -> MemoryStats;
323}
324
325pub struct Device {
327 inner: Box<dyn DeviceHandle>,
328 global_id: usize,
329 local_id: usize,
330 backend_id: usize,
331}
332
333impl Device {
334 pub fn global_index(&self) -> usize {
338 self.global_id
339 }
340
341 pub fn local_id(&self) -> usize {
345 self.local_id
346 }
347
348 pub fn backend_index(&self) -> usize {
352 self.backend_id
353 }
354}
355
356impl Deref for Device {
357 type Target = Box<dyn DeviceHandle>;
358
359 fn deref(&self) -> &Self::Target {
360 &self.inner
361 }
362}