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
//! GPU compute dispatch, framebuffer upload, fence/sync, and SIMD/GPU tag helpers.

use crate::core::engine::acces_hardware::{self, DrmDriver, KernelConfig};
use crate::core::engine::rendering::{
    framebuffer::FrameBuffer, raytracing::Scene, shader_dispatcher::TileComputeDescriptor,
};

use super::state::Renderer;

impl Renderer {
    pub(super) fn submit_compute_workload(
        &self,
        scene: &Scene,
        width: usize,
        height: usize,
    ) -> bool {
        let mut dispatcher = Self::lock_unpoisoned(&self.compute_dispatcher);
        if dispatcher.device_count() == 0 {
            return false;
        }

        let workgroup_size = acces_hardware::arch_optimal_workgroup().max(1);
        let kernel_x = (workgroup_size.min(8)) as u16;
        let kernel_y = workgroup_size.div_ceil(kernel_x as usize).max(1) as u16;
        let tile_size = (kernel_x as usize)
            .saturating_mul(kernel_y as usize)
            .max(16);
        let kernel = KernelConfig::new(kernel_x, kernel_y, 1)
            .with_shared_memory((workgroup_size * 32) as u32);
        let kernel_source = format!(
            "kernel trace_tile(u32 tile_id, u32 object_count, u32 triangle_count, u64 scene_signature) {{ u32 lanes = {}; u32 tiles_x = {}; u32 tiles_y = {}; }}",
            workgroup_size,
            width.div_ceil(tile_size),
            height.div_ceil(tile_size),
        );

        dispatcher
            .dispatch_tile_compute_with_kernel(TileComputeDescriptor {
                image_width: width,
                image_height: height,
                tile_size,
                config: kernel,
                kernel_name: "trace-tile",
                kernel_source: kernel_source.as_bytes(),
                scene_signature: scene.geometry_signature(),
                object_count: scene.objects.len(),
                triangle_count: scene.triangles.len(),
            })
            .is_ok()
    }

    pub(super) fn upload_framebuffer_to_gpu(&self, framebuffer: &FrameBuffer) -> bool {
        let Some(gpu) = self.gpu.as_ref() else {
            return false;
        };
        let mut rgb = vec![0u8; framebuffer.width * framebuffer.height * 3];
        for (pixel, out) in framebuffer.color.iter().zip(rgb.chunks_exact_mut(3)) {
            let corrected = pixel.clamp(0.0, 1.0).powf(1.0 / 2.2);
            out[0] = (corrected.x * 255.0).round() as u8;
            out[1] = (corrected.y * 255.0).round() as u8;
            out[2] = (corrected.z * 255.0).round() as u8;
        }
        gpu.write_framebuffer_rgb(&rgb)
    }

    pub(super) fn gpu_info_tag(&self) -> String {
        match &self.gpu {
            Some(g) if g.has_valid_metrics() => format!(
                "gpu(driver={} vram={}MB cu={} {:04x})",
                g.driver_name(),
                g.vram_bytes() / (1024 * 1024),
                g.compute_units(),
                g.info().device_id,
            ),
            Some(g) => format!("gpu(driver={} telemetry=unavailable)", g.driver_name()),
            None => "cpu-only".to_string(),
        }
    }

    pub(super) fn simd_tag(&self) -> &'static str {
        let s = &self.cpu_profile.simd_features;
        if s.avx512f {
            "AVX-512"
        } else if s.avx2 {
            "AVX2"
        } else if s.avx {
            "AVX"
        } else if s.fma {
            "FMA"
        } else if s.sse4_2 {
            "SSE4.2"
        } else if s.sse2 {
            "SSE2"
        } else if s.neon {
            "NEON"
        } else {
            "scalar"
        }
    }

    pub(super) fn gpu_fence_and_sync(&self) -> Option<f64> {
        if let Some(ref g) = self.gpu {
            let t0 = acces_hardware::precise_timestamp_ns();

            if g.has_active_framebuffer() && !matches!(g.driver(), DrmDriver::Radeon) {
                let nop_ib: [u32; 4] = [0xC0021000, 0x00000000, 0x00000000, 0x00000000];
                match g.submit_ib(&nop_ib) {
                    Ok(cs_id) => {
                        g.sync_framebuffer();
                        let elapsed =
                            acces_hardware::elapsed_ms(t0, acces_hardware::precise_timestamp_ns());
                        crate::runtime_log!("gpu: fence cs_id={} sync={:.2}ms", cs_id, elapsed);
                        return Some(elapsed);
                    }
                    Err(e) => {
                        crate::runtime_log!("gpu: fence submit failed ({}), fallback sync", e);
                    }
                }
            }

            g.sync_framebuffer();
            let elapsed = acces_hardware::elapsed_ms(t0, acces_hardware::precise_timestamp_ns());
            Some(elapsed)
        } else {
            None
        }
    }
}