From 0c62fbc3b5046b8cd3b89f09564f83dccbe295d6 Mon Sep 17 00:00:00 2001 From: Wito Wiala Date: Tue, 26 May 2026 19:07:45 +0100 Subject: [PATCH] Fixed logic issues in BSDF sampling and pdf calculations, moving on to GPU integrators --- shared/src/bxdfs/conductor.rs | 14 ++++----- shared/src/bxdfs/dielectric.rs | 11 +++---- shared/src/bxdfs/diffuse.rs | 2 +- shared/src/bxdfs/layered.rs | 54 ++++++++++++++++++------------- shared/src/core/bsdf.rs | 4 +-- shared/src/core/geometry/ray.rs | 56 +++++++++++++++++++++++++++++++-- shared/src/core/scattering.rs | 23 +++++++++----- shared/src/lib.rs | 4 ++- shared/src/shapes/triangle.rs | 4 --- shared/src/spectra/sampled.rs | 8 ++--- shared/src/utils/alloc.rs | 2 +- shared/src/utils/mod.rs | 1 + shared/src/utils/ptr.rs | 2 +- shared/src/utils/soa.rs | 15 +++++++++ src/core/render.rs | 3 -- src/core/scene/scene.rs | 11 ++----- src/core/texture.rs | 25 --------------- src/gpu/mod.rs | 21 ------------- src/gpu/wavefront/mod.rs | 45 -------------------------- src/integrators/pipeline.rs | 1 - src/lib.rs | 4 +-- src/utils/parameters.rs | 2 ++ src/wavefront/aggregate.rs | 11 +++++++ src/wavefront/integrator.rs | 38 ++++++++++++++++++++++ src/wavefront/mod.rs | 4 +++ 25 files changed, 202 insertions(+), 163 deletions(-) create mode 100644 shared/src/utils/soa.rs delete mode 100644 src/gpu/mod.rs delete mode 100644 src/gpu/wavefront/mod.rs create mode 100644 src/wavefront/aggregate.rs create mode 100644 src/wavefront/integrator.rs create mode 100644 src/wavefront/mod.rs diff --git a/shared/src/bxdfs/conductor.rs b/shared/src/bxdfs/conductor.rs index 167d4e9..45b5fd6 100644 --- a/shared/src/bxdfs/conductor.rs +++ b/shared/src/bxdfs/conductor.rs @@ -1,9 +1,9 @@ use crate::core::bsdf::BSDFSample; use crate::core::bxdf::{BxDFFlags, BxDFReflTransFlags, BxDFTrait, FArgs, TransportMode}; use crate::core::geometry::{ - Normal3f, Point2f, Vector3f, VectorLike, abs_cos_theta, same_hemisphere, + abs_cos_theta, same_hemisphere, Normal3f, Point2f, Vector3f, VectorLike, }; -use crate::core::scattering::{TrowbridgeReitzDistribution, fr_complex_from_spectrum, reflect}; +use crate::core::scattering::{fr_complex_from_spectrum, reflect, TrowbridgeReitzDistribution}; use crate::spectra::SampledSpectrum; use crate::utils::sampling::{cosine_hemisphere_pdf, sample_cosine_hemisphere}; use crate::{Float, INV_PI}; @@ -84,7 +84,7 @@ impl BxDFTrait for ConductorBxDF { return None; } - let f_spectrum = fr_complex_from_spectrum(wo.dot(wi).abs(), self.eta, self.k); + let f_spectrum = fr_complex_from_spectrum(wo.dot(wm).abs(), self.eta, self.k); let f = self.mf_distrib.d(wm) * f_spectrum * self.mf_distrib.g(wo, wi) / (4. * cos_theta_i * cos_theta_o); @@ -118,8 +118,7 @@ impl BxDFTrait for ConductorBxDF { return SampledSpectrum::new(0.); } let wm_norm = wm.normalize(); - - let f_spectrum = fr_complex_from_spectrum(wo.dot(wm).abs(), self.eta, self.k); + let f_spectrum = fr_complex_from_spectrum(wo.dot(wm_norm).abs(), self.eta, self.k); self.mf_distrib.d(wm_norm) * f_spectrum * self.mf_distrib.g(wo, wi) / (4. * cos_theta_i * cos_theta_o) } @@ -140,8 +139,9 @@ impl BxDFTrait for ConductorBxDF { if wm.norm_squared() == 0. { return 0.; } - let wm_corr = Normal3f::new(0., 0., 1.).face_forward(wm); - self.mf_distrib.pdf(wo, wm_corr.into()) / (4. * wo.dot(wm).abs()) + let wm_norm = Normal3f::from(wm.normalize()); + let wm_corr = wm_norm.face_forward(Vector3f::new(0., 0., 1.)); + self.mf_distrib.pdf(wo, wm_corr.into()) / (4. * wo.dot(Vector3f::from(wm_norm)).abs()) } fn regularize(&mut self) { diff --git a/shared/src/bxdfs/dielectric.rs b/shared/src/bxdfs/dielectric.rs index 8a8c7b5..1a5098d 100644 --- a/shared/src/bxdfs/dielectric.rs +++ b/shared/src/bxdfs/dielectric.rs @@ -1,10 +1,10 @@ use crate::core::bsdf::BSDFSample; use crate::core::bxdf::{BxDFFlags, BxDFReflTransFlags, BxDFTrait, FArgs, TransportMode}; use crate::core::geometry::{ - Normal3f, Point2f, Vector3f, VectorLike, abs_cos_theta, cos_theta, same_hemisphere, + abs_cos_theta, cos_theta, same_hemisphere, Normal3f, Point2f, Vector3f, VectorLike, }; use crate::core::scattering::{ - TrowbridgeReitzDistribution, fr_complex_from_spectrum, fr_dielectric, reflect, refract, + fr_complex_from_spectrum, fr_dielectric, reflect, refract, TrowbridgeReitzDistribution, }; use crate::spectra::SampledSpectrum; use crate::utils::math::square; @@ -141,14 +141,11 @@ impl BxDFTrait for DielectricBxDF { } if reflect { - self.mf_distrib.pdf( - wo, - Vector3f::from(wm) / (4. * wo.dot(wm.into()).abs()) * pr / (pt + pr), - ) + self.mf_distrib.pdf(wo, wm.into()) / (4. * wo.dot(wm.into()).abs()) * pr / (pr + pt) } else { let denom = square(wi.dot(wm.into()) + wo.dot(wm.into()) / etap); let dwm_dwi = wi.dot(wm.into()).abs() / denom; - self.mf_distrib.pdf(wo, wm.into()) * dwm_dwi * pr / (pr + pt) + self.mf_distrib.pdf(wo, wm.into()) * dwm_dwi * pt / (pr + pt) } } diff --git a/shared/src/bxdfs/diffuse.rs b/shared/src/bxdfs/diffuse.rs index 01b4429..0643b4f 100644 --- a/shared/src/bxdfs/diffuse.rs +++ b/shared/src/bxdfs/diffuse.rs @@ -57,7 +57,7 @@ impl BxDFTrait for DiffuseBxDF { fn pdf(&self, wo: Vector3f, wi: Vector3f, f_args: FArgs) -> Float { let reflection_flags = - BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::ALL.bits()); + BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits()); if !f_args.sample_flags.contains(reflection_flags) || !same_hemisphere(wo, wi) { return 0.; } diff --git a/shared/src/bxdfs/layered.rs b/shared/src/bxdfs/layered.rs index 06b716d..ecbd9d4 100644 --- a/shared/src/bxdfs/layered.rs +++ b/shared/src/bxdfs/layered.rs @@ -5,17 +5,17 @@ use crate::core::bsdf::BSDFSample; use crate::core::bxdf::{BxDFFlags, BxDFReflTransFlags, BxDFTrait, FArgs, TransportMode}; use crate::core::color::RGB; use crate::core::geometry::{ - Frame, Normal3f, Point2f, Vector3f, VectorLike, abs_cos_theta, cos_theta, same_hemisphere, - spherical_direction, spherical_theta, + abs_cos_theta, cos_theta, same_hemisphere, spherical_direction, spherical_theta, Frame, + Normal3f, Point2f, Vector3f, VectorLike, }; use crate::core::medium::{HGPhaseFunction, PhaseFunctionTrait}; use crate::core::scattering::{ - TrowbridgeReitzDistribution, fr_complex, fr_complex_from_spectrum, fr_dielectric, reflect, - refract, + fr_complex, fr_complex_from_spectrum, fr_dielectric, reflect, refract, + TrowbridgeReitzDistribution, }; use crate::spectra::{ - DeviceStandardColorSpaces, N_SPECTRUM_SAMPLES, RGBColorSpace, RGBUnboundedSpectrum, - SampledSpectrum, SampledWavelengths, + DeviceStandardColorSpaces, RGBColorSpace, RGBUnboundedSpectrum, SampledSpectrum, + SampledWavelengths, N_SPECTRUM_SAMPLES, }; use crate::utils::hash::hash_buffer; use crate::utils::math::{ @@ -24,8 +24,8 @@ use crate::utils::math::{ }; use crate::utils::rng::Rng; use crate::utils::sampling::{ - PiecewiseLinear2D, cosine_hemisphere_pdf, power_heuristic, sample_cosine_hemisphere, - sample_exponential, sample_trimmed_logistic, sample_uniform_hemisphere, uniform_hemisphere_pdf, + cosine_hemisphere_pdf, power_heuristic, sample_cosine_hemisphere, sample_exponential, + sample_trimmed_logistic, sample_uniform_hemisphere, uniform_hemisphere_pdf, PiecewiseLinear2D, }; use crate::{Float, INV_2_PI, INV_4_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2}; use core::any::Any; @@ -173,6 +173,12 @@ where mode, sample_flags: BxDFReflTransFlags::TRANSMISSION, }; + + let reverse_trans_args = FArgs { + mode: !mode, + sample_flags: BxDFReflTransFlags::TRANSMISSION, + }; + let refl_args = FArgs { mode, sample_flags: BxDFReflTransFlags::REFLECTION, @@ -188,7 +194,7 @@ where }; let Some(wis) = exit_interface - .sample_f(wi, r(), Point2f::new(r(), r()), trans_args) + .sample_f(wi, r(), Point2f::new(r(), r()), reverse_trans_args) .filter(|s| !s.f.is_black() && s.pdf > 0.0 && s.wi.z() != 0.0) else { return SampledSpectrum::new(0.0); @@ -226,18 +232,21 @@ where let sigma_t = 1.0; let dz = sample_exponential(r(), sigma_t / w.z().abs()); let zp = if w.z() > 0.0 { z + dz } else { z - dz }; + if zp == z { + continue; + } if zp > 0.0 && zp < self.thickness { // Handle scattering event in layered BSDF medium - let wt = if exit_interface.flags().is_specular() { - power_heuristic(1, wis.pdf, 1, phase.pdf(-w, wis.wi)) + let wt = if !exit_interface.flags().is_specular() { + power_heuristic(1, wis.pdf, 1, phase.pdf(-w, -wis.wi)) } else { 1.0 }; f += beta * self.albedo - * phase.p(-wi, -wis.wi) + * phase.p(-w, -wis.wi) * wt * self.tr(zp - exit_z, wis.wi) * wis.f @@ -257,7 +266,7 @@ where // Account for scattering through exit if (z < exit_z && w.z() > 0.0) || (z > exit_z && w.z() < 0.0) { - let f_exit = exit_interface.f(-w, -wi, mode); + let f_exit = exit_interface.f(-w, wi, mode); if !f_exit.is_black() { let exit_pdf = exit_interface.pdf(-w, wi, trans_args); let wt = power_heuristic(1, ps.pdf, 1, exit_pdf); @@ -283,7 +292,7 @@ where } else { // Hitting the non-exit surface -> Reflection if !non_exit_interface.flags().is_specular() { - let wt = if exit_interface.flags().is_specular() { + let wt = if !exit_interface.flags().is_specular() { power_heuristic( 1, wis.pdf, @@ -308,7 +317,7 @@ where .sample_f(-w, r(), Point2f::new(r(), r()), refl_args) .filter(|s| !s.f.is_black() && s.pdf > 0.0 && s.wi.z() != 0.0) else { - continue; + break; }; beta *= bs.f * abs_cos_theta(bs.wi) / bs.pdf; @@ -319,7 +328,7 @@ where let f_exit = exit_interface.f(-w, wi, mode); if !f_exit.is_black() { let mut wt = 1.0; - if non_exit_interface.flags().is_specular() { + if !non_exit_interface.flags().is_specular() { wt = power_heuristic( 1, bs.pdf, @@ -476,7 +485,7 @@ where } pdf *= 1. - q; } - if w.z() < 0. { + if w.z() == 0. { return None; } @@ -484,12 +493,15 @@ where let sigma_t = 1.; let dz = sample_exponential(r(), sigma_t / abs_cos_theta(w)); let zp = if w.z() > 0. { z + dz } else { z - dz }; + if zp == z { + return None; + } if zp > 0. && zp < self.thickness { let Some(ps) = phase - .sample_p(-wo, Point2f::new(r(), r())) - .filter(|s| s.pdf == 0. && s.wi.z() == 0.) + .sample_p(-w, Point2f::new(r(), r())) + .filter(|s| s.pdf != 0. && s.wi.z() != 0.) else { - continue; + return None; }; f *= self.albedo * ps.p; pdf *= ps.pdf; @@ -518,7 +530,7 @@ where // Sample interface BSDF to determine new path direction let bs = interface .sample_f(-w, r(), Point2f::new(r(), r()), f_args) - .filter(|s| s.f.is_black() && s.pdf == 0. && s.wi.z() == 0.)?; + .filter(|s| !s.f.is_black() && s.pdf != 0. && s.wi.z() != 0.)?; f *= bs.f; pdf *= bs.pdf; specular_path &= bs.is_specular(); diff --git a/shared/src/core/bsdf.rs b/shared/src/core/bsdf.rs index 9d95d13..7681eac 100644 --- a/shared/src/core/bsdf.rs +++ b/shared/src/core/bsdf.rs @@ -70,7 +70,7 @@ impl BSDF { let sampling_flags = BxDFFlags::from_bits_truncate(f_args.sample_flags.bits()); let wo = self.render_to_local(wo_render); - if wo.z() == 0.0 || !bxdf.flags().contains(sampling_flags) { + if wo.z() == 0.0 || !bxdf.flags().intersects(sampling_flags) { return None; } @@ -93,7 +93,7 @@ impl BSDF { let wo = self.render_to_local(wo_render); let wi = self.render_to_local(wi_render); - if wo.z() == 0.0 || !self.bxdf.flags().contains(sample_flags) { + if wo.z() == 0.0 || !self.bxdf.flags().intersects(sample_flags) { return 0.0; } diff --git a/shared/src/core/geometry/ray.rs b/shared/src/core/geometry/ray.rs index 1851ea5..d6f801a 100644 --- a/shared/src/core/geometry/ray.rs +++ b/shared/src/core/geometry/ray.rs @@ -1,8 +1,7 @@ use super::{Normal3f, Point3f, Point3fi, Vector3f, VectorLike}; use crate::core::medium::Medium; -use crate::core::pbrt::Float; use crate::utils::math::{next_float_down, next_float_up}; -use crate::utils::ptr::Ptr; +use crate::{gvec_with_capacity, Float, GVec, Ptr, SOA}; #[repr(C)] #[derive(Clone, Copy, Debug)] @@ -124,3 +123,56 @@ pub struct RayDifferential { pub rx_direction: Vector3f, pub ry_direction: Vector3f, } + +#[derive(Clone)] +pub struct RaySoA { + pub o: GVec, + pub d: GVec, + pub time: GVec, + pub medium: GVec>, + pub has_differentials: GVec, + pub differential: GVec, +} + +impl SoA for RaySoA { + type Item = Ray; + + fn with_capacity(n: usize) -> Self { + Self { + o: gvec_with_capacity(n), + d: gvec_with_capacity(n), + time: gvec_with_capacity(n), + medium: gvec_with_capacity(n), + has_differentials: gvec_with_capacity(n), + differential: gvec_with_capacity(n), + } + } + + fn len(&self) -> usize { + self.o.len() + } + + unsafe fn get_unchecked(&self, i: usize) -> Ray { + Ray { + o: *self.o.get_unchecked(i), + d: *self.d.get_unchecked(i), + time: *self.time.get_unchecked(i), + medium: *self.medium.get_unchecked(i), + has_differentials: *self.has_differentials.get_unchecked(i), + differential: *self.differential.get_unchecked(i), + } + } + + unsafe fn set_unchecked(&mut self, i: usize, v: Ray) { + *self.o.get_unchecked_mut(i) = v.o; + *self.d.get_unchecked_mut(i) = v.d; + *self.time.get_unchecked_mut(i) = v.time; + *self.medium.get_unchecked_mut(i) = v.medium; + *self.has_differentials.get_unchecked_mut(i) = v.has_differentials; + *self.differential.get_unchecked_mut(i) = v.differential; + } +} + +impl SoAElement for Ray { + type SoA = RaySoA; +} diff --git a/shared/src/core/scattering.rs b/shared/src/core/scattering.rs index 37f1ae4..5cd6c20 100644 --- a/shared/src/core/scattering.rs +++ b/shared/src/core/scattering.rs @@ -1,9 +1,9 @@ use crate::core::geometry::{ - Normal3f, Point2f, Vector2f, Vector3f, VectorLike, abs_cos_theta, cos_phi, cos2_theta, sin_phi, - tan2_theta, + abs_cos_theta, cos2_theta, cos_phi, sin_phi, tan2_theta, Normal3f, Point2f, Vector2f, Vector3f, + VectorLike, }; use crate::core::pbrt::{Float, PI}; -use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum}; +use crate::spectra::{SampledSpectrum, N_SPECTRUM_SAMPLES}; use crate::utils::math::{clamp, lerp, safe_sqrt, square}; use crate::utils::sampling::sample_uniform_disk_polar; use num_traits::Float as NumFloat; @@ -18,7 +18,11 @@ pub struct TrowbridgeReitzDistribution { } impl TrowbridgeReitzDistribution { - pub fn new(alpha_x: Float, alpha_y: Float) -> Self { + pub fn new(mut alpha_x: Float, mut alpha_y: Float) -> Self { + if alpha_x.max(alpha_y) >= 1e-3 { + alpha_x = alpha_x.max(1e-4); + alpha_y = alpha_y.max(1e-4); + } Self { alpha_x, alpha_y } } @@ -28,8 +32,13 @@ impl TrowbridgeReitzDistribution { return 0.; } let cos4_theta = square(cos2_theta(wm)); + + if cos4_theta < 1e-16 { + return 0.; + } let e = tan2_theta * (square(cos_phi(wm) / self.alpha_x) + square(sin_phi(wm) / self.alpha_y)); + 1.0 / (PI * self.alpha_x * self.alpha_y * cos4_theta * square(1. + e)) } @@ -51,7 +60,7 @@ impl TrowbridgeReitzDistribution { } pub fn g1(&self, w: Vector3f) -> Float { - 1. / (1. / self.lambda(w)) + 1. / (1. + self.lambda(w)) } pub fn d_from_w(&self, w: Vector3f, wm: Vector3f) -> Float { @@ -76,7 +85,7 @@ impl TrowbridgeReitzDistribution { let mut p = sample_uniform_disk_polar(u); let h = (1. - square(p.x())).sqrt(); p[1] = lerp((1. + wh.z()) / 2., h, p.y()); - let pz = 0_f32.max(1. - Vector2f::from(p).norm_squared()); + let pz = (1. - Vector2f::from(p).norm_squared()).max(0.).sqrt(); let nh = p.x() * t1 + p.y() * t2 + pz * wh; Vector3f::new( self.alpha_x * nh.x(), @@ -158,7 +167,7 @@ pub fn fr_complex(cos_theta_i: Float, eta: Complex) -> Float { let r_parl = (eta * cos_corr - cos2_theta_t) / (eta * cos_corr + cos2_theta_t); let r_perp = (cos_corr - eta * cos2_theta_t) / (cos_corr + eta * cos2_theta_t); - (r_parl.norm() + r_perp.norm()) / 2. + (square(r_parl.norm()) + square(r_perp.norm())) / 2. } pub fn fr_complex_from_spectrum( diff --git a/shared/src/lib.rs b/shared/src/lib.rs index b6160f5..79144ba 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -16,7 +16,9 @@ pub mod shapes; pub mod spectra; pub mod textures; pub mod utils; +pub mod wavefront; pub use core::pbrt::*; pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, leak, GBox, GVec}; -pub use utils::{Array2D, PBRTOptions, Ptr, Transform}; +pub use utils::{Array2D, PBRTOptions, Ptr, Transform, SOA}; +pub use wavefront::{WavefrontAggregate, WorkQueue}; diff --git a/shared/src/shapes/triangle.rs b/shared/src/shapes/triangle.rs index 6e795de..95f6fdc 100644 --- a/shared/src/shapes/triangle.rs +++ b/shared/src/shapes/triangle.rs @@ -320,7 +320,6 @@ impl TriangleShape { determinant: Float, degenerate_uv: bool, ) { - // Interpolate vertex normals if they exist let ns = if let Some(normals) = self.get_shading_normals() { let n = ti.b0 * normals[0] + ti.b1 * normals[1] + ti.b2 * normals[2]; if n.norm_squared() > 0.0 { @@ -332,7 +331,6 @@ impl TriangleShape { isect.n() }; - // Interpolate tangents if they exist let mut ss = if let Some(tangents) = self.get_tangents() { let s = ti.b0 * tangents[0] + ti.b1 * tangents[1] + ti.b2 * tangents[2]; if s.norm_squared() > 0.0 { @@ -344,7 +342,6 @@ impl TriangleShape { dpdu_geom }; - // Ensure shading tangent (ss) is perpendicular to shading normal (ns) let mut ts = ns.cross(ss.into()); if ts.norm_squared() > 0.0 { ss = ts.cross(ns.into()).into(); @@ -354,7 +351,6 @@ impl TriangleShape { ts = t.into(); } - // How does the normal change as we move across UVs? let (dndu, dndv) = if let Some(normals) = self.get_shading_normals() { if degenerate_uv { let dn = (normals[2] - normals[0]).cross(normals[1] - normals[0]); diff --git a/shared/src/spectra/sampled.rs b/shared/src/spectra/sampled.rs index c65fa60..b83f50d 100644 --- a/shared/src/spectra/sampled.rs +++ b/shared/src/spectra/sampled.rs @@ -1,7 +1,7 @@ use crate::core::pbrt::Float; use crate::core::spectrum::{SpectrumTrait, StandardSpectra}; use crate::utils::gpu_array_from_fn; -use crate::utils::math::{clamp, lerp}; +use crate::utils::math::{clamp, lerp, square}; use core::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; @@ -342,7 +342,7 @@ impl SampledWavelengths { pub fn sample_uniform(u: Float, lambda_min: Float, lambda_max: Float) -> Self { let mut lambda = [0.0; N_SPECTRUM_SAMPLES]; - lambda[0] = lerp(u, lambda_min, lambda_min); + lambda[0] = lerp(u, lambda_min, lambda_max); let delta = (lambda_max - lambda_min) / N_SPECTRUM_SAMPLES as Float; for i in 1..N_SPECTRUM_SAMPLES { lambda[i] = lambda[i - 1] + delta; @@ -357,14 +357,14 @@ impl SampledWavelengths { } pub fn sample_visible_wavelengths(u: Float) -> Float { - 538.0 - 138.888889 * Float::atanh(0.85691062 - 1.82750197 * u) + (538.0_f64 - 138.888889_f64 * (0.85691062_f64 - 1.82750197_f64 * u as f64).atanh()) as Float } pub fn visible_wavelengths_pdf(lambda: Float) -> Float { if !(360.0..830.0).contains(&lambda) { return 0.0; } - 0.0039398042 / (Float::cosh(0.0072 * (lambda - 538.0))).sqrt() + (0.0039398042_f64 / (0.0072_f64 * (lambda as f64 - 538.0)).cosh().powi(2)) as Float } pub fn sample_visible(u: Float) -> Self { diff --git a/shared/src/utils/alloc.rs b/shared/src/utils/alloc.rs index 724d994..fe1a511 100644 --- a/shared/src/utils/alloc.rs +++ b/shared/src/utils/alloc.rs @@ -7,7 +7,7 @@ use alloc::vec::Vec; use core::alloc::{AllocError, Allocator, Layout}; use core::ptr::NonNull; -// CPU fallback, delegates to Global +// CPU fallback to GlobalAllocator #[derive(Debug, Clone, Copy, Default)] pub struct SystemAlloc; diff --git a/shared/src/utils/mod.rs b/shared/src/utils/mod.rs index beef286..07d278d 100644 --- a/shared/src/utils/mod.rs +++ b/shared/src/utils/mod.rs @@ -18,6 +18,7 @@ pub use options::PBRTOptions; pub use ptr::Ptr; pub use transform::{AnimatedTransform, Transform, TransformGeneric}; pub use containers::Array2D; +pub use soa::SOA: use crate::Float; use core::sync::atomic::{AtomicU32, Ordering}; diff --git a/shared/src/utils/ptr.rs b/shared/src/utils/ptr.rs index 8de2017..6f4e9a4 100644 --- a/shared/src/utils/ptr.rs +++ b/shared/src/utils/ptr.rs @@ -24,7 +24,7 @@ impl PartialEq for Ptr { impl PartialOrd for Ptr { fn partial_cmp(&self, other: &Self) -> Option { - Some(self.ptr.cmp(&other.ptr)) + Some(self.ptr.cast::<()>().cmp(&other.ptr.cast::<()>())) } } diff --git a/shared/src/utils/soa.rs b/shared/src/utils/soa.rs new file mode 100644 index 0000000..10f0406 --- /dev/null +++ b/shared/src/utils/soa.rs @@ -0,0 +1,15 @@ +pub trait SoA: Clone { + type Item: Copy; + fn with_capacity(n: usize) -> Self; + fn len(&self) -> usize; + fn is_empty(&self) -> bool { + self.len() == 0 + } + + unsafe fn get_unchecked(&self, i: usize) -> Self::Item; + unsafe fn set_unchecked(&mut self, i: usize, v: Self::Item); +} + +pub trait SoAElement: Copy { + type SoA: SoA; +} diff --git a/src/core/render.rs b/src/core/render.rs index d63b5c6..32ee6f0 100644 --- a/src/core/render.rs +++ b/src/core/render.rs @@ -17,9 +17,6 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> { let media = scene.create_media(); let textures = scene.create_textures(arena); let (named_materials, materials) = scene.create_materials(&textures, arena)?; - for (i, m) in materials.iter().enumerate() { - eprintln!("materials[{}]: {:?}", i, std::mem::discriminant(m)); - } let lights = scene.create_lights(&textures, arena); let have_scattering = { diff --git a/src/core/scene/scene.rs b/src/core/scene/scene.rs index 9270be6..1f8d72f 100644 --- a/src/core/scene/scene.rs +++ b/src/core/scene/scene.rs @@ -701,7 +701,7 @@ impl BasicScene { sampler, aggregate, lights, - PathConfig::SIMPLE, + PathConfig::FULL, arena, ) .expect("Integrator creation failed"), @@ -779,6 +779,8 @@ impl BasicScene { continue; } + eprintln!("shape '{}' n={}", entity.base.name, created_shapes.len()); + let mtl = resolve_material( &entity.material, named_materials, @@ -967,10 +969,6 @@ impl BasicScene { primitives } - // ======================================================================= - // Private — texture helper - // ======================================================================= - fn add_texture_generic( &self, name: String, @@ -1105,9 +1103,6 @@ impl BasicScene { Err(anyhow!("{} requested but not initialized!", name)) } - // ======================================================================= - // GPU path stubs — to be implemented with wavefront integrator - // ======================================================================= #[allow(dead_code)] fn upload_shapes( diff --git a/src/core/texture.rs b/src/core/texture.rs index 7356680..ada66ec 100644 --- a/src/core/texture.rs +++ b/src/core/texture.rs @@ -120,31 +120,6 @@ pub enum SpectrumTexture { DirectionMix(SpectrumDirectionMixTexture), } -impl SpectrumTexture { - fn upload_spectrum_image( - inner: &SpectrumImageTexture, - arena: &Arena, - ) -> GPUSpectrumImageTexture { - let tex_obj = arena.get_texture_object(&inner.base.mipmap); - GPUSpectrumImageTexture { - mapping: inner.base.mapping, - tex_obj, - scale: inner.base.scale, - invert: inner.base.invert, - is_single_channel: inner.base.mipmap.is_single_channel(), - color_space: arena.alloc( - inner - .base - .mipmap - .color_space - .clone() - .unwrap_or_else(crate::spectra::default_colorspace), - ), - spectrum_type: inner.spectrum_type, - } - } -} - pub trait CreateSpectrumTexture { fn create( render_from_texture: Transform, diff --git a/src/gpu/mod.rs b/src/gpu/mod.rs deleted file mode 100644 index 88c4bf2..0000000 --- a/src/gpu/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub mod context; - -pub use context::{ - GPU_STATE, GpuContext, GpuState, gpu_init, gpu_state, gpu_state_or_panic, gpu_thread_init, -}; - -pub mod wavefront; - -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum GpuError { - #[error("CUDA driver error: {0}")] - Driver(#[from] cudarc::driver::DriverError), - #[error("No GPU context initialized")] - NoContext, -} - -pub fn gpu_unwrap() -> &'static GpuContext { - context::GPU_STATE.get().expect("GPU not initialized") -} diff --git a/src/gpu/wavefront/mod.rs b/src/gpu/wavefront/mod.rs deleted file mode 100644 index 5c8ac72..0000000 --- a/src/gpu/wavefront/mod.rs +++ /dev/null @@ -1,45 +0,0 @@ -// use crate::core::scene::BasicScene; -// use crate::{ -// EscapedRayQueue, GetBSSRDFAndProbeRayQueue, HitAreaLightQueue, MaterialEvalQueue, -// MediumSampleQueue, MediumScatterQueue, PixelSampleStateStorage, RayQueue, ShadowRayQueue, -// SubsurfaceScatterQueue, -// }; -// use shared::core::camera::Camera; -// use shared::core::film::Film; -// use shared::core::filter::Filter; -// use shared::core::light::Light; -// use shared::core::sampler::Sampler; -// use shared::lights::sampler::LightSampler; -// use std::sync::Arc; -// -// pub struct WavefrontPathIntegrator { -// pub film: Film, -// pub filter: Filter, -// pub sampler: Sampler, -// pub camera: Arc, -// pub light_sampler: LightSampler, -// pub infinite_lights: Option>>, -// pub max_depth: i32, -// pub samples_per_pixel: i32, -// pub regularize: bool, -// pub scanlines_per_pixel: i32, -// pub max_queue_size: i32, -// pub pixel_sample_state: PixelSampleStateStorage, -// pub ray_queue: [RayQueue; 2], -// pub hit_area_light_queue: HitAreaLightQueue, -// pub shadow_ray_queue: ShadowRayQueue, -// pub escaped_ray_queue: Option, -// pub basic_material_queue: Option, -// pub universal_material_queue: Option, -// pub medium_sample_queue: Option, -// pub medium_scatter_queue: Option, -// pub bssrf_queue: Option, -// pub subsurface_queue: Option, -// } - -#[cfg(feature = "use_gpu")] -impl WavefrontPathIntegrator { - pub fn new(scene: BasicScene) -> Self { - todo!() - } -} diff --git a/src/integrators/pipeline.rs b/src/integrators/pipeline.rs index b7f9b13..6949ec8 100644 --- a/src/integrators/pipeline.rs +++ b/src/integrators/pipeline.rs @@ -82,7 +82,6 @@ pub fn render( ) where T: RayIntegratorTrait + Sync, { - println!("RENDER CALLED"); let options = get_options(); if let Some((p_pixel, sample_index)) = options.debug_start { let s_index = sample_index as usize; diff --git a/src/lib.rs b/src/lib.rs index a2e091b..e2cad71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ pub mod shapes; pub mod spectra; pub mod textures; pub mod utils; +pub mod wavefront; -#[cfg(feature = "cuda")] -pub mod gpu; pub use utils::{Arena, FileLoc, ParameterDictionary, Upload, ArenaUpload}; +pub const MAX_TAGS = 16; diff --git a/src/utils/parameters.rs b/src/utils/parameters.rs index 3a3ffda..40e9ec1 100644 --- a/src/utils/parameters.rs +++ b/src/utils/parameters.rs @@ -695,6 +695,7 @@ impl ParameterDictionary { } fn extract_file_spectrum(&self, param: &ParsedParameter) -> Vec { + eprintln!("extract_file_spectrum: param='{}' files={:?}", param.name, param.strings); param .strings .iter() @@ -894,6 +895,7 @@ impl TextureParameterDictionary { p.looked_up.store(true, Ordering::Relaxed); let tex_name = &p.strings[0]; + eprintln!("looking up texture '{}'", tex_name); if let Some(nt) = &self.textures { let map = match stype { diff --git a/src/wavefront/aggregate.rs b/src/wavefront/aggregate.rs new file mode 100644 index 0000000..0605ece --- /dev/null +++ b/src/wavefront/aggregate.rs @@ -0,0 +1,11 @@ +use shared::core::geometry::Bounds3f; +use super::{RayQueue, EscapedRayQueue, HitAreaLightQueue, MaterialEvalQueue, MediumSampleQueue, SubsurfaceScatterQueue}; + +#[derive(Clone, Debug)] +pub trait WavefrontAggregate { + fn bounds(&self) -> Bounds3f; + fn intersect_closest(max_rays: usize, ray_q: &mut RayQueue, hit_area_light_q: &mut HitAreaLightQueue, basic_mlt_q: &mut MaterialEvalQueue, universal_mtl_q: &mut MaterialEvalQueue, medium_sample_q: &mut MediumSampleQueue); + fn intersect_shadow(max_rays: usize, shadow_ray_q: &mut ShadowRayQueue, pixel_sample_state: &mut SOA); + fn intersect_shadow_tr(max_rays: usize, shadow_ray_q: &mut ShadowRayQueue, pixel_sample_state: &mut SOA); + fn intersect_one_random(max_rays: usize, subsurface_scatte_q: &mut SubsurfaceScatterQueue); +} diff --git a/src/wavefront/integrator.rs b/src/wavefront/integrator.rs new file mode 100644 index 0000000..8cba5ee --- /dev/null +++ b/src/wavefront/integrator.rs @@ -0,0 +1,38 @@ +use crate::MAX_TAGS; +use shared::{Ptr, GVec}; +use shared::core::film::Film; +use shared::core::color::RGB; +use shared::core::filter::Filter; +use shared::core::light::Light; +use shared::core::sampler::Filter; +use shared::wavefront::{WavefrontAggregate, RayQueue, MediumSampleQueue, EscapedRayQueue, HitAreaLightQueue, MaterialEvalQueue, ShadowRayQueue, GetBSSRDFAndProbeRayQueue, SubsurfaceScatterQueue}; + +pub struct WavefrontPathIntegrator { + pub init_visible_surface: bool, + pub have_subsurface: bool, + pub have_media: bool, + pub have_basic_eval_material: [bool; MAX_TAGS + 1], + pub have_universal_eval_material: [bool; MAX_TAGS + 1], + pub filter: Filter, + pub film: Film, + pub sampler: Sampler, + pub camera: Camera, + pub infinite_lights: GVec, + pub max_depth: usize, + pub sampler_per_pixel: usize, + pub regularize: bool, + pub scanlines_per_pixel: usize, + pub max_queue_size: usize, + pub medium_sample_queue: Ptr, + pub medium_scatter_queue: Ptr, + pub escaped_ray_queue: Ptr, + pub hit_area_light_queue: Ptr, + pub basic_eval_material_queue: Ptr, + pub universal_eval_material_queue: Ptr, + pub shadow_ray_queue: Ptr, + pub bssrdf_eval_queue: PTr, + pub subsurface_scatter_queue: Ptr, + pub display_rgb: Ptr, + pub display_rgb_host: Ptr