From f18aed2c91639bbd9967ba8dc7e16e554390368d Mon Sep 17 00:00:00 2001 From: Wito Wiala Date: Fri, 29 May 2026 22:41:27 +0100 Subject: [PATCH] Broken state --- shared/src/wavefront/workitems.rs | 44 +---- src/wavefront/aggregate.rs | 121 ++++++------ src/wavefront/integrator.rs | 301 +++++++++++++++--------------- 3 files changed, 213 insertions(+), 253 deletions(-) diff --git a/shared/src/wavefront/workitems.rs b/shared/src/wavefront/workitems.rs index 5f5e5ea..3becd60 100644 --- a/shared/src/wavefront/workitems.rs +++ b/shared/src/wavefront/workitems.rs @@ -1,6 +1,6 @@ use crate::core::bxdf::BxDFFlags; use crate::core::geometry::{ - Normal3f, Point2f, Point2i, Point3f, Point3fi, RayDifferential, Vector3f, + Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray, RayDifferential, Vector3f, }; use crate::core::light::Light; use crate::core::light::LightSampleContext; @@ -64,12 +64,7 @@ impl SoA for PixelSampleState { #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct RayWorkItem { - pub ray_o: Point3f, - pub ray_d: Vector3f, - pub ray_medium: Ptr, - pub differential: RayDifferential, - pub has_differentials: bool, - pub ray_time: Float, + pub ray: Ray, pub depth: u32, pub lambda: SampledWavelengths, pub pixel_index: u32, @@ -85,12 +80,7 @@ pub struct RayWorkItem { #[repr(C)] #[derive(Clone, Copy)] pub struct RayWorkItemSoA { - pub ray_o: SoABuffer, - pub ray_d: SoABuffer, - pub ray_time: SoABuffer, - pub ray_medium: SoABuffer>, - pub has_differentials: SoABuffer, - pub differential: SoABuffer, + pub ray: SoABuffer, pub depth: SoABuffer, pub lambda: SoABuffer, pub pixel_index: SoABuffer, @@ -101,7 +91,6 @@ pub struct RayWorkItemSoA { pub eta_scale: SoABuffer, pub specular_bounce: SoABuffer, pub any_non_specular_bounces: SoABuffer, - } impl SoA for RayWorkItemSoA { @@ -109,12 +98,7 @@ impl SoA for RayWorkItemSoA { fn allocate(n: u32, alloc: &dyn SoAAllocator) -> Self { Self { - ray_o: alloc_soa_buffer(n, alloc), - ray_d: alloc_soa_buffer(n, alloc), - ray_time: alloc_soa_buffer(n, alloc), - ray_medium: alloc_soa_buffer(n, alloc), - has_differentials: alloc_soa_buffer(n, alloc), - differential: alloc_soa_buffer(n, alloc), + ray: alloc_soa_buffer(n, alloc), depth: alloc_soa_buffer(n, alloc), lambda: alloc_soa_buffer(n, alloc), pixel_index: alloc_soa_buffer(n, alloc), @@ -130,12 +114,7 @@ impl SoA for RayWorkItemSoA { unsafe fn get(&self, i: usize) -> RayWorkItem { RayWorkItem { - ray_o: self.ray_o.get(i), - ray_d: self.ray_d.get(i), - ray_time: self.ray_time.get(i), - ray_medium: self.ray_medium.get(i), - has_differentials: self.has_differentials.get(i), - differential: self.differential.get(i), + ray: self.ray.get(i), depth: self.depth.get(i), lambda: self.lambda.get(i), pixel_index: self.pixel_index.get(i), @@ -146,17 +125,11 @@ impl SoA for RayWorkItemSoA { eta_scale: self.eta_scale.get(i), specular_bounce: self.specular_bounce.get(i), any_non_specular_bounces: self.any_non_specular_bounces.get(i), - } } unsafe fn set(&self, i: usize, v: RayWorkItem) { - self.ray_o.set(i, v.ray_o); - self.ray_d.set(i, v.ray_d); - self.ray_time.set(i, v.ray_time); - self.ray_medium.set(i, v.ray_medium); - self.has_differentials.set(i, v.has_differentials); - self.differential.set(i, v.differential); + self.ray.set(i, v.ray); self.depth.set(i, v.depth); self.lambda.set(i, v.lambda); self.pixel_index.set(i, v.pixel_index); @@ -166,7 +139,8 @@ impl SoA for RayWorkItemSoA { self.prev_intr_ctx.set(i, v.prev_intr_ctx); self.eta_scale.set(i, v.eta_scale); self.specular_bounce.set(i, v.specular_bounce); - self.any_non_specular_bounces.set(i, v.any_non_specular_bounces); + self.any_non_specular_bounces + .set(i, v.any_non_specular_bounces); } } @@ -538,6 +512,8 @@ impl SoA for ShadowRayWorkItemSoA { self.lambda.set(i, v.lambda); self.l_d.set(i, v.l_d); self.pixel_index.set(i, v.pixel_index); + self.r_u.set(i, v.r_u); + self.r_l.set(i, v.r_l); } } diff --git a/src/wavefront/aggregate.rs b/src/wavefront/aggregate.rs index 1982edc..df1b06d 100644 --- a/src/wavefront/aggregate.rs +++ b/src/wavefront/aggregate.rs @@ -1,10 +1,11 @@ use crate::globals::get_options; use rayon::prelude::*; -use shared::core::geometry::{Bounds3f, Ray, Vector3f, VectorLike}; +use shared::core::geometry::{Bounds3f, Ray, VectorLike}; use shared::core::interaction::InteractionTrait; use shared::core::material::MaterialTrait; use shared::core::primitive::{Primitive, PrimitiveTrait}; -use shared::core::texture::{BasicTextureEvaluator, TextureEvaluator, UniversalTextureEvaluator}; +use shared::core::texture::BasicTextureEvaluator; +use shared::core::texture::TextureEvaluator; use shared::wavefront::workitems::*; use shared::wavefront::WavefrontAggregate; use shared::Ptr; @@ -33,86 +34,73 @@ impl WavefrontAggregate for CpuAggregate { basic_eval_mtl_q: &MaterialEvalQueue, universal_eval_mtl_q: &MaterialEvalQueue, next_ray_q: &RayQueue, - pixel_sample_state: &PixelSampleState, + _pixel_sample_state: &PixelSampleState, ) { let n_rays = ray_q.size().min(max_rays as u32); - let options = get_options(); + // Intersect _r_'s ray with the scene and enqueue resulting work (0..n_rays as usize).into_par_iter().for_each(|i| { - let work = unsafe { ray_q.get(i) }; + let r = unsafe { ray_q.get(i) }; - let ray = Ray::new(work.ray_o, work.ray_d, Some(work.ray_time), work.ray_medium); - - let pi = work.pixel_index as usize; - let beta = pixel_sample_state.beta.get(pi); - let r_u = pixel_sample_state.r_u.get(pi); - let r_l = pixel_sample_state.r_l.get(pi); - let lambda = pixel_sample_state.lambda.get(pi); - let depth = pixel_sample_state.depth.get(pi); - let specular_bounce = pixel_sample_state.specular_bounce.get(pi) != 0; - let any_non_specular = pixel_sample_state.any_non_specular_bounces.get(pi) != 0; - let eta_scale = pixel_sample_state.eta_scale.get(pi); - let prev_intr_ctx = pixel_sample_state.prev_intr_ctx.get(pi); - - let Some(si) = self.aggregate.intersect(&ray, None) else { + let Some(si) = self.aggregate.intersect(&r.ray, None) else { + // EnqueueMiss - push to escaped queue with r's path state escaped_ray_q.push(EscapedRayWorkItem { - ray_o: work.ray_o, - ray_d: work.ray_d, - lambda, - pixel_index: work.pixel_index, - beta, - r_u, - r_l, - depth, - specular_bounce, - prev_intr_ctx, + ray_o: r.ray.o, + ray_d: r.ray.d, + lambda: r.lambda, + pixel_index: r.pixel_index, + beta: r.beta, + r_u: r.r_u, + r_l: r.r_l, + depth: r.depth, + specular_bounce: r.specular_bounce != 0, + prev_intr_ctx: r.prev_intr_ctx, }); return; }; let intr = &si.intr; + // Medium transition — re-push as indirect ray with same path state if intr.material.is_null() { - let ray_o = Ray::offset_origin(&intr.pi(), &intr.n(), &work.ray_d); - let ray_medium = if work.ray_d.dot(intr.n().into()) > 0.0 { - intr.common.medium_interface.outside - } else { - intr.common.medium_interface.inside - }; - next_ray_q.push(RayWorkItem { - ray_o, - ray_d: work.ray_d, - ray_time: work.ray_time, - ray_medium, - has_differentials: work.has_differentials, - differential: work.differential, - pixel_index: work.pixel_index, - }); + let mut next = r; + intr.spawn_ray(r.ray.d); + next.ray = intr.spawn_ray(r.ray.d); + next_ray_q.push(next); return; } - // Check for area light hit + // Area light hit if !intr.area_light.is_null() { hit_area_light_q.push(HitAreaLightWorkItem { area_light: intr.area_light, p: intr.p(), n: intr.n(), uv: intr.common.uv, - wo: -work.ray_d, - lambda, - pixel_index: work.pixel_index, - beta, - r_u, - r_l, - depth, - specular_bounce, - prev_intr_ctx, + wo: -r.ray.d, + lambda: r.lambda, + pixel_index: r.pixel_index, + beta: r.beta, + r_u: r.r_u, + r_l: r.r_l, + depth: r.depth, + specular_bounce: r.specular_bounce != 0, + prev_intr_ctx: r.prev_intr_ctx, }); } - // Determine which material evaluation queue to use based on - // whether the material's textures can be evaluated with the - // basic evaluator (cheaper) or need the universal one. + // Material eval queue dispatch + // let material = *intr.material.get().unwrap(); + // let displacement = material.get_displacement(); + // let eval_q = if material.can_evaluate_textures(&BasicTextureEvaluator) + // && (displacement.is_null() + // || BasicTextureEvaluator.can_evaluate(&[displacement], &[])) + // { + // basic_eval_mtl_q + // } else { + // universal_eval_mtl_q + // }; + // let material = *intr.material.get().unwrap(); let eval_q = if material.can_evaluate_textures(&BasicTextureEvaluator) { basic_eval_mtl_q @@ -127,19 +115,19 @@ impl WavefrontAggregate for CpuAggregate { dpdu: intr.shading.dpdu, dpdv: intr.shading.dpdv, uv: intr.common.uv, - wo: -work.ray_d, - time: work.ray_time, + wo: -r.ray.d, + time: r.ray.time, face_index: intr.face_index, material: intr.material, area_light: intr.area_light, medium_interface: intr.common.medium_interface, - pixel_index: work.pixel_index, - lambda, - beta, - r_u, - any_non_specular_bounces: any_non_specular, - depth, - eta_scale, + pixel_index: r.pixel_index, + lambda: r.lambda, + beta: r.beta, + r_u: r.r_u, + any_non_specular_bounces: r.any_non_specular_bounces != 0, + depth: r.depth, + eta_scale: r.eta_scale, }); }); } @@ -156,7 +144,6 @@ impl WavefrontAggregate for CpuAggregate { let work = unsafe { shadow_ray_q.get(i) }; let ray = Ray::new(work.ray_o, work.ray_d, Some(work.ray_time), Ptr::null()); - if !self.aggregate.intersect_p(&ray, Some(work.t_max)) { let pi = work.pixel_index as usize; let ld = work.l_d / (work.r_u + work.r_l).average(); diff --git a/src/wavefront/integrator.rs b/src/wavefront/integrator.rs index 6dbb341..15da42e 100644 --- a/src/wavefront/integrator.rs +++ b/src/wavefront/integrator.rs @@ -1,6 +1,7 @@ use super::CpuAggregate; use crate::globals::get_options; use crate::PbrtProgress; +use rayon::prelude::*; use shared::core::bxdf::{FArgs, TransportMode}; use shared::core::camera::{Camera, CameraTrait}; use shared::core::film::VisibleSurface; @@ -39,6 +40,9 @@ impl DerefMut for CpuWavefrontRenderer { } } +use std::sync::Mutex; +static M: Mutex<()> = Mutex::new(()); + impl CpuWavefrontRenderer { pub fn render(&mut self) { let film = self.camera.get_film(); @@ -64,7 +68,7 @@ impl CpuWavefrontRenderer { let current = (depth % 2) as usize; let next = ((depth + 1) % 2) as usize; - // Reset queues + // Reset queues before tracing next batch of rays self.ray_queues[next].reset(); self.escaped_ray_queue.reset(); self.hit_area_light_queue.reset(); @@ -78,14 +82,6 @@ impl CpuWavefrontRenderer { self.generate_ray_samples(depth, sample_index); - if depth == 0 { - let rs = self.pixel_sample_state.samples.get(0); - eprintln!( - "sample check: direct.uc={} indirect.uc={} indirect.rr={}", - rs.direct.uc, rs.indirect.uc, rs.indirect.rr - ); - } - self.aggregate.intersect_closest( self.max_queue_size as usize, &self.ray_queues[current], @@ -122,8 +118,6 @@ impl CpuWavefrontRenderer { } } - // Enqueue camera ray and set pixel state for sample - // Compute pixel coordinates for _pixelIndex_ fn generate_camera_rays( &mut self, y0: i32, @@ -132,136 +126,146 @@ impl CpuWavefrontRenderer { pixel_bounds: &Bounds2i, ) { // For each pixel in the scanline range, generate a camera ray - // and push it to the ray queue. Also initialize the PixelSampleState. - let filter = self.filter.clone(); + // and push it to the ray queue. Initialize the PixelSampleState. + let filter = self.filter; let film = self.film; - for y in y0..y1 { - for x in pixel_bounds.p_min.x()..pixel_bounds.p_max.x() { - let p_pixel = Point2i::new(x, y); + let camera = &self.camera; + let sampler_proto = &self.sampler; + let pixel_sample_state = &self.pixel_sample_state; + let ray_queue = &self.ray_queues[0]; - // TODO: proper sampler state per pixel/sample - // For now, use a simple approach - self.sampler - .start_pixel_sample(p_pixel, sample_index as i32, Some(0)); + let x_resolution = pixel_bounds.p_max.x() - pixel_bounds.p_min.x(); + let n_scanlines = y1 - y0; + let total = (n_scanlines * x_resolution) as usize; - let lu = self.sampler.get1d(); - let lambda = film.sample_wavelengths(lu); - let camera_sample = get_camera_sample(&mut self.sampler, p_pixel, &filter); + (0..total).into_par_iter().for_each(|idx| { + let dx = idx as i32 % x_resolution; + let dy = idx as i32 / x_resolution; + let p_pixel = Point2i::new(pixel_bounds.p_min.x() + dx, y0 + dy); + let pixel_index = idx as u32; + let pi = idx; - let Some(camera_ray) = self.camera.generate_ray(camera_sample, &lambda) else { - continue; - }; - - // Compute pixel index for this sample - let pixel_index = self.ray_queues[0].size(); - - // Initialize persistent pixel state - let pi = pixel_index as usize; - self.pixel_sample_state.l.set(pi, SampledSpectrum::new(0.0)); - self.pixel_sample_state - .beta - .set(pi, SampledSpectrum::new(1.0)); - self.pixel_sample_state - .camera_ray_weight - .set(pi, camera_ray.weight); - self.pixel_sample_state.lambda.set(pi, lambda); - self.pixel_sample_state - .r_u - .set(pi, SampledSpectrum::new(1.0)); - self.pixel_sample_state - .r_l - .set(pi, SampledSpectrum::new(1.0)); - self.pixel_sample_state.depth.set(pi, 0); - self.pixel_sample_state.specular_bounce.set(pi, 1); - self.pixel_sample_state.any_non_specular_bounces.set(pi, 0); - self.pixel_sample_state.eta_scale.set(pi, 1.0); - self.pixel_sample_state.p_film.set(pi, camera_sample.p_film); - self.pixel_sample_state - .filter_weight - .set(pi, camera_sample.filter_weight); - self.pixel_sample_state - .prev_intr_ctx - .set(pi, LightSampleContext::default()); - self.pixel_sample_state.p_pixel.set(pi, p_pixel); - - // Push ray to queue - self.ray_queues[0].push(RayWorkItem { - ray_o: camera_ray.ray.o, - ray_d: camera_ray.ray.d, - ray_time: camera_ray.ray.time, - ray_medium: camera_ray.ray.medium, - pixel_index: pixel_index, - has_differentials: true, - differential: RayDifferential::default(), - }); + if !pixel_bounds.contains_exclusive(p_pixel) { + return; } - } + + let mut sampler = sampler_proto.clone(); + sampler.start_pixel_sample(p_pixel, sample_index as i32, Some(0)); + + let lu = sampler.get1d(); + let lambda = film.sample_wavelengths(lu); + let camera_sample = get_camera_sample(&mut sampler, p_pixel, &filter); + + pixel_sample_state.l.set(pi, SampledSpectrum::new(0.0)); + pixel_sample_state.lambda.set(pi, lambda); + pixel_sample_state + .filter_weight + .set(pi, camera_sample.filter_weight); + pixel_sample_state.p_film.set(pi, camera_sample.p_film); + pixel_sample_state.p_pixel.set(pi, p_pixel); + + let Some(camera_ray) = camera.generate_ray(camera_sample, &lambda) else { + pixel_sample_state + .camera_ray_weight + .set(pi, SampledSpectrum::new(0.0)); + return; + }; + + pixel_sample_state + .camera_ray_weight + .set(pi, camera_ray.weight); + + ray_queue.push(RayWorkItem { + ray: camera_ray.ray, + depth: 0, + pixel_index, + lambda, + beta: SampledSpectrum::new(1.0), + r_u: SampledSpectrum::new(1.0), + r_l: SampledSpectrum::new(1.0), + prev_intr_ctx: LightSampleContext::default(), + eta_scale: 1.0, + specular_bounce: 0, + any_non_specular_bounces: 0, + }); + }); } /// Evaluate infinite lights. fn handle_escaped_rays(&self) { let n = self.escaped_ray_queue.size(); - for i in 0..n as usize { - let w = unsafe { self.escaped_ray_queue.storage.get(i) }; + let infinite_lights = &self.infinite_lights; + let light_sampler = &self.light_sampler; + let pixel_sample_state = &self.pixel_sample_state; + let escaped_ray_queue = &self.escaped_ray_queue; + + (0..n as usize).into_par_iter().for_each(|i| { + let w = unsafe { escaped_ray_queue.storage.get(i) }; let mut l_contrib = SampledSpectrum::new(0.0); - // Evaluate all infinite lights - for light_ptr in &self.infinite_lights { + for light_ptr in infinite_lights { let light = light_ptr.get().unwrap(); let ray = Ray::new(w.ray_o, w.ray_d, None, Ptr::null()); let le = light.le(&ray, &w.lambda); if le.is_black() { - continue; + return; } if w.depth == 0 || w.specular_bounce { - // No MIS for direct camera rays or specular bounces l_contrib += w.beta * le / w.r_u.average(); } else { - // MIS with light sampling - // TODO: compute light PDF for MIS weight - // For now, use unidirectional weight only - l_contrib += w.beta * le / w.r_u.average(); + // MIS: combine BSDF and light sampling weights via ratio tracking + let ctx = w.prev_intr_ctx; + let light_choice_pdf = light_sampler.pmf_with_context(&ctx, light); + let r_l = w.r_l * light_choice_pdf * light.pdf_li(&ctx, w.ray_d, true); + l_contrib += w.beta * le / (w.r_u + r_l).average(); } } if !l_contrib.is_black() { let pi = w.pixel_index as usize; - let mut l = self.pixel_sample_state.l.get(pi); + let mut l = pixel_sample_state.l.get(pi); l += l_contrib; - self.pixel_sample_state.l.set(pi, l); + pixel_sample_state.l.set(pi, l); } - } + }); } - /// Handle emissive intersections — area light contribution with MIS. fn handle_emissive_intersections(&self) { let n = self.hit_area_light_queue.size(); - for i in 0..n as usize { - let w = unsafe { self.hit_area_light_queue.storage.get(i) }; + let light_sampler = &self.light_sampler; + let pixel_sample_state = &self.pixel_sample_state; + let hit_area_light_queue = &self.hit_area_light_queue; + + (0..n as usize).into_par_iter().for_each(|i| { + let w = unsafe { hit_area_light_queue.storage.get(i) }; let light = w.area_light.get().unwrap(); let le = light.l(w.p, w.n, w.uv, w.wo, &w.lambda); if le.is_black() { - continue; + return; } let l_contrib = if w.depth == 0 || w.specular_bounce { w.beta * le / w.r_u.average() } else { - // MIS: combine BSDF and light sampling weights - // TODO: full MIS with light sampler PDF - w.beta * le / w.r_u.average() + let ctx = w.prev_intr_ctx; + let light_choice_pdf = light_sampler.pmf_with_context(&ctx, light); + // wi from previous interaction to this light hit + let wi = (w.p - Point3f::from(ctx.pi)).normalize(); + let light_pdf = light_choice_pdf * light.pdf_li(&ctx, wi, true); + let r_l = w.r_l * light_pdf; + w.beta * le / (w.r_u + r_l).average() }; if !l_contrib.is_black() { let pi = w.pixel_index as usize; - let mut l = self.pixel_sample_state.l.get(pi); + let mut l = pixel_sample_state.l.get(pi); l += l_contrib; - self.pixel_sample_state.l.set(pi, l); + pixel_sample_state.l.set(pi, l); } - } + }); } fn evaluate_materials_and_bsdfs(&mut self, depth: u32) { @@ -279,13 +283,20 @@ impl CpuWavefrontRenderer { let n = queue.size(); let next = ((depth + 1) % 2) as usize; + let pixel_sample_state = &self.pixel_sample_state; + let light_sampler = &self.light_sampler; + let shadow_ray_queue = &self.shadow_ray_queue; + let next_ray_queue = &self.ray_queues[next]; + let regularize = self.regularize; + + // (0..n as usize).into_par_iter().for_each(|i| { for i in 0..n as usize { let w = unsafe { queue.storage.get(i) }; let pi = w.pixel_index as usize; - let rs = self.pixel_sample_state.samples.get(pi); + let rs = pixel_sample_state.samples.get(pi); let Some(material) = w.material.get() else { - continue; + return; }; let tex_eval = UniversalTextureEvaluator; @@ -309,13 +320,13 @@ impl CpuWavefrontRenderer { let lambda = w.lambda; let mut bsdf = material.get_bsdf(&tex_eval, &ctx, &lambda); if bsdf.flags().is_empty() { - continue; + return; } if self.regularize && w.any_non_specular_bounces { bsdf.regularize(); } - // BSDF sampling for indirect ray + // BSDF sample for indirect ray let wo = w.wo; let ns = w.ns; if let Some(bs) = bsdf.sample_f(wo, rs.indirect.uc, rs.indirect.u, FArgs::default()) { @@ -333,7 +344,6 @@ impl CpuWavefrontRenderer { eta_scale *= square(bs.eta); } - // Russian roulette let rr_beta = (beta * eta_scale / r_u.average()).max_component_value(); if rr_beta < 1.0 && w.depth >= 1 { let q = (1.0 - rr_beta).max(0.0_f32); @@ -353,34 +363,23 @@ impl CpuWavefrontRenderer { ns, }; - // Push indirect ray with updated path state - self.ray_queues[next].push(RayWorkItem { - ray_o: ray.o, - ray_d: ray.d, - ray_time: w.time, - ray_medium: Ptr::null(), + next_ray_queue.push(RayWorkItem { + ray, + depth: w.depth + 1, pixel_index: w.pixel_index, - has_differentials: false, - differential: RayDifferential::default(), + lambda, + beta, + r_u, + r_l, + prev_intr_ctx: ctx, + eta_scale, + specular_bounce: bs.is_specular() as u8, + any_non_specular_bounces: any_non_specular as u8, }); - - // Update PixelSampleState for next bounce - self.pixel_sample_state.beta.set(pi, beta); - self.pixel_sample_state.r_u.set(pi, r_u); - self.pixel_sample_state.r_l.set(pi, r_l); - self.pixel_sample_state.depth.set(pi, w.depth + 1); - self.pixel_sample_state.eta_scale.set(pi, eta_scale); - self.pixel_sample_state - .specular_bounce - .set(pi, bs.is_specular() as u8); - self.pixel_sample_state - .any_non_specular_bounces - .set(pi, any_non_specular as u8); - self.pixel_sample_state.prev_intr_ctx.set(pi, ctx); } } - // --- Direct lighting (independent of BSDF sample) --- + // Direct lighting — independent let flags = bsdf.flags(); if flags.is_non_specular() { let light_ctx = LightSampleContext { @@ -388,7 +387,6 @@ impl CpuWavefrontRenderer { n: w.n, ns, }; - if let Some(sampled_light) = self .light_sampler .sample_with_context(&light_ctx, rs.direct.uc) @@ -441,48 +439,47 @@ impl CpuWavefrontRenderer { } } } + // }); } fn update_film(&self, y0: i32, y1: i32, pixel_bounds: &Bounds2i) { - // The pixel_sample_state indices map to rays generated in - // generate_camera_rays. We need to walk the same pixel order - // and read back the accumulated L values. - let mut pi = 0usize; - for y in y0..y1 { - for x in pixel_bounds.p_min.x()..pixel_bounds.p_max.x() { - let l = self.pixel_sample_state.l.get(pi); - let camera_weight = self.pixel_sample_state.camera_ray_weight.get(pi); - let weigthed_l = l * camera_weight; + let x_resolution = pixel_bounds.p_max.x() - pixel_bounds.p_min.x(); + let n_scanlines = y1 - y0; + let total = (n_scanlines * x_resolution) as usize; - let lambda = self.pixel_sample_state.lambda.get(pi); - let filter_weight = self.pixel_sample_state.filter_weight.get(pi); - let p_film = self.pixel_sample_state.p_film.get(pi); - - // Add sample to film - self.film.add_sample( - Point2i::new(p_film.x() as i32, p_film.y() as i32), - weigthed_l, - &lambda, - Some(&VisibleSurface::default()), - filter_weight, - ); - - pi += 1; + (0..total).into_par_iter().for_each(|idx| { + let pi = idx; + let p_pixel = self.pixel_sample_state.p_pixel.get(pi); + if !pixel_bounds.contains_exclusive(p_pixel) { + return; } - } + + let l = self.pixel_sample_state.l.get(pi); + let camera_weight = self.pixel_sample_state.camera_ray_weight.get(pi); + let weighted_l = l * camera_weight; + let lambda = self.pixel_sample_state.lambda.get(pi); + let filter_weight = self.pixel_sample_state.filter_weight.get(pi); + + self.film + .add_sample(p_pixel, weighted_l, &lambda, None, filter_weight); + }); } fn generate_ray_samples(&mut self, depth: u32, sample_index: u32) { let current = (depth % 2) as usize; - let n = self.ray_queues[current].size(); + let ray_queue = &self.ray_queues[current]; + let n = ray_queue.size(); let dimension = 6 + 7 * depth; + let pixel_sample_state = &self.pixel_sample_state; + let sampler_proto = &self.sampler; - for i in 0..n as usize { - let w = unsafe { self.ray_queues[current].storage.get(i) }; + (0..n as usize).into_par_iter().for_each(|i| { + let _g = M.lock().unwrap(); + let w = unsafe { ray_queue.storage.get(i) }; let pi = w.pixel_index as usize; - let p_pixel = self.pixel_sample_state.p_pixel.get(pi); + let p_pixel = pixel_sample_state.p_pixel.get(pi); - let mut sampler = self.sampler.clone(); + let mut sampler = sampler_proto.clone(); sampler.start_pixel_sample(p_pixel, sample_index as i32, Some(dimension)); self.pixel_sample_state.samples.set( @@ -499,6 +496,6 @@ impl CpuWavefrontRenderer { }, }, ); - } + }); } }