use crate::batch::GpuBatchPlan;
use crate::error::{GpuError, GpuFailure};
use crate::telemetry::{measure_phase, GpuPhase, GpuTelemetry};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct FallbackPolicy;
impl FallbackPolicy {
pub fn should_fallback(self, error: &GpuError) -> bool {
matches!(
error,
GpuError::GpuFailure(_)
| GpuError::GpuFailureDetail { .. }
| GpuError::BackendUnavailable
| GpuError::DeviceLost
| GpuError::OutOfMemory { .. }
| GpuError::ShaderCompilation { .. }
| GpuError::QueueSubmission(_)
| GpuError::ReadbackTimeout { .. }
)
}
pub fn reason(self, error: &GpuError) -> Option<GpuFailure> {
match error {
GpuError::GpuFailure(reason) => Some(*reason),
GpuError::GpuFailureDetail { failure, .. } => Some(*failure),
GpuError::BackendUnavailable => Some(GpuFailure::BackendUnavailable),
GpuError::DeviceLost => Some(GpuFailure::DeviceLost),
GpuError::OutOfMemory { .. } => Some(GpuFailure::OutOfMemory),
GpuError::ShaderCompilation { .. } => Some(GpuFailure::ShaderCompilation),
GpuError::QueueSubmission(_) => Some(GpuFailure::QueueSubmission),
GpuError::ReadbackTimeout { .. } => Some(GpuFailure::Readback),
_ => None,
}
}
}
pub fn run_cpu_fallback<T, O, F>(
gpu_error: &GpuError,
items: &[T],
plan: &GpuBatchPlan,
telemetry: &mut GpuTelemetry,
mut process_batch: F,
) -> Result<Vec<O>, GpuError>
where
F: FnMut(&[T]) -> Result<Vec<O>, GpuError>,
{
let policy = FallbackPolicy;
if !policy.should_fallback(gpu_error) {
return Err(GpuError::InvalidBatchPlan(
"CPU fallback requires a real GPU failure",
));
}
plan.validate_for_len(items.len())?;
measure_phase(telemetry, GpuPhase::Post, || {
let mut output = Vec::new();
for span in plan.spans() {
output.extend(process_batch(&items[span.range()])?);
}
Ok(output)
})
}