From 050698c1d040806cd93ce650271d6f320c250839 Mon Sep 17 00:00:00 2001 From: Wito Wiala Date: Tue, 19 May 2026 19:58:21 +0100 Subject: [PATCH] Finished work on the shared side. Now moving to host code. May god have mercy on my soul --- Cargo.toml | 2 +- crates/ptex-filter/Cargo.toml | 13 ++ crates/ptex-filter/build.rs | 17 ++ .../ptex-filter/cpp/ptex_filter_wrapper.cpp | 65 ++++++++ crates/ptex-filter/cpp/ptex_filter_wrapper.h | 51 ++++++ crates/ptex-filter/src/ffi.rs | 60 +++++++ crates/ptex-filter/src/lib.rs | 66 ++++++++ shared/Cargo.toml | 5 + shared/src/bxdfs/measured.rs | 12 +- shared/src/cameras/realistic.rs | 28 ++-- shared/src/core/aggregates.rs | 10 +- shared/src/core/camera.rs | 2 +- shared/src/core/color.rs | 53 +++--- shared/src/core/film.rs | 19 +-- shared/src/core/filter.rs | 31 +++- shared/src/core/image.rs | 40 +++-- shared/src/core/medium.rs | 38 ++--- shared/src/core/primitive.rs | 19 ++- shared/src/core/sampler.rs | 4 +- shared/src/core/spectrum.rs | 25 ++- shared/src/filters/gaussian.rs | 4 +- shared/src/filters/lanczos.rs | 34 +++- shared/src/filters/mitchell.rs | 50 +++--- shared/src/lib.rs | 5 +- shared/src/lights/diffuse.rs | 6 +- shared/src/lights/distant.rs | 16 -- shared/src/lights/goniometric.rs | 2 +- shared/src/lights/infinite.rs | 18 ++- shared/src/lights/point.rs | 19 --- shared/src/lights/projection.rs | 6 +- shared/src/lights/sampler.rs | 2 +- shared/src/lights/spot.rs | 25 --- shared/src/shapes/bilinear.rs | 36 ++--- shared/src/shapes/mesh.rs | 9 +- shared/src/shapes/triangle.rs | 27 +++- shared/src/spectra/colorspace.rs | 2 +- shared/src/spectra/rgb.rs | 2 +- shared/src/spectra/simple.rs | 14 +- shared/src/textures/image.rs | 2 +- shared/src/utils/alloc.rs | 3 +- shared/src/utils/containers.rs | 41 ++++- shared/src/utils/math.rs | 27 ++-- shared/src/utils/mod.rs | 2 + shared/src/utils/ptr.rs | 10 +- shared/src/utils/sampling.rs | 152 ++++++++++-------- src/lights/distant.rs | 24 ++- src/lights/point.rs | 29 ++++ src/lights/spot.rs | 40 ++++- src/spectra/mod.rs | 8 +- 49 files changed, 818 insertions(+), 357 deletions(-) create mode 100644 crates/ptex-filter/Cargo.toml create mode 100644 crates/ptex-filter/build.rs create mode 100644 crates/ptex-filter/cpp/ptex_filter_wrapper.cpp create mode 100644 crates/ptex-filter/cpp/ptex_filter_wrapper.h create mode 100644 crates/ptex-filter/src/ffi.rs create mode 100644 crates/ptex-filter/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index e47ec79..49be0e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ use_f64 = [] use_gpu = ["dep:wgpu"] use_nvtx = ["dep:nvtx"] cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw", "dep:cuda-runtime-sys"] -vulkan = ["ash", "gpu-allocator"] +vulkan = ["dep:ash", "dep:gpu-allocator", "shared/vulkan"] ash = ["dep:ash"] gpu-allocator = ["dep:gpu-allocator"] jemalloc = ["jemallocator"] diff --git a/crates/ptex-filter/Cargo.toml b/crates/ptex-filter/Cargo.toml new file mode 100644 index 0000000..f0a52e7 --- /dev/null +++ b/crates/ptex-filter/Cargo.toml @@ -0,0 +1,13 @@ +#![allow(unused)] + +[package] +name = "ptex-filter" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +cc = "1.0" +pkg-config = "0.3" + +[dependencies] +ptex = "0.3.0" diff --git a/crates/ptex-filter/build.rs b/crates/ptex-filter/build.rs new file mode 100644 index 0000000..0b6774c --- /dev/null +++ b/crates/ptex-filter/build.rs @@ -0,0 +1,17 @@ +fn main() { + println!("cargo:rerun-if-changed=cpp/ptex_filter_wrapper.cpp"); + + let home = std::env::var("HOME").unwrap(); + let ptex_include = format!("{}/.local/include", home); + let ptex_lib = format!("{}/.local/lib", home); + + cc::Build::new() + .cpp(true) + .file("cpp/ptex_filter_wrapper.cpp") + .include(&ptex_include) + .flag("-std=c++17") + .compile("ptex_filter_wrapper"); + + println!("cargo:rustc-link-search=native={}", ptex_lib); + println!("cargo:rustc-link-lib=Ptex"); +} diff --git a/crates/ptex-filter/cpp/ptex_filter_wrapper.cpp b/crates/ptex-filter/cpp/ptex_filter_wrapper.cpp new file mode 100644 index 0000000..b2a119c --- /dev/null +++ b/crates/ptex-filter/cpp/ptex_filter_wrapper.cpp @@ -0,0 +1,65 @@ +#include "ptex_filter_wrapper.h" +#include + +extern "C" { + +PtexFilterHandle ptex_filter_create(PtexTextureHandle texture, const PtexFilterOptions* opts) { + Ptex::PtexTexture* tex = static_cast(texture); + if (!tex || !opts) return nullptr; + + Ptex::PtexFilter::Options ptex_opts; + ptex_opts.filter = static_cast(opts->filter); + ptex_opts.lerp = opts->lerp; + ptex_opts.sharpness = opts->sharpness; + ptex_opts.noedgeblend = opts->noedgeblend != 0; + + return Ptex::PtexFilter::getFilter(tex, ptex_opts); +} + +void ptex_filter_eval( + PtexFilterHandle filter, + float* result, + int32_t first_channel, + int32_t num_channels, + int32_t face_id, + float u, float v, + float dudx, float dvdx, + float dudy, float dvdy +) { + Ptex::PtexFilter* f = static_cast(filter); + if (f && result) { + f->eval(result, first_channel, num_channels, face_id, u, v, dudx, dvdx, dudy, dvdy); + } +} + +void ptex_filter_release(PtexFilterHandle filter) { + Ptex::PtexFilter* f = static_cast(filter); + if (f) { + f->release(); + } +} + +PtexTextureHandle ptex_texture_open(const char* filename, char** error_str) { + Ptex::String error; + Ptex::PtexTexture* tex = Ptex::PtexTexture::open(filename, error); + + if (!tex && error_str) { + *error_str = strdup(error.c_str()); + } + + return tex; +} + +void ptex_texture_release(PtexTextureHandle texture) { + Ptex::PtexTexture* tex = static_cast(texture); + if (tex) { + tex->release(); + } +} + +int32_t ptex_texture_num_channels(PtexTextureHandle texture) { + Ptex::PtexTexture* tex = static_cast(texture); + return tex ? tex->numChannels() : 0; +} + +} diff --git a/crates/ptex-filter/cpp/ptex_filter_wrapper.h b/crates/ptex-filter/cpp/ptex_filter_wrapper.h new file mode 100644 index 0000000..b999325 --- /dev/null +++ b/crates/ptex-filter/cpp/ptex_filter_wrapper.h @@ -0,0 +1,51 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef void* PtexTextureHandle; +typedef void* PtexFilterHandle; + +typedef enum { + PTEX_FILTER_POINT = 0, + PTEX_FILTER_BILINEAR = 1, + PTEX_FILTER_BOX = 2, + PTEX_FILTER_GAUSSIAN = 3, + PTEX_FILTER_BICUBIC = 4, + PTEX_FILTER_BSPLINE = 5, + PTEX_FILTER_CATMULLROM = 6, + PTEX_FILTER_MITCHELL = 7 +} PtexFilterType; + +typedef struct { + PtexFilterType filter; + int32_t lerp; + float sharpness; + int32_t noedgeblend; +} PtexFilterOptions; + +PtexTextureHandle ptex_texture_open(const char* filename, char** error_str); +void ptex_filter_release(PtexFilterHandle filter); +int32_t ptex_texture_num_channels(PtexTextureHandle texture); +PtexFilterHandle ptex_filter_create(PtexTextureHandle texture, const PtexFilterOptions* opts); + + +void ptex_filter_eval( + PtexFilterHandle filter, + float* result, + int32_t first_channel, + int32_t num_channels, + int32_t face_id, + float u, float v, + float dudx, float dvdx, + float dudy, float dvdy +); +void ptex_filter_release(PtexFilterHandle filter); + + +#ifdef __cplusplus +} +#endif diff --git a/crates/ptex-filter/src/ffi.rs b/crates/ptex-filter/src/ffi.rs new file mode 100644 index 0000000..be7b391 --- /dev/null +++ b/crates/ptex-filter/src/ffi.rs @@ -0,0 +1,60 @@ +use std::ffi::c_void; + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PtexFilterType { + Point = 0, + Bilinear = 1, + Box = 2, + Gaussian = 3, + Bicubic = 4, + BSpline = 5, + CatmullRom = 6, + Mitchell = 7, +} + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct PtexFilterOptions { + pub filter: PtexFilterType, + pub lerp: i32, + pub sharpness: f32, + pub noedgeblend: i32, +} + +impl Default for PtexFilterOptions { + fn default() -> Self { + Self { + filter: PtexFilterType::BSpline, + lerp: 1, + sharpness: 0.0, + noedgeblend: 0, + } + } +} + +extern "C" { + pub fn ptex_filter_create(texture: *mut c_void, opts: *const PtexFilterOptions) -> *mut c_void; + + pub fn ptex_filter_eval( + filter: *mut c_void, + result: *mut f32, + first_channel: i32, + num_channels: i32, + face_id: i32, + u: f32, + v: f32, + dudx: f32, + dvdx: f32, + dudy: f32, + dvdy: f32, + ); + + pub fn ptex_filter_release(filter: *mut c_void); + pub fn ptex_texture_open( + filename: *const std::ffi::c_char, + error_str: *mut *mut std::ffi::c_char, + ) -> *mut c_void; + pub fn ptex_texture_release(texture: *mut c_void); + pub fn ptex_texture_num_channels(texture: *mut c_void) -> i32; +} diff --git a/crates/ptex-filter/src/lib.rs b/crates/ptex-filter/src/lib.rs new file mode 100644 index 0000000..38dbc27 --- /dev/null +++ b/crates/ptex-filter/src/lib.rs @@ -0,0 +1,66 @@ +#![allow(unused)] +#![allow(dead_code)] + +mod ffi; + +pub use ffi::{ptex_filter_create, PtexFilterOptions, PtexFilterType}; + +use std::ffi::c_void; +use std::marker::PhantomData; +use std::ptr::NonNull; + +pub struct PtexFilter { + handle: NonNull, + _marker: PhantomData<*mut ()>, +} + +impl PtexFilter { + /// Creates a new Ptex filter pointer + /// + /// # Safety + pub unsafe fn new(texture_ptr: *mut c_void, opts: &PtexFilterOptions) -> Option { + let handle = ffi::ptex_filter_create(texture_ptr, opts); + NonNull::new(handle).map(|h| Self { + handle: h, + _marker: PhantomData, + }) + } + + pub fn eval( + &self, + face_id: i32, + u: f32, + v: f32, + dudx: f32, + dvdx: f32, + dudy: f32, + dvdy: f32, + num_channels: i32, + ) -> [f32; 4] { + let mut result = [0.0f32; 4]; + unsafe { + ffi::ptex_filter_eval( + self.handle.as_ptr(), + result.as_mut_ptr(), + 0, + num_channels.min(4), + face_id, + u, + v, + dudx, + dvdx, + dudy, + dvdy, + ); + } + result + } +} + +impl Drop for PtexFilter { + fn drop(&mut self) { + unsafe { + ffi::ptex_filter_release(self.handle.as_ptr()); + } + } +} diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 8db17c2..1bd5c49 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -5,8 +5,12 @@ edition = "2024" [dependencies] bitflags = "2.10.0" +half = "2.7.1" bytemuck = { version = "1.24.0", features = ["derive"] } enum_dispatch = "0.3.13" +ash = { version = "0.38", optional = true } +parking_lot = { version = "0.12.5", optional = true } +gpu-allocator = { version = "0.28", features = ["vulkan"], optional = true } num-traits = { version = "0.2.19", default-features = false, features = ["libm"] } cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true } @@ -14,3 +18,4 @@ cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", def use_f64 = [] cuda = ["cuda_std"] cpu_debug = [] +vulkan = ["dep:ash", "dep:gpu-allocator", "dep:parking_lot"] diff --git a/shared/src/bxdfs/measured.rs b/shared/src/bxdfs/measured.rs index ce1ae1f..27d9078 100644 --- a/shared/src/bxdfs/measured.rs +++ b/shared/src/bxdfs/measured.rs @@ -16,13 +16,13 @@ use num_traits::Float as NumFloat; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct MeasuredBxDFData { - pub wavelengths: Ptr, - pub spectra: PiecewiseLinear2D<3>, - pub ndf: PiecewiseLinear2D<0>, - pub vndf: PiecewiseLinear2D<2>, - pub sigma: PiecewiseLinear2D<0>, pub isotropic: bool, - pub luminance: PiecewiseLinear2D<2>, + pub wavelengths: Ptr, + pub spectra: Ptr>, + pub ndf: Ptr>, + pub vndf: Ptr>, + pub sigma: Ptr>, + pub luminance: Ptr>, } #[repr(C)] diff --git a/shared/src/cameras/realistic.rs b/shared/src/cameras/realistic.rs index 523a302..6cdf29a 100644 --- a/shared/src/cameras/realistic.rs +++ b/shared/src/cameras/realistic.rs @@ -10,7 +10,7 @@ use crate::core::sampler::CameraSample; use crate::core::scattering::refract; use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::utils::math::{lerp, quadratic, square}; -use crate::{Float, GVec, Ptr, PI}; +use crate::{Float, GVec, Ptr, PI, gvec}; use num_traits::Float as NumFloat; #[repr(C)] @@ -32,7 +32,7 @@ pub struct ExitPupilSample { pub const EXIT_PUPIL_SAMPLES: usize = 64; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct RealisticCamera { pub base: CameraBase, pub focus_distance: Float, @@ -75,7 +75,8 @@ impl RealisticCamera { if curvature_radius == 0.0 { aperture_diameter /= 1000.0; if set_aperture_diameter > aperture_diameter { - println!("Aperture is larger than possible") + // println!("Aperture is larger than possible") + aperture_diameter = -1.; } else { aperture_diameter = set_aperture_diameter; } @@ -113,6 +114,10 @@ impl RealisticCamera { } } + unsafe fn lens(&self, idx: usize) -> &LensElementInterface { + unsafe { &*self.element_interfaces.as_ptr().add(idx) } + } + pub fn compute_cardinal_points(r_in: Ray, r_out: Ray) -> (Float, Float) { let tf = -r_out.o.x() / r_out.d.x(); let tp = (r_in.o.x() - r_out.o.x()) / r_out.d.x(); @@ -162,14 +167,12 @@ impl RealisticCamera { ); } let delta = (pz[1] - z + pz[0] - c.sqrt()) / 2.; - let last_interface = unsafe { self.element_interfaces.add(self.n_elements) }; + let last_interface = unsafe { self.lens(self.n_elements - 1) }; last_interface.thickness + delta } pub fn bound_exit_pupil(&self, film_x_0: Float, film_x_1: Float) -> Bounds2f { - let interface_array = unsafe { - core::slice::from_raw_parts(self.element_interfaces.as_raw(), self.n_elements as usize) - }; + let interface_array = self.element_interfaces.as_slice(); Self::compute_exit_pupil_bounds(interface_array, film_x_0, film_x_1) } @@ -191,7 +194,6 @@ impl RealisticCamera { let trace_lenses_from_film = |_ray: Ray, _place: Option| true; for i in 0..n_samples { // Find location of sample points on $x$ segment and rear lens element - // let p_film = Point3f::new( lerp((i as Float + 0.5) / n_samples as Float, film_x_0, film_x_1), 0., @@ -272,7 +274,7 @@ impl RealisticCamera { ); for i in (0..self.n_elements - 1).rev() { - let element: &LensElementInterface = unsafe { &self.element_interfaces.add(i) }; + let element: &LensElementInterface = unsafe { self.lens(i) }; // Update ray from film accounting for interaction with _element_ element_z -= element.thickness; @@ -311,7 +313,7 @@ impl RealisticCamera { // Update ray path for element interface interaction if !is_stop { let eta_i = element.eta; - let interface_i = unsafe { self.element_interfaces.add(i - 1) }; + let interface_i = unsafe { self.lens(i) }; let eta_t = if i > 0 && interface_i.eta != 0. { interface_i.eta } else { @@ -372,21 +374,21 @@ impl RealisticCamera { } pub fn lens_rear_z(&self) -> Float { - let last_interface = unsafe { self.element_interfaces.add(self.n_elements - 1) }; + let last_interface = unsafe { self.lens(self.n_elements - 1) }; last_interface.thickness } pub fn lens_front_z(&self) -> Float { let mut z_sum = 0.; for i in 0..self.n_elements { - let element = unsafe { self.element_interfaces.add(i) }; + let element = unsafe { self.lens(i) }; z_sum += element.thickness; } z_sum } pub fn rear_element_radius(&self) -> Float { - let last_interface = unsafe { self.element_interfaces.add(self.n_elements - 1) }; + let last_interface = unsafe { self.lens(self.n_elements - 1) }; last_interface.aperture_radius } } diff --git a/shared/src/core/aggregates.rs b/shared/src/core/aggregates.rs index 885b00d..8acf0ba 100644 --- a/shared/src/core/aggregates.rs +++ b/shared/src/core/aggregates.rs @@ -1,9 +1,7 @@ use crate::core::geometry::{Bounds3f, Ray, Vector3f}; -use crate::core::pbrt::Float; use crate::core::primitive::{Primitive, PrimitiveTrait}; use crate::core::shape::ShapeIntersection; use crate::{Float, Ptr, GVec, gvec}; -use crate::utils::Ptr; #[repr(C)] #[derive(Debug, Clone, Copy)] @@ -18,11 +16,11 @@ pub struct LinearBVHNode { #[repr(C)] #[derive(Debug, Clone)] pub struct BVHAggregate { - pub max_prims_in_node: u32, - pub primitives: GVec, - pub primitive_count: u32, - pub nodes: GVec, pub node_count: u32, + pub max_prims_in_node: u32, + pub primitive_count: u32, + pub primitives: GVec, + pub nodes: GVec, } impl BVHAggregate { diff --git a/shared/src/core/camera.rs b/shared/src/core/camera.rs index a7e052a..b192f7d 100644 --- a/shared/src/core/camera.rs +++ b/shared/src/core/camera.rs @@ -118,7 +118,7 @@ pub struct CameraBase { } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] #[enum_dispatch(CameraTrait)] pub enum Camera { Perspective(PerspectiveCamera), diff --git a/shared/src/core/color.rs b/shared/src/core/color.rs index 381b081..5064fbf 100644 --- a/shared/src/core/color.rs +++ b/shared/src/core/color.rs @@ -1,18 +1,15 @@ +use crate::core::geometry::Point2f; +use crate::core::spectrum::Spectrum; +use crate::utils::find_interval; +use crate::utils::math::{clamp, evaluate_polynomial, lerp, SquareMatrix, SquareMatrix3f}; +use crate::{Float, GVec, Ptr}; use core::any::TypeId; use core::fmt; use core::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; - -use crate::Float; -use crate::core::geometry::Point2f; -use crate::core::spectrum::Spectrum; -use crate::utils::Ptr; -use crate::utils::find_interval; -use crate::utils::math::{SquareMatrix, SquareMatrix3f, clamp, evaluate_polynomial, lerp}; -use num_traits::Float as NumFloat; - use enum_dispatch::enum_dispatch; +use num_traits::Float as NumFloat; #[repr(C)] #[derive(Debug, Default, Clone, Copy)] @@ -311,17 +308,33 @@ impl RGB { pub fn min_component_index(&self) -> u32 { if self.r < self.g { - if self.r < self.b { 0 } else { 2 } + if self.r < self.b { + 0 + } else { + 2 + } } else { - if self.g < self.b { 1 } else { 2 } + if self.g < self.b { + 1 + } else { + 2 + } } } pub fn max_component_index(&self) -> u32 { if self.r > self.g { - if self.r > self.b { 0 } else { 2 } + if self.r > self.b { + 0 + } else { + 2 + } } else { - if self.g > self.b { 1 } else { 2 } + if self.g > self.b { + 1 + } else { + 2 + } } } @@ -1086,10 +1099,10 @@ impl Mul for Coeffs { } #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct RGBToSpectrumTable { - pub z_nodes: GVec, - pub coeffs: GVec, + pub z_nodes: Ptr, + pub coeffs: Ptr, pub n_nodes: u32, } @@ -1114,7 +1127,11 @@ impl RGBToSpectrumTable { // Identify the primary bucket (c) based on the dominant axis let c_idx = if rgb[0] > rgb[1] { - if rgb[0] > rgb[2] { 0 } else { 2 } + if rgb[0] > rgb[2] { + 0 + } else { + 2 + } } else if rgb[1] > rgb[2] { 1 } else { @@ -1136,7 +1153,7 @@ impl RGBToSpectrumTable { let x = coord_a / z; let y = coord_b / z; - let z_nodes = &self.z_nodes; + let z_nodes = unsafe { core::slice::from_raw_parts(self.z_nodes.as_raw(), RES as usize) }; let zi = find_interval(RES, |i| z_nodes[i as usize] < z) as usize; let dz = (z - z_nodes[zi]) / (z_nodes[zi + 1] - z_nodes[zi]); let x_float = x * (RES - 1) as Float; diff --git a/shared/src/core/film.rs b/shared/src/core/film.rs index f5ca9aa..4dfbee0 100644 --- a/shared/src/core/film.rs +++ b/shared/src/core/film.rs @@ -17,7 +17,7 @@ use crate::utils::math::{wrap_equal_area_square, SquareMatrix}; use crate::utils::sampling::VarianceEstimator; use crate::utils::transform::AnimatedTransform; use crate::utils::{gpu_array_from_fn, AtomicFloat}; -use crate::{gvec_with_capacity, Array2D, Float, Ptr}; +use crate::{gvec_from_slice, gvec_with_capacity, Array2D, Float, GVec, Ptr}; use num_traits::Float as NumFloat; #[repr(C)] @@ -84,7 +84,7 @@ impl RGBFilm { filter_integral, output_rgbf_from_sensor_rgb, pixels, - }; + } } pub fn base(&self) -> &FilmBase { @@ -465,7 +465,7 @@ impl SpectralFilm { } SpectralFilm { - base, + base: *base, colorspace: colorspace.clone(), lambda_min, lambda_max, @@ -474,12 +474,7 @@ impl SpectralFilm { write_fp16, filter_integral: base.filter.integral(), output_rgbf_from_sensor_rgb: SquareMatrix::identity(), - - pixels: Array2D { - values: pixels.values.as_mut_ptr(), - extent: base.pixel_bounds, - }, - + pixels: Array2D::from_slice(base.pixel_bounds, pixels.as_slice()), bucket_sums, weight_sums, bucket_splats, @@ -522,9 +517,9 @@ impl SpectralFilm { #[derive(Debug, Copy, Clone)] pub struct PixelSensor { pub xyz_from_sensor_rgb: SquareMatrix, - pub r_bar: DenselySampledSpectrum, - pub g_bar: DenselySampledSpectrum, - pub b_bar: DenselySampledSpectrum, + pub r_bar: Ptr, + pub g_bar: Ptr, + pub b_bar: Ptr, pub imaging_ratio: Float, } diff --git a/shared/src/core/filter.rs b/shared/src/core/filter.rs index a794463..65cd6e2 100644 --- a/shared/src/core/filter.rs +++ b/shared/src/core/filter.rs @@ -2,7 +2,7 @@ use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i, Vector2f}; use crate::filters::*; use crate::utils::math::{gaussian, gaussian_integral, lerp, sample_tent, windowed_sinc}; use crate::utils::sampling::PiecewiseConstant2D; -use crate::{Array2D, Float}; +use crate::{Array2D, Float, Ptr}; use enum_dispatch::enum_dispatch; pub struct FilterSample { @@ -11,7 +11,7 @@ pub struct FilterSample { } #[repr(C)] -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Debug)] pub struct FilterSampler { pub domain: Bounds2f, pub distrib: PiecewiseConstant2D, @@ -66,9 +66,26 @@ pub trait FilterTrait { #[enum_dispatch(FilterTrait)] #[derive(Clone, Copy, Debug)] pub enum Filter { - Box(BoxFilter), - Gaussian(GaussianFilter), - Mitchell(MitchellFilter), - LanczosSinc(LanczosSincFilter), - Triangle(TriangleFilter), + Box(Ptr), + Gaussian(Ptr), + Mitchell(Ptr), + LanczosSinc(Ptr), + Triangle(Ptr), +} + +impl FilterTrait for Ptr { + fn radius(&self) -> Vector2f { + unsafe { self.as_ref().radius() } + } + fn integral(&self) -> Float { + unsafe { self.as_ref().integral() } + } + + fn evaluate(&self, p: Point2f) -> Float { + unsafe { self.as_ref().evaluate(p) } + } + + fn sample(&self, p: Point2f) -> FilterSample { + unsafe { self.as_ref().sample(p) } + } } diff --git a/shared/src/core/image.rs b/shared/src/core/image.rs index ea13e03..af48ddd 100644 --- a/shared/src/core/image.rs +++ b/shared/src/core/image.rs @@ -1,8 +1,7 @@ use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR}; use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i}; use crate::utils::math::{f16_to_f32_software, lerp, square}; -use crate::Float; -use crate::GVec; +use crate::{gvec_with_capacity, Float, GVec}; use core::hash; use core::ops::{Deref, DerefMut}; use num_traits::Float as NumFloat; @@ -69,7 +68,7 @@ impl PixelFormat { #[repr(C)] #[derive(Clone, Debug)] pub struct Pixels { - pixels: GVec, + data: GVec, format: PixelFormat, } @@ -95,17 +94,17 @@ impl Pixels { } pub unsafe fn read_u8(&self, texel_offset: usize) -> u8 { - *self.data.as_ptr().add(texel_offset) + unsafe { *self.data.as_ptr().add(texel_offset) } } pub unsafe fn read_f16(&self, texel_offset: usize) -> u16 { let byte_offset = texel_offset * 2; - *(self.data.as_ptr().add(byte_offset) as *const u16) + unsafe { *(self.data.as_ptr().add(byte_offset) as *const u16) } } pub unsafe fn read_f32(&self, texel_offset: usize) -> f32 { let byte_offset = texel_offset * 4; - *(self.data.as_ptr().add(byte_offset) as *const f32) + unsafe { *(self.data.as_ptr().add(byte_offset) as *const f32) } } pub unsafe fn read(&self, texel_offset: usize, encoding: &ColorEncoding) -> Float { @@ -117,17 +116,17 @@ impl Pixels { } pub unsafe fn write_u8(&mut self, texel_offset: usize, val: u8) { - *self.data.as_mut_ptr().add(texel_offset) = val; + unsafe { *self.data.as_mut_ptr().add(texel_offset) = val }; } pub unsafe fn write_f16(&mut self, texel_offset: usize, val: u16) { let byte_offset = texel_offset * 2; - *(self.data.as_mut_ptr().add(byte_offset) as *mut u16) = val; + unsafe { *(self.data.as_mut_ptr().add(byte_offset) as *mut u16) = val }; } pub unsafe fn write_f32(&mut self, texel_offset: usize, val: f32) { let byte_offset = texel_offset * 4; - *(self.data.as_mut_ptr().add(byte_offset) as *mut f32) = val; + unsafe { *(self.data.as_mut_ptr().add(byte_offset) as *mut f32) = val }; } pub fn empty(texel_count: usize, format: PixelFormat) -> Self { @@ -194,6 +193,14 @@ impl Pixels { } } +#[derive(Clone, Debug)] +pub struct ImageBase { + pub format: PixelFormat, + pub encoding: ColorEncoding, + pub resolution: Point2i, + pub n_channels: i32, +} + #[derive(Clone, Debug)] pub struct Image { pub format: PixelFormat, @@ -299,6 +306,15 @@ impl Image { true } + pub fn base(&self) -> ImageBase { + ImageBase { + format: self.format, + encoding: self.encoding, + resolution: self.resolution, + n_channels: self.n_channels, + } + } + pub fn get_channel(&self, p: Point2i, c: i32) -> Float { self.get_channel_with_wrap(p, c, WrapMode::Clamp.into()) } @@ -329,11 +345,11 @@ impl Image { self.get_channel_with_wrap(pi, c, wrap_mode) } - pub fn get_channels_array(&self, p: Point2i) -> [Float; N] { - self.get_channels_array_with_wrap(p, WrapMode::Clamp.into()) + pub fn get_channels(&self, p: Point2i) -> [Float; N] { + self.get_channels_with_wrap(p, WrapMode::Clamp.into()) } - pub fn get_channels_array_with_wrap( + pub fn get_channels_with_wrap( &self, mut p: Point2i, wrap_mode: WrapMode2D, diff --git a/shared/src/core/medium.rs b/shared/src/core/medium.rs index 233dce1..bc9d18a 100644 --- a/shared/src/core/medium.rs +++ b/shared/src/core/medium.rs @@ -142,7 +142,7 @@ impl MajorantGrid { let idx = x + self.res.x() * (y + self.res.y() * z); unsafe { - *self.voxels.as_ptr().add(idx as usize) = v; + *self.voxels.as_mut_ptr().add(idx as usize) = v; } } @@ -229,7 +229,7 @@ pub struct DDAMajorantIterator { sigma_t: SampledSpectrum, t_min: Float, t_max: Float, - grid: MajorantGrid, + grid: Ptr, next_crossing_t: [Float; 3], delta_t: [Float; 3], step: [i32; 3], @@ -249,7 +249,7 @@ impl DDAMajorantIterator { t_min, t_max, sigma_t: *sigma_t, - grid: *grid, + grid: Ptr::from(&*grid), next_crossing_t: [0.0; 3], delta_t: [0.0; 3], step: [0; 3], @@ -452,9 +452,9 @@ pub enum Medium { #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct HomogeneousMedium { - pub sigma_a_spec: DenselySampledSpectrum, - pub sigma_s_spec: DenselySampledSpectrum, - pub le_spec: DenselySampledSpectrum, + pub sigma_a_spec: Ptr, + pub sigma_s_spec: Ptr, + pub le_spec: Ptr, pub phase: HGPhaseFunction, } @@ -496,15 +496,15 @@ impl MediumTrait for HomogeneousMedium { pub struct GridMedium { pub bounds: Bounds3f, pub render_from_medium: Transform, - pub sigma_a_spec: DenselySampledSpectrum, - pub sigma_s_spec: DenselySampledSpectrum, - pub density_grid: SampledGrid, + pub sigma_a_spec: Ptr, + pub sigma_s_spec: Ptr, + pub density_grid: Ptr>, pub phase: HGPhaseFunction, - pub temperature_grid: Option>, - pub le_spec: DenselySampledSpectrum, - pub le_scale: SampledGrid, + pub temperature_grid: Ptr>, + pub le_spec: Ptr, + pub le_scale: Ptr>, pub is_emissive: bool, - pub majorant_grid: MajorantGrid, + pub majorant_grid: Ptr, } impl MediumTrait for GridMedium { @@ -527,8 +527,8 @@ impl MediumTrait for GridMedium { }; let le = if scale > 0.0 { - let raw_emission = if let Some(temp_grid) = &self.temperature_grid { - let temp = temp_grid.lookup(p); + let raw_emission = if !self.temperature_grid.is_null() { + let temp = self.temperature_grid.lookup(p); BlackbodySpectrum::new(temp).sample(lambda) } else { self.le_spec.sample(lambda) @@ -592,10 +592,10 @@ pub struct RGBGridMedium { pub phase: HGPhaseFunction, pub le_scale: Float, pub sigma_scale: Float, - pub sigma_a_grid: SampledGrid, - pub sigma_s_grid: SampledGrid, - pub le_grid: SampledGrid, - pub majorant_grid: MajorantGrid, + pub sigma_a_grid: Ptr>, + pub sigma_s_grid: Ptr>, + pub le_grid: Ptr>, + pub majorant_grid: Ptr, } impl MediumTrait for RGBGridMedium { diff --git a/shared/src/core/primitive.rs b/shared/src/core/primitive.rs index 98df32f..e758957 100644 --- a/shared/src/core/primitive.rs +++ b/shared/src/core/primitive.rs @@ -213,6 +213,23 @@ pub enum Primitive { Geometric(GeometricPrimitive), Transformed(TransformedPrimitive), Animated(AnimatedPrimitive), - BVH(BVHAggregate), + BVH(Ptr), KdTree(KdTreeAggregate), } + + +impl PrimitiveTrait for Ptr { + fn bounds(&self) -> Bounds3f { + unsafe { self.as_ref().bounds() } + } + + fn intersect(&self, r: &Ray, t_max: Option) -> Option { + unsafe { self.as_ref().intersect(r, t_max) } + } + + fn intersect_p(&self, r: &Ray, t_max: Option) -> bool { + unsafe { self.as_ref().intersect_p(r, t_max) } + } +} + + diff --git a/shared/src/core/sampler.rs b/shared/src/core/sampler.rs index cfb7320..2ed1840 100644 --- a/shared/src/core/sampler.rs +++ b/shared/src/core/sampler.rs @@ -100,7 +100,7 @@ pub struct HaltonSampler { pub mult_inverse: [u64; 2], pub halton_index: u64, pub dim: u32, - pub digit_permutations: GVec, + pub digit_permutations: Ptr, } #[allow(clippy::derivable_impls)] @@ -114,7 +114,7 @@ impl Default for HaltonSampler { mult_inverse: [0; 2], halton_index: 0, dim: 0, - digit_permutations: gvec(), + digit_permutations: Ptr::default(), } } } diff --git a/shared/src/core/spectrum.rs b/shared/src/core/spectrum.rs index 9648be4..232d352 100644 --- a/shared/src/core/spectrum.rs +++ b/shared/src/core/spectrum.rs @@ -1,11 +1,11 @@ -use crate::Float; +use crate::{Float, Ptr}; use crate::core::color::{RGB, XYZ}; use enum_dispatch::enum_dispatch; pub use crate::spectra::*; #[enum_dispatch] -pub trait SpectrumTrait: Copy { +pub trait SpectrumTrait { fn evaluate(&self, lambda: Float) -> Float; fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum { SampledSpectrum::from_fn(|i| self.evaluate(lambda[i])) @@ -16,10 +16,10 @@ pub trait SpectrumTrait: Copy { #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct StandardSpectra { - pub x: DenselySampledSpectrum, - pub y: DenselySampledSpectrum, - pub z: DenselySampledSpectrum, - pub d65: DenselySampledSpectrum, + pub x: Ptr, + pub y: Ptr, + pub z: Ptr, + pub d65: Ptr, } unsafe impl Send for StandardSpectra {} @@ -30,14 +30,23 @@ unsafe impl Sync for StandardSpectra {} #[derive(Debug, Clone, Copy)] pub enum Spectrum { Constant(ConstantSpectrum), - Dense(DenselySampledSpectrum), - Piecewise(PiecewiseLinearSpectrum), + Dense(Ptr), + Piecewise(Ptr), Blackbody(BlackbodySpectrum), RGBAlbedo(RGBAlbedoSpectrum), RGBIlluminant(RGBIlluminantSpectrum), RGBUnbounded(RGBUnboundedSpectrum), } +impl SpectrumTrait for Ptr { + fn evaluate(&self, lambda: Float) -> Float { + unsafe { self.as_ref().evaluate(lambda) } + } + fn max_value(&self) -> Float { + unsafe { self.as_ref().max_value() } + } +} + impl Spectrum { pub fn std_illuminant_d65() -> Self { unimplemented!("Use crate::spectra::default_illuminant() on host") diff --git a/shared/src/filters/gaussian.rs b/shared/src/filters/gaussian.rs index e10a494..0152ce6 100644 --- a/shared/src/filters/gaussian.rs +++ b/shared/src/filters/gaussian.rs @@ -1,10 +1,10 @@ use crate::core::filter::{FilterSample, FilterSampler, FilterTrait}; use crate::core::geometry::{Point2f, Vector2f}; use crate::utils::math::{gaussian, gaussian_integral}; -use crate::Float; +use crate::{Ptr, Float}; #[repr(C)] -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Debug)] pub struct GaussianFilter { pub radius: Vector2f, pub sigma: Float, diff --git a/shared/src/filters/lanczos.rs b/shared/src/filters/lanczos.rs index 130551d..532a7ca 100644 --- a/shared/src/filters/lanczos.rs +++ b/shared/src/filters/lanczos.rs @@ -1,10 +1,11 @@ use crate::core::filter::{FilterSampler, FilterSample, FilterTrait}; use crate::core::geometry::{Point2f, Vector2f}; use crate::utils::math::{lerp, windowed_sinc}; +use crate::utils::rng::Rng; use crate::Float; #[repr(C)] -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Debug)] pub struct LanczosSincFilter { pub radius: Vector2f, pub tau: Float, @@ -14,14 +15,33 @@ pub struct LanczosSincFilter { impl LanczosSincFilter { pub fn new(radius: Vector2f, tau: Float) -> Self { - let sampler = FilterSampler::new(radius, move |p: Point2f| { + let evaluate = move |p: Point2f| -> Float { windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau) - }); - Self { - radius, - tau, - sampler, + }; + + let sampler = FilterSampler::new(radius, evaluate); + + let sqrt_samples = 64u32; + let n_samples = sqrt_samples * sqrt_samples; + let area = (2.0 * radius.x()) * (2.0 * radius.y()); + let mut sum = 0.0; + let mut rng = Rng::new(0); + for y in 0..sqrt_samples { + for x in 0..sqrt_samples { + let u = Point2f::new( + (x as Float + rng.uniform::()) / sqrt_samples as Float, + (y as Float + rng.uniform::()) / sqrt_samples as Float, + ); + let p = Point2f::new( + lerp(u.x(), -radius.x(), radius.x()), + lerp(u.y(), -radius.y(), radius.y()), + ); + sum += evaluate(p); + } } + let integral = sum / n_samples as Float * area; + + Self { radius, tau, sampler, integral } } } diff --git a/shared/src/filters/mitchell.rs b/shared/src/filters/mitchell.rs index 4f19b4a..e703881 100644 --- a/shared/src/filters/mitchell.rs +++ b/shared/src/filters/mitchell.rs @@ -1,10 +1,10 @@ -use crate::core::filter::{FilterSampler, FilterSample, FilterTrait}; +use crate::core::filter::{FilterSample, FilterSampler, FilterTrait}; use crate::core::geometry::{Point2f, Vector2f}; use crate::Float; use num_traits::Float as NumFloat; #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub struct MitchellFilter { pub radius: Vector2f, pub b: Float, @@ -12,32 +12,28 @@ pub struct MitchellFilter { pub sampler: FilterSampler, } +pub fn mitchell_1d_eval(b: Float, c: Float, x: Float) -> Float { + let x = x.abs(); + if x <= 1.0 { + ((12.0 - 9.0 * b - 6.0 * c) * x.powi(3) + + (-18.0 + 12.0 * b + 6.0 * c) * x.powi(2) + + (6.0 - 2.0 * b)) + * (1.0 / 6.0) + } else if x <= 2.0 { + ((-b - 6.0 * c) * x.powi(3) + + (6.0 * b + 30.0 * c) * x.powi(2) + + (-12.0 * b - 48.0 * c) * x + + (8.0 * b + 24.0 * c)) + * (1.0 / 6.0) + } else { + 0.0 + } +} + impl MitchellFilter { - pub fn mitchell_1d_eval(b: Float, c: Float, x: Float) -> Float { - let x = x.abs(); - if x <= 1.0 { - ((12.0 - 9.0 * b - 6.0 * c) * x.powi(3) - + (-18.0 + 12.0 * b + 6.0 * c) * x.powi(2) - + (6.0 - 2.0 * b)) - * (1.0 / 6.0) - } else if x <= 2.0 { - ((-b - 6.0 * c) * x.powi(3) - + (6.0 * b + 30.0 * c) * x.powi(2) - + (-12.0 * b - 48.0 * c) * x - + (8.0 * b + 24.0 * c)) - * (1.0 / 6.0) - } else { - 0.0 - } - } - - fn mitchell_1d(&self, x: Float) -> Float { - Self::mitchell_1d_eval(self.b, self.c, x) - } - pub fn new(radius: Vector2f, b: Float, c: Float) -> Self { let sampler = FilterSampler::new(radius, move |p: Point2f| { - mitchell_1d(p.x() / radius.x(), b, c) * mitchell_1d(p.y() / radius.y(), b, c) + mitchell_1d_eval(p.x() / radius.x(), b, c) * mitchell_1d_eval(p.y() / radius.y(), b, c) }); Self { radius, @@ -46,6 +42,10 @@ impl MitchellFilter { sampler, } } + + fn mitchell_1d(&self, x: Float) -> Float { + mitchell_1d_eval(self.b, self.c, x) + } } impl FilterTrait for MitchellFilter { diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 68d6b8e..2e9d39c 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -1,4 +1,5 @@ #![allow(unused_imports, dead_code)] +#![feature(allocator_api)] #![feature(associated_type_defaults)] #![no_std] extern crate alloc; @@ -17,5 +18,5 @@ pub mod textures; pub mod utils; pub use core::pbrt::*; -pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, GVec, Gbox}; -pub use utils::{Transform, Ptr, Array2D, PBRTOptions}; +pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, GVec, GBox}; +pub use utils::{Array2D, PBRTOptions, Ptr, Transform}; diff --git a/shared/src/lights/diffuse.rs b/shared/src/lights/diffuse.rs index 22f4341..9b47e93 100644 --- a/shared/src/lights/diffuse.rs +++ b/shared/src/lights/diffuse.rs @@ -1,6 +1,6 @@ use crate::core::color::{RGB, XYZ}; use crate::core::geometry::*; -use crate::core::image::{Image, ImageAccess}; +use crate::core::image::Image; use crate::core::interaction::{ Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction, }; @@ -171,8 +171,8 @@ impl LightTrait for DiffuseAreaLight { fn bounds(&self) -> Option { let mut phi = 0.; if !self.image.is_null() { - for y in 0..self.image.base.resolution.y() { - for x in 0..self.image.base.resolution.x() { + for y in 0..self.image.resolution().y() { + for x in 0..self.image.resolution().x() { for c in 0..3 { phi += self.image.get_channel(Point2i::new(x, y), c); } diff --git a/shared/src/lights/distant.rs b/shared/src/lights/distant.rs index 4cd3231..017cca0 100644 --- a/shared/src/lights/distant.rs +++ b/shared/src/lights/distant.rs @@ -20,22 +20,6 @@ pub struct DistantLight { } impl DistantLight { - pub fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self { - let base = LightBase::new( - LightType::DeltaDirection, - render_from_light, - MediumInterface::empty(), - ); - let lemit = lookup_spectrum(&le); - Self { - base, - lemit: Ptr::from(&lemit.device()), - scale, - scene_center: Point3f::default(), - scene_radius: 0., - } - } - pub fn sample_li_base( &self, ctx_p: Point3f, diff --git a/shared/src/lights/goniometric.rs b/shared/src/lights/goniometric.rs index 995a8f3..1abf00a 100644 --- a/shared/src/lights/goniometric.rs +++ b/shared/src/lights/goniometric.rs @@ -1,5 +1,5 @@ use crate::core::geometry::{Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f}; -use crate::core::image::{Image, ImageAccess}; +use crate::core::image::Image; use crate::core::light::{ LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType, }; diff --git a/shared/src/lights/infinite.rs b/shared/src/lights/infinite.rs index db93c96..e8bef27 100644 --- a/shared/src/lights/infinite.rs +++ b/shared/src/lights/infinite.rs @@ -3,7 +3,7 @@ use crate::core::geometry::{ Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector2f, Vector3f, }; use crate::core::geometry::{Frame, VectorLike}; -use crate::core::image::{Image, ImageAccess, PixelFormat, WrapMode}; +use crate::core::image::{Image, PixelFormat, WrapMode}; use crate::core::interaction::InteractionBase; use crate::core::interaction::{Interaction, SimpleInteraction}; use crate::core::light::{ @@ -15,7 +15,7 @@ use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths use crate::spectra::{RGBColorSpace, RGBIlluminantSpectrum}; use crate::utils::math::{clamp, equal_area_sphere_to_square, equal_area_square_to_sphere, square}; use crate::utils::sampling::{ - sample_uniform_sphere, uniform_sphere_pdf, AliasTable, DevicePiecewiseConstant2D, + sample_uniform_sphere, uniform_sphere_pdf, AliasTable, PiecewiseConstant2D, WindowedPiecewiseConstant2D, }; use crate::utils::{Ptr, Transform}; @@ -151,7 +151,7 @@ impl ImageInfiniteLight { pub fn new( render_from_light: Transform, scale: Float, - image: Ptr, + image: Ptr, image_color_space: Ptr, distrib: Ptr, compensated_distrib: Ptr, @@ -176,7 +176,11 @@ impl ImageInfiniteLight { fn image_le(&self, uv: Point2f, lambda: &SampledWavelengths) -> SampledSpectrum { let mut rgb = RGB::default(); for c in 0..3 { - rgb[c] = self.image.get_ch(uv, c, WrapMode::OctahedralSphere.into()); + rgb[c] = self.image.lookup_nearest_channel_with_wrap( + uv, + c, + WrapMode::OctahedralSphere.into(), + ); } let spec = RGBIlluminantSpectrum::new(&self.image_color_space, rgb.clamp_zero()); self.scale * spec.sample(lambda) @@ -297,7 +301,7 @@ pub struct PortalInfiniteLight { pub scale: Float, pub portal: [Point3f; 4], pub portal_frame: Frame, - pub distribution: WindowedPiecewiseConstant2D, + pub distribution: Ptr, pub scene_center: Point3f, pub scene_radius: Float, } @@ -306,7 +310,7 @@ impl PortalInfiniteLight { pub fn new( render_from_light: Transform, scale: Float, - image: Ptr, + image: Ptr, image_color_space: Ptr, portal: [Point3f; 4], portal_frame: Frame, @@ -324,7 +328,7 @@ impl PortalInfiniteLight { scale, portal, portal_frame, - distribution: *distribution, + distribution, scene_center: Point3f::default(), scene_radius: 0.0, } diff --git a/shared/src/lights/point.rs b/shared/src/lights/point.rs index 6059335..d590b8b 100644 --- a/shared/src/lights/point.rs +++ b/shared/src/lights/point.rs @@ -18,25 +18,6 @@ pub struct PointLight { pub i: Ptr, } -impl PointLight { - pub fn new( - render_from_light: Transform, - medium_interface: MediumInterface, - le: Spectrum, - scale: Float, - ) -> Self { - let base = LightBase::new( - LightType::DeltaPosition, - render_from_light, - medium_interface, - ); - let iemit = lookup_spectrum(&le); - let i = Ptr::from(&iemit.device()); - - Self { base, scale, i } - } -} - impl LightTrait for PointLight { fn base(&self) -> &LightBase { &self.base diff --git a/shared/src/lights/projection.rs b/shared/src/lights/projection.rs index 612d9dd..28bc875 100644 --- a/shared/src/lights/projection.rs +++ b/shared/src/lights/projection.rs @@ -3,7 +3,7 @@ use crate::core::color::RGB; use crate::core::geometry::{ Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f, VectorLike, cos_theta, }; -use crate::core::image::{Image, ImageAccess}; +use crate::core::image::Image; use crate::core::light::{ LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType, }; @@ -13,7 +13,7 @@ use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::utils::math::{radians, square}; use crate::{ spectra::{RGBColorSpace, RGBIlluminantSpectrum}, - utils::{Ptr, Transform, sampling::DevicePiecewiseConstant2D}, + utils::{Ptr, Transform, sampling::PiecewiseConstant2D}, }; use num_traits::Float as NumFloat; @@ -28,7 +28,7 @@ pub struct ProjectionLight { pub light_from_screen: Transform, pub a: Float, pub image: Ptr, - pub distrib: Ptr, + pub distrib: Ptr, pub image_color_space: Ptr, } diff --git a/shared/src/lights/sampler.rs b/shared/src/lights/sampler.rs index a8548d1..4c21c5c 100644 --- a/shared/src/lights/sampler.rs +++ b/shared/src/lights/sampler.rs @@ -251,7 +251,7 @@ pub struct Alias { pub struct PowerLightSampler { pub lights: Ptr, pub lights_len: u32, - pub alias_table: AliasTable, + pub alias_table: Ptr, } unsafe impl Send for PowerLightSampler {} diff --git a/shared/src/lights/spot.rs b/shared/src/lights/spot.rs index 5857240..8cb44a9 100644 --- a/shared/src/lights/spot.rs +++ b/shared/src/lights/spot.rs @@ -19,31 +19,6 @@ pub struct SpotLight { } impl SpotLight { - pub fn new( - render_from_light: Transform, - _medium_interface: MediumInterface, - le: Spectrum, - scale: shared::Float, - cos_falloff_start: Float, - total_width: Float, - ) -> Self { - let base = LightBase::new( - LightType::DeltaPosition, - render_from_light, - MediumInterface::empty(), - ); - - let i = lookup_spectrum(&le); - let iemit = Ptr::from(&i.device()); - Self { - base, - iemit, - scale, - cos_falloff_end: radians(total_width).cos(), - cos_falloff_start: radians(cos_falloff_start).cos(), - } - } - pub fn i(&self, w: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum { let cos_theta = w.z(); // assuming normalized in light space let falloff = crate::utils::math::smooth_step( diff --git a/shared/src/shapes/bilinear.rs b/shared/src/shapes/bilinear.rs index a9269de..8433e50 100644 --- a/shared/src/shapes/bilinear.rs +++ b/shared/src/shapes/bilinear.rs @@ -61,7 +61,7 @@ impl BilinearPatchShape { #[inline(always)] fn get_vertex_indices(&self) -> [usize; 4] { unsafe { - let base_ptr = self.mesh.vertex_indices.add((self.blp_index as usize) * 4); + let base_ptr = self.mesh.vertex_indices.as_ptr().add((self.blp_index as usize) * 4); [ *base_ptr.add(0) as usize, *base_ptr.add(1) as usize, @@ -73,47 +73,29 @@ impl BilinearPatchShape { #[inline(always)] fn get_points(&self) -> [Point3f; 4] { + let mesh = self.mesh(); let [v0, v1, v2, v3] = self.get_vertex_indices(); - unsafe { - [ - *self.mesh.p.add(v0), - *self.mesh.p.add(v1), - *self.mesh.p.add(v2), - *self.mesh.p.add(v3), - ] - } + [mesh.p[v0], mesh.p[v1], mesh.p[v2], mesh.p[v3]] } #[inline(always)] fn get_uvs(&self) -> Option<[Point2f; 4]> { - if self.mesh.uv.is_null() { + let mesh = self.mesh(); + if mesh.uv.is_empty() { return None; } let [v0, v1, v2, v3] = self.get_vertex_indices(); - unsafe { - Some([ - *self.mesh.uv.add(v0), - *self.mesh.uv.add(v1), - *self.mesh.uv.add(v2), - *self.mesh.uv.add(v3), - ]) - } + Some([mesh.uv[v0], mesh.uv[v1], mesh.uv[v2], mesh.uv[v3]]) } #[inline(always)] fn get_shading_normals(&self) -> Option<[Normal3f; 4]> { - if self.mesh.n.is_null() { + let mesh = self.mesh(); + if mesh.n.is_empty() { return None; } let [v0, v1, v2, v3] = self.get_vertex_indices(); - unsafe { - Some([ - *self.mesh.n.add(v0), - *self.mesh.n.add(v1), - *self.mesh.n.add(v2), - *self.mesh.n.add(v3), - ]) - } + Some([mesh.n[v0], mesh.n[v1], mesh.n[v2], mesh.n[v3]]) } #[cfg(not(target_os = "cuda"))] diff --git a/shared/src/shapes/mesh.rs b/shared/src/shapes/mesh.rs index 280b703..bad7c6b 100644 --- a/shared/src/shapes/mesh.rs +++ b/shared/src/shapes/mesh.rs @@ -1,10 +1,9 @@ use crate::core::geometry::{Normal3f, Point2f, Point3f, Vector3f}; use crate::utils::sampling::PiecewiseConstant2D; -use crate::utils::Transform; -use crate::{Float, Gvec}; +use crate::{gvec_from_slice, gvec_with_capacity, Float, GVec, Ptr, Transform}; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct TriangleMesh { pub p: GVec, pub n: GVec, @@ -19,7 +18,7 @@ pub struct TriangleMesh { } #[repr(C)] -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct BilinearPatchMesh { pub p: GVec, pub n: GVec, @@ -121,7 +120,7 @@ impl BilinearPatchMesh { p: &[Point3f], n: &[Normal3f], uv: &[Point2f], - image_distribution: Option, + image_distribution: Option<&PiecewiseConstant2D>, ) -> Self { let n_patches = (vertex_indices.len() / 4) as u32; let n_vertices = p.len() as u32; diff --git a/shared/src/shapes/triangle.rs b/shared/src/shapes/triangle.rs index aa1d33f..8ba2d97 100644 --- a/shared/src/shapes/triangle.rs +++ b/shared/src/shapes/triangle.rs @@ -41,8 +41,11 @@ impl TriangleShape { pub const MIN_SPHERICAL_SAMPLE_AREA: Float = 3e-4; pub const MAX_SPHERICAL_SAMPLE_AREA: Float = 6.22; + fn mesh(&self) -> &TriangleMesh { + &*self.mesh + } fn get_vertex_indices(&self) -> [usize; 3] { - let mesh = unsafe { &*self.mesh }; + let mesh = self.mesh(); let base = (self.tri_index as usize) * 3; [ mesh.vertex_indices[base] as usize, @@ -52,13 +55,13 @@ impl TriangleShape { } fn get_points(&self) -> [Point3f; 3] { - let mesh = unsafe { &*self.mesh }; + let mesh = self.mesh(); let [v0, v1, v2] = self.get_vertex_indices(); [mesh.p[v0], mesh.p[v1], mesh.p[v2]] } fn get_shading_normals(&self) -> Option<[Normal3f; 3]> { - let mesh = unsafe { &*self.mesh }; + let mesh = self.mesh(); if mesh.n.is_empty() { return None; } @@ -67,7 +70,7 @@ impl TriangleShape { } fn get_tangents(&self) -> Option<[Vector3f; 3]> { - let mesh = unsafe { &*self.mesh }; + let mesh = self.mesh(); if mesh.s.is_empty() { return None; } @@ -75,6 +78,16 @@ impl TriangleShape { Some([mesh.s[v0], mesh.s[v1], mesh.s[v2]]) } + + fn get_uvs(&self) -> Option<[Point2f; 3]> { + let mesh = self.mesh(); + if mesh.s.is_empty() { + return None; + } + let [v0, v1, v2] = self.get_vertex_indices(); + Some([mesh.uv[v0], mesh.uv[v1], mesh.uv[v2]]) + } + pub fn new(mesh: Ptr, tri_index: i32) -> Self { Self { mesh, tri_index } } @@ -172,8 +185,8 @@ impl TriangleShape { flip_normal, ); - isect.face_index = if !self.mesh.face_indices.is_null() { - unsafe { *self.mesh.face_indices.add(self.tri_index as usize) } + isect.face_index = if !self.mesh.face_indices.is_empty() { + unsafe { *self.mesh.face_indices.as_ptr().add(self.tri_index as usize) } } else { 0 }; @@ -181,7 +194,7 @@ impl TriangleShape { isect.common.n = ng; isect.shading.n = ng; - if !self.mesh.p.is_null() || !self.mesh.s.is_null() { + if !self.mesh.p.is_empty() || !self.mesh.s.is_empty() { self.compute_shading_geometry(&mut isect, &ti, uv, dpdu, determinant, degenerate); } isect diff --git a/shared/src/spectra/colorspace.rs b/shared/src/spectra/colorspace.rs index 78f78cf..f2f1e19 100644 --- a/shared/src/spectra/colorspace.rs +++ b/shared/src/spectra/colorspace.rs @@ -71,7 +71,7 @@ pub struct RGBColorSpace { pub xyz_from_rgb: SquareMatrix3f, pub rgb_from_xyz: SquareMatrix3f, pub illuminant: Ptr, - pub rgb_to_spectrum_table: Ptr, + pub rgb_to_spectrum_table: RGBToSpectrumTable, } unsafe impl Send for RGBColorSpace {} diff --git a/shared/src/spectra/rgb.rs b/shared/src/spectra/rgb.rs index 1044b10..1dca124 100644 --- a/shared/src/spectra/rgb.rs +++ b/shared/src/spectra/rgb.rs @@ -85,7 +85,7 @@ impl RGBIlluminantSpectrum { Self { scale, rsp, - illuminant: Ptr::from(&illuminant), + illuminant, } } } diff --git a/shared/src/spectra/simple.rs b/shared/src/spectra/simple.rs index b6c5385..c90951c 100644 --- a/shared/src/spectra/simple.rs +++ b/shared/src/spectra/simple.rs @@ -180,11 +180,13 @@ pub struct PiecewiseLinearSpectrum { impl PiecewiseLinearSpectrum { #[inline(always)] pub fn count(&self) -> usize { - if self.values.is_empty() { - 0 - } else { - (self.lambda_max - self.lambda_min + 1) as usize - } + self.count.try_into().unwrap() + } + + + #[inline(always)] + pub fn lambda(&self, idx: u32) -> Float { + unsafe { *self.lambdas.as_ptr().add(idx as usize) } } #[inline(always)] @@ -263,7 +265,7 @@ impl SpectrumTrait for PiecewiseLinearSpectrum { for i in 0..n { unsafe { - let val = *self.values.add(i as usize); + let val = *self.values.as_ptr().add(i as usize); if val > max_val { max_val = val; } diff --git a/shared/src/textures/image.rs b/shared/src/textures/image.rs index 336f895..1a4099b 100644 --- a/shared/src/textures/image.rs +++ b/shared/src/textures/image.rs @@ -18,7 +18,7 @@ pub struct GPUSpectrumImageTexture { pub scale: Float, pub invert: bool, pub is_single_channel: bool, - pub color_space: RGBColorSpace, + pub color_space: Ptr, pub spectrum_type: SpectrumType, } diff --git a/shared/src/utils/alloc.rs b/shared/src/utils/alloc.rs index 265b3dd..1b55e7d 100644 --- a/shared/src/utils/alloc.rs +++ b/shared/src/utils/alloc.rs @@ -1,7 +1,8 @@ -#![feature(allocator_api)] extern crate alloc; use alloc::alloc::Global; +use alloc::vec::Vec; +use alloc::boxed::Box; use core::alloc::{AllocError, Allocator, Layout}; use core::ptr::NonNull; diff --git a/shared/src/utils/containers.rs b/shared/src/utils/containers.rs index 723a9f7..181ecf6 100644 --- a/shared/src/utils/containers.rs +++ b/shared/src/utils/containers.rs @@ -76,6 +76,22 @@ impl Array2D { values.resize(n, val); Self { extent, values } } + + pub fn from_slice(extent: Bounds2i, slice: &[T]) -> Self { + assert_eq!(slice.len(), extent.area() as usize); + Self { + extent, + values: gvec_from_slice(slice), + } + } + + pub fn get(&self, p: Point2i) -> &T { + &self[p] + } + + pub fn get_linear_mut(&mut self, index: usize) -> &mut T { + unsafe { &mut *self.values.as_mut_ptr().add(index) } + } } impl Index<(i32, i32)> for Array2D { @@ -93,8 +109,25 @@ impl IndexMut<(i32, i32)> for Array2D { } } +impl Index for Array2D { + type Output = T; + fn index(&self, p: Point2i) -> &T { + let offset = + (p.y() - self.extent.p_min.y()) * self.stride() + (p.x() - self.extent.p_min.x()); + &self.values[offset as usize] + } +} + +impl IndexMut for Array2D { + fn index_mut(&mut self, p: Point2i) -> &mut T { + let offset = + (p.y() - self.extent.p_min.y()) * self.stride() + (p.x() - self.extent.p_min.x()); + &mut self.values[offset as usize] + } +} + #[repr(C)] -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct SampledGrid { pub values: GVec, pub values_len: u32, @@ -106,7 +139,7 @@ pub struct SampledGrid { unsafe impl Sync for SampledGrid {} unsafe impl Send for SampledGrid {} -impl SampledGrid { +impl SampledGrid { pub fn new(slice: &[T], nx: i32, ny: i32, nz: i32) -> Self { assert_eq!(slice.len(), (nx * ny * nz) as usize); Self { @@ -129,7 +162,7 @@ impl SampledGrid { } pub fn is_valid(&self) -> bool { - !self.values.is_null() && self.nx > 0 && self.ny > 0 && self.nz > 0 + !self.values.is_empty() && self.nx > 0 && self.ny > 0 && self.nz > 0 } pub fn bytes_allocated(&self) -> u32 { @@ -168,7 +201,7 @@ impl SampledGrid { let idx = (p.z() * self.ny + p.y()) * self.nx + p.x(); unsafe { - let val = &*self.values.add(idx as usize); + let val = &*self.values.as_ptr().add(idx as usize); convert(val) } } diff --git a/shared/src/utils/math.rs b/shared/src/utils/math.rs index f222a90..ff75ec3 100644 --- a/shared/src/utils/math.rs +++ b/shared/src/utils/math.rs @@ -1,11 +1,10 @@ use crate::core::color::{RGB, XYZ}; use crate::core::geometry::{Lerp, MulAdd, Point, Point2f, Point2i, Vector, Vector3f, VectorLike}; use crate::core::pbrt::{Float, FloatBitOps, FloatBits, ONE_MINUS_EPSILON, PI, PI_OVER_4}; +use crate::utils::gpu_array_from_fn; use crate::utils::hash::{hash_buffer, mix_bits}; -use crate::utils::math::permutation_element; use crate::utils::sobol::{SOBOL_MATRICES_32, VDC_SOBOL_MATRICES, VDC_SOBOL_MATRICES_INV}; - -use crate::utils::{gpu_array_from_fn, Ptr}; +use crate::{gvec, gvec_with_capacity, GVec, Ptr}; use core::fmt::{self, Display, Write}; use core::iter::{Product, Sum}; use core::mem; @@ -770,13 +769,23 @@ pub fn inverse_radical_inverse(mut inverse: u64, base: u64, n_digits: u64) -> u6 // Digit scrambling #[repr(C)] -#[derive(Default, Debug, Clone)] +#[derive(Debug, Clone)] pub struct DigitPermutation { pub base: i32, pub n_digits: u32, pub permutations: GVec, } +impl Default for DigitPermutation { + fn default() -> Self { + Self { + base: i32::default(), + n_digits: u32::default(), + permutations: gvec(), + } + } +} + impl DigitPermutation { pub fn new(base: i32, seed: u64) -> Self { assert!(base < 65536); @@ -804,9 +813,9 @@ impl DigitPermutation { } Self { - device, - permutations, + base, n_digits, + permutations, } } @@ -818,11 +827,7 @@ impl DigitPermutation { } } -pub fn scrambled_radical_inverse( - base_index: u32, - mut a: u64, - perm: &DigitPermutation, -) -> Float { +pub fn scrambled_radical_inverse(base_index: u32, mut a: u64, perm: &DigitPermutation) -> Float { let base = PRIMES[base_index as usize] as u64; let limit = (u64::MAX / base).saturating_sub(base); diff --git a/shared/src/utils/mod.rs b/shared/src/utils/mod.rs index b160d1d..beef286 100644 --- a/shared/src/utils/mod.rs +++ b/shared/src/utils/mod.rs @@ -1,3 +1,4 @@ +pub mod alloc; pub mod complex; pub mod containers; pub mod hash; @@ -16,6 +17,7 @@ pub mod transform; pub use options::PBRTOptions; pub use ptr::Ptr; pub use transform::{AnimatedTransform, Transform, TransformGeneric}; +pub use containers::Array2D; use crate::Float; use core::sync::atomic::{AtomicU32, Ordering}; diff --git a/shared/src/utils/ptr.rs b/shared/src/utils/ptr.rs index 78f5ad2..ef2b993 100644 --- a/shared/src/utils/ptr.rs +++ b/shared/src/utils/ptr.rs @@ -50,7 +50,6 @@ impl Ptr { unsafe { &*self.ptr } } - /// Get as Option - safe for optional fields #[inline(always)] pub fn get<'a>(self) -> Option<&'a T> { if self.is_null() { @@ -141,3 +140,12 @@ impl From<*const T> for Ptr { Self { ptr } } } + +impl From> for Ptr { + fn from(opt: Option<&T>) -> Self { + match opt { + Some(r) => Ptr::from(r), + None => Ptr::null(), + } + } +} diff --git a/shared/src/utils/sampling.rs b/shared/src/utils/sampling.rs index 50007f7..315d691 100644 --- a/shared/src/utils/sampling.rs +++ b/shared/src/utils/sampling.rs @@ -1,14 +1,14 @@ use crate::core::geometry::{ Bounds2f, Frame, Point2f, Point2i, Point3f, Vector2f, Vector2i, Vector3f, VectorLike, }; +use crate::core::image::Image; use crate::utils::find_interval; use crate::utils::math::{ catmull_rom_weights, clamp, difference_of_products, evaluate_polynomial, lerp, logistic, newton_bisection, safe_sqrt, square, sum_of_products, }; -use crate::utils::ptr::Ptr; use crate::utils::rng::Rng; -use crate::{gvec_from_slice, gvec_with_capacity, Array2D, GVec}; +use crate::{gvec, gvec_from_slice, gvec_with_capacity, Array2D, GVec, Ptr}; use crate::{Float, INV_2_PI, INV_4_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4}; use num_traits::Float as NumFloat; use num_traits::Num; @@ -751,9 +751,11 @@ impl PiecewiseConstant1D { F: Fn(Float) -> Float, { let delta = (max - min) / n as Float; - let values: Vec = (0..n) - .map(|i| f(min + (i as Float + 0.5) * delta)) - .collect(); + let delta = (max - min) / n as Float; + let mut values = gvec_with_capacity(n); + for i in 0..n { + values.push(f(min + (i as Float + 0.5) * delta)); + } Self::new_with_bounds(&values, min, max) } @@ -830,12 +832,12 @@ impl PiecewiseConstant2D { assert_eq!(data.len(), n_u * n_v); let mut conditionals = gvec_with_capacity(n_v); - let mut marginal_func = GVec::with_capacity(n_v); + let mut marginal_func = gvec_with_capacity(n_v); for v in 0..n_v { - let row = &data[v * n_u..(v + 1) * n_u]; + let row = data[v * n_u..(v + 1) * n_u].to_vec(); let conditional = - PiecewiseConstant1D::new_with_bounds(row, domain.p_min.x(), domain.p_max.x()); + PiecewiseConstant1D::new_with_bounds(&row, domain.p_min.x(), domain.p_max.x()); marginal_func.push(conditional.integral()); conditionals.push(conditional); } @@ -846,37 +848,41 @@ impl PiecewiseConstant2D { domain.p_max.y(), ); - pub fn from_image(image: &Image) -> Self { - let res = image.resolution(); - let n_u = res.x() as usize; - let n_v = res.y() as usize; - - let mut data = Vec::with_capacity(n_u * n_v); - for v in 0..n_v { - for u in 0..n_u { - data.push( - image - .get_channels(Point2i::new(u as i32, v as i32)) - .average(), - ); - } - } - - Self::from_slice(&data, n_u, n_v, Bounds2f::unit()) - } - Self { conditionals, marginal, - n_u: n_u as u32, - n_v: n_v as u32, + n_u: n_u.try_into().unwrap(), + n_v: n_v.try_into().unwrap(), } } + pub fn from_image(image: &Image) -> Self { + let res = image.resolution(); + let n_u = res.x() as usize; + let n_v = res.y() as usize; + + let mut data = gvec_with_capacity(n_u * n_v); + for v in 0..n_v { + for u in 0..n_u { + data.push(image.get_channels_average(Point2i::new(u as i32, v as i32))); + } + } + + PiecewiseConstant2D::from_slice(&data, n_u, n_v, Bounds2f::unit()) + } + pub fn integral(&self) -> Float { self.marginal.integral() } + pub fn pdf(&self, p: Point2f) -> Float { + let u_offset = ((p.x() * self.n_u as Float) as usize).min(self.n_u as usize - 1); + let v_offset = ((p.y() * self.n_v as Float) as usize).min(self.n_v as usize - 1); + let conditional = unsafe { &*self.conditionals.as_ptr().add(v_offset) }; + let func_val = unsafe { *conditional.func.as_ptr().add(u_offset) }; + func_val / self.integral() + } + pub fn sample(&self, u: Point2f) -> (Point2f, Float, Point2i) { let (d1, pdf1, off_y) = self.marginal.sample(u.y()); let (d0, pdf0, off_x) = self.conditionals[off_y].sample(u.x()); @@ -890,7 +896,7 @@ impl PiecewiseConstant2D { } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct SummedAreaTable { pub sum: Array2D, } @@ -964,7 +970,7 @@ impl SummedAreaTable { } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct WindowedPiecewiseConstant2D { pub sat: SummedAreaTable, pub func: Array2D, @@ -989,7 +995,8 @@ impl WindowedPiecewiseConstant2D { }; let nx = self.func.x_size(); - let px_val = Self::sample_bisection(px, u.x(), b.p_min.x(), b.p_max.x(), nx); + let px_val = + Self::sample_bisection(px, u.x(), b.p_min.x(), b.p_max.x(), nx.try_into().unwrap()); let nx_f = nx as Float; @@ -1017,7 +1024,8 @@ impl WindowedPiecewiseConstant2D { }; let ny = self.func.y_size(); - let py_val = Self::sample_bisection(py, u.y(), b.p_min.y(), b.p_max.y(), ny); + let py_val = + Self::sample_bisection(py, u.y(), b.p_min.y(), b.p_max.y(), ny.try_into().unwrap()); let p = Point2f::new(px_val, py_val); @@ -1117,8 +1125,8 @@ impl AliasTable { index: usize, } - let mut under = Vec::with_capacity(n); - let mut over = Vec::with_capacity(n); + let mut under = gvec_with_capacity(n); + let mut over = gvec_with_capacity(n); for (i, bin) in bins.iter().enumerate() { let p_hat = (bin.p as f64) * (n as f64); @@ -1201,7 +1209,7 @@ impl AliasTable { } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct PiecewiseLinear2D { pub size: Vector2i, pub inv_patch_size: Vector2f, @@ -1233,7 +1241,8 @@ impl PiecewiseLinear2D { let mut param_size = [0u32; N]; let mut param_strides = [0u32; N]; - let owned_param_values: [Vec; N] = gpu_array_from_fn(|i| param_values[i].to_vec()); + let owned_param_values: [GVec; N] = + core::array::from_fn(|i| gvec_from_slice(param_values[i])); let mut slices: u32 = 1; for i in (0..N).rev() { @@ -1244,16 +1253,23 @@ impl PiecewiseLinear2D { } let n_values = (x_size * y_size) as usize; - let mut new_data = vec![0.0; slices as usize * n_values]; + let mut new_data = gvec_with_capacity(slices as usize * n_values); + new_data.resize(slices as usize * n_values, 0.0); + let mut marginal_cdf = if build_cdf { - vec![0.0; slices as usize * y_size as usize] + let mut v = gvec_with_capacity(slices as usize * y_size as usize); + v.resize(slices as usize * y_size as usize, 0.0); + v } else { - Vec::new() + gvec() }; + let mut conditional_cdf = if build_cdf { - vec![0.0; slices as usize * n_values] + let mut v = gvec_with_capacity(slices as usize * n_values); + v.resize(slices as usize * n_values, 0.0); + v } else { - Vec::new() + gvec() }; let mut data_offset = 0; @@ -1314,7 +1330,6 @@ impl PiecewiseLinear2D { inv_patch_size, param_size, param_strides, - storage, data: new_data, marginal_cdf, conditional_cdf, @@ -1336,7 +1351,7 @@ impl PiecewiseLinear2D { let conditional_offset = slice_offset * conditional_size; let fetch_marginal = |idx: u32| { self.lookup( - self.marginal_cdf, + &self.marginal_cdf, marginal_offset + idx, marginal_size, ¶m_weights, @@ -1346,13 +1361,13 @@ impl PiecewiseLinear2D { let marginal_cdf_row = fetch_marginal(row); sample[1] -= marginal_cdf_row; let r0 = self.lookup( - self.conditional_cdf, + &self.conditional_cdf, conditional_offset + (row + 1) * self.size.x() as u32 - 1, conditional_size, ¶m_weights, ); let r1 = self.lookup( - self.conditional_cdf, + &self.conditional_cdf, conditional_offset + (row + 2) * self.size.x() as u32 - 1, conditional_size, ¶m_weights, @@ -1369,13 +1384,13 @@ impl PiecewiseLinear2D { let conditional_row_offset = conditional_offset + row * self.size.x() as u32; let fetch_conditional = |idx: u32| { let v0 = self.lookup( - self.conditional_cdf, + &self.conditional_cdf, conditional_row_offset + idx, conditional_size, ¶m_weights, ); let v1 = self.lookup( - self.conditional_cdf, + &self.conditional_cdf, conditional_row_offset + idx + self.size.x() as u32, conditional_size, ¶m_weights, @@ -1387,16 +1402,16 @@ impl PiecewiseLinear2D { }); sample[0] -= fetch_conditional(col); let offset = conditional_row_offset + col; - let v00 = self.lookup(self.data, offset, slice_size, ¶m_weights); - let v10 = self.lookup(self.data, offset + 1, slice_size, ¶m_weights); + let v00 = self.lookup(&self.data, offset, slice_size, ¶m_weights); + let v10 = self.lookup(&self.data, offset + 1, slice_size, ¶m_weights); let v01 = self.lookup( - self.data, + &self.data, offset + self.size.x() as u32, slice_size, ¶m_weights, ); let v11 = self.lookup( - self.data, + &self.data, offset + self.size.x() as u32 + 1, slice_size, ¶m_weights, @@ -1433,16 +1448,16 @@ impl PiecewiseLinear2D { let frac = Point2f::new(p.x() - col as Float, p.y() - row as Float); let slice_size = (self.size.x() * self.size.y()) as u32; let offset = slice_offset * slice_size + (row * self.size.x() + col) as u32; - let v00 = self.lookup(self.data, offset, slice_size, ¶m_weights); - let v10 = self.lookup(self.data, offset + 1, slice_size, ¶m_weights); + let v00 = self.lookup(&self.data, offset, slice_size, ¶m_weights); + let v10 = self.lookup(&self.data, offset + 1, slice_size, ¶m_weights); let v01 = self.lookup( - self.data, + &self.data, offset + self.size.x() as u32, slice_size, ¶m_weights, ); let v11 = self.lookup( - self.data, + &self.data, offset + self.size.x() as u32 + 1, slice_size, ¶m_weights, @@ -1456,26 +1471,26 @@ impl PiecewiseLinear2D { u[0] = w1.x() * (c0 + 0.5 * w1.x() * (c1 - c0)); let conditional_row_offset = slice_offset * slice_size + (row * self.size.x()) as u32; let v0 = self.lookup( - self.conditional_cdf, + &self.conditional_cdf, conditional_row_offset + col as u32, slice_size, ¶m_weights, ); let v1 = self.lookup( - self.conditional_cdf, + &self.conditional_cdf, conditional_row_offset + col as u32 + self.size.x() as u32, slice_size, ¶m_weights, ); u[0] += (1.0 - u.y()) * v0 + u.y() * v1; let r0 = self.lookup( - self.conditional_cdf, + &self.conditional_cdf, conditional_row_offset + self.size.x() as u32 - 1, slice_size, ¶m_weights, ); let r1 = self.lookup( - self.conditional_cdf, + &self.conditional_cdf, conditional_row_offset + self.size.x() as u32 * 2 - 1, slice_size, ¶m_weights, @@ -1484,7 +1499,7 @@ impl PiecewiseLinear2D { u[1] = w1.y() * (r0 + 0.5 * w1.y() * (r1 - r0)); let marginal_offset = slice_offset * self.size.y() as u32 + row as u32; u[1] += self.lookup( - self.marginal_cdf, + &self.marginal_cdf, marginal_offset, self.size.y() as u32, ¶m_weights, @@ -1508,16 +1523,16 @@ impl PiecewiseLinear2D { let w0 = Point2f::new(1.0 - w1.x(), 1.0 - w1.y()); let slice_size = (self.size.x() * self.size.y()) as u32; let offset = slice_offset * slice_size + (row * self.size.x() + col) as u32; - let v00 = self.lookup(self.data, offset, slice_size, ¶m_weights); - let v10 = self.lookup(self.data, offset + 1, slice_size, ¶m_weights); + let v00 = self.lookup(&self.data, offset, slice_size, ¶m_weights); + let v10 = self.lookup(&self.data, offset + 1, slice_size, ¶m_weights); let v01 = self.lookup( - self.data, + &self.data, offset + self.size.x() as u32, slice_size, ¶m_weights, ); let v11 = self.lookup( - self.data, + &self.data, offset + self.size.x() as u32 + 1, slice_size, ¶m_weights, @@ -1529,8 +1544,7 @@ impl PiecewiseLinear2D { #[inline(always)] fn get_param_value(&self, dim: usize, idx: usize) -> Float { - // Safety: Bounds checking against param_size ensures this is valid - unsafe { *self.param_values[dim].add(idx) } + unsafe { *self.param_values[dim].as_ptr().add(idx) } } fn get_slice_info(&self, params: [Float; N]) -> (u32, [(Float, Float); N]) { @@ -1570,7 +1584,7 @@ impl PiecewiseLinear2D { fn lookup( &self, - data: Ptr, + data: &GVec, i0: u32, size: u32, param_weight: &[(Float, Float); N], @@ -1592,7 +1606,7 @@ impl PiecewiseLinear2D { current_mask >>= 1; } let idx = (i0 + offset) as usize; - let val = unsafe { *data.add(idx) }; + let val = unsafe { *data.as_ptr().add(idx) }; result += weight * val; } result diff --git a/src/lights/distant.rs b/src/lights/distant.rs index fb18dd9..272498e 100644 --- a/src/lights/distant.rs +++ b/src/lights/distant.rs @@ -3,7 +3,6 @@ use crate::core::spectrum::spectrum_to_photometric; use crate::core::texture::FloatTexture; use crate::utils::{Arena, FileLoc, ParameterDictionary}; use anyhow::Result; -use shared::Float; use shared::core::geometry::{Point3f, VectorLike}; use shared::core::light::{Light, LightBase, LightType}; use shared::core::medium::{Medium, MediumInterface}; @@ -13,6 +12,29 @@ use shared::core::texture::SpectrumType; use shared::lights::DistantLight; use shared::spectra::RGBColorSpace; use shared::utils::{Ptr, Transform}; +use shared::Float; + +trait CreateDistantLight { + fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self; +} + +impl CreateDistantLight for DistantLight { + fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self { + let base = LightBase::new( + LightType::DeltaDirection, + render_from_light, + MediumInterface::empty(), + ); + let lemit = lookup_spectrum(&le); + Self { + base, + lemit: Ptr::from(&lemit.device()), + scale, + scene_center: Point3f::default(), + scene_radius: 0., + } + } +} pub fn create( render_from_light: Transform, diff --git a/src/lights/point.rs b/src/lights/point.rs index 6e7793e..4a5b35b 100644 --- a/src/lights/point.rs +++ b/src/lights/point.rs @@ -13,6 +13,35 @@ use shared::lights::PointLight; use shared::spectra::RGBColorSpace; use shared::{Float, PI, Ptr, Transform}; +pub trait CreatePointLight { + fn new( + render_from_light: Transform, + medium_interface: MediumInterface, + le: Spectrum, + scale: Float, + arena: &Arena, + ) -> Self; +} + +impl CreatePointLight for PointLight { + fn new( + render_from_light: Transform, + medium_interface: MediumInterface, + le: Spectrum, + scale: Float, + arena: &Arena, + ) -> Self { + let base = LightBase::new( + LightType::DeltaPosition, + render_from_light, + medium_interface, + ); + let iemit = lookup_spectrum(&le); + let i = arena.alloc((*iemit).clone()); + Self { base, scale, i } + } +} + pub fn create( render_from_light: Transform, diff --git a/src/lights/spot.rs b/src/lights/spot.rs index 7e1d3fa..6ebecc5 100644 --- a/src/lights/spot.rs +++ b/src/lights/spot.rs @@ -13,7 +13,45 @@ use shared::lights::SpotLight; use shared::spectra::RGBColorSpace; use shared::utils::math::radians; use shared::utils::{Ptr, Transform}; -use shared::{Float, PI, Ptr, Transform}; +use shared::{Float, Ptr, Transform, PI}; + +trait CreateSpotLight { + fn new( + render_from_light: Transform, + _medium_interface: MediumInterface, + le: Spectrum, + scale: Float, + cos_falloff_start: Float, + total_width: Float, + ) -> Self; +} + +impl CreateSpotLight for SpotLight { + fn new( + render_from_light: Transform, + _medium_interface: MediumInterface, + le: Spectrum, + scale: Float, + cos_falloff_start: Float, + total_width: Float, + ) -> Self { + let base = LightBase::new( + LightType::DeltaPosition, + render_from_light, + MediumInterface::empty(), + ); + + let i = lookup_spectrum(&le); + let iemit = Ptr::from(&i.device()); + Self { + base, + iemit, + scale, + cos_falloff_end: radians(total_width).cos(), + cos_falloff_start: radians(cos_falloff_start).cos(), + } + } +} pub fn create( render_from_light: Transform, diff --git a/src/spectra/mod.rs b/src/spectra/mod.rs index 1067cc7..9d89680 100644 --- a/src/spectra/mod.rs +++ b/src/spectra/mod.rs @@ -45,10 +45,10 @@ pub fn cie_d65() -> Spectrum { pub fn get_spectra_context() -> StandardSpectra { StandardSpectra { - x: CIE_X_DATA, - y: CIE_Y_DATA, - z: CIE_Z_DATA, - d65: CIE_D65_DATA, + x: Ptr::from(&*CIE_X_DATA), + y: Ptr::from(&*CIE_Y_DATA), + z: Ptr::from(&*CIE_Z_DATA), + d65: Ptr::from(&*CIE_D65_DATA), } }