From b36105edc169aa722cc69e2421a0bc8b1f32518d Mon Sep 17 00:00:00 2001 From: Wito Wiala Date: Thu, 19 Feb 2026 15:41:24 +0000 Subject: [PATCH] Changing error handling with scene file parsing --- Cargo.toml | 6 +- src/lib.rs | 4 +- src/materials/coated.rs | 120 ++++++++++--------- src/materials/complex.rs | 12 +- src/samplers/halton.rs | 15 +-- src/samplers/independent.rs | 14 ++- src/samplers/sobol.rs | 45 +++---- src/samplers/stratified.rs | 8 +- src/shapes/bilinear.rs | 12 +- src/shapes/curves.rs | 18 +-- src/shapes/cylinder.rs | 8 +- src/shapes/disk.rs | 8 +- src/shapes/mesh.rs | 8 +- src/shapes/sphere.rs | 8 +- src/shapes/triangle.rs | 12 +- src/textures/mix.rs | 12 +- src/textures/scaled.rs | 4 +- src/utils/arena.rs | 143 +++++++--------------- src/utils/mod.rs | 12 +- src/utils/parameters.rs | 230 +++++++++++++++++++----------------- src/utils/parser.rs | 5 +- src/utils/sampling.rs | 5 +- 22 files changed, 340 insertions(+), 369 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d4fd833..909fe3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,8 @@ use_f64 = [] use_gpu = [] use_nvtx = [] cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw"] +vulkan = ["ash", "gpu-allocator"] +ash = ["dep:ash"] [dependencies] anyhow = "1.0.100" @@ -33,12 +35,12 @@ wgpu = "27.0.1" shared = { path = "shared" } ptex-filter = { path = "crates/ptex-filter" } # kernels = { path = "kernels" } - +ash = { version = "0.38", optional = true } +gpu-allocator = { version = "0.28", features = ["vulkan"], optional = true } cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true } cust = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, features = ["glam"], optional = true } cust_raw = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true } ptex = "0.3.0" -# ptex-sys = "0.3.0" slice = "0.0.4" crossbeam-channel = "0.5.15" num_cpus = "1.17.0" diff --git a/src/lib.rs b/src/lib.rs index 5b2f17c..0bbb0b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,4 @@ pub mod utils; #[cfg(feature = "cuda")] pub mod gpu; -pub use utils::arena::Arena; -pub use utils::error::FileLoc; -pub use utils::parameters::ParameterDictionary; +pub use utils::{Arena, FileLoc, ParameterDictionary}; diff --git a/src/materials/coated.rs b/src/materials/coated.rs index 3d966c1..60f6fb6 100644 --- a/src/materials/coated.rs +++ b/src/materials/coated.rs @@ -3,7 +3,7 @@ use crate::core::material::CreateMaterial; use crate::core::texture::SpectrumTexture; use crate::spectra::data::get_named_spectrum; use crate::utils::{Arena, FileLoc, TextureParameterDictionary, Upload, parameters::error_exit}; -use anyhow::Result; +use anyhow::{Result, bail}; use shared::core::material::Material; use shared::core::spectrum::Spectrum; use shared::core::texture::SpectrumType; @@ -27,32 +27,32 @@ impl CreateMaterial for CoatedDiffuseMaterial { SpectrumConstantTexture::new(Spectrum::Constant(ConstantSpectrum::new(0.5))), ))); - let u_roughness = parameters - .get_float_texture_or_null("uroughness") - .unwrap_or_else(|| parameters.get_float_texture("roughness", 0.5)); - let v_roughness = parameters - .get_float_texture_or_null("vroughness") - .unwrap_or_else(|| parameters.get_float_texture("roughness", 0.5)); + let u_roughness = + parameters.get_float_texture_with_fallback("uroughness", "roughness", 0.5)?; - let thickness = parameters.get_float_texture("thickness", 0.01); + let v_roughness = + parameters.get_float_texture_with_fallback("vroughness", "roughness", 0.5)?; + + let thickness = parameters.get_float_texture("thickness", 0.01)?; let eta = parameters - .get_float_array("eta") + .get_float_array("eta")? .first() .map(|&v| Spectrum::Constant(ConstantSpectrum::new(v))) .or_else(|| parameters.get_one_spectrum("eta", None, SpectrumType::Unbounded)) .unwrap_or_else(|| Spectrum::Constant(ConstantSpectrum::new(1.5))); - let max_depth = parameters.get_one_int("maxdepth", 10); - let n_samples = parameters.get_one_int("nsamples", 1); - let g = parameters.get_float_texture("g", 0.); + let max_depth = parameters.get_one_int("maxdepth", 10)?; + let n_samples = parameters.get_one_int("nsamples", 1)?; + let g = parameters.get_float_texture("g", 0.)?; let albedo = parameters .get_spectrum_texture("albedo", None, SpectrumType::Albedo) .unwrap_or_else(|| { let default_spectrum = Spectrum::Constant(ConstantSpectrum::new(0.)); SpectrumTexture::Constant(SpectrumConstantTexture::new(default_spectrum)).into() }); - let displacement = parameters.get_float_texture("displacement", 0.); - let remap_roughness = parameters.get_one_bool("remaproughness", true); + let displacement = parameters.get_float_texture("displacement", 0.)?; + let remap_roughness = parameters.get_one_bool("remaproughness", true)?; + let specific = CoatedDiffuseMaterial::new( reflectance.upload(arena), u_roughness.upload(arena), @@ -80,68 +80,76 @@ impl CreateMaterial for CoatedConductorMaterial { loc: &FileLoc, arena: &Arena, ) -> Result { - let interface_u_roughness = parameters - .get_float_texture_or_null("interface.uroughness") - .unwrap_or_else(|| parameters.get_float_texture("interface.roughness", 0.)); - let interface_v_roughness = parameters - .get_float_texture_or_null("interface.vroughness") - .unwrap_or_else(|| parameters.get_float_texture("interface.vroughness", 0.)); - let thickness = parameters.get_float_texture("interface.thickness", 0.01); + let interface_u_roughness = parameters.get_float_texture_with_fallback( + "interface.uroughness", + "interface.roughness", + 0., + )?; + let interface_v_roughness = parameters.get_float_texture_with_fallback( + "interface.vroughness", + "interface.roughness", + 0., + )?; + + let thickness = parameters.get_float_texture("interface.thickness", 0.01)?; let interface_eta: Spectrum = parameters - .get_float_array("interface.eta") + .get_float_array("interface.eta")? .first() .map(|&v| Spectrum::Constant(ConstantSpectrum::new(v))) .or_else(|| parameters.get_one_spectrum("interface.eta", None, SpectrumType::Unbounded)) .unwrap_or_else(|| Spectrum::Constant(ConstantSpectrum::new(1.5))); - let conductor_u_roughness = parameters - .get_float_texture_or_null("conductor.uroughness") - .unwrap_or_else(|| parameters.get_float_texture("conductor.roughness", 0.)); - let conductor_v_roughness = parameters - .get_float_texture_or_null("conductor.vroughness") - .unwrap_or_else(|| parameters.get_float_texture("conductor.vroughness", 0.)); + + let conductor_u_roughness = parameters.get_float_texture_with_fallback( + "conductor.uroughness", + "conductor.roughness", + 0., + )?; + + let conductor_v_roughness = parameters.get_float_texture_with_fallback( + "conductor.vroughness", + "conductor.roughness", + 0., + )?; + let reflectance = parameters.get_spectrum_texture_or_null("reflectance", SpectrumType::Albedo); let conductor_eta = parameters.get_spectrum_texture_or_null("conductor.eta", SpectrumType::Unbounded); let k = parameters.get_spectrum_texture_or_null("conductor.k", SpectrumType::Unbounded); - let (conductor_eta, k) = match (&reflectance, conductor_eta, k) { - (Some(_), Some(_), _) | (Some(_), _, Some(_)) => { - return Err(error_exit( - Some(loc), - "For the coated conductor material, both \"reflectance\" \ - and \"eta\" and \"k\" can't be provided.", - )); + let (conductor_eta, k) = if reflectance.is_some() { + if conductor_eta.is_some() || k.is_some() { + bail!( + "{}: For the coated conductor material, both \"reflectance\" \ + and \"eta\"/\"k\" cannot be provided simultaneously.", + loc + ); } - - (None, eta, k) => { - let final_eta = eta.unwrap_or_else(|| { - let s = get_named_spectrum("metal-Cu-eta").expect("Missing copper spectrum"); - Arc::new(SpectrumTexture::Constant(SpectrumConstantTexture::new(s))) - }); - - let final_k = k.unwrap_or_else(|| { - let s = get_named_spectrum("metal-Cu-k").expect("Missing copper spectrum"); - Arc::new(SpectrumTexture::Constant(SpectrumConstantTexture::new(s))) - }); - - (Some(final_eta), Some(final_k)) - } - - (Some(_), None, None) => (None, None), + (None, None) + } else { + let eta = conductor_eta.unwrap_or_else(|| { + let s = get_named_spectrum("metal-Cu-eta").expect("Missing copper spectrum"); + Arc::new(SpectrumTexture::Constant(SpectrumConstantTexture::new(s))) + }); + let k = k.unwrap_or_else(|| { + let s = get_named_spectrum("metal-Cu-k").expect("Missing copper spectrum"); + Arc::new(SpectrumTexture::Constant(SpectrumConstantTexture::new(s))) + }); + (Some(eta), Some(k)) }; - let max_depth = parameters.get_one_int("maxdepth", 10); - let n_samples = parameters.get_one_int("nsamples", 1); - let g = parameters.get_float_texture("g", 0.); + let max_depth = parameters.get_one_int("maxdepth", 10)?; + let n_samples = parameters.get_one_int("nsamples", 1)?; + let g = parameters.get_float_texture("g", 0.)?; let albedo = parameters .get_spectrum_texture("albedo", None, SpectrumType::Albedo) .unwrap_or_else(|| { let spectrum = Spectrum::Constant(ConstantSpectrum::new(0.)); SpectrumTexture::Constant(SpectrumConstantTexture::new(spectrum)).into() }); - let displacement = parameters.get_float_texture_or_null("displacement"); - let remap_roughness = parameters.get_one_bool("remaproughness", true); + let displacement = parameters.get_float_texture_or_null("displacement")?; + let remap_roughness = parameters.get_one_bool("remaproughness", true)?; + let material = Self::new( normal_map.upload(arena), displacement.upload(arena), diff --git a/src/materials/complex.rs b/src/materials/complex.rs index f10dcb2..925b439 100644 --- a/src/materials/complex.rs +++ b/src/materials/complex.rs @@ -27,8 +27,8 @@ impl CreateMaterial for HairMaterial { let reflectance = parameters .get_spectrum_texture_or_null("reflectance", SpectrumType::Albedo) .or_else(|| parameters.get_spectrum_texture_or_null("color", SpectrumType::Albedo)); - let eumelanin = parameters.get_float_texture_or_null("eumelanin"); - let pheomelanin = parameters.get_float_texture_or_null("pheomelanin"); + let eumelanin = parameters.get_float_texture_or_null("eumelanin")?; + let pheomelanin = parameters.get_float_texture_or_null("pheomelanin")?; let has_melanin = eumelanin.is_some() || pheomelanin.is_some(); // Default distribution if nothing is spceified @@ -42,10 +42,10 @@ impl CreateMaterial for HairMaterial { sigma_a }; - let eta = parameters.get_float_texture("eta", 1.55); - let beta_m = parameters.get_float_texture("beta_m", 0.3); - let beta_n = parameters.get_float_texture("beta_n", 0.3); - let alpha = parameters.get_float_texture("alpha", 2.); + let eta = parameters.get_float_texture("eta", 1.55)?; + let beta_m = parameters.get_float_texture("beta_m", 0.3)?; + let beta_n = parameters.get_float_texture("beta_n", 0.3)?; + let alpha = parameters.get_float_texture("alpha", 2.)?; let material = HairMaterial::new( sigma_a.upload(arena), reflectance.upload(arena), diff --git a/src/samplers/halton.rs b/src/samplers/halton.rs index 4ccda8e..826ca3a 100644 --- a/src/samplers/halton.rs +++ b/src/samplers/halton.rs @@ -71,14 +71,15 @@ impl CreateSampler for HaltonSampler { _arena: &Arena, ) -> Result { let options = get_options(); - let nsamp = options - .quick_render - .then_some(1) - .or(options.pixel_samples) - .unwrap_or_else(|| params.get_one_int("pixelsamples", 16)); - let seed = params.get_one_int("seed", options.seed); + let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) { + n + } else { + params.get_one_int("pixelsamples", 16)? + }; + + let seed = params.get_one_int("seed", options.seed)?; let s = match params - .get_one_string("randomization", "permutedigits") + .get_one_string("randomization", "permutedigits")? .as_str() { "none" => RandomizeStrategy::None, diff --git a/src/samplers/independent.rs b/src/samplers/independent.rs index b463596..d5f407b 100644 --- a/src/samplers/independent.rs +++ b/src/samplers/independent.rs @@ -11,12 +11,14 @@ impl CreateSampler for IndependentSampler { arena: &Arena, ) -> Result { let options = get_options(); - let nsamp = options - .quick_render - .then_some(1) - .or(options.pixel_samples) - .unwrap_or_else(|| params.get_one_int("pixelsamples", 16)); - let seed = params.get_one_int("seed", options.seed); + + let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) { + n + } else { + params.get_one_int("pixelsamples", 16)? + }; + + let seed = params.get_one_int("seed", options.seed)?; let sampler = Self::new(nsamp, seed as u64); arena.alloc(sampler); Ok(Sampler::Independent(sampler)) diff --git a/src/samplers/sobol.rs b/src/samplers/sobol.rs index 9c57849..ae39531 100644 --- a/src/samplers/sobol.rs +++ b/src/samplers/sobol.rs @@ -12,13 +12,15 @@ impl CreateSampler for SobolSampler { _arena: &Arena, ) -> Result { let options = get_options(); - let nsamp = options - .quick_render - .then_some(1) - .or(options.pixel_samples) - .unwrap_or_else(|| params.get_one_int("pixelsamples", 16)); - let seed = params.get_one_int("seed", options.seed); - let s = match params.get_one_string("randomization", "fastowen").as_str() { + let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) { + n + } else { + params.get_one_int("pixelsamples", 16)? + }; + + let seed = params.get_one_int("seed", options.seed)?; + + let s = match params.get_one_string("randomization", "fastowen")?.as_str() { "none" => RandomizeStrategy::None, "permutedigits" => RandomizeStrategy::PermuteDigits, "fastowen" => RandomizeStrategy::FastOwen, @@ -42,13 +44,13 @@ impl CreateSampler for PaddedSobolSampler { _arena: &Arena, ) -> Result { let options = get_options(); - let nsamp = options - .quick_render - .then_some(1) - .or(options.pixel_samples) - .unwrap_or_else(|| params.get_one_int("pixelsamples", 16)); - let seed = params.get_one_int("seed", options.seed); - let s = match params.get_one_string("randomization", "fastowen").as_str() { + let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) { + n + } else { + params.get_one_int("pixelsamples", 16)? + }; + let seed = params.get_one_int("seed", options.seed)?; + let s = match params.get_one_string("randomization", "fastowen")?.as_str() { "none" => RandomizeStrategy::None, "permutedigits" => RandomizeStrategy::PermuteDigits, "fastowen" => RandomizeStrategy::FastOwen, @@ -75,13 +77,14 @@ impl CreateSampler for ZSobolSampler { _arena: &Arena, ) -> Result { let options = get_options(); - let nsamp = options - .quick_render - .then_some(1) - .or(options.pixel_samples) - .unwrap_or_else(|| params.get_one_int("pixelsamples", 16)); - let seed = params.get_one_int("seed", options.seed); - let s = match params.get_one_string("randomization", "fastowen").as_str() { + let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) { + n + } else { + params.get_one_int("pixelsamples", 16)? + }; + + let seed = params.get_one_int("seed", options.seed)?; + let s = match params.get_one_string("randomization", "fastowen")?.as_str() { "none" => RandomizeStrategy::None, "permutedigits" => RandomizeStrategy::PermuteDigits, "fastowen" => RandomizeStrategy::FastOwen, diff --git a/src/samplers/stratified.rs b/src/samplers/stratified.rs index 5f4591a..8003700 100644 --- a/src/samplers/stratified.rs +++ b/src/samplers/stratified.rs @@ -11,7 +11,7 @@ impl CreateSampler for StratifiedSampler { _arena: &Arena, ) -> Result { let options = get_options(); - let jitter = params.get_one_bool("jitter", true); + let jitter = params.get_one_bool("jitter", true)?; let (x_samples, y_samples) = if options.quick_render { (1, 1) } else if let Some(n) = options.pixel_samples { @@ -21,11 +21,11 @@ impl CreateSampler for StratifiedSampler { (n / y, y) } else { ( - params.get_one_int("xsamples", 4), - params.get_one_int("ysamples", 4), + params.get_one_int("xsamples", 4)?, + params.get_one_int("ysamples", 4)?, ) }; - let seed = params.get_one_int("seed", options.seed); + let seed = params.get_one_int("seed", options.seed)?; let sampler = Self::new(x_samples, y_samples, Some(seed as u64), jitter); // arena.aloc(sampler); diff --git a/src/shapes/bilinear.rs b/src/shapes/bilinear.rs index 22aa899..b85ab48 100644 --- a/src/shapes/bilinear.rs +++ b/src/shapes/bilinear.rs @@ -23,11 +23,11 @@ impl CreateShape for BilinearPatchShape { _loc: FileLoc, _arena: &Arena, ) -> Result> { - let mut vertex_indices = parameters.get_int_array("indices"); - let p = parameters.get_point3f_array("P"); - let mut uv = parameters.get_point2f_array("uv"); - let mut n = parameters.get_normal3f_array("N"); - let mut face_indices = parameters.get_int_array("faceIndices"); + let mut vertex_indices = parameters.get_int_array("indices")?; + let p = parameters.get_point3f_array("P")?; + let mut uv = parameters.get_point2f_array("uv")?; + let mut n = parameters.get_normal3f_array("N")?; + let mut face_indices = parameters.get_int_array("faceIndices")?; if vertex_indices.is_empty() { if p.len() == 4 { @@ -84,7 +84,7 @@ impl CreateShape for BilinearPatchShape { face_indices.clear(); } - let filename = parameters.get_one_string("emissionfilename", ""); + let filename = parameters.get_one_string("emissionfilename", "")?; let image_dist = if !filename.is_empty() { if !uv.is_empty() { diff --git a/src/shapes/curves.rs b/src/shapes/curves.rs index 45340b2..53570f0 100644 --- a/src/shapes/curves.rs +++ b/src/shapes/curves.rs @@ -66,11 +66,11 @@ impl CreateShape for CurveShape { _loc: FileLoc, _arena: &Arena, ) -> Result> { - let width = parameters.get_one_float("width", 1.0); - let width0 = parameters.get_one_float("width0", width); - let width1 = parameters.get_one_float("width1", width); + let width = parameters.get_one_float("width", 1.0)?; + let width0 = parameters.get_one_float("width0", width)?; + let width1 = parameters.get_one_float("width1", width)?; - let degree = parameters.get_one_int("degree", 3); + let degree = parameters.get_one_int("degree", 3)?; if degree != 2 && degree != 3 { return Err(anyhow!( "Invalid degree {}: only degree 2 and 3 curves are supported.", @@ -78,7 +78,7 @@ impl CreateShape for CurveShape { )); } - let basis = parameters.get_one_string("basis", "bezier"); + let basis = parameters.get_one_string("basis", "bezier")?; if basis != "bezier" && basis != "bspline" { return Err(anyhow!( "Invalid basis \"{}\": only \"bezier\" and \"bspline\" are supported.", @@ -86,7 +86,7 @@ impl CreateShape for CurveShape { )); } - let cp = parameters.get_point3f_array("P"); + let cp = parameters.get_point3f_array("P")?; let n_segments; if basis == "bezier" { @@ -114,7 +114,7 @@ impl CreateShape for CurveShape { n_segments = cp.len() - degree as usize; } - let curve_type_str = parameters.get_one_string("type", "flat"); + let curve_type_str = parameters.get_one_string("type", "flat")?; let curve_type = match curve_type_str.as_str() { "flat" => CurveType::Flat, "ribbon" => CurveType::Ribbon, @@ -124,7 +124,7 @@ impl CreateShape for CurveShape { } }; - let mut n = parameters.get_normal3f_array("N"); + let mut n = parameters.get_normal3f_array("N")?; if !n.is_empty() { if curve_type != CurveType::Ribbon { warn!("Curve normals are only used with \"ribbon\" type curves. Discarding."); @@ -147,7 +147,7 @@ impl CreateShape for CurveShape { let split_depth = if use_gpu { 0 } else { - parameters.get_one_int("splitdepth", 3) + parameters.get_one_int("splitdepth", 3)? }; let mut curves: Vec = Vec::new(); diff --git a/src/shapes/cylinder.rs b/src/shapes/cylinder.rs index c235f1d..797ad2a 100644 --- a/src/shapes/cylinder.rs +++ b/src/shapes/cylinder.rs @@ -18,10 +18,10 @@ impl CreateShape for CylinderShape { _loc: FileLoc, arena: &Arena, ) -> Result> { - let radius = parameters.get_one_float("radius", 1.); - let z_min = parameters.get_one_float("zmin", -1.); - let z_max = parameters.get_one_float("zmax", 1.); - let phi_max = parameters.get_one_float("phimax", 360.); + let radius = parameters.get_one_float("radius", 1.)?; + let z_min = parameters.get_one_float("zmin", -1.)?; + let z_max = parameters.get_one_float("zmax", 1.)?; + let phi_max = parameters.get_one_float("phimax", 360.)?; let shape = CylinderShape::new( render_from_object, object_from_render, diff --git a/src/shapes/disk.rs b/src/shapes/disk.rs index 9433156..a082685 100644 --- a/src/shapes/disk.rs +++ b/src/shapes/disk.rs @@ -18,10 +18,10 @@ impl CreateShape for DiskShape { _loc: FileLoc, arena: &Arena, ) -> Result> { - let height = parameters.get_one_float("height", 0.); - let radius = parameters.get_one_float("radius", 1.); - let inner_radius = parameters.get_one_float("innerradius", 0.); - let phi_max = parameters.get_one_float("phimax", 360.); + let height = parameters.get_one_float("height", 0.)?; + let radius = parameters.get_one_float("radius", 1.)?; + let inner_radius = parameters.get_one_float("innerradius", 0.)?; + let phi_max = parameters.get_one_float("phimax", 360.)?; let shape = DiskShape::new( radius, inner_radius, diff --git a/src/shapes/mesh.rs b/src/shapes/mesh.rs index 2ec5dfe..4056c5c 100644 --- a/src/shapes/mesh.rs +++ b/src/shapes/mesh.rs @@ -246,9 +246,7 @@ impl BilinearPatchMesh { } } -// ============================================================================ // PLY Helper Functions -// ============================================================================ fn get_float(elem: &DefaultElement, key: &str) -> AnyResult { match elem.get(key) { @@ -290,9 +288,7 @@ fn get_list_uint(elem: &DefaultElement, key: &str) -> AnyResult> { } } -// ============================================================================ // TriQuadMesh Implementation -// ============================================================================ impl TriQuadMesh { pub fn read_ply>(filename: P) -> AnyResult { @@ -510,9 +506,7 @@ impl TriQuadMesh { } } -// ============================================================================ -// Convenience: Create from PLY directly -// ============================================================================ +// Create from PLY directly impl TriangleMesh { pub fn from_ply>( diff --git a/src/shapes/sphere.rs b/src/shapes/sphere.rs index 1c595a2..ec8adfa 100644 --- a/src/shapes/sphere.rs +++ b/src/shapes/sphere.rs @@ -18,10 +18,10 @@ impl CreateShape for SphereShape { _loc: FileLoc, arena: &Arena, ) -> Result> { - let radius = parameters.get_one_float("radius", 1.); - let zmin = parameters.get_one_float("zmin", -radius); - let zmax = parameters.get_one_float("zmax", radius); - let phimax = parameters.get_one_float("phimax", 360.); + let radius = parameters.get_one_float("radius", 1.)?; + let zmin = parameters.get_one_float("zmin", -radius)?; + let zmax = parameters.get_one_float("zmax", radius)?; + let phimax = parameters.get_one_float("phimax", 360.)?; let shape = SphereShape::new( render_from_object, object_from_render, diff --git a/src/shapes/triangle.rs b/src/shapes/triangle.rs index 87b16d6..b6fcfff 100644 --- a/src/shapes/triangle.rs +++ b/src/shapes/triangle.rs @@ -20,11 +20,11 @@ impl CreateShape for TriangleShape { _loc: FileLoc, _arena: &Arena, ) -> Result> { - let mut vertex_indices = parameters.get_int_array("indices"); - let p = parameters.get_point3f_array("P"); - let mut uvs = parameters.get_point2f_array("uv"); - let mut s = parameters.get_vector3f_array("S"); - let mut n = parameters.get_normal3f_array("N"); + let mut vertex_indices = parameters.get_int_array("indices")?; + let p = parameters.get_point3f_array("P")?; + let mut uvs = parameters.get_point2f_array("uv")?; + let mut s = parameters.get_vector3f_array("S")?; + let mut n = parameters.get_normal3f_array("N")?; if vertex_indices.is_empty() { if p.len() == 3 { @@ -76,7 +76,7 @@ impl CreateShape for TriangleShape { } } - let mut face_indices = parameters.get_int_array("faceIndices"); + let mut face_indices = parameters.get_int_array("faceIndices")?; let n_triangles = vertex_indices.len() / 3; if !face_indices.is_empty() && face_indices.len() != n_triangles { diff --git a/src/textures/mix.rs b/src/textures/mix.rs index 43b374b..8cdc5da 100644 --- a/src/textures/mix.rs +++ b/src/textures/mix.rs @@ -33,9 +33,9 @@ impl FloatMixTexture { _loc: &FileLoc, _arena: &Arena, ) -> Result { - let tex1 = params.get_float_texture("tex1", 0.); - let tex2 = params.get_float_texture("tex2", 1.); - let amount = params.get_float_texture("amount", 0.5); + let tex1 = params.get_float_texture("tex1", 0.)?; + let tex2 = params.get_float_texture("tex2", 1.)?; + let amount = params.get_float_texture("amount", 0.5)?; // arena.alloc(Self::new(tex1, tex2, amount)); Ok(FloatTexture::Mix(Self::new(tex1, tex2, amount))) } @@ -74,10 +74,10 @@ impl FloatDirectionMixTexture { _loc: &FileLoc, _arena: &Arena, ) -> Result { - let dir_raw = params.get_one_vector3f("dir", Vector3f::new(0., 1., 0.)); + let dir_raw = params.get_one_vector3f("dir", Vector3f::new(0., 1., 0.))?; let dir = render_from_texture.apply_to_vector(dir_raw).normalize(); - let tex1 = params.get_float_texture("tex1", 0.); - let tex2 = params.get_float_texture("tex2", 1.); + let tex1 = params.get_float_texture("tex1", 0.)?; + let tex2 = params.get_float_texture("tex2", 1.)?; // arena.alloc(Self::new(tex1, tex2, dir)) Ok(FloatTexture::DirectionMix(Self::new(tex1, tex2, dir))) } diff --git a/src/textures/scaled.rs b/src/textures/scaled.rs index fa2e217..239f085 100644 --- a/src/textures/scaled.rs +++ b/src/textures/scaled.rs @@ -26,8 +26,8 @@ impl FloatScaledTexture { _loc: &FileLoc, _arena: &Arena, ) -> Result { - let mut tex = params.get_float_texture("tex", 1.); - let mut scale = params.get_float_texture("scale", 1.); + let mut tex = params.get_float_texture("tex", 1.)?; + let mut scale = params.get_float_texture("scale", 1.)?; for _ in 0..2 { if let FloatTexture::Constant(c_tex) = &*scale { diff --git a/src/utils/arena.rs b/src/utils/arena.rs index 42c2e84..a65be97 100644 --- a/src/utils/arena.rs +++ b/src/utils/arena.rs @@ -2,6 +2,7 @@ use crate::core::image::Image; use crate::core::texture::{FloatTexture, SpectrumTexture}; use crate::shapes::{BilinearPatchMesh, TriangleMesh}; use crate::spectra::DenselySampledSpectrumBuffer; +use crate::utils::backend::GpuAllocator; use crate::utils::mipmap::MIPMap; use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D}; use parking_lot::Mutex; @@ -24,7 +25,8 @@ use std::collections::HashMap; use std::slice::from_raw_parts; use std::sync::Arc; -pub struct Arena { +pub struct Arena { + allocator: A, inner: Mutex, } @@ -33,9 +35,10 @@ struct ArenaInner { texture_cache: HashMap, } -impl Arena { - pub fn new() -> Self { +impl Arena { + pub fn new(allocator: A) -> Self { Self { + allocator, inner: Mutex::new(ArenaInner { buffer: Vec::new(), texture_cache: HashMap::new(), @@ -45,12 +48,10 @@ impl Arena { pub fn alloc(&self, value: T) -> Ptr { let layout = Layout::new::(); - let ptr = unsafe { self.alloc_unified(layout) } as *mut T; - - unsafe { - ptr.write(value); - } + let ptr = unsafe { self.allocator.alloc(layout) } as *mut T; + unsafe { ptr.write(value) }; + self.inner.lock().buffer.push((ptr as *mut u8, layout)); Ptr::from_raw(ptr) } @@ -67,44 +68,13 @@ impl Arena { } let layout = Layout::array::(values.len()).unwrap(); - let ptr = unsafe { self.alloc_unified(layout) } as *mut T; - - unsafe { - std::ptr::copy_nonoverlapping(values.as_ptr(), ptr, values.len()); - } + let ptr = unsafe { self.allocator.alloc(layout) } as *mut T; + unsafe { std::ptr::copy_nonoverlapping(values.as_ptr(), ptr, values.len()) }; + self.inner.lock().buffer.push((ptr as *mut u8, layout)); (Ptr::from_raw(ptr), values.len()) } - #[cfg(feature = "cuda")] - unsafe fn alloc_unified(&self, layout: Layout) -> *mut u8 { - use cust::memory::{UnifiedPointer, cuda_free_unified, cuda_malloc_unified}; - - let size = layout.size().max(layout.align()); - if size == 0 { - return layout.align() as *mut u8; - } - - let mut unified_ptr = - unsafe { cuda_malloc_unified::(size).expect("cuda_malloc_unified failed") }; - let raw = unified_ptr.as_raw_mut(); - - let mut inner = self.inner.lock(); - inner.buffer.push((raw, layout)); - raw - } - - #[cfg(not(feature = "cuda"))] - unsafe fn alloc_unified(&self, layout: Layout) -> *mut u8 { - if layout.size() == 0 { - return layout.align() as *mut u8; - } - let ptr = unsafe { std::alloc::alloc(layout) }; - let mut inner = self.inner.lock(); - inner.buffer.push((ptr, layout)); - ptr - } - pub fn get_texture_object(&self, mipmap: &Arc) -> u64 { let key = Arc::as_ptr(mipmap) as usize; let mut inner = self.inner.lock(); @@ -113,107 +83,85 @@ impl Arena { return tex_obj; } - #[cfg(feature = "cuda")] - let tex_obj = self.create_cuda_texture(mipmap); - - #[cfg(not(feature = "cuda"))] + // TODO: Backend-specific texture object creation. + // CUDA: cudaCreateTextureObject + // Vulkan: VkImageView + VkSampler -> descriptor index let tex_obj = 0u64; inner.texture_cache.insert(key, tex_obj); tex_obj } +} - #[cfg(feature = "cuda")] - fn create_cuda_texture(&self, mipmap: &MIPMap) -> u64 { - // TODO: Implement with cudarc - // 1. Get image data from mipmap.base_image() - // 2. cudaMallocArray - // 3. cudaMemcpy2DToArray - // 4. cudaCreateTextureObject - // 5. Return handle - 0 +impl Default for Arena { + fn default() -> Self { + Self::new(A::default()) } } -impl Drop for Arena { +impl Drop for Arena { fn drop(&mut self) { let inner = self.inner.get_mut(); for (ptr, layout) in inner.buffer.drain(..) { - if layout.size() == 0 { - continue; - } - - unsafe { - #[cfg(feature = "cuda")] - { - use cust::memory::{UnifiedPointer, cuda_free_unified}; - if layout.size() > 0 { - let _ = cuda_free_unified(UnifiedPointer::wrap(ptr as *mut u8)); - } - } - #[cfg(not(feature = "cuda"))] - { - std::alloc::dealloc(ptr, layout); - } - } + unsafe { self.allocator.dealloc(ptr, layout) }; } } } -unsafe impl Send for Arena {} -unsafe impl Sync for Arena {} +unsafe impl Send for Arena {} +unsafe impl Sync for Arena {} pub trait Upload { type Target: Copy; - fn upload(&self, arena: &Arena) -> Ptr; + fn upload(&self, arena: &Arena) -> Ptr; } impl Upload for Shape { type Target = Shape; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { arena.alloc(self.clone()) } } impl Upload for Light { type Target = Light; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { arena.alloc(self.clone()) } } impl Upload for Image { type Target = DeviceImage; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { arena.alloc(*self.device()) } } impl Upload for Spectrum { type Target = Spectrum; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { arena.alloc(self.clone()) } } impl Upload for Material { type Target = Material; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { arena.alloc(self.clone()) } } impl Upload for DenselySampledSpectrumBuffer { type Target = DenselySampledSpectrum; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { arena.alloc(*&self.device()) } } impl Upload for SpectrumTexture { type Target = GPUSpectrumTexture; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { let gpu_variant = match self { SpectrumTexture::Constant(tex) => GPUSpectrumTexture::Constant(tex.clone()), SpectrumTexture::Checkerboard(tex) => GPUSpectrumTexture::Checkerboard(tex.clone()), @@ -241,15 +189,6 @@ impl Upload for SpectrumTexture { }; GPUSpectrumTexture::Scaled(gpu_scaled) } - // SpectrumTexture::Ptex(tex) => { - // let gpu_ptex = GPUSpectrumPtexTexture::new( - // tex.base.filename, - // tex.base.encoding, - // tex.base.scale, - // tex.spectrum_type, - // ); - // GPUSpectrumTexture::Ptex(gpu_ptex) - // } SpectrumTexture::Marble(tex) => GPUSpectrumTexture::Marble(tex.clone()), SpectrumTexture::Mix(tex) => { let tex1_ptr = tex.tex1.upload(arena); @@ -284,7 +223,7 @@ impl Upload for SpectrumTexture { impl Upload for FloatTexture { type Target = GPUFloatTexture; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { let gpu_variant = match self { FloatTexture::Constant(tex) => GPUFloatTexture::Constant(tex.clone()), FloatTexture::Checkerboard(tex) => GPUFloatTexture::Checkerboard(tex.clone()), @@ -346,7 +285,7 @@ impl Upload for FloatTexture { impl Upload for RGBToSpectrumTable { type Target = RGBToSpectrumTable; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { let n_nodes = self.n_nodes as usize; let z_slice = unsafe { from_raw_parts(self.z_nodes.as_raw(), n_nodes) }; let coeffs_slice = unsafe { from_raw_parts(self.coeffs.as_raw(), n_nodes) }; @@ -366,7 +305,7 @@ impl Upload for RGBToSpectrumTable { impl Upload for RGBColorSpace { type Target = RGBColorSpace; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { let table_ptr = self.rgb_to_spectrum_table.upload(arena); let shared_space = RGBColorSpace { @@ -387,7 +326,7 @@ impl Upload for RGBColorSpace { impl Upload for DeviceStandardColorSpaces { type Target = DeviceStandardColorSpaces; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { let srgb_ptr = self.srgb.upload(arena); let dci_ptr = self.dci_p3.upload(arena); let rec_ptr = self.rec2020.upload(arena); @@ -407,7 +346,7 @@ impl Upload for DeviceStandardColorSpaces { impl Upload for PiecewiseConstant2D { type Target = DevicePiecewiseConstant2D; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { let marginal_shared = self.marginal.to_shared(arena); let conditionals_shared: Vec = self @@ -430,7 +369,7 @@ impl Upload for PiecewiseConstant2D { impl Upload for WindowedPiecewiseConstant2D { type Target = DeviceWindowedPiecewiseConstant2D; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { let specific = DeviceWindowedPiecewiseConstant2D { sat: self.sat, func: self.func, @@ -442,7 +381,7 @@ impl Upload for WindowedPiecewiseConstant2D { impl Upload for TriangleMesh { type Target = DeviceTriangleMesh; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { let storage = &self.storage; // Upload all arrays to arena @@ -490,7 +429,7 @@ impl Upload for TriangleMesh { impl Upload for BilinearPatchMesh { type Target = DeviceBilinearPatchMesh; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { let storage = &self.storage; let (vertex_indices_ptr, _) = arena.alloc_slice(&storage.vertex_indices); @@ -525,7 +464,7 @@ impl Upload for BilinearPatchMesh { impl Upload for Option { type Target = T::Target; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { match self { Some(val) => val.upload(arena), None => Ptr::null(), @@ -536,7 +475,7 @@ impl Upload for Option { impl Upload for Arc { type Target = T::Target; - fn upload(&self, arena: &Arena) -> Ptr { + fn upload(&self, arena: &Arena) -> Ptr { (**self).upload(arena) } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 79c4955..88da7a8 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,4 +1,5 @@ pub mod arena; +pub mod backend; pub mod containers; pub mod error; pub mod file; @@ -11,10 +12,19 @@ pub mod parser; pub mod sampling; pub mod strings; -pub use arena::{Arena, Upload}; +pub use arena::Upload; pub use error::FileLoc; pub use file::{read_float_file, resolve_filename}; pub use parameters::{ ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary, }; pub use strings::*; + +#[cfg(feature = "vulkan")] +pub type Arena = arena::Arena; + +#[cfg(all(feature = "cuda", not(feature = "vulkan")))] +pub type Arena = arena::Arena; + +#[cfg(not(any(feature = "cuda", feature = "vulkan")))] +pub type Arena = arena::Arena; diff --git a/src/utils/parameters.rs b/src/utils/parameters.rs index f057d9c..d9b07b1 100644 --- a/src/utils/parameters.rs +++ b/src/utils/parameters.rs @@ -3,6 +3,7 @@ use crate::core::texture::{FloatTexture, SpectrumTexture}; use crate::spectra::data::get_named_spectrum; use crate::spectra::piecewise::PiecewiseLinearSpectrumBuffer; use crate::utils::FileLoc; +use anyhow::{Result, bail}; use shared::Float; use shared::core::color::RGB; use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector2f, Vector3f}; @@ -19,14 +20,12 @@ use std::sync::{ atomic::{AtomicBool, Ordering}, }; -pub fn error_exit(loc: Option<&FileLoc>, message: &str) -> ! { +pub fn error_exit(loc: Option<&FileLoc>, message: &str) -> String { if let Some(l) = loc { - log::error!("Error: {}: {}", l, message); + format!("{}: {}", l, message) } else { - log::error!("Error: {}", message); + message.to_string() } - - std::process::exit(1); } #[derive(Debug)] @@ -250,7 +249,10 @@ pub struct ParameterDictionary { } impl ParameterDictionary { - pub fn new(mut params: ParsedParameterVector, color_space: Option>) -> Self { + pub fn new( + mut params: ParsedParameterVector, + color_space: Option>, + ) -> Result { let n_owned_params = params.len(); params.reverse(); let dict = Self { @@ -258,15 +260,15 @@ impl ParameterDictionary { color_space, n_owned_params, }; - dict.check_parameter_types(); - dict + dict.check_parameter_types()?; + Ok(dict) } pub fn from_array( mut p0: ParsedParameterVector, params: &[ParsedParameter], color_space: Option>, - ) -> Self { + ) -> Result { let n_owned_params = params.len(); p0.extend(params.iter().rev().cloned()); @@ -275,21 +277,19 @@ impl ParameterDictionary { color_space, n_owned_params, }; - dict.check_parameter_types(); - dict + dict.check_parameter_types()?; + Ok(dict) } - fn check_parameter_types(&self) { + fn check_parameter_types(&self) -> Result<()> { for p in &self.params { match p.type_name.as_str() { bool::TYPE_NAME => { if p.bools.is_empty() { - error_exit( - Some(&p.loc), - &format!( - "\"{}\": non-Boolean values provided for Boolean-valued parameter", - p.name - ), + bail!( + "{}: non-Boolean values provided for Boolean-valued parameter \"{}\"", + &p.loc, + p.name ); } } @@ -304,51 +304,49 @@ impl ParameterDictionary { | "rgb" | "blackbody" => { if p.ints.is_empty() && p.floats.is_empty() { - error_exit( - Some(&p.loc), - &format!( - "\"{}\": non-numeric values provided for numeric-valued parameter", - p.name - ), + bail!( + "{}: non-numeric values provided for numeric-valued parameter \"{}\"", + &p.loc, + p.name ); } } String::TYPE_NAME | "texture" => { if p.strings.is_empty() { - error_exit( - Some(&p.loc), - &format!( - "\"{}\": non-string values provided for string-valued parameter", - p.name - ), + bail!( + "{}: non-string values provided for string-valued parameter \"{}\"", + &p.loc, + p.name ); } } "spectrum" => { if p.strings.is_empty() && p.ints.is_empty() && p.floats.is_empty() { - error_exit( - Some(&p.loc), - &format!( - "\"{}\": expecting string or numeric-valued parameter for spectrum parameter", - p.name - ), + bail!( + "{}: expecting string or numeric-valued parameter for spectrum parameter \"{}\"", + &p.loc, + p.name ); } } unknown => { - error_exit( - Some(&p.loc), - &format!("\"{}\": unknown parameter type '{}'", p.name, unknown), + bail!( + "{}: unknown parameter type \"{}\" '{}'", + &p.loc, + p.name, + unknown, ); } } } + + Ok(()) } - fn lookup_single(&self, name: &str, default_val: T) -> T + fn lookup_single(&self, name: &str, default_val: T) -> Result where T: PBRTParameter, { @@ -357,32 +355,31 @@ impl ParameterDictionary { let values = T::get_values(¶m); if values.is_empty() { - error_exit( - Some(¶m.loc), - &format!("No values provided for parameter \"{}\".", name), + bail!( + "{}: No values provided for parameter \"{}\".", + ¶m.loc, + name ); } if values.len() != T::N_PER_ITEM { - error_exit( - Some(¶m.loc), - &format!( - "Expected {} values for parameter \"{}\". Found {}.", - T::N_PER_ITEM, - name, - values.len() - ), + bail!( + "{}: Expected {} values for parameter \"{}\". Found {}.", + ¶m.loc, + T::N_PER_ITEM, + name, + values.len() ); } param.looked_up.store(true, Ordering::Relaxed); - return T::convert(values); + return Ok(T::convert(values)); } - default_val + Ok(default_val) } - pub fn lookup_array(&self, name: &str) -> Vec + pub fn lookup_array(&self, name: &str) -> Result> where T: PBRTParameter, { @@ -391,92 +388,90 @@ impl ParameterDictionary { let values = T::get_values(param); if values.len() % T::N_PER_ITEM != 0 { - error_exit( - Some(¶m.loc), - &format!( - "Number of values for \"{}\" is not a multiple of {}", - name, - T::N_PER_ITEM - ), + bail!( + "{}: Number of values for \"{}\" is not a multiple of {}", + ¶m.loc, + name, + T::N_PER_ITEM ); } param.looked_up.store(true, Ordering::Relaxed); - return values + return Ok(values .chunks(T::N_PER_ITEM) .map(|chunk| T::convert(chunk)) - .collect(); + .collect()); } } - Vec::new() + Ok(Vec::new()) } - pub fn get_one_float(&self, name: &str, def: Float) -> Float { + pub fn get_one_float(&self, name: &str, def: Float) -> Result { self.lookup_single(name, def) } - pub fn get_one_int(&self, name: &str, def: i32) -> i32 { + pub fn get_one_int(&self, name: &str, def: i32) -> Result { self.lookup_single(name, def) } - pub fn get_one_bool(&self, name: &str, def: bool) -> bool { + pub fn get_one_bool(&self, name: &str, def: bool) -> Result { self.lookup_single(name, def) } - pub fn get_one_string(&self, name: &str, def: &str) -> String { + pub fn get_one_string(&self, name: &str, def: &str) -> Result { self.lookup_single(name, def.to_string()) } - pub fn get_one_point2f(&self, name: &str, def: Point2f) -> Point2f { + pub fn get_one_point2f(&self, name: &str, def: Point2f) -> Result { self.lookup_single(name, def) } - pub fn get_one_point3f(&self, name: &str, def: Point3f) -> Point3f { + pub fn get_one_point3f(&self, name: &str, def: Point3f) -> Result { self.lookup_single(name, def) } - pub fn get_one_vector2f(&self, name: &str, def: Vector2f) -> Vector2f { + pub fn get_one_vector2f(&self, name: &str, def: Vector2f) -> Result { self.lookup_single(name, def) } - pub fn get_one_vector3f(&self, name: &str, def: Vector3f) -> Vector3f { + pub fn get_one_vector3f(&self, name: &str, def: Vector3f) -> Result { self.lookup_single(name, def) } - pub fn get_one_normal3f(&self, name: &str, def: Normal3f) -> Normal3f { + pub fn get_one_normal3f(&self, name: &str, def: Normal3f) -> Result { self.lookup_single(name, def) } - pub fn get_float_array(&self, name: &str) -> Vec { + pub fn get_float_array(&self, name: &str) -> Result> { self.lookup_array(name) } - pub fn get_int_array(&self, name: &str) -> Vec { + pub fn get_int_array(&self, name: &str) -> Result> { self.lookup_array(name) } - pub fn get_bool_array(&self, name: &str) -> Vec { + pub fn get_bool_array(&self, name: &str) -> Result> { self.lookup_array(name) } - pub fn get_string_array(&self, name: &str) -> Vec { + pub fn get_string_array(&self, name: &str) -> Result> { self.lookup_array(name) } - pub fn get_point2f_array(&self, name: &str) -> Vec { + pub fn get_point2f_array(&self, name: &str) -> Result> { self.lookup_array(name) } - pub fn get_point3f_array(&self, name: &str) -> Vec { + pub fn get_point3f_array(&self, name: &str) -> Result> { self.lookup_array(name) } - pub fn get_vector3f_array(&self, name: &str) -> Vec { + pub fn get_vector3f_array(&self, name: &str) -> Result> { self.lookup_array(name) } - pub fn get_normal3f_array(&self, name: &str) -> Vec { + pub fn get_normal3f_array(&self, name: &str) -> Result> { self.lookup_array(name) } @@ -709,70 +704,73 @@ pub struct TextureParameterDictionary { impl TextureParameterDictionary { pub fn new(dict: Arc, textures: Option<&NamedTextures>) -> Self { - Self { dict, textures: textures.cloned() } + Self { + dict, + textures: textures.cloned(), + } } - pub fn get_one_float(&self, name: &str, def: Float) -> Float { + pub fn get_one_float(&self, name: &str, def: Float) -> Result { self.dict.get_one_float(name, def) } - pub fn get_one_int(&self, name: &str, def: i32) -> i32 { + pub fn get_one_int(&self, name: &str, def: i32) -> Result { self.dict.get_one_int(name, def) } - pub fn get_one_bool(&self, name: &str, def: bool) -> bool { + pub fn get_one_bool(&self, name: &str, def: bool) -> Result { self.dict.get_one_bool(name, def) } - pub fn get_one_string(&self, name: &str, def: &str) -> String { + pub fn get_one_string(&self, name: &str, def: &str) -> Result { self.dict.get_one_string(name, def) } - pub fn get_float_array(&self, name: &str) -> Vec { + pub fn get_float_array(&self, name: &str) -> Result> { self.dict.get_float_array(name) } - pub fn get_int_array(&self, name: &str) -> Vec { + pub fn get_int_array(&self, name: &str) -> Result> { self.dict.get_int_array(name) } - pub fn get_bool_array(&self, name: &str) -> Vec { + pub fn get_bool_array(&self, name: &str) -> Result> { self.dict.get_bool_array(name) } - pub fn get_string_array(&self, name: &str) -> Vec { + pub fn get_string_array(&self, name: &str) -> Result> { self.dict.get_string_array(name) } - pub fn get_one_point3f(&self, name: &str, def: Point3f) -> Point3f { + pub fn get_one_point3f(&self, name: &str, def: Point3f) -> Result { self.dict.get_one_point3f(name, def) } - pub fn get_one_vector3f(&self, name: &str, def: Vector3f) -> Vector3f { + pub fn get_one_vector3f(&self, name: &str, def: Vector3f) -> Result { self.dict.get_one_vector3f(name, def) } - pub fn get_one_normal3f(&self, name: &str, def: Normal3f) -> Normal3f { + pub fn get_one_normal3f(&self, name: &str, def: Normal3f) -> Result { self.dict.get_one_normal3f(name, def) } - pub fn get_point3f_array(&self, name: &str) -> Vec { + pub fn get_point3f_array(&self, name: &str) -> Result> { self.dict.get_point3f_array(name) } - pub fn get_vector3f_array(&self, name: &str) -> Vec { + pub fn get_vector3f_array(&self, name: &str) -> Result> { self.dict.get_vector3f_array(name) } - pub fn get_normal3f_array(&self, name: &str) -> Vec { + pub fn get_normal3f_array(&self, name: &str) -> Result> { self.dict.get_normal3f_array(name) } - pub fn get_one_point2f(&self, name: &str, def: Point2f) -> Point2f { + pub fn get_one_point2f(&self, name: &str, def: Point2f) -> Result { self.dict.get_one_point2f(name, def) } - pub fn get_one_vector2f(&self, name: &str, def: Vector2f) -> Vector2f { + pub fn get_one_vector2f(&self, name: &str, def: Vector2f) -> Result { self.dict.get_one_vector2f(name, def) } @@ -815,11 +813,25 @@ impl TextureParameterDictionary { } } - pub fn get_float_texture(&self, name: &str, val: Float) -> Arc { - if let Some(tex) = self.get_float_texture_or_null(name) { - return tex; + pub fn get_float_texture(&self, name: &str, val: Float) -> Result> { + if let Some(tex) = self.get_float_texture_or_null(name)? { + return Ok(tex); } else { - return Arc::new(FloatTexture::Constant(FloatConstantTexture::new(val))); + return Ok(Arc::new(FloatTexture::Constant(FloatConstantTexture::new( + val, + )))); + } + } + + pub fn get_float_texture_with_fallback( + &self, + primary: &str, + fallback: &str, + default: Float, + ) -> Result> { + match self.get_float_texture_or_null(primary)? { + Some(tex) => Ok(tex), + None => self.get_float_texture(fallback, default), } } @@ -909,7 +921,7 @@ impl TextureParameterDictionary { return None; } - pub fn get_float_texture_or_null(&self, name: &str) -> Option> { + pub fn get_float_texture_or_null(&self, name: &str) -> Result>> { for p in &self.dict.params { if p.name != name { continue; @@ -932,19 +944,19 @@ impl TextureParameterDictionary { if let Some(nt) = &self.textures { let map = &nt.float_textures; if let Some(tex) = map.get(tex_name) { - return Some(Arc::clone(tex)); + return Ok(Some(Arc::clone(tex))); } panic!( "[{:?}] Couldn't find float texture named '{}'", p.loc, tex_name ); } - return None; + return Ok(None); } "float" => { - let v = self.get_one_float(name, 0.); - return Some(Arc::new(FloatTexture::Constant(FloatConstantTexture::new( - v, + let v = self.get_one_float(name, 0.)?; + return Ok(Some(Arc::new(FloatTexture::Constant( + FloatConstantTexture::new(v), )))); } _ => { @@ -952,6 +964,6 @@ impl TextureParameterDictionary { } } } - return None; + Ok(None) } } diff --git a/src/utils/parser.rs b/src/utils/parser.rs index ec6b352..81d35de 100644 --- a/src/utils/parser.rs +++ b/src/utils/parser.rs @@ -744,11 +744,12 @@ impl<'a> SceneParser<'a> { } pub fn run(&mut self) -> Result<(), ParserError> { - let arena = Arc::new(Arena::new()); + let arena = Arc::new(Arena::default()); loop { let token = match self.next_token()? { Some(t) => t, - None => break, // EOF + // EOF + None => break, }; let first_char = token.text.chars().next().unwrap(); diff --git a/src/utils/sampling.rs b/src/utils/sampling.rs index 0beb9d1..7057b6b 100644 --- a/src/utils/sampling.rs +++ b/src/utils/sampling.rs @@ -1,5 +1,6 @@ use crate::core::image::Image; -use crate::utils::Arena; +use crate::utils::arena::Arena; +use crate::utils::backend::GpuAllocator; use crate::utils::containers::Array2D; use shared::Float; use shared::core::geometry::{Bounds2f, Point2i, Vector2f, Vector2i}; @@ -23,7 +24,7 @@ impl PiecewiseConstant1D { Self::new_with_bounds(f.to_vec(), 0.0, 1.0) } - pub fn to_shared(&self, arena: &Arena) -> DevicePiecewiseConstant1D { + pub fn to_shared(&self, arena: &Arena) -> DevicePiecewiseConstant1D { let (func_ptr, _) = arena.alloc_slice(&self.func); let (cdf_ptr, _) = arena.alloc_slice(&self.cdf);