enginerenderer 0.0.1

A zero-dependency offline rendering engine in pure Rust — CPU path tracing, BVH acceleration, 16-band spectral rendering, PBR materials, animation & video export.
Documentation
use super::compute_dispatch;

const GPU_MAX_PERCENT: u64 = 80;
const CPU_MAX_PERCENT: usize = 80;
const RAM_MAX_PERCENT: u64 = 50;
const BYTES_PER_PIXEL_F64_RGB: u64 = 24;

#[derive(Debug, Clone)]
pub struct HardwareCapabilities {
    pub logical_cores: u32,
    pub vram_bytes: u64,
    total_ram_bytes: u64,
}

impl HardwareCapabilities {
    pub fn detect() -> Self {
        let config = compute_dispatch::default_config();
        let logical_cores = std::thread::available_parallelism()
            .map(|n| n.get() as u32)
            .unwrap_or(1);
        let total_ram_bytes = config.ram.total_bytes;
        let vram_bytes = detect_vram_bytes();
        Self {
            logical_cores,
            vram_bytes,
            total_ram_bytes,
        }
    }

    pub fn optimal_render_threads(&self) -> usize {
        self.optimal_render_threads_for_input(usize::MAX)
    }

    pub fn optimal_render_threads_for_input(&self, input_work_items: usize) -> usize {
        let logical = (self.logical_cores as usize).max(1);
        let cpu_cap = ((logical * CPU_MAX_PERCENT) / 100).max(1);
        let os_cap = compute_dispatch::default_cpu_config().render_workers.max(1);
        let input_cap = input_work_items.max(1).div_ceil(4_000).max(1);
        cpu_cap.min(os_cap).min(input_cap).max(1)
    }

    pub fn max_framebuffer_bytes(&self) -> u64 {
        if self.total_ram_bytes == 0 {
            512 * 1024 * 1024
        } else {
            (self.total_ram_bytes.saturating_mul(RAM_MAX_PERCENT)) / 100
        }
    }

    pub fn max_framebuffer_bytes_for_input(&self, input_pixels: usize) -> u64 {
        let requested = (input_pixels as u64).saturating_mul(BYTES_PER_PIXEL_F64_RGB);
        requested.min(self.max_framebuffer_bytes()).max(1)
    }

    pub fn max_gpu_allocation_bytes(&self) -> u64 {
        if self.vram_bytes == 0 {
            0
        } else {
            (self.vram_bytes.saturating_mul(GPU_MAX_PERCENT)) / 100
        }
    }

    pub fn log_summary(&self) {
        crate::runtime_log!(
            "hardware: logical_cores={} vram={}MB ram={}MB",
            self.logical_cores,
            self.vram_bytes / (1024 * 1024),
            self.total_ram_bytes / (1024 * 1024),
        );
    }
}

fn detect_vram_bytes() -> u64 {
    #[cfg(target_os = "linux")]
    {
        for i in 0..8 {
            let path = format!("/sys/class/drm/card{}/device/mem_info_vram_total", i);
            if let Ok(s) = std::fs::read_to_string(&path)
                && let Ok(n) = s.trim().parse::<u64>()
                && n > 0
            {
                return n;
            }
        }
    }
    0
}