use std::error::Error;
use std::fmt::{self, Display, Formatter};
use crate::job::JobError;
#[derive(Debug)]
pub enum ExecutorInitError {
NoGpuAvailable,
#[cfg(feature = "gpu")]
DeviceRequestFailed(wgpu::RequestDeviceError),
#[cfg(not(feature = "gpu"))]
DeviceRequestFailed(String),
InvalidConfig(String),
}
impl Display for ExecutorInitError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::NoGpuAvailable => f.write_str("no GPU adapter is available"),
Self::DeviceRequestFailed(error) => write!(f, "failed to request GPU device: {error}"),
Self::InvalidConfig(message) => write!(f, "invalid executor config: {message}"),
}
}
}
impl Error for ExecutorInitError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
#[cfg(feature = "gpu")]
Self::DeviceRequestFailed(error) => Some(error),
_ => None,
}
}
}
#[derive(Debug)]
pub enum ExecutionError {
JobEncoding(JobError),
Gpu(GpuError),
CpuFallback(JobError),
AllBatchesFailed { attempted: usize, failed: usize },
}
impl Display for ExecutionError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::JobEncoding(error) => write!(f, "job encoding failed: {error}"),
Self::Gpu(error) => write!(f, "GPU execution failed: {error}"),
Self::CpuFallback(error) => write!(f, "CPU fallback failed: {error}"),
Self::AllBatchesFailed { attempted, failed } => {
write!(
f,
"all batches failed: attempted={attempted} failed={failed}"
)
}
}
}
}
impl Error for ExecutionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::JobEncoding(error) | Self::CpuFallback(error) => Some(error),
Self::Gpu(error) => Some(error),
Self::AllBatchesFailed { .. } => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GpuFailure {
BackendUnavailable,
DeviceLost,
OutOfMemory,
ShaderCompilation,
QueueSubmission,
Readback,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum GpuError {
InvalidBudget(&'static str),
ItemExceedsSafeBudget {
item_index: usize,
item_bytes: usize,
safe_vram_bytes: usize,
},
InvalidBatchPlan(&'static str),
GpuFailure(GpuFailure),
GpuFailureDetail {
failure: GpuFailure,
message: String,
},
BackendUnavailable,
DeviceLost,
OutOfMemory {
requested: usize,
available: usize,
},
ShaderCompilation {
message: String,
},
QueueSubmission(String),
ReadbackTimeout {
ms: u64,
},
BudgetExceeded {
estimated: usize,
budget: usize,
},
}
impl Display for GpuError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidBudget(message) => write!(f, "invalid GPU memory budget: {message}"),
Self::ItemExceedsSafeBudget {
item_index,
item_bytes,
safe_vram_bytes,
} => write!(
f,
"item {item_index} requires {item_bytes} bytes, above safe VRAM budget {safe_vram_bytes}"
),
Self::InvalidBatchPlan(message) => write!(f, "invalid GPU batch plan: {message}"),
Self::GpuFailure(failure) => write!(f, "GPU failure: {failure:?}"),
Self::GpuFailureDetail { failure, message } => {
write!(f, "GPU failure: {failure:?}: {message}")
}
Self::BackendUnavailable => f.write_str("GPU backend is unavailable"),
Self::DeviceLost => f.write_str("GPU device was lost"),
Self::OutOfMemory {
requested,
available,
} => write!(
f,
"GPU out of memory: requested={requested} available={available}"
),
Self::ShaderCompilation { message } => {
write!(f, "shader compilation failed: {message}")
}
Self::QueueSubmission(message) => write!(f, "queue submission failed: {message}"),
Self::ReadbackTimeout { ms } => write!(f, "GPU readback timed out after {ms} ms"),
Self::BudgetExceeded { estimated, budget } => {
write!(
f,
"GPU budget exceeded: estimated={estimated} budget={budget}"
)
}
}
}
}
impl Error for GpuError {}
impl From<GpuFailure> for GpuError {
fn from(value: GpuFailure) -> Self {
Self::GpuFailure(value)
}
}