use std::ffi::CString;
use std::path::Path;
use transcribe_cpp_sys as sys;
use crate::error::{check, Result};
use crate::result::{owned_opt_str, owned_str};
use crate::types::Backend;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DeviceType {
Cpu,
Gpu,
Igpu,
Accel,
Unknown,
}
impl DeviceType {
fn from_raw(raw: sys::transcribe_device_type) -> Self {
use sys::transcribe_device_type as T;
match raw {
T::TRANSCRIBE_DEVICE_TYPE_CPU => DeviceType::Cpu,
T::TRANSCRIBE_DEVICE_TYPE_GPU => DeviceType::Gpu,
T::TRANSCRIBE_DEVICE_TYPE_IGPU => DeviceType::Igpu,
T::TRANSCRIBE_DEVICE_TYPE_ACCEL => DeviceType::Accel,
_ => DeviceType::Unknown,
}
}
}
#[derive(Debug, Clone)]
pub struct Device {
pub name: String,
pub description: String,
pub kind: String,
pub device_type: DeviceType,
pub device_id: Option<String>,
pub memory_total: u64,
pub memory_free: u64,
pub index: Option<usize>,
pub(crate) handle: sys::transcribe_device_t,
}
unsafe impl Send for Device {}
unsafe impl Sync for Device {}
impl PartialEq for Device {
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle
}
}
impl Eq for Device {}
impl Device {
pub(crate) fn from_raw(
raw: &sys::transcribe_device_info,
handle: sys::transcribe_device_t,
index: Option<usize>,
) -> Device {
Device {
name: owned_str(raw.name),
description: owned_str(raw.description),
kind: owned_str(raw.kind),
device_type: DeviceType::from_raw(raw.device_type),
device_id: owned_opt_str(raw.device_id),
memory_total: raw.memory_total,
memory_free: raw.memory_free,
index,
handle,
}
}
}
pub fn init_backends(dir: impl AsRef<Path>) -> Result<()> {
let dir = dir.as_ref();
let c_dir = CString::new(crate::model::path_bytes(dir)?)?;
let status = unsafe { sys::transcribe_init_backends(c_dir.as_ptr()) };
check(status, "init_backends")
}
pub fn init_backends_default() -> Result<()> {
let status = unsafe { sys::transcribe_init_backends_default() };
check(status, "init_backends_default")
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BackendMask(u32);
impl BackendMask {
pub const CPU: BackendMask = BackendMask(sys::TRANSCRIBE_BACKEND_MASK_CPU);
pub const METAL: BackendMask = BackendMask(sys::TRANSCRIBE_BACKEND_MASK_METAL);
pub const VULKAN: BackendMask = BackendMask(sys::TRANSCRIBE_BACKEND_MASK_VULKAN);
pub const CUDA: BackendMask = BackendMask(sys::TRANSCRIBE_BACKEND_MASK_CUDA);
pub const ROCM: BackendMask = BackendMask(sys::TRANSCRIBE_BACKEND_MASK_ROCM);
pub const OTHER: BackendMask = BackendMask(sys::TRANSCRIBE_BACKEND_MASK_OTHER);
pub const ALL: BackendMask = BackendMask(sys::TRANSCRIBE_BACKEND_MASK_ALL);
pub const fn for_backend(backend: Backend) -> BackendMask {
match backend {
Backend::Auto => BackendMask::ALL,
Backend::Cpu | Backend::CpuAccel => BackendMask::CPU,
Backend::Metal => BackendMask::METAL.union(BackendMask::CPU),
Backend::Vulkan => BackendMask::VULKAN.union(BackendMask::CPU),
Backend::Cuda => BackendMask::CUDA.union(BackendMask::CPU),
Backend::Rocm => BackendMask::ROCM.union(BackendMask::CPU),
}
}
pub const fn bits(self) -> u32 {
self.0
}
pub const fn from_bits(bits: u32) -> BackendMask {
BackendMask(bits)
}
pub const fn union(self, other: BackendMask) -> BackendMask {
BackendMask(self.0 | other.0)
}
pub const fn contains(self, other: BackendMask) -> bool {
self.0 & other.0 == other.0
}
}
impl Default for BackendMask {
fn default() -> Self {
BackendMask::ALL
}
}
impl std::ops::BitOr for BackendMask {
type Output = BackendMask;
fn bitor(self, rhs: BackendMask) -> BackendMask {
self.union(rhs)
}
}
impl std::ops::BitOrAssign for BackendMask {
fn bitor_assign(&mut self, rhs: BackendMask) {
*self = self.union(rhs);
}
}
pub fn init_backends_with(dir: Option<impl AsRef<Path>>, allowed: BackendMask) -> Result<()> {
let c_dir = match dir {
Some(dir) => Some(CString::new(crate::model::path_bytes(dir.as_ref())?)?),
None => None,
};
let mut params: sys::transcribe_backend_init_params = unsafe { std::mem::zeroed() };
unsafe { sys::transcribe_backend_init_params_init(&mut params) };
params.artifact_dir = c_dir.as_ref().map_or(std::ptr::null(), |d| d.as_ptr());
params.allowed_backends = allowed.bits();
let status = unsafe { sys::transcribe_init_backends_ex(¶ms) };
check(status, "init_backends_with")
}
pub fn allowed_backends() -> BackendMask {
BackendMask(unsafe { sys::transcribe_allowed_backends() })
}
pub fn device_count() -> usize {
let n = unsafe { sys::transcribe_device_count() };
n.max(0) as usize
}
pub fn devices() -> Vec<Device> {
let mut out = Vec::with_capacity(device_count());
for i in 0..device_count() as i32 {
let handle = unsafe { sys::transcribe_device_get(i) };
if handle.is_null() {
continue;
}
let mut raw: sys::transcribe_device_info = unsafe { std::mem::zeroed() };
unsafe { sys::transcribe_device_info_init(&mut raw) };
let status = unsafe { sys::transcribe_device_get_info(handle, &mut raw) };
if status == sys::transcribe_status::TRANSCRIBE_OK {
out.push(Device::from_raw(&raw, handle, Some(i as usize)));
}
}
out
}
pub fn backend_available(backend: Backend) -> bool {
unsafe { sys::transcribe_backend_available(backend.to_raw()) }
}