use super::gpu_backend::{BackendFrame, FrameOutputs};
use glam::Vec3;
use indicatrix::optics::raytracer::{
HitRecord, build_plane_soa, pixel_rotations, sample_draws, trace_spectral_ray_with_finish_soa,
};
use std::{
sync::{
Mutex,
atomic::{AtomicUsize, Ordering},
},
thread,
};
struct RowSlices<'a> {
acc: &'a mut [Vec3],
depth: &'a mut [f32],
normal: &'a mut [Vec3],
facet: &'a mut [i32],
}
pub(super) fn render_frame_scanlines(
frame: &BackendFrame<'_>,
spp: u32,
current_sample_count: u32,
outputs: &mut FrameOutputs<'_>,
) {
let width = frame.width;
let height = frame.height;
let num_threads = thread::available_parallelism().map_or(8, std::num::NonZero::get);
let width_usize = width as usize;
let FrameOutputs {
accum,
depth,
normal,
facet_id,
} = outputs;
let rows: Vec<Option<RowSlices<'_>>> = accum
.chunks_mut(width_usize)
.zip(depth.chunks_mut(width_usize))
.zip(normal.chunks_mut(width_usize))
.zip(facet_id.chunks_mut(width_usize))
.map(|(((acc, depth), normal), facet)| {
Some(RowSlices {
acc,
depth,
normal,
facet,
})
})
.collect();
let rows = Mutex::new(rows);
let next_row = AtomicUsize::new(0);
let plane_soa = build_plane_soa(frame.planes);
thread::scope(|s| {
for _ in 0..num_threads {
let rows = &rows;
let next_row = &next_row;
let plane_soa = &plane_soa;
s.spawn(move || {
loop {
let y = next_row.fetch_add(1, Ordering::Relaxed);
if y >= height as usize {
break;
}
let RowSlices {
acc: acc_row,
depth: depth_row,
normal: normal_row,
facet: facet_row,
} = rows
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)[y]
.take()
.expect("each row index is claimed by exactly one thread via fetch_add");
for x in 0..width_usize {
let global_pixel_idx = (y * width_usize + x) as u32;
let mut sample_sum = Vec3::ZERO;
let mut primary_hit: Option<HitRecord> = None;
let rot = pixel_rotations(global_pixel_idx);
for s_idx in 0..spp {
let sample_num = current_sample_count - spp + s_idx;
let draws = sample_draws(global_pixel_idx, sample_num, &rot);
let ray = frame.camera.generate_ray(
x as f32,
y as f32,
width as f32,
height as f32,
draws.jitter_x,
draws.jitter_y,
);
let sample_xyz = trace_spectral_ray_with_finish_soa(
ray,
frame.planes,
plane_soa,
frame.facet_finishes,
frame.material,
frame.max_bounces,
frame.environment,
draws.seed,
draws.hero_rand,
Some(&mut primary_hit),
);
sample_sum += sample_xyz;
}
acc_row[x] += sample_sum;
depth_row[x] = primary_hit.map_or(1.0e6, |h| h.t);
normal_row[x] = primary_hit.map_or(Vec3::ZERO, |h| h.normal);
facet_row[x] = primary_hit.map_or(-1, |h| h.facet_idx as i32);
}
}
});
}
});
}