//! `wai-splat-render:i1` with its compositing on a GPU (extensions/splat-exact.md §5.6–§5.7,
//! §11): the host projects, culls and orders the Gaussians ([`super::screens`]) and the
//! device composes every pixel with `wai_ops_gpu`'s splat kernel, whose 64-bit products are
//! the operator set's, probed on the device before it runs anything. The frame is the CPU's
//! byte for byte; `tests/splat_exact_gpu.rs` holds it to that on every adapter found.

pub use wai_ops_gpu;
use wai_ops_gpu::splat::SCREEN_WORDS;
use wai_ops_gpu::{Gpu, GpuError};

use super::{Camera, Gaussian, Pose, Reason, Screen, Variant};

/// What the device reads of each kept Gaussian, in order (§5.5).
pub fn words(list: &[Screen]) -> Vec<[i32; SCREEN_WORDS]> {
    let w = |v: i64| i32::try_from(v).expect("§5.9: every value compositing reads fits i32");
    list.iter()
        .map(|s| [w(s.mx), w(s.my), w(s.x0), w(s.x1), w(s.y0), w(s.y1), w(s.k1), w(s.k2), w(s.k3), w(s.opacity), w(s.color[0]), w(s.color[1]), w(s.color[2])])
        .collect()
}

/// The frame of a splat set at a pose, composed on `g`; the renderer's refusal, or the
/// device's error.
#[cfg(not(target_arch = "wasm32"))]
pub fn render(g: &Gpu, gs: &[Gaussian], pose: &Pose) -> Result<Result<Vec<u8>, Reason>, GpuError> {
    render_with(g, None, gs, pose)
}

/// [`render`] with the kernel compiled from `text` (a test's mutant), where given.
#[cfg(not(target_arch = "wasm32"))]
pub fn render_with(g: &Gpu, text: Option<&str>, gs: &[Gaussian], pose: &Pose) -> Result<Result<Vec<u8>, Reason>, GpuError> {
    let cam = match Camera::new(pose) {
        Ok(c) => c,
        Err(r) => return Ok(Err(r)),
    };
    let list = words(&super::screens(gs, pose, &cam, Variant::default()));
    let (w, h) = (pose.width as u32, pose.height as u32);
    let frame = match text {
        None => wai_ops_gpu::splat::blocking::composite(g, &list, w, h)?,
        Some(t) => wai_ops_gpu::splat::blocking::composite_with(g, t, &list, w, h)?,
    };
    Ok(Ok(frame))
}
