use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc;
use oxicuda::{Context, CudaError, CudaResult, Device, JitOptions, Kernel, Module, Stream};
use super::ptx_math::{ptx_target_for_cc, PtxTarget};
struct GpuRuntime {
ctx: Arc<Context>,
stream: Stream,
target: PtxTarget,
modules: HashMap<&'static str, Arc<Module>>,
}
impl GpuRuntime {
fn new() -> CudaResult<Self> {
oxicuda::init()?;
let device = Device::get(0)?;
let (cc_major, cc_minor) = device.compute_capability()?;
let ctx = Arc::new(Context::new(&device)?);
let stream = Stream::new(&ctx)?;
Ok(Self {
ctx,
stream,
target: ptx_target_for_cc(cc_major, cc_minor),
modules: HashMap::new(),
})
}
}
enum Probe {
Unprobed,
Unavailable,
Ready(GpuRuntime),
}
thread_local! {
static RUNTIME: RefCell<Probe> = const { RefCell::new(Probe::Unprobed) };
}
fn ensure(slot: &mut Probe) -> CudaResult<&mut GpuRuntime> {
if matches!(slot, Probe::Unprobed) {
*slot = match GpuRuntime::new() {
Ok(rt) => Probe::Ready(rt),
Err(_) => Probe::Unavailable,
};
}
match slot {
Probe::Ready(rt) => Ok(rt),
_ => Err(CudaError::NoDevice),
}
}
#[must_use]
pub fn cuda_available() -> bool {
RUNTIME.with(|cell| ensure(&mut cell.borrow_mut()).is_ok())
}
#[must_use]
pub fn device_name() -> Option<String> {
RUNTIME.with(|cell| {
let mut slot = cell.borrow_mut();
let rt = ensure(&mut slot).ok()?;
rt.ctx.device().name().ok()
})
}
pub fn with_kernel<B, F, R>(key: &'static str, entry: &str, build_ptx: B, body: F) -> CudaResult<R>
where
B: FnOnce(&PtxTarget) -> String,
F: FnOnce(&Kernel, &Stream) -> CudaResult<R>,
{
RUNTIME.with(|cell| {
let mut slot = cell.borrow_mut();
let rt = ensure(&mut slot)?;
rt.ctx.set_current()?;
let module = match rt.modules.get(key) {
Some(m) => Arc::clone(m),
None => {
let ptx = build_ptx(&rt.target);
let opts = JitOptions {
optimization_level: 4,
target_from_context: true,
..Default::default()
};
let (compiled, log) = Module::from_ptx_with_options(&ptx, &opts)?;
if log.has_errors() {
return Err(CudaError::InvalidImage);
}
let m = Arc::new(compiled);
rt.modules.insert(key, Arc::clone(&m));
m
}
};
let kernel = Kernel::from_module(module, entry)?;
body(&kernel, &rt.stream)
})
}