cgpu 0.1.0

A tunable GPU compute executor with automatic CPU fallback, byte-based batching, and inline shader generation.
Documentation
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)
    })
}